| File: | out/../deps/v8/src/base/bit-field.h |
| Warning: | line 56, column 34 The result of the '<<' expression is undefined |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | // Copyright 2016 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 | #include "src/codegen/code-stub-assembler.h" | |||
| 6 | ||||
| 7 | #include <functional> | |||
| 8 | ||||
| 9 | #include "include/v8-internal.h" | |||
| 10 | #include "src/base/macros.h" | |||
| 11 | #include "src/codegen/code-factory.h" | |||
| 12 | #include "src/codegen/tnode.h" | |||
| 13 | #include "src/common/globals.h" | |||
| 14 | #include "src/execution/frames-inl.h" | |||
| 15 | #include "src/execution/frames.h" | |||
| 16 | #include "src/execution/protectors.h" | |||
| 17 | #include "src/heap/heap-inl.h" // For MemoryChunk. TODO(jkummerow): Drop. | |||
| 18 | #include "src/heap/memory-chunk.h" | |||
| 19 | #include "src/logging/counters.h" | |||
| 20 | #include "src/numbers/integer-literal-inl.h" | |||
| 21 | #include "src/objects/api-callbacks.h" | |||
| 22 | #include "src/objects/cell.h" | |||
| 23 | #include "src/objects/descriptor-array.h" | |||
| 24 | #include "src/objects/function-kind.h" | |||
| 25 | #include "src/objects/heap-number.h" | |||
| 26 | #include "src/objects/instance-type.h" | |||
| 27 | #include "src/objects/js-generator.h" | |||
| 28 | #include "src/objects/oddball.h" | |||
| 29 | #include "src/objects/ordered-hash-table-inl.h" | |||
| 30 | #include "src/objects/property-cell.h" | |||
| 31 | #include "src/roots/roots.h" | |||
| 32 | ||||
| 33 | #if V8_ENABLE_WEBASSEMBLY1 | |||
| 34 | #include "src/wasm/wasm-objects.h" | |||
| 35 | #endif // V8_ENABLE_WEBASSEMBLY | |||
| 36 | ||||
| 37 | namespace v8 { | |||
| 38 | namespace internal { | |||
| 39 | ||||
| 40 | CodeStubAssembler::CodeStubAssembler(compiler::CodeAssemblerState* state) | |||
| 41 | : compiler::CodeAssembler(state), | |||
| 42 | TorqueGeneratedExportedMacrosAssembler(state) { | |||
| 43 | if (DEBUG_BOOLfalse && FLAG_csa_trap_on_node != nullptr) { | |||
| 44 | HandleBreakOnNode(); | |||
| 45 | } | |||
| 46 | } | |||
| 47 | ||||
| 48 | void CodeStubAssembler::HandleBreakOnNode() { | |||
| 49 | // FLAG_csa_trap_on_node should be in a form "STUB,NODE" where STUB is a | |||
| 50 | // string specifying the name of a stub and NODE is number specifying node id. | |||
| 51 | const char* name = state()->name(); | |||
| 52 | size_t name_length = strlen(name); | |||
| 53 | if (strncmp(FLAG_csa_trap_on_node, name, name_length) != 0) { | |||
| 54 | // Different name. | |||
| 55 | return; | |||
| 56 | } | |||
| 57 | size_t option_length = strlen(FLAG_csa_trap_on_node); | |||
| 58 | if (option_length < name_length + 2 || | |||
| 59 | FLAG_csa_trap_on_node[name_length] != ',') { | |||
| 60 | // Option is too short. | |||
| 61 | return; | |||
| 62 | } | |||
| 63 | const char* start = &FLAG_csa_trap_on_node[name_length + 1]; | |||
| 64 | char* end; | |||
| 65 | int node_id = static_cast<int>(strtol(start, &end, 10)); | |||
| 66 | if (start == end) { | |||
| 67 | // Bad node id. | |||
| 68 | return; | |||
| 69 | } | |||
| 70 | BreakOnNode(node_id); | |||
| 71 | } | |||
| 72 | ||||
| 73 | void CodeStubAssembler::Dcheck(const BranchGenerator& branch, | |||
| 74 | const char* message, const char* file, int line, | |||
| 75 | std::initializer_list<ExtraNode> extra_nodes) { | |||
| 76 | #if defined(DEBUG) | |||
| 77 | if (FLAG_debug_code) { | |||
| 78 | Check(branch, message, file, line, extra_nodes); | |||
| 79 | } | |||
| 80 | #endif | |||
| 81 | } | |||
| 82 | ||||
| 83 | void CodeStubAssembler::Dcheck(const NodeGenerator<BoolT>& condition_body, | |||
| 84 | const char* message, const char* file, int line, | |||
| 85 | std::initializer_list<ExtraNode> extra_nodes) { | |||
| 86 | #if defined(DEBUG) | |||
| 87 | if (FLAG_debug_code) { | |||
| 88 | Check(condition_body, message, file, line, extra_nodes); | |||
| 89 | } | |||
| 90 | #endif | |||
| 91 | } | |||
| 92 | ||||
| 93 | void CodeStubAssembler::Dcheck(TNode<Word32T> condition_node, | |||
| 94 | const char* message, const char* file, int line, | |||
| 95 | std::initializer_list<ExtraNode> extra_nodes) { | |||
| 96 | #if defined(DEBUG) | |||
| 97 | if (FLAG_debug_code) { | |||
| 98 | Check(condition_node, message, file, line, extra_nodes); | |||
| 99 | } | |||
| 100 | #endif | |||
| 101 | } | |||
| 102 | ||||
| 103 | void CodeStubAssembler::Check(const BranchGenerator& branch, | |||
| 104 | const char* message, const char* file, int line, | |||
| 105 | std::initializer_list<ExtraNode> extra_nodes) { | |||
| 106 | Label ok(this); | |||
| 107 | Label not_ok(this, Label::kDeferred); | |||
| 108 | if (message != nullptr) { | |||
| 109 | Comment("[ Assert: ", message); | |||
| 110 | } else { | |||
| 111 | Comment("[ Assert"); | |||
| 112 | } | |||
| 113 | branch(&ok, ¬_ok); | |||
| 114 | ||||
| 115 | BIND(¬_ok)Bind(¬_ok); | |||
| 116 | std::vector<FileAndLine> file_and_line; | |||
| 117 | if (file != nullptr) { | |||
| 118 | file_and_line.push_back({file, line}); | |||
| 119 | } | |||
| 120 | FailAssert(message, file_and_line, extra_nodes); | |||
| 121 | ||||
| 122 | BIND(&ok)Bind(&ok); | |||
| 123 | Comment("] Assert"); | |||
| 124 | } | |||
| 125 | ||||
| 126 | void CodeStubAssembler::Check(const NodeGenerator<BoolT>& condition_body, | |||
| 127 | const char* message, const char* file, int line, | |||
| 128 | std::initializer_list<ExtraNode> extra_nodes) { | |||
| 129 | BranchGenerator branch = [=](Label* ok, Label* not_ok) { | |||
| 130 | TNode<BoolT> condition = condition_body(); | |||
| 131 | Branch(condition, ok, not_ok); | |||
| 132 | }; | |||
| 133 | ||||
| 134 | Check(branch, message, file, line, extra_nodes); | |||
| 135 | } | |||
| 136 | ||||
| 137 | void CodeStubAssembler::Check(TNode<Word32T> condition_node, | |||
| 138 | const char* message, const char* file, int line, | |||
| 139 | std::initializer_list<ExtraNode> extra_nodes) { | |||
| 140 | BranchGenerator branch = [=](Label* ok, Label* not_ok) { | |||
| 141 | Branch(condition_node, ok, not_ok); | |||
| 142 | }; | |||
| 143 | ||||
| 144 | Check(branch, message, file, line, extra_nodes); | |||
| 145 | } | |||
| 146 | ||||
| 147 | void CodeStubAssembler::IncrementCallCount( | |||
| 148 | TNode<FeedbackVector> feedback_vector, TNode<UintPtrT> slot_id) { | |||
| 149 | Comment("increment call count"); | |||
| 150 | TNode<Smi> call_count = | |||
| 151 | CAST(LoadFeedbackVectorSlot(feedback_vector, slot_id, kTaggedSize))Cast(LoadFeedbackVectorSlot(feedback_vector, slot_id, kTaggedSize )); | |||
| 152 | // The lowest {FeedbackNexus::CallCountField::kShift} bits of the call | |||
| 153 | // count are used as flags. To increment the call count by 1 we hence | |||
| 154 | // have to increment by 1 << {FeedbackNexus::CallCountField::kShift}. | |||
| 155 | TNode<Smi> new_count = SmiAdd( | |||
| 156 | call_count, SmiConstant(1 << FeedbackNexus::CallCountField::kShift)); | |||
| 157 | // Count is Smi, so we don't need a write barrier. | |||
| 158 | StoreFeedbackVectorSlot(feedback_vector, slot_id, new_count, | |||
| 159 | SKIP_WRITE_BARRIER, kTaggedSize); | |||
| 160 | } | |||
| 161 | ||||
| 162 | void CodeStubAssembler::FastCheck(TNode<BoolT> condition) { | |||
| 163 | Label ok(this), not_ok(this, Label::kDeferred); | |||
| 164 | Branch(condition, &ok, ¬_ok); | |||
| 165 | BIND(¬_ok)Bind(¬_ok); | |||
| 166 | Unreachable(); | |||
| 167 | BIND(&ok)Bind(&ok); | |||
| 168 | } | |||
| 169 | ||||
| 170 | void CodeStubAssembler::FailAssert( | |||
| 171 | const char* message, const std::vector<FileAndLine>& files_and_lines, | |||
| 172 | std::initializer_list<ExtraNode> extra_nodes) { | |||
| 173 | DCHECK_NOT_NULL(message)((void) 0); | |||
| 174 | base::EmbeddedVector<char, 1024> chars; | |||
| 175 | std::stringstream stream; | |||
| 176 | for (auto it = files_and_lines.rbegin(); it != files_and_lines.rend(); ++it) { | |||
| 177 | if (it->first != nullptr) { | |||
| 178 | stream << " [" << it->first << ":" << it->second << "]"; | |||
| 179 | #ifndef DEBUG | |||
| 180 | // To limit the size of these strings in release builds, we include only | |||
| 181 | // the innermost macro's file name and line number. | |||
| 182 | break; | |||
| 183 | #endif | |||
| 184 | } | |||
| 185 | } | |||
| 186 | std::string files_and_lines_text = stream.str(); | |||
| 187 | if (files_and_lines_text.size() != 0) { | |||
| 188 | SNPrintF(chars, "%s%s", message, files_and_lines_text.c_str()); | |||
| 189 | message = chars.begin(); | |||
| 190 | } | |||
| 191 | TNode<String> message_node = StringConstant(message); | |||
| 192 | ||||
| 193 | #ifdef DEBUG | |||
| 194 | // Only print the extra nodes in debug builds. | |||
| 195 | for (auto& node : extra_nodes) { | |||
| 196 | CallRuntime(Runtime::kPrintWithNameForAssert, SmiConstant(0), | |||
| 197 | StringConstant(node.second), node.first); | |||
| 198 | } | |||
| 199 | #endif | |||
| 200 | ||||
| 201 | AbortCSADcheck(message_node); | |||
| 202 | Unreachable(); | |||
| 203 | } | |||
| 204 | ||||
| 205 | TNode<Int32T> CodeStubAssembler::SelectInt32Constant(TNode<BoolT> condition, | |||
| 206 | int true_value, | |||
| 207 | int false_value) { | |||
| 208 | return SelectConstant<Int32T>(condition, Int32Constant(true_value), | |||
| 209 | Int32Constant(false_value)); | |||
| 210 | } | |||
| 211 | ||||
| 212 | TNode<IntPtrT> CodeStubAssembler::SelectIntPtrConstant(TNode<BoolT> condition, | |||
| 213 | int true_value, | |||
| 214 | int false_value) { | |||
| 215 | return SelectConstant<IntPtrT>(condition, IntPtrConstant(true_value), | |||
| 216 | IntPtrConstant(false_value)); | |||
| 217 | } | |||
| 218 | ||||
| 219 | TNode<Oddball> CodeStubAssembler::SelectBooleanConstant( | |||
| 220 | TNode<BoolT> condition) { | |||
| 221 | return SelectConstant<Oddball>(condition, TrueConstant(), FalseConstant()); | |||
| 222 | } | |||
| 223 | ||||
| 224 | TNode<Smi> CodeStubAssembler::SelectSmiConstant(TNode<BoolT> condition, | |||
| 225 | Smi true_value, | |||
| 226 | Smi false_value) { | |||
| 227 | return SelectConstant<Smi>(condition, SmiConstant(true_value), | |||
| 228 | SmiConstant(false_value)); | |||
| 229 | } | |||
| 230 | ||||
| 231 | TNode<Smi> CodeStubAssembler::NoContextConstant() { | |||
| 232 | return SmiConstant(Context::kNoContext); | |||
| 233 | } | |||
| 234 | ||||
| 235 | #define HEAP_CONSTANT_ACCESSOR(rootIndexName, rootAccessorName, name) \ | |||
| 236 | TNode<std::remove_pointer<std::remove_reference<decltype( \ | |||
| 237 | std::declval<Heap>().rootAccessorName())>::type>::type> \ | |||
| 238 | CodeStubAssembler::name##Constant() { \ | |||
| 239 | return UncheckedCast<std::remove_pointer<std::remove_reference<decltype( \ | |||
| 240 | std::declval<Heap>().rootAccessorName())>::type>::type>( \ | |||
| 241 | LoadRoot(RootIndex::k##rootIndexName)); \ | |||
| 242 | } | |||
| 243 | HEAP_MUTABLE_IMMOVABLE_OBJECT_LIST(HEAP_CONSTANT_ACCESSOR)HEAP_CONSTANT_ACCESSOR(ArrayIteratorProtector, array_iterator_protector , ArrayIteratorProtector) HEAP_CONSTANT_ACCESSOR(ArraySpeciesProtector , array_species_protector, ArraySpeciesProtector) HEAP_CONSTANT_ACCESSOR (AsyncFunctionAwaitRejectSharedFun, async_function_await_reject_shared_fun , AsyncFunctionAwaitRejectSharedFun) HEAP_CONSTANT_ACCESSOR(AsyncFunctionAwaitResolveSharedFun , async_function_await_resolve_shared_fun, AsyncFunctionAwaitResolveSharedFun ) HEAP_CONSTANT_ACCESSOR(AsyncGeneratorAwaitRejectSharedFun, async_generator_await_reject_shared_fun , AsyncGeneratorAwaitRejectSharedFun) HEAP_CONSTANT_ACCESSOR( AsyncGeneratorAwaitResolveSharedFun, async_generator_await_resolve_shared_fun , AsyncGeneratorAwaitResolveSharedFun) HEAP_CONSTANT_ACCESSOR (AsyncGeneratorReturnClosedRejectSharedFun, async_generator_return_closed_reject_shared_fun , AsyncGeneratorReturnClosedRejectSharedFun) HEAP_CONSTANT_ACCESSOR (AsyncGeneratorReturnClosedResolveSharedFun, async_generator_return_closed_resolve_shared_fun , AsyncGeneratorReturnClosedResolveSharedFun) HEAP_CONSTANT_ACCESSOR (AsyncGeneratorReturnResolveSharedFun, async_generator_return_resolve_shared_fun , AsyncGeneratorReturnResolveSharedFun) HEAP_CONSTANT_ACCESSOR (AsyncGeneratorYieldResolveSharedFun, async_generator_yield_resolve_shared_fun , AsyncGeneratorYieldResolveSharedFun) HEAP_CONSTANT_ACCESSOR (AsyncIteratorValueUnwrapSharedFun, async_iterator_value_unwrap_shared_fun , AsyncIteratorValueUnwrapSharedFun) HEAP_CONSTANT_ACCESSOR(IsConcatSpreadableProtector , is_concat_spreadable_protector, IsConcatSpreadableProtector ) HEAP_CONSTANT_ACCESSOR(MapIteratorProtector, map_iterator_protector , MapIteratorProtector) HEAP_CONSTANT_ACCESSOR(NoElementsProtector , no_elements_protector, NoElementsProtector) HEAP_CONSTANT_ACCESSOR (MegaDOMProtector, mega_dom_protector, MegaDOMProtector) HEAP_CONSTANT_ACCESSOR (NumberStringCache, number_string_cache, NumberStringCache) HEAP_CONSTANT_ACCESSOR (PromiseAllResolveElementSharedFun, promise_all_resolve_element_shared_fun , PromiseAllResolveElementSharedFun) HEAP_CONSTANT_ACCESSOR(PromiseAllSettledRejectElementSharedFun , promise_all_settled_reject_element_shared_fun, PromiseAllSettledRejectElementSharedFun ) HEAP_CONSTANT_ACCESSOR(PromiseAllSettledResolveElementSharedFun , promise_all_settled_resolve_element_shared_fun, PromiseAllSettledResolveElementSharedFun ) HEAP_CONSTANT_ACCESSOR(PromiseAnyRejectElementSharedFun, promise_any_reject_element_shared_fun , PromiseAnyRejectElementSharedFun) HEAP_CONSTANT_ACCESSOR(PromiseCapabilityDefaultRejectSharedFun , promise_capability_default_reject_shared_fun, PromiseCapabilityDefaultRejectSharedFun ) HEAP_CONSTANT_ACCESSOR(PromiseCapabilityDefaultResolveSharedFun , promise_capability_default_resolve_shared_fun, PromiseCapabilityDefaultResolveSharedFun ) HEAP_CONSTANT_ACCESSOR(PromiseCatchFinallySharedFun, promise_catch_finally_shared_fun , PromiseCatchFinallySharedFun) HEAP_CONSTANT_ACCESSOR(PromiseGetCapabilitiesExecutorSharedFun , promise_get_capabilities_executor_shared_fun, PromiseGetCapabilitiesExecutorSharedFun ) HEAP_CONSTANT_ACCESSOR(PromiseResolveProtector, promise_resolve_protector , PromiseResolveProtector) HEAP_CONSTANT_ACCESSOR(PromiseSpeciesProtector , promise_species_protector, PromiseSpeciesProtector) HEAP_CONSTANT_ACCESSOR (PromiseThenFinallySharedFun, promise_then_finally_shared_fun , PromiseThenFinallySharedFun) HEAP_CONSTANT_ACCESSOR(PromiseThenProtector , promise_then_protector, PromiseThenProtector) HEAP_CONSTANT_ACCESSOR (PromiseThrowerFinallySharedFun, promise_thrower_finally_shared_fun , PromiseThrowerFinallySharedFun) HEAP_CONSTANT_ACCESSOR(PromiseValueThunkFinallySharedFun , promise_value_thunk_finally_shared_fun, PromiseValueThunkFinallySharedFun ) HEAP_CONSTANT_ACCESSOR(ProxyRevokeSharedFun, proxy_revoke_shared_fun , ProxyRevokeSharedFun) HEAP_CONSTANT_ACCESSOR(RegExpSpeciesProtector , regexp_species_protector, RegExpSpeciesProtector) HEAP_CONSTANT_ACCESSOR (SetIteratorProtector, set_iterator_protector, SetIteratorProtector ) HEAP_CONSTANT_ACCESSOR(SingleCharacterStringCache, single_character_string_cache , SingleCharacterStringCache) HEAP_CONSTANT_ACCESSOR(StringIteratorProtector , string_iterator_protector, StringIteratorProtector) HEAP_CONSTANT_ACCESSOR (TypedArraySpeciesProtector, typed_array_species_protector, TypedArraySpeciesProtector ) | |||
| 244 | #undef HEAP_CONSTANT_ACCESSOR | |||
| 245 | ||||
| 246 | #define HEAP_CONSTANT_ACCESSOR(rootIndexName, rootAccessorName, name) \ | |||
| 247 | TNode<std::remove_pointer<std::remove_reference<decltype( \ | |||
| 248 | std::declval<ReadOnlyRoots>().rootAccessorName())>::type>::type> \ | |||
| 249 | CodeStubAssembler::name##Constant() { \ | |||
| 250 | return UncheckedCast<std::remove_pointer<std::remove_reference<decltype( \ | |||
| 251 | std::declval<ReadOnlyRoots>().rootAccessorName())>::type>::type>( \ | |||
| 252 | LoadRoot(RootIndex::k##rootIndexName)); \ | |||
| 253 | } | |||
| 254 | HEAP_IMMUTABLE_IMMOVABLE_OBJECT_LIST(HEAP_CONSTANT_ACCESSOR)HEAP_CONSTANT_ACCESSOR(AllocationSiteWithoutWeakNextMap, allocation_site_without_weaknext_map , AllocationSiteWithoutWeakNextMap) HEAP_CONSTANT_ACCESSOR(AllocationSiteWithWeakNextMap , allocation_site_map, AllocationSiteMap) HEAP_CONSTANT_ACCESSOR (arguments_to_string, arguments_to_string, ArgumentsToString) HEAP_CONSTANT_ACCESSOR(Array_string, Array_string, ArrayString ) HEAP_CONSTANT_ACCESSOR(array_to_string, array_to_string, ArrayToString ) HEAP_CONSTANT_ACCESSOR(BooleanMap, boolean_map, BooleanMap) HEAP_CONSTANT_ACCESSOR(boolean_to_string, boolean_to_string, BooleanToString) HEAP_CONSTANT_ACCESSOR(ConsOneByteStringMap , cons_one_byte_string_map, ConsOneByteStringMap) HEAP_CONSTANT_ACCESSOR (ConsStringMap, cons_string_map, ConsStringMap) HEAP_CONSTANT_ACCESSOR (constructor_string, constructor_string, ConstructorString) HEAP_CONSTANT_ACCESSOR (date_to_string, date_to_string, DateToString) HEAP_CONSTANT_ACCESSOR (default_string, default_string, DefaultString) HEAP_CONSTANT_ACCESSOR (EmptyByteArray, empty_byte_array, EmptyByteArray) HEAP_CONSTANT_ACCESSOR (EmptyFixedArray, empty_fixed_array, EmptyFixedArray) HEAP_CONSTANT_ACCESSOR (EmptyScopeInfo, empty_scope_info, EmptyScopeInfo) HEAP_CONSTANT_ACCESSOR (EmptyPropertyDictionary, empty_property_dictionary, EmptyPropertyDictionary ) HEAP_CONSTANT_ACCESSOR(EmptyOrderedPropertyDictionary, empty_ordered_property_dictionary , EmptyOrderedPropertyDictionary) HEAP_CONSTANT_ACCESSOR(EmptySwissPropertyDictionary , empty_swiss_property_dictionary, EmptySwissPropertyDictionary ) HEAP_CONSTANT_ACCESSOR(EmptySlowElementDictionary, empty_slow_element_dictionary , EmptySlowElementDictionary) HEAP_CONSTANT_ACCESSOR(empty_string , empty_string, EmptyString) HEAP_CONSTANT_ACCESSOR(error_to_string , error_to_string, ErrorToString) HEAP_CONSTANT_ACCESSOR(errors_string , errors_string, ErrorsString) HEAP_CONSTANT_ACCESSOR(FalseValue , false_value, False) HEAP_CONSTANT_ACCESSOR(FixedArrayMap, fixed_array_map , FixedArrayMap) HEAP_CONSTANT_ACCESSOR(FixedCOWArrayMap, fixed_cow_array_map , FixedCOWArrayMap) HEAP_CONSTANT_ACCESSOR(Function_string, function_string , FunctionString) HEAP_CONSTANT_ACCESSOR(function_to_string, function_to_string , FunctionToString) HEAP_CONSTANT_ACCESSOR(GlobalPropertyCellMap , global_property_cell_map, PropertyCellMap) HEAP_CONSTANT_ACCESSOR (has_instance_symbol, has_instance_symbol, HasInstanceSymbol) HEAP_CONSTANT_ACCESSOR(Infinity_string, Infinity_string, InfinityString ) HEAP_CONSTANT_ACCESSOR(is_concat_spreadable_symbol, is_concat_spreadable_symbol , IsConcatSpreadableSymbol) HEAP_CONSTANT_ACCESSOR(iterator_symbol , iterator_symbol, IteratorSymbol) HEAP_CONSTANT_ACCESSOR(length_string , length_string, LengthString) HEAP_CONSTANT_ACCESSOR(ManyClosuresCellMap , many_closures_cell_map, ManyClosuresCellMap) HEAP_CONSTANT_ACCESSOR (match_symbol, match_symbol, MatchSymbol) HEAP_CONSTANT_ACCESSOR (megamorphic_symbol, megamorphic_symbol, MegamorphicSymbol) HEAP_CONSTANT_ACCESSOR (mega_dom_symbol, mega_dom_symbol, MegaDOMSymbol) HEAP_CONSTANT_ACCESSOR (message_string, message_string, MessageString) HEAP_CONSTANT_ACCESSOR (minus_Infinity_string, minus_Infinity_string, MinusInfinityString ) HEAP_CONSTANT_ACCESSOR(MinusZeroValue, minus_zero_value, MinusZero ) HEAP_CONSTANT_ACCESSOR(name_string, name_string, NameString ) HEAP_CONSTANT_ACCESSOR(NanValue, nan_value, Nan) HEAP_CONSTANT_ACCESSOR (NaN_string, NaN_string, NaNString) HEAP_CONSTANT_ACCESSOR(next_string , next_string, NextString) HEAP_CONSTANT_ACCESSOR(NoClosuresCellMap , no_closures_cell_map, NoClosuresCellMap) HEAP_CONSTANT_ACCESSOR (null_to_string, null_to_string, NullToString) HEAP_CONSTANT_ACCESSOR (NullValue, null_value, Null) HEAP_CONSTANT_ACCESSOR(number_string , number_string, NumberString) HEAP_CONSTANT_ACCESSOR(number_to_string , number_to_string, NumberToString) HEAP_CONSTANT_ACCESSOR(Object_string , Object_string, ObjectString) HEAP_CONSTANT_ACCESSOR(object_to_string , object_to_string, ObjectToString) HEAP_CONSTANT_ACCESSOR(OneByteStringMap , one_byte_string_map, OneByteStringMap) HEAP_CONSTANT_ACCESSOR (OneClosureCellMap, one_closure_cell_map, OneClosureCellMap) HEAP_CONSTANT_ACCESSOR (OnePointerFillerMap, one_pointer_filler_map, OnePointerFillerMap ) HEAP_CONSTANT_ACCESSOR(PromiseCapabilityMap, promise_capability_map , PromiseCapabilityMap) HEAP_CONSTANT_ACCESSOR(promise_forwarding_handler_symbol , promise_forwarding_handler_symbol, PromiseForwardingHandlerSymbol ) HEAP_CONSTANT_ACCESSOR(PromiseFulfillReactionJobTaskMap, promise_fulfill_reaction_job_task_map , PromiseFulfillReactionJobTaskMap) HEAP_CONSTANT_ACCESSOR(promise_handled_by_symbol , promise_handled_by_symbol, PromiseHandledBySymbol) HEAP_CONSTANT_ACCESSOR (PromiseReactionMap, promise_reaction_map, PromiseReactionMap ) HEAP_CONSTANT_ACCESSOR(PromiseRejectReactionJobTaskMap, promise_reject_reaction_job_task_map , PromiseRejectReactionJobTaskMap) HEAP_CONSTANT_ACCESSOR(PromiseResolveThenableJobTaskMap , promise_resolve_thenable_job_task_map, PromiseResolveThenableJobTaskMap ) HEAP_CONSTANT_ACCESSOR(prototype_string, prototype_string, PrototypeString ) HEAP_CONSTANT_ACCESSOR(replace_symbol, replace_symbol, ReplaceSymbol ) HEAP_CONSTANT_ACCESSOR(regexp_to_string, regexp_to_string, RegexpToString ) HEAP_CONSTANT_ACCESSOR(resolve_string, resolve_string, ResolveString ) HEAP_CONSTANT_ACCESSOR(return_string, return_string, ReturnString ) HEAP_CONSTANT_ACCESSOR(search_symbol, search_symbol, SearchSymbol ) HEAP_CONSTANT_ACCESSOR(species_symbol, species_symbol, SpeciesSymbol ) HEAP_CONSTANT_ACCESSOR(StaleRegister, stale_register, StaleRegister ) HEAP_CONSTANT_ACCESSOR(StoreHandler0Map, store_handler0_map , StoreHandler0Map) HEAP_CONSTANT_ACCESSOR(string_string, string_string , StringString) HEAP_CONSTANT_ACCESSOR(string_to_string, string_to_string , StringToString) HEAP_CONSTANT_ACCESSOR(StringMap, string_map , StringMap) HEAP_CONSTANT_ACCESSOR(TheHoleValue, the_hole_value , TheHole) HEAP_CONSTANT_ACCESSOR(then_string, then_string, ThenString ) HEAP_CONSTANT_ACCESSOR(toString_string, toString_string, ToStringString ) HEAP_CONSTANT_ACCESSOR(to_primitive_symbol, to_primitive_symbol , ToPrimitiveSymbol) HEAP_CONSTANT_ACCESSOR(to_string_tag_symbol , to_string_tag_symbol, ToStringTagSymbol) HEAP_CONSTANT_ACCESSOR (TrueValue, true_value, True) HEAP_CONSTANT_ACCESSOR(undefined_to_string , undefined_to_string, UndefinedToString) HEAP_CONSTANT_ACCESSOR (UndefinedValue, undefined_value, Undefined) HEAP_CONSTANT_ACCESSOR (uninitialized_symbol, uninitialized_symbol, UninitializedSymbol ) HEAP_CONSTANT_ACCESSOR(valueOf_string, valueOf_string, ValueOfString ) HEAP_CONSTANT_ACCESSOR(wasm_wrapped_object_symbol, wasm_wrapped_object_symbol , WasmWrappedObjectSymbol) HEAP_CONSTANT_ACCESSOR(zero_string , zero_string, ZeroString) HEAP_CONSTANT_ACCESSOR(AccessorInfoMap , accessor_info_map, AccessorInfoMap) HEAP_CONSTANT_ACCESSOR( AccessorPairMap, accessor_pair_map, AccessorPairMap) HEAP_CONSTANT_ACCESSOR (AllocationMementoMap, allocation_memento_map, AllocationMementoMap ) HEAP_CONSTANT_ACCESSOR(ArrayBoilerplateDescriptionMap, array_boilerplate_description_map , ArrayBoilerplateDescriptionMap) HEAP_CONSTANT_ACCESSOR(BreakPointMap , break_point_map, BreakPointMap) HEAP_CONSTANT_ACCESSOR(BreakPointInfoMap , break_point_info_map, BreakPointInfoMap) HEAP_CONSTANT_ACCESSOR (BytecodeArrayMap, bytecode_array_map, BytecodeArrayMap) HEAP_CONSTANT_ACCESSOR (CachedTemplateObjectMap, cached_template_object_map, CachedTemplateObjectMap ) HEAP_CONSTANT_ACCESSOR(CellMap, cell_map, CellMap) HEAP_CONSTANT_ACCESSOR (WeakCellMap, weak_cell_map, WeakCellMap) HEAP_CONSTANT_ACCESSOR (CodeMap, code_map, CodeMap) HEAP_CONSTANT_ACCESSOR(CodeDataContainerMap , code_data_container_map, CodeDataContainerMap) HEAP_CONSTANT_ACCESSOR (CoverageInfoMap, coverage_info_map, CoverageInfoMap) HEAP_CONSTANT_ACCESSOR (DebugInfoMap, debug_info_map, DebugInfoMap) HEAP_CONSTANT_ACCESSOR (FreeSpaceMap, free_space_map, FreeSpaceMap) HEAP_CONSTANT_ACCESSOR (FeedbackVectorMap, feedback_vector_map, FeedbackVectorMap) HEAP_CONSTANT_ACCESSOR (FixedDoubleArrayMap, fixed_double_array_map, FixedDoubleArrayMap ) HEAP_CONSTANT_ACCESSOR(FunctionTemplateInfoMap, function_template_info_map , FunctionTemplateInfoMap) HEAP_CONSTANT_ACCESSOR(MegaDomHandlerMap , mega_dom_handler_map, MegaDomHandlerMap) HEAP_CONSTANT_ACCESSOR (MetaMap, meta_map, MapMap) HEAP_CONSTANT_ACCESSOR(PreparseDataMap , preparse_data_map, PreparseDataMap) HEAP_CONSTANT_ACCESSOR( PropertyArrayMap, property_array_map, PropertyArrayMap) HEAP_CONSTANT_ACCESSOR (PrototypeInfoMap, prototype_info_map, PrototypeInfoMap) HEAP_CONSTANT_ACCESSOR (SharedFunctionInfoMap, shared_function_info_map, SharedFunctionInfoMap ) HEAP_CONSTANT_ACCESSOR(SmallOrderedHashSetMap, small_ordered_hash_set_map , SmallOrderedHashSetMap) HEAP_CONSTANT_ACCESSOR(SmallOrderedHashMapMap , small_ordered_hash_map_map, SmallOrderedHashMapMap) HEAP_CONSTANT_ACCESSOR (SmallOrderedNameDictionaryMap, small_ordered_name_dictionary_map , SmallOrderedNameDictionaryMap) HEAP_CONSTANT_ACCESSOR(SwissNameDictionaryMap , swiss_name_dictionary_map, SwissNameDictionaryMap) HEAP_CONSTANT_ACCESSOR (SymbolMap, symbol_map, SymbolMap) HEAP_CONSTANT_ACCESSOR(TransitionArrayMap , transition_array_map, TransitionArrayMap) HEAP_CONSTANT_ACCESSOR (Tuple2Map, tuple2_map, Tuple2Map) HEAP_CONSTANT_ACCESSOR(HeapNumberMap , heap_number_map, HeapNumberMap) HEAP_CONSTANT_ACCESSOR(WeakFixedArrayMap , weak_fixed_array_map, WeakFixedArrayMap) HEAP_CONSTANT_ACCESSOR (SloppyArgumentsElementsMap, sloppy_arguments_elements_map, SloppyArgumentsElementsMap ) HEAP_CONSTANT_ACCESSOR(DescriptorArrayMap, descriptor_array_map , DescriptorArrayMap) HEAP_CONSTANT_ACCESSOR(StrongDescriptorArrayMap , strong_descriptor_array_map, StrongDescriptorArrayMap) HEAP_CONSTANT_ACCESSOR (UncompiledDataWithoutPreparseDataMap, uncompiled_data_without_preparse_data_map , UncompiledDataWithoutPreparseDataMap) HEAP_CONSTANT_ACCESSOR (UncompiledDataWithPreparseDataMap, uncompiled_data_with_preparse_data_map , UncompiledDataWithPreparseDataMap) HEAP_CONSTANT_ACCESSOR(UncompiledDataWithoutPreparseDataWithJobMap , uncompiled_data_without_preparse_data_with_job_map, UncompiledDataWithoutPreparseDataWithJobMap ) HEAP_CONSTANT_ACCESSOR(UncompiledDataWithPreparseDataAndJobMap , uncompiled_data_with_preparse_data_and_job_map, UncompiledDataWithPreparseDataAndJobMap ) HEAP_CONSTANT_ACCESSOR(OnHeapBasicBlockProfilerDataMap, on_heap_basic_block_profiler_data_map , OnHeapBasicBlockProfilerDataMap) HEAP_CONSTANT_ACCESSOR(TurbofanBitsetTypeMap , turbofan_bitset_type_map, TurbofanBitsetTypeMap) HEAP_CONSTANT_ACCESSOR (TurbofanUnionTypeMap, turbofan_union_type_map, TurbofanUnionTypeMap ) HEAP_CONSTANT_ACCESSOR(TurbofanRangeTypeMap, turbofan_range_type_map , TurbofanRangeTypeMap) HEAP_CONSTANT_ACCESSOR(TurbofanHeapConstantTypeMap , turbofan_heap_constant_type_map, TurbofanHeapConstantTypeMap ) HEAP_CONSTANT_ACCESSOR(TurbofanOtherNumberConstantTypeMap, turbofan_other_number_constant_type_map , TurbofanOtherNumberConstantTypeMap) HEAP_CONSTANT_ACCESSOR( InternalClassMap, internal_class_map, InternalClassMap) HEAP_CONSTANT_ACCESSOR (SmiPairMap, smi_pair_map, SmiPairMap) HEAP_CONSTANT_ACCESSOR (SmiBoxMap, smi_box_map, SmiBoxMap) HEAP_CONSTANT_ACCESSOR(ExportedSubClassBaseMap , exported_sub_class_base_map, ExportedSubClassBaseMap) HEAP_CONSTANT_ACCESSOR (ExportedSubClassMap, exported_sub_class_map, ExportedSubClassMap ) HEAP_CONSTANT_ACCESSOR(AbstractInternalClassSubclass1Map, abstract_internal_class_subclass1_map , AbstractInternalClassSubclass1Map) HEAP_CONSTANT_ACCESSOR(AbstractInternalClassSubclass2Map , abstract_internal_class_subclass2_map, AbstractInternalClassSubclass2Map ) HEAP_CONSTANT_ACCESSOR(InternalClassWithSmiElementsMap, internal_class_with_smi_elements_map , InternalClassWithSmiElementsMap) HEAP_CONSTANT_ACCESSOR(InternalClassWithStructElementsMap , internal_class_with_struct_elements_map, InternalClassWithStructElementsMap ) HEAP_CONSTANT_ACCESSOR(ExportedSubClass2Map, exported_sub_class2_map , ExportedSubClass2Map) HEAP_CONSTANT_ACCESSOR(SortStateMap, sort_state_map , SortStateMap) | |||
| 255 | #undef HEAP_CONSTANT_ACCESSOR | |||
| 256 | ||||
| 257 | #define HEAP_CONSTANT_TEST(rootIndexName, rootAccessorName, name) \ | |||
| 258 | TNode<BoolT> CodeStubAssembler::Is##name(TNode<Object> value) { \ | |||
| 259 | return TaggedEqual(value, name##Constant()); \ | |||
| 260 | } \ | |||
| 261 | TNode<BoolT> CodeStubAssembler::IsNot##name(TNode<Object> value) { \ | |||
| 262 | return TaggedNotEqual(value, name##Constant()); \ | |||
| 263 | } | |||
| 264 | HEAP_IMMOVABLE_OBJECT_LIST(HEAP_CONSTANT_TEST)HEAP_CONSTANT_TEST(ArrayIteratorProtector, array_iterator_protector , ArrayIteratorProtector) HEAP_CONSTANT_TEST(ArraySpeciesProtector , array_species_protector, ArraySpeciesProtector) HEAP_CONSTANT_TEST (AsyncFunctionAwaitRejectSharedFun, async_function_await_reject_shared_fun , AsyncFunctionAwaitRejectSharedFun) HEAP_CONSTANT_TEST(AsyncFunctionAwaitResolveSharedFun , async_function_await_resolve_shared_fun, AsyncFunctionAwaitResolveSharedFun ) HEAP_CONSTANT_TEST(AsyncGeneratorAwaitRejectSharedFun, async_generator_await_reject_shared_fun , AsyncGeneratorAwaitRejectSharedFun) HEAP_CONSTANT_TEST(AsyncGeneratorAwaitResolveSharedFun , async_generator_await_resolve_shared_fun, AsyncGeneratorAwaitResolveSharedFun ) HEAP_CONSTANT_TEST(AsyncGeneratorReturnClosedRejectSharedFun , async_generator_return_closed_reject_shared_fun, AsyncGeneratorReturnClosedRejectSharedFun ) HEAP_CONSTANT_TEST(AsyncGeneratorReturnClosedResolveSharedFun , async_generator_return_closed_resolve_shared_fun, AsyncGeneratorReturnClosedResolveSharedFun ) HEAP_CONSTANT_TEST(AsyncGeneratorReturnResolveSharedFun, async_generator_return_resolve_shared_fun , AsyncGeneratorReturnResolveSharedFun) HEAP_CONSTANT_TEST(AsyncGeneratorYieldResolveSharedFun , async_generator_yield_resolve_shared_fun, AsyncGeneratorYieldResolveSharedFun ) HEAP_CONSTANT_TEST(AsyncIteratorValueUnwrapSharedFun, async_iterator_value_unwrap_shared_fun , AsyncIteratorValueUnwrapSharedFun) HEAP_CONSTANT_TEST(IsConcatSpreadableProtector , is_concat_spreadable_protector, IsConcatSpreadableProtector ) HEAP_CONSTANT_TEST(MapIteratorProtector, map_iterator_protector , MapIteratorProtector) HEAP_CONSTANT_TEST(NoElementsProtector , no_elements_protector, NoElementsProtector) HEAP_CONSTANT_TEST (MegaDOMProtector, mega_dom_protector, MegaDOMProtector) HEAP_CONSTANT_TEST (NumberStringCache, number_string_cache, NumberStringCache) HEAP_CONSTANT_TEST (PromiseAllResolveElementSharedFun, promise_all_resolve_element_shared_fun , PromiseAllResolveElementSharedFun) HEAP_CONSTANT_TEST(PromiseAllSettledRejectElementSharedFun , promise_all_settled_reject_element_shared_fun, PromiseAllSettledRejectElementSharedFun ) HEAP_CONSTANT_TEST(PromiseAllSettledResolveElementSharedFun , promise_all_settled_resolve_element_shared_fun, PromiseAllSettledResolveElementSharedFun ) HEAP_CONSTANT_TEST(PromiseAnyRejectElementSharedFun, promise_any_reject_element_shared_fun , PromiseAnyRejectElementSharedFun) HEAP_CONSTANT_TEST(PromiseCapabilityDefaultRejectSharedFun , promise_capability_default_reject_shared_fun, PromiseCapabilityDefaultRejectSharedFun ) HEAP_CONSTANT_TEST(PromiseCapabilityDefaultResolveSharedFun , promise_capability_default_resolve_shared_fun, PromiseCapabilityDefaultResolveSharedFun ) HEAP_CONSTANT_TEST(PromiseCatchFinallySharedFun, promise_catch_finally_shared_fun , PromiseCatchFinallySharedFun) HEAP_CONSTANT_TEST(PromiseGetCapabilitiesExecutorSharedFun , promise_get_capabilities_executor_shared_fun, PromiseGetCapabilitiesExecutorSharedFun ) HEAP_CONSTANT_TEST(PromiseResolveProtector, promise_resolve_protector , PromiseResolveProtector) HEAP_CONSTANT_TEST(PromiseSpeciesProtector , promise_species_protector, PromiseSpeciesProtector) HEAP_CONSTANT_TEST (PromiseThenFinallySharedFun, promise_then_finally_shared_fun , PromiseThenFinallySharedFun) HEAP_CONSTANT_TEST(PromiseThenProtector , promise_then_protector, PromiseThenProtector) HEAP_CONSTANT_TEST (PromiseThrowerFinallySharedFun, promise_thrower_finally_shared_fun , PromiseThrowerFinallySharedFun) HEAP_CONSTANT_TEST(PromiseValueThunkFinallySharedFun , promise_value_thunk_finally_shared_fun, PromiseValueThunkFinallySharedFun ) HEAP_CONSTANT_TEST(ProxyRevokeSharedFun, proxy_revoke_shared_fun , ProxyRevokeSharedFun) HEAP_CONSTANT_TEST(RegExpSpeciesProtector , regexp_species_protector, RegExpSpeciesProtector) HEAP_CONSTANT_TEST (SetIteratorProtector, set_iterator_protector, SetIteratorProtector ) HEAP_CONSTANT_TEST(SingleCharacterStringCache, single_character_string_cache , SingleCharacterStringCache) HEAP_CONSTANT_TEST(StringIteratorProtector , string_iterator_protector, StringIteratorProtector) HEAP_CONSTANT_TEST (TypedArraySpeciesProtector, typed_array_species_protector, TypedArraySpeciesProtector ) HEAP_CONSTANT_TEST(AllocationSiteWithoutWeakNextMap, allocation_site_without_weaknext_map , AllocationSiteWithoutWeakNextMap) HEAP_CONSTANT_TEST(AllocationSiteWithWeakNextMap , allocation_site_map, AllocationSiteMap) HEAP_CONSTANT_TEST( arguments_to_string, arguments_to_string, ArgumentsToString) HEAP_CONSTANT_TEST (Array_string, Array_string, ArrayString) HEAP_CONSTANT_TEST( array_to_string, array_to_string, ArrayToString) HEAP_CONSTANT_TEST (BooleanMap, boolean_map, BooleanMap) HEAP_CONSTANT_TEST(boolean_to_string , boolean_to_string, BooleanToString) HEAP_CONSTANT_TEST(ConsOneByteStringMap , cons_one_byte_string_map, ConsOneByteStringMap) HEAP_CONSTANT_TEST (ConsStringMap, cons_string_map, ConsStringMap) HEAP_CONSTANT_TEST (constructor_string, constructor_string, ConstructorString) HEAP_CONSTANT_TEST (date_to_string, date_to_string, DateToString) HEAP_CONSTANT_TEST (default_string, default_string, DefaultString) HEAP_CONSTANT_TEST (EmptyByteArray, empty_byte_array, EmptyByteArray) HEAP_CONSTANT_TEST (EmptyFixedArray, empty_fixed_array, EmptyFixedArray) HEAP_CONSTANT_TEST (EmptyScopeInfo, empty_scope_info, EmptyScopeInfo) HEAP_CONSTANT_TEST (EmptyPropertyDictionary, empty_property_dictionary, EmptyPropertyDictionary ) HEAP_CONSTANT_TEST(EmptyOrderedPropertyDictionary, empty_ordered_property_dictionary , EmptyOrderedPropertyDictionary) HEAP_CONSTANT_TEST(EmptySwissPropertyDictionary , empty_swiss_property_dictionary, EmptySwissPropertyDictionary ) HEAP_CONSTANT_TEST(EmptySlowElementDictionary, empty_slow_element_dictionary , EmptySlowElementDictionary) HEAP_CONSTANT_TEST(empty_string , empty_string, EmptyString) HEAP_CONSTANT_TEST(error_to_string , error_to_string, ErrorToString) HEAP_CONSTANT_TEST(errors_string , errors_string, ErrorsString) HEAP_CONSTANT_TEST(FalseValue, false_value, False) HEAP_CONSTANT_TEST(FixedArrayMap, fixed_array_map , FixedArrayMap) HEAP_CONSTANT_TEST(FixedCOWArrayMap, fixed_cow_array_map , FixedCOWArrayMap) HEAP_CONSTANT_TEST(Function_string, function_string , FunctionString) HEAP_CONSTANT_TEST(function_to_string, function_to_string , FunctionToString) HEAP_CONSTANT_TEST(GlobalPropertyCellMap, global_property_cell_map, PropertyCellMap) HEAP_CONSTANT_TEST (has_instance_symbol, has_instance_symbol, HasInstanceSymbol) HEAP_CONSTANT_TEST(Infinity_string, Infinity_string, InfinityString ) HEAP_CONSTANT_TEST(is_concat_spreadable_symbol, is_concat_spreadable_symbol , IsConcatSpreadableSymbol) HEAP_CONSTANT_TEST(iterator_symbol , iterator_symbol, IteratorSymbol) HEAP_CONSTANT_TEST(length_string , length_string, LengthString) HEAP_CONSTANT_TEST(ManyClosuresCellMap , many_closures_cell_map, ManyClosuresCellMap) HEAP_CONSTANT_TEST (match_symbol, match_symbol, MatchSymbol) HEAP_CONSTANT_TEST( megamorphic_symbol, megamorphic_symbol, MegamorphicSymbol) HEAP_CONSTANT_TEST (mega_dom_symbol, mega_dom_symbol, MegaDOMSymbol) HEAP_CONSTANT_TEST (message_string, message_string, MessageString) HEAP_CONSTANT_TEST (minus_Infinity_string, minus_Infinity_string, MinusInfinityString ) HEAP_CONSTANT_TEST(MinusZeroValue, minus_zero_value, MinusZero ) HEAP_CONSTANT_TEST(name_string, name_string, NameString) HEAP_CONSTANT_TEST (NanValue, nan_value, Nan) HEAP_CONSTANT_TEST(NaN_string, NaN_string , NaNString) HEAP_CONSTANT_TEST(next_string, next_string, NextString ) HEAP_CONSTANT_TEST(NoClosuresCellMap, no_closures_cell_map, NoClosuresCellMap) HEAP_CONSTANT_TEST(null_to_string, null_to_string , NullToString) HEAP_CONSTANT_TEST(NullValue, null_value, Null ) HEAP_CONSTANT_TEST(number_string, number_string, NumberString ) HEAP_CONSTANT_TEST(number_to_string, number_to_string, NumberToString ) HEAP_CONSTANT_TEST(Object_string, Object_string, ObjectString ) HEAP_CONSTANT_TEST(object_to_string, object_to_string, ObjectToString ) HEAP_CONSTANT_TEST(OneByteStringMap, one_byte_string_map, OneByteStringMap ) HEAP_CONSTANT_TEST(OneClosureCellMap, one_closure_cell_map, OneClosureCellMap) HEAP_CONSTANT_TEST(OnePointerFillerMap, one_pointer_filler_map , OnePointerFillerMap) HEAP_CONSTANT_TEST(PromiseCapabilityMap , promise_capability_map, PromiseCapabilityMap) HEAP_CONSTANT_TEST (promise_forwarding_handler_symbol, promise_forwarding_handler_symbol , PromiseForwardingHandlerSymbol) HEAP_CONSTANT_TEST(PromiseFulfillReactionJobTaskMap , promise_fulfill_reaction_job_task_map, PromiseFulfillReactionJobTaskMap ) HEAP_CONSTANT_TEST(promise_handled_by_symbol, promise_handled_by_symbol , PromiseHandledBySymbol) HEAP_CONSTANT_TEST(PromiseReactionMap , promise_reaction_map, PromiseReactionMap) HEAP_CONSTANT_TEST (PromiseRejectReactionJobTaskMap, promise_reject_reaction_job_task_map , PromiseRejectReactionJobTaskMap) HEAP_CONSTANT_TEST(PromiseResolveThenableJobTaskMap , promise_resolve_thenable_job_task_map, PromiseResolveThenableJobTaskMap ) HEAP_CONSTANT_TEST(prototype_string, prototype_string, PrototypeString ) HEAP_CONSTANT_TEST(replace_symbol, replace_symbol, ReplaceSymbol ) HEAP_CONSTANT_TEST(regexp_to_string, regexp_to_string, RegexpToString ) HEAP_CONSTANT_TEST(resolve_string, resolve_string, ResolveString ) HEAP_CONSTANT_TEST(return_string, return_string, ReturnString ) HEAP_CONSTANT_TEST(search_symbol, search_symbol, SearchSymbol ) HEAP_CONSTANT_TEST(species_symbol, species_symbol, SpeciesSymbol ) HEAP_CONSTANT_TEST(StaleRegister, stale_register, StaleRegister ) HEAP_CONSTANT_TEST(StoreHandler0Map, store_handler0_map, StoreHandler0Map ) HEAP_CONSTANT_TEST(string_string, string_string, StringString ) HEAP_CONSTANT_TEST(string_to_string, string_to_string, StringToString ) HEAP_CONSTANT_TEST(StringMap, string_map, StringMap) HEAP_CONSTANT_TEST (TheHoleValue, the_hole_value, TheHole) HEAP_CONSTANT_TEST(then_string , then_string, ThenString) HEAP_CONSTANT_TEST(toString_string , toString_string, ToStringString) HEAP_CONSTANT_TEST(to_primitive_symbol , to_primitive_symbol, ToPrimitiveSymbol) HEAP_CONSTANT_TEST( to_string_tag_symbol, to_string_tag_symbol, ToStringTagSymbol ) HEAP_CONSTANT_TEST(TrueValue, true_value, True) HEAP_CONSTANT_TEST (undefined_to_string, undefined_to_string, UndefinedToString) HEAP_CONSTANT_TEST(UndefinedValue, undefined_value, Undefined ) HEAP_CONSTANT_TEST(uninitialized_symbol, uninitialized_symbol , UninitializedSymbol) HEAP_CONSTANT_TEST(valueOf_string, valueOf_string , ValueOfString) HEAP_CONSTANT_TEST(wasm_wrapped_object_symbol , wasm_wrapped_object_symbol, WasmWrappedObjectSymbol) HEAP_CONSTANT_TEST (zero_string, zero_string, ZeroString) HEAP_CONSTANT_TEST(AccessorInfoMap , accessor_info_map, AccessorInfoMap) HEAP_CONSTANT_TEST(AccessorPairMap , accessor_pair_map, AccessorPairMap) HEAP_CONSTANT_TEST(AllocationMementoMap , allocation_memento_map, AllocationMementoMap) HEAP_CONSTANT_TEST (ArrayBoilerplateDescriptionMap, array_boilerplate_description_map , ArrayBoilerplateDescriptionMap) HEAP_CONSTANT_TEST(BreakPointMap , break_point_map, BreakPointMap) HEAP_CONSTANT_TEST(BreakPointInfoMap , break_point_info_map, BreakPointInfoMap) HEAP_CONSTANT_TEST (BytecodeArrayMap, bytecode_array_map, BytecodeArrayMap) HEAP_CONSTANT_TEST (CachedTemplateObjectMap, cached_template_object_map, CachedTemplateObjectMap ) HEAP_CONSTANT_TEST(CellMap, cell_map, CellMap) HEAP_CONSTANT_TEST (WeakCellMap, weak_cell_map, WeakCellMap) HEAP_CONSTANT_TEST( CodeMap, code_map, CodeMap) HEAP_CONSTANT_TEST(CodeDataContainerMap , code_data_container_map, CodeDataContainerMap) HEAP_CONSTANT_TEST (CoverageInfoMap, coverage_info_map, CoverageInfoMap) HEAP_CONSTANT_TEST (DebugInfoMap, debug_info_map, DebugInfoMap) HEAP_CONSTANT_TEST (FreeSpaceMap, free_space_map, FreeSpaceMap) HEAP_CONSTANT_TEST (FeedbackVectorMap, feedback_vector_map, FeedbackVectorMap) HEAP_CONSTANT_TEST (FixedDoubleArrayMap, fixed_double_array_map, FixedDoubleArrayMap ) HEAP_CONSTANT_TEST(FunctionTemplateInfoMap, function_template_info_map , FunctionTemplateInfoMap) HEAP_CONSTANT_TEST(MegaDomHandlerMap , mega_dom_handler_map, MegaDomHandlerMap) HEAP_CONSTANT_TEST (MetaMap, meta_map, MapMap) HEAP_CONSTANT_TEST(PreparseDataMap , preparse_data_map, PreparseDataMap) HEAP_CONSTANT_TEST(PropertyArrayMap , property_array_map, PropertyArrayMap) HEAP_CONSTANT_TEST(PrototypeInfoMap , prototype_info_map, PrototypeInfoMap) HEAP_CONSTANT_TEST(SharedFunctionInfoMap , shared_function_info_map, SharedFunctionInfoMap) HEAP_CONSTANT_TEST (SmallOrderedHashSetMap, small_ordered_hash_set_map, SmallOrderedHashSetMap ) HEAP_CONSTANT_TEST(SmallOrderedHashMapMap, small_ordered_hash_map_map , SmallOrderedHashMapMap) HEAP_CONSTANT_TEST(SmallOrderedNameDictionaryMap , small_ordered_name_dictionary_map, SmallOrderedNameDictionaryMap ) HEAP_CONSTANT_TEST(SwissNameDictionaryMap, swiss_name_dictionary_map , SwissNameDictionaryMap) HEAP_CONSTANT_TEST(SymbolMap, symbol_map , SymbolMap) HEAP_CONSTANT_TEST(TransitionArrayMap, transition_array_map , TransitionArrayMap) HEAP_CONSTANT_TEST(Tuple2Map, tuple2_map , Tuple2Map) HEAP_CONSTANT_TEST(HeapNumberMap, heap_number_map , HeapNumberMap) HEAP_CONSTANT_TEST(WeakFixedArrayMap, weak_fixed_array_map , WeakFixedArrayMap) HEAP_CONSTANT_TEST(SloppyArgumentsElementsMap , sloppy_arguments_elements_map, SloppyArgumentsElementsMap) HEAP_CONSTANT_TEST (DescriptorArrayMap, descriptor_array_map, DescriptorArrayMap ) HEAP_CONSTANT_TEST(StrongDescriptorArrayMap, strong_descriptor_array_map , StrongDescriptorArrayMap) HEAP_CONSTANT_TEST(UncompiledDataWithoutPreparseDataMap , uncompiled_data_without_preparse_data_map, UncompiledDataWithoutPreparseDataMap ) HEAP_CONSTANT_TEST(UncompiledDataWithPreparseDataMap, uncompiled_data_with_preparse_data_map , UncompiledDataWithPreparseDataMap) HEAP_CONSTANT_TEST(UncompiledDataWithoutPreparseDataWithJobMap , uncompiled_data_without_preparse_data_with_job_map, UncompiledDataWithoutPreparseDataWithJobMap ) HEAP_CONSTANT_TEST(UncompiledDataWithPreparseDataAndJobMap, uncompiled_data_with_preparse_data_and_job_map, UncompiledDataWithPreparseDataAndJobMap ) HEAP_CONSTANT_TEST(OnHeapBasicBlockProfilerDataMap, on_heap_basic_block_profiler_data_map , OnHeapBasicBlockProfilerDataMap) HEAP_CONSTANT_TEST(TurbofanBitsetTypeMap , turbofan_bitset_type_map, TurbofanBitsetTypeMap) HEAP_CONSTANT_TEST (TurbofanUnionTypeMap, turbofan_union_type_map, TurbofanUnionTypeMap ) HEAP_CONSTANT_TEST(TurbofanRangeTypeMap, turbofan_range_type_map , TurbofanRangeTypeMap) HEAP_CONSTANT_TEST(TurbofanHeapConstantTypeMap , turbofan_heap_constant_type_map, TurbofanHeapConstantTypeMap ) HEAP_CONSTANT_TEST(TurbofanOtherNumberConstantTypeMap, turbofan_other_number_constant_type_map , TurbofanOtherNumberConstantTypeMap) HEAP_CONSTANT_TEST(InternalClassMap , internal_class_map, InternalClassMap) HEAP_CONSTANT_TEST(SmiPairMap , smi_pair_map, SmiPairMap) HEAP_CONSTANT_TEST(SmiBoxMap, smi_box_map , SmiBoxMap) HEAP_CONSTANT_TEST(ExportedSubClassBaseMap, exported_sub_class_base_map , ExportedSubClassBaseMap) HEAP_CONSTANT_TEST(ExportedSubClassMap , exported_sub_class_map, ExportedSubClassMap) HEAP_CONSTANT_TEST (AbstractInternalClassSubclass1Map, abstract_internal_class_subclass1_map , AbstractInternalClassSubclass1Map) HEAP_CONSTANT_TEST(AbstractInternalClassSubclass2Map , abstract_internal_class_subclass2_map, AbstractInternalClassSubclass2Map ) HEAP_CONSTANT_TEST(InternalClassWithSmiElementsMap, internal_class_with_smi_elements_map , InternalClassWithSmiElementsMap) HEAP_CONSTANT_TEST(InternalClassWithStructElementsMap , internal_class_with_struct_elements_map, InternalClassWithStructElementsMap ) HEAP_CONSTANT_TEST(ExportedSubClass2Map, exported_sub_class2_map , ExportedSubClass2Map) HEAP_CONSTANT_TEST(SortStateMap, sort_state_map , SortStateMap) | |||
| 265 | #undef HEAP_CONSTANT_TEST | |||
| 266 | ||||
| 267 | TNode<BInt> CodeStubAssembler::BIntConstant(int value) { | |||
| 268 | #if defined(BINT_IS_SMI) | |||
| 269 | return SmiConstant(value); | |||
| 270 | #elif defined(BINT_IS_INTPTR) | |||
| 271 | return IntPtrConstant(value); | |||
| 272 | #else | |||
| 273 | #error Unknown architecture. | |||
| 274 | #endif | |||
| 275 | } | |||
| 276 | ||||
| 277 | template <> | |||
| 278 | TNode<Smi> CodeStubAssembler::IntPtrOrSmiConstant<Smi>(int value) { | |||
| 279 | return SmiConstant(value); | |||
| 280 | } | |||
| 281 | ||||
| 282 | template <> | |||
| 283 | TNode<IntPtrT> CodeStubAssembler::IntPtrOrSmiConstant<IntPtrT>(int value) { | |||
| 284 | return IntPtrConstant(value); | |||
| 285 | } | |||
| 286 | ||||
| 287 | template <> | |||
| 288 | TNode<UintPtrT> CodeStubAssembler::IntPtrOrSmiConstant<UintPtrT>(int value) { | |||
| 289 | return Unsigned(IntPtrConstant(value)); | |||
| 290 | } | |||
| 291 | ||||
| 292 | template <> | |||
| 293 | TNode<RawPtrT> CodeStubAssembler::IntPtrOrSmiConstant<RawPtrT>(int value) { | |||
| 294 | return ReinterpretCast<RawPtrT>(IntPtrConstant(value)); | |||
| 295 | } | |||
| 296 | ||||
| 297 | bool CodeStubAssembler::TryGetIntPtrOrSmiConstantValue( | |||
| 298 | TNode<Smi> maybe_constant, int* value) { | |||
| 299 | Smi smi_constant; | |||
| 300 | if (TryToSmiConstant(maybe_constant, &smi_constant)) { | |||
| 301 | *value = Smi::ToInt(smi_constant); | |||
| 302 | return true; | |||
| 303 | } | |||
| 304 | return false; | |||
| 305 | } | |||
| 306 | ||||
| 307 | bool CodeStubAssembler::TryGetIntPtrOrSmiConstantValue( | |||
| 308 | TNode<IntPtrT> maybe_constant, int* value) { | |||
| 309 | int32_t int32_constant; | |||
| 310 | if (TryToInt32Constant(maybe_constant, &int32_constant)) { | |||
| 311 | *value = int32_constant; | |||
| 312 | return true; | |||
| 313 | } | |||
| 314 | return false; | |||
| 315 | } | |||
| 316 | ||||
| 317 | TNode<IntPtrT> CodeStubAssembler::IntPtrRoundUpToPowerOfTwo32( | |||
| 318 | TNode<IntPtrT> value) { | |||
| 319 | Comment("IntPtrRoundUpToPowerOfTwo32"); | |||
| 320 | CSA_DCHECK(this, UintPtrLessThanOrEqual(value, IntPtrConstant(0x80000000u)))((void)0); | |||
| 321 | value = Signed(IntPtrSub(value, IntPtrConstant(1))); | |||
| 322 | for (int i = 1; i <= 16; i *= 2) { | |||
| 323 | value = Signed(WordOr(value, WordShr(value, IntPtrConstant(i)))); | |||
| 324 | } | |||
| 325 | return Signed(IntPtrAdd(value, IntPtrConstant(1))); | |||
| 326 | } | |||
| 327 | ||||
| 328 | TNode<BoolT> CodeStubAssembler::WordIsPowerOfTwo(TNode<IntPtrT> value) { | |||
| 329 | intptr_t constant; | |||
| 330 | if (TryToIntPtrConstant(value, &constant)) { | |||
| 331 | return BoolConstant(base::bits::IsPowerOfTwo(constant)); | |||
| 332 | } | |||
| 333 | // value && !(value & (value - 1)) | |||
| 334 | return IntPtrEqual( | |||
| 335 | Select<IntPtrT>( | |||
| 336 | IntPtrEqual(value, IntPtrConstant(0)), | |||
| 337 | [=] { return IntPtrConstant(1); }, | |||
| 338 | [=] { return WordAnd(value, IntPtrSub(value, IntPtrConstant(1))); }), | |||
| 339 | IntPtrConstant(0)); | |||
| 340 | } | |||
| 341 | ||||
| 342 | TNode<Float64T> CodeStubAssembler::Float64Round(TNode<Float64T> x) { | |||
| 343 | TNode<Float64T> one = Float64Constant(1.0); | |||
| 344 | TNode<Float64T> one_half = Float64Constant(0.5); | |||
| 345 | ||||
| 346 | Label return_x(this); | |||
| 347 | ||||
| 348 | // Round up {x} towards Infinity. | |||
| 349 | TVARIABLE(Float64T, var_x, Float64Ceil(x))TVariable<Float64T> var_x(Float64Ceil(x), this); | |||
| 350 | ||||
| 351 | GotoIf(Float64LessThanOrEqual(Float64Sub(var_x.value(), one_half), x), | |||
| 352 | &return_x); | |||
| 353 | var_x = Float64Sub(var_x.value(), one); | |||
| 354 | Goto(&return_x); | |||
| 355 | ||||
| 356 | BIND(&return_x)Bind(&return_x); | |||
| 357 | return var_x.value(); | |||
| 358 | } | |||
| 359 | ||||
| 360 | TNode<Float64T> CodeStubAssembler::Float64Ceil(TNode<Float64T> x) { | |||
| 361 | if (IsFloat64RoundUpSupported()) { | |||
| 362 | return Float64RoundUp(x); | |||
| 363 | } | |||
| 364 | ||||
| 365 | TNode<Float64T> one = Float64Constant(1.0); | |||
| 366 | TNode<Float64T> zero = Float64Constant(0.0); | |||
| 367 | TNode<Float64T> two_52 = Float64Constant(4503599627370496.0E0); | |||
| 368 | TNode<Float64T> minus_two_52 = Float64Constant(-4503599627370496.0E0); | |||
| 369 | ||||
| 370 | TVARIABLE(Float64T, var_x, x)TVariable<Float64T> var_x(x, this); | |||
| 371 | Label return_x(this), return_minus_x(this); | |||
| 372 | ||||
| 373 | // Check if {x} is greater than zero. | |||
| 374 | Label if_xgreaterthanzero(this), if_xnotgreaterthanzero(this); | |||
| 375 | Branch(Float64GreaterThan(x, zero), &if_xgreaterthanzero, | |||
| 376 | &if_xnotgreaterthanzero); | |||
| 377 | ||||
| 378 | BIND(&if_xgreaterthanzero)Bind(&if_xgreaterthanzero); | |||
| 379 | { | |||
| 380 | // Just return {x} unless it's in the range ]0,2^52[. | |||
| 381 | GotoIf(Float64GreaterThanOrEqual(x, two_52), &return_x); | |||
| 382 | ||||
| 383 | // Round positive {x} towards Infinity. | |||
| 384 | var_x = Float64Sub(Float64Add(two_52, x), two_52); | |||
| 385 | GotoIfNot(Float64LessThan(var_x.value(), x), &return_x); | |||
| 386 | var_x = Float64Add(var_x.value(), one); | |||
| 387 | Goto(&return_x); | |||
| 388 | } | |||
| 389 | ||||
| 390 | BIND(&if_xnotgreaterthanzero)Bind(&if_xnotgreaterthanzero); | |||
| 391 | { | |||
| 392 | // Just return {x} unless it's in the range ]-2^52,0[ | |||
| 393 | GotoIf(Float64LessThanOrEqual(x, minus_two_52), &return_x); | |||
| 394 | GotoIfNot(Float64LessThan(x, zero), &return_x); | |||
| 395 | ||||
| 396 | // Round negated {x} towards Infinity and return the result negated. | |||
| 397 | TNode<Float64T> minus_x = Float64Neg(x); | |||
| 398 | var_x = Float64Sub(Float64Add(two_52, minus_x), two_52); | |||
| 399 | GotoIfNot(Float64GreaterThan(var_x.value(), minus_x), &return_minus_x); | |||
| 400 | var_x = Float64Sub(var_x.value(), one); | |||
| 401 | Goto(&return_minus_x); | |||
| 402 | } | |||
| 403 | ||||
| 404 | BIND(&return_minus_x)Bind(&return_minus_x); | |||
| 405 | var_x = Float64Neg(var_x.value()); | |||
| 406 | Goto(&return_x); | |||
| 407 | ||||
| 408 | BIND(&return_x)Bind(&return_x); | |||
| 409 | return var_x.value(); | |||
| 410 | } | |||
| 411 | ||||
| 412 | TNode<Float64T> CodeStubAssembler::Float64Floor(TNode<Float64T> x) { | |||
| 413 | if (IsFloat64RoundDownSupported()) { | |||
| 414 | return Float64RoundDown(x); | |||
| 415 | } | |||
| 416 | ||||
| 417 | TNode<Float64T> one = Float64Constant(1.0); | |||
| 418 | TNode<Float64T> zero = Float64Constant(0.0); | |||
| 419 | TNode<Float64T> two_52 = Float64Constant(4503599627370496.0E0); | |||
| 420 | TNode<Float64T> minus_two_52 = Float64Constant(-4503599627370496.0E0); | |||
| 421 | ||||
| 422 | TVARIABLE(Float64T, var_x, x)TVariable<Float64T> var_x(x, this); | |||
| 423 | Label return_x(this), return_minus_x(this); | |||
| 424 | ||||
| 425 | // Check if {x} is greater than zero. | |||
| 426 | Label if_xgreaterthanzero(this), if_xnotgreaterthanzero(this); | |||
| 427 | Branch(Float64GreaterThan(x, zero), &if_xgreaterthanzero, | |||
| 428 | &if_xnotgreaterthanzero); | |||
| 429 | ||||
| 430 | BIND(&if_xgreaterthanzero)Bind(&if_xgreaterthanzero); | |||
| 431 | { | |||
| 432 | // Just return {x} unless it's in the range ]0,2^52[. | |||
| 433 | GotoIf(Float64GreaterThanOrEqual(x, two_52), &return_x); | |||
| 434 | ||||
| 435 | // Round positive {x} towards -Infinity. | |||
| 436 | var_x = Float64Sub(Float64Add(two_52, x), two_52); | |||
| 437 | GotoIfNot(Float64GreaterThan(var_x.value(), x), &return_x); | |||
| 438 | var_x = Float64Sub(var_x.value(), one); | |||
| 439 | Goto(&return_x); | |||
| 440 | } | |||
| 441 | ||||
| 442 | BIND(&if_xnotgreaterthanzero)Bind(&if_xnotgreaterthanzero); | |||
| 443 | { | |||
| 444 | // Just return {x} unless it's in the range ]-2^52,0[ | |||
| 445 | GotoIf(Float64LessThanOrEqual(x, minus_two_52), &return_x); | |||
| 446 | GotoIfNot(Float64LessThan(x, zero), &return_x); | |||
| 447 | ||||
| 448 | // Round negated {x} towards -Infinity and return the result negated. | |||
| 449 | TNode<Float64T> minus_x = Float64Neg(x); | |||
| 450 | var_x = Float64Sub(Float64Add(two_52, minus_x), two_52); | |||
| 451 | GotoIfNot(Float64LessThan(var_x.value(), minus_x), &return_minus_x); | |||
| 452 | var_x = Float64Add(var_x.value(), one); | |||
| 453 | Goto(&return_minus_x); | |||
| 454 | } | |||
| 455 | ||||
| 456 | BIND(&return_minus_x)Bind(&return_minus_x); | |||
| 457 | var_x = Float64Neg(var_x.value()); | |||
| 458 | Goto(&return_x); | |||
| 459 | ||||
| 460 | BIND(&return_x)Bind(&return_x); | |||
| 461 | return var_x.value(); | |||
| 462 | } | |||
| 463 | ||||
| 464 | TNode<Float64T> CodeStubAssembler::Float64RoundToEven(TNode<Float64T> x) { | |||
| 465 | if (IsFloat64RoundTiesEvenSupported()) { | |||
| 466 | return Float64RoundTiesEven(x); | |||
| 467 | } | |||
| 468 | // See ES#sec-touint8clamp for details. | |||
| 469 | TNode<Float64T> f = Float64Floor(x); | |||
| 470 | TNode<Float64T> f_and_half = Float64Add(f, Float64Constant(0.5)); | |||
| 471 | ||||
| 472 | TVARIABLE(Float64T, var_result)TVariable<Float64T> var_result(this); | |||
| 473 | Label return_f(this), return_f_plus_one(this), done(this); | |||
| 474 | ||||
| 475 | GotoIf(Float64LessThan(f_and_half, x), &return_f_plus_one); | |||
| 476 | GotoIf(Float64LessThan(x, f_and_half), &return_f); | |||
| 477 | { | |||
| 478 | TNode<Float64T> f_mod_2 = Float64Mod(f, Float64Constant(2.0)); | |||
| 479 | Branch(Float64Equal(f_mod_2, Float64Constant(0.0)), &return_f, | |||
| 480 | &return_f_plus_one); | |||
| 481 | } | |||
| 482 | ||||
| 483 | BIND(&return_f)Bind(&return_f); | |||
| 484 | var_result = f; | |||
| 485 | Goto(&done); | |||
| 486 | ||||
| 487 | BIND(&return_f_plus_one)Bind(&return_f_plus_one); | |||
| 488 | var_result = Float64Add(f, Float64Constant(1.0)); | |||
| 489 | Goto(&done); | |||
| 490 | ||||
| 491 | BIND(&done)Bind(&done); | |||
| 492 | return var_result.value(); | |||
| 493 | } | |||
| 494 | ||||
| 495 | TNode<Float64T> CodeStubAssembler::Float64Trunc(TNode<Float64T> x) { | |||
| 496 | if (IsFloat64RoundTruncateSupported()) { | |||
| 497 | return Float64RoundTruncate(x); | |||
| 498 | } | |||
| 499 | ||||
| 500 | TNode<Float64T> one = Float64Constant(1.0); | |||
| 501 | TNode<Float64T> zero = Float64Constant(0.0); | |||
| 502 | TNode<Float64T> two_52 = Float64Constant(4503599627370496.0E0); | |||
| 503 | TNode<Float64T> minus_two_52 = Float64Constant(-4503599627370496.0E0); | |||
| 504 | ||||
| 505 | TVARIABLE(Float64T, var_x, x)TVariable<Float64T> var_x(x, this); | |||
| 506 | Label return_x(this), return_minus_x(this); | |||
| 507 | ||||
| 508 | // Check if {x} is greater than 0. | |||
| 509 | Label if_xgreaterthanzero(this), if_xnotgreaterthanzero(this); | |||
| 510 | Branch(Float64GreaterThan(x, zero), &if_xgreaterthanzero, | |||
| 511 | &if_xnotgreaterthanzero); | |||
| 512 | ||||
| 513 | BIND(&if_xgreaterthanzero)Bind(&if_xgreaterthanzero); | |||
| 514 | { | |||
| 515 | if (IsFloat64RoundDownSupported()) { | |||
| 516 | var_x = Float64RoundDown(x); | |||
| 517 | } else { | |||
| 518 | // Just return {x} unless it's in the range ]0,2^52[. | |||
| 519 | GotoIf(Float64GreaterThanOrEqual(x, two_52), &return_x); | |||
| 520 | ||||
| 521 | // Round positive {x} towards -Infinity. | |||
| 522 | var_x = Float64Sub(Float64Add(two_52, x), two_52); | |||
| 523 | GotoIfNot(Float64GreaterThan(var_x.value(), x), &return_x); | |||
| 524 | var_x = Float64Sub(var_x.value(), one); | |||
| 525 | } | |||
| 526 | Goto(&return_x); | |||
| 527 | } | |||
| 528 | ||||
| 529 | BIND(&if_xnotgreaterthanzero)Bind(&if_xnotgreaterthanzero); | |||
| 530 | { | |||
| 531 | if (IsFloat64RoundUpSupported()) { | |||
| 532 | var_x = Float64RoundUp(x); | |||
| 533 | Goto(&return_x); | |||
| 534 | } else { | |||
| 535 | // Just return {x} unless its in the range ]-2^52,0[. | |||
| 536 | GotoIf(Float64LessThanOrEqual(x, minus_two_52), &return_x); | |||
| 537 | GotoIfNot(Float64LessThan(x, zero), &return_x); | |||
| 538 | ||||
| 539 | // Round negated {x} towards -Infinity and return result negated. | |||
| 540 | TNode<Float64T> minus_x = Float64Neg(x); | |||
| 541 | var_x = Float64Sub(Float64Add(two_52, minus_x), two_52); | |||
| 542 | GotoIfNot(Float64GreaterThan(var_x.value(), minus_x), &return_minus_x); | |||
| 543 | var_x = Float64Sub(var_x.value(), one); | |||
| 544 | Goto(&return_minus_x); | |||
| 545 | } | |||
| 546 | } | |||
| 547 | ||||
| 548 | BIND(&return_minus_x)Bind(&return_minus_x); | |||
| 549 | var_x = Float64Neg(var_x.value()); | |||
| 550 | Goto(&return_x); | |||
| 551 | ||||
| 552 | BIND(&return_x)Bind(&return_x); | |||
| 553 | return var_x.value(); | |||
| 554 | } | |||
| 555 | ||||
| 556 | TNode<IntPtrT> CodeStubAssembler::PopulationCountFallback( | |||
| 557 | TNode<UintPtrT> value) { | |||
| 558 | // Taken from slow path of base::bits::CountPopulation, the comments here show | |||
| 559 | // C++ code and comments from there for reference. | |||
| 560 | // Fall back to divide-and-conquer popcount (see "Hacker's Delight" by Henry | |||
| 561 | // S. Warren, Jr.), chapter 5-1. | |||
| 562 | constexpr uintptr_t mask[] = {static_cast<uintptr_t>(0x5555555555555555), | |||
| 563 | static_cast<uintptr_t>(0x3333333333333333), | |||
| 564 | static_cast<uintptr_t>(0x0f0f0f0f0f0f0f0f)}; | |||
| 565 | ||||
| 566 | // TNode<UintPtrT> value = Unsigned(value_word); | |||
| 567 | TNode<UintPtrT> lhs, rhs; | |||
| 568 | ||||
| 569 | // Start with 64 buckets of 1 bits, holding values from [0,1]. | |||
| 570 | // {value = ((value >> 1) & mask[0]) + (value & mask[0])} | |||
| 571 | lhs = WordAnd(WordShr(value, UintPtrConstant(1)), UintPtrConstant(mask[0])); | |||
| 572 | rhs = WordAnd(value, UintPtrConstant(mask[0])); | |||
| 573 | value = UintPtrAdd(lhs, rhs); | |||
| 574 | ||||
| 575 | // Having 32 buckets of 2 bits, holding values from [0,2] now. | |||
| 576 | // {value = ((value >> 2) & mask[1]) + (value & mask[1])} | |||
| 577 | lhs = WordAnd(WordShr(value, UintPtrConstant(2)), UintPtrConstant(mask[1])); | |||
| 578 | rhs = WordAnd(value, UintPtrConstant(mask[1])); | |||
| 579 | value = UintPtrAdd(lhs, rhs); | |||
| 580 | ||||
| 581 | // Having 16 buckets of 4 bits, holding values from [0,4] now. | |||
| 582 | // {value = ((value >> 4) & mask[2]) + (value & mask[2])} | |||
| 583 | lhs = WordAnd(WordShr(value, UintPtrConstant(4)), UintPtrConstant(mask[2])); | |||
| 584 | rhs = WordAnd(value, UintPtrConstant(mask[2])); | |||
| 585 | value = UintPtrAdd(lhs, rhs); | |||
| 586 | ||||
| 587 | // Having 8 buckets of 8 bits, holding values from [0,8] now. | |||
| 588 | // From this point on, the buckets are bigger than the number of bits | |||
| 589 | // required to hold the values, and the buckets are bigger the maximum | |||
| 590 | // result, so there's no need to mask value anymore, since there's no | |||
| 591 | // more risk of overflow between buckets. | |||
| 592 | // {value = (value >> 8) + value} | |||
| 593 | lhs = WordShr(value, UintPtrConstant(8)); | |||
| 594 | value = UintPtrAdd(lhs, value); | |||
| 595 | ||||
| 596 | // Having 4 buckets of 16 bits, holding values from [0,16] now. | |||
| 597 | // {value = (value >> 16) + value} | |||
| 598 | lhs = WordShr(value, UintPtrConstant(16)); | |||
| 599 | value = UintPtrAdd(lhs, value); | |||
| 600 | ||||
| 601 | if (Is64()) { | |||
| 602 | // Having 2 buckets of 32 bits, holding values from [0,32] now. | |||
| 603 | // {value = (value >> 32) + value} | |||
| 604 | lhs = WordShr(value, UintPtrConstant(32)); | |||
| 605 | value = UintPtrAdd(lhs, value); | |||
| 606 | } | |||
| 607 | ||||
| 608 | // Having 1 buckets of sizeof(intptr_t) bits, holding values from [0,64] now. | |||
| 609 | // {return static_cast<unsigned>(value & 0xff)} | |||
| 610 | return Signed(WordAnd(value, UintPtrConstant(0xff))); | |||
| 611 | } | |||
| 612 | ||||
| 613 | TNode<Int64T> CodeStubAssembler::PopulationCount64(TNode<Word64T> value) { | |||
| 614 | if (IsWord64PopcntSupported()) { | |||
| 615 | return Word64Popcnt(value); | |||
| 616 | } | |||
| 617 | ||||
| 618 | if (Is32()) { | |||
| 619 | // Unsupported. | |||
| 620 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 621 | } | |||
| 622 | ||||
| 623 | return ReinterpretCast<Int64T>( | |||
| 624 | PopulationCountFallback(ReinterpretCast<UintPtrT>(value))); | |||
| 625 | } | |||
| 626 | ||||
| 627 | TNode<Int32T> CodeStubAssembler::PopulationCount32(TNode<Word32T> value) { | |||
| 628 | if (IsWord32PopcntSupported()) { | |||
| 629 | return Word32Popcnt(value); | |||
| 630 | } | |||
| 631 | ||||
| 632 | if (Is32()) { | |||
| 633 | TNode<IntPtrT> res = | |||
| 634 | PopulationCountFallback(ReinterpretCast<UintPtrT>(value)); | |||
| 635 | return ReinterpretCast<Int32T>(res); | |||
| 636 | } else { | |||
| 637 | TNode<IntPtrT> res = PopulationCountFallback( | |||
| 638 | ReinterpretCast<UintPtrT>(ChangeUint32ToUint64(value))); | |||
| 639 | return TruncateInt64ToInt32(ReinterpretCast<Int64T>(res)); | |||
| 640 | } | |||
| 641 | } | |||
| 642 | ||||
| 643 | TNode<Int64T> CodeStubAssembler::CountTrailingZeros64(TNode<Word64T> value) { | |||
| 644 | if (IsWord64CtzSupported()) { | |||
| 645 | return Word64Ctz(value); | |||
| 646 | } | |||
| 647 | ||||
| 648 | if (Is32()) { | |||
| 649 | // Unsupported. | |||
| 650 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 651 | } | |||
| 652 | ||||
| 653 | // Same fallback as in base::bits::CountTrailingZeros. | |||
| 654 | // Fall back to popcount (see "Hacker's Delight" by Henry S. Warren, Jr.), | |||
| 655 | // chapter 5-4. On x64, since is faster than counting in a loop and faster | |||
| 656 | // than doing binary search. | |||
| 657 | TNode<Word64T> lhs = Word64Not(value); | |||
| 658 | TNode<Word64T> rhs = Uint64Sub(Unsigned(value), Uint64Constant(1)); | |||
| 659 | return PopulationCount64(Word64And(lhs, rhs)); | |||
| 660 | } | |||
| 661 | ||||
| 662 | TNode<Int32T> CodeStubAssembler::CountTrailingZeros32(TNode<Word32T> value) { | |||
| 663 | if (IsWord32CtzSupported()) { | |||
| 664 | return Word32Ctz(value); | |||
| 665 | } | |||
| 666 | ||||
| 667 | if (Is32()) { | |||
| 668 | // Same fallback as in Word64CountTrailingZeros. | |||
| 669 | TNode<Word32T> lhs = Word32BitwiseNot(value); | |||
| 670 | TNode<Word32T> rhs = Int32Sub(Signed(value), Int32Constant(1)); | |||
| 671 | return PopulationCount32(Word32And(lhs, rhs)); | |||
| 672 | } else { | |||
| 673 | TNode<Int64T> res64 = CountTrailingZeros64(ChangeUint32ToUint64(value)); | |||
| 674 | return TruncateInt64ToInt32(Signed(res64)); | |||
| 675 | } | |||
| 676 | } | |||
| 677 | ||||
| 678 | TNode<Int64T> CodeStubAssembler::CountLeadingZeros64(TNode<Word64T> value) { | |||
| 679 | return Word64Clz(value); | |||
| 680 | } | |||
| 681 | ||||
| 682 | TNode<Int32T> CodeStubAssembler::CountLeadingZeros32(TNode<Word32T> value) { | |||
| 683 | return Word32Clz(value); | |||
| 684 | } | |||
| 685 | ||||
| 686 | template <> | |||
| 687 | TNode<Smi> CodeStubAssembler::TaggedToParameter(TNode<Smi> value) { | |||
| 688 | return value; | |||
| 689 | } | |||
| 690 | ||||
| 691 | template <> | |||
| 692 | TNode<IntPtrT> CodeStubAssembler::TaggedToParameter(TNode<Smi> value) { | |||
| 693 | return SmiUntag(value); | |||
| 694 | } | |||
| 695 | ||||
| 696 | TNode<IntPtrT> CodeStubAssembler::TaggedIndexToIntPtr( | |||
| 697 | TNode<TaggedIndex> value) { | |||
| 698 | return Signed(WordSarShiftOutZeros(BitcastTaggedToWordForTagAndSmiBits(value), | |||
| 699 | IntPtrConstant(kSmiTagSize))); | |||
| 700 | } | |||
| 701 | ||||
| 702 | TNode<TaggedIndex> CodeStubAssembler::IntPtrToTaggedIndex( | |||
| 703 | TNode<IntPtrT> value) { | |||
| 704 | return ReinterpretCast<TaggedIndex>( | |||
| 705 | BitcastWordToTaggedSigned(WordShl(value, IntPtrConstant(kSmiTagSize)))); | |||
| 706 | } | |||
| 707 | ||||
| 708 | TNode<Smi> CodeStubAssembler::TaggedIndexToSmi(TNode<TaggedIndex> value) { | |||
| 709 | if (SmiValuesAre32Bits()) { | |||
| 710 | DCHECK_EQ(kSmiShiftSize, 31)((void) 0); | |||
| 711 | return BitcastWordToTaggedSigned( | |||
| 712 | WordShl(BitcastTaggedToWordForTagAndSmiBits(value), | |||
| 713 | IntPtrConstant(kSmiShiftSize))); | |||
| 714 | } | |||
| 715 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 716 | DCHECK_EQ(kSmiShiftSize, 0)((void) 0); | |||
| 717 | return ReinterpretCast<Smi>(value); | |||
| 718 | } | |||
| 719 | ||||
| 720 | TNode<TaggedIndex> CodeStubAssembler::SmiToTaggedIndex(TNode<Smi> value) { | |||
| 721 | if (kSystemPointerSize == kInt32Size) { | |||
| 722 | return ReinterpretCast<TaggedIndex>(value); | |||
| 723 | } | |||
| 724 | if (SmiValuesAre32Bits()) { | |||
| 725 | DCHECK_EQ(kSmiShiftSize, 31)((void) 0); | |||
| 726 | return ReinterpretCast<TaggedIndex>(BitcastWordToTaggedSigned( | |||
| 727 | WordSar(BitcastTaggedToWordForTagAndSmiBits(value), | |||
| 728 | IntPtrConstant(kSmiShiftSize)))); | |||
| 729 | } | |||
| 730 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 731 | DCHECK_EQ(kSmiShiftSize, 0)((void) 0); | |||
| 732 | // Just sign-extend the lower 32 bits. | |||
| 733 | TNode<Int32T> raw = | |||
| 734 | TruncateWordToInt32(BitcastTaggedToWordForTagAndSmiBits(value)); | |||
| 735 | return ReinterpretCast<TaggedIndex>( | |||
| 736 | BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(raw))); | |||
| 737 | } | |||
| 738 | ||||
| 739 | TNode<Smi> CodeStubAssembler::NormalizeSmiIndex(TNode<Smi> smi_index) { | |||
| 740 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 741 | TNode<Int32T> raw = | |||
| 742 | TruncateWordToInt32(BitcastTaggedToWordForTagAndSmiBits(smi_index)); | |||
| 743 | smi_index = BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(raw)); | |||
| 744 | } | |||
| 745 | return smi_index; | |||
| 746 | } | |||
| 747 | ||||
| 748 | TNode<Smi> CodeStubAssembler::SmiFromInt32(TNode<Int32T> value) { | |||
| 749 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 750 | static_assert(!COMPRESS_POINTERS_BOOLfalse || (kSmiShiftSize + kSmiTagSize == 1), | |||
| 751 | "Use shifting instead of add"); | |||
| 752 | return BitcastWordToTaggedSigned( | |||
| 753 | ChangeUint32ToWord(Int32Add(value, value))); | |||
| 754 | } | |||
| 755 | return SmiTag(ChangeInt32ToIntPtr(value)); | |||
| 756 | } | |||
| 757 | ||||
| 758 | TNode<Smi> CodeStubAssembler::SmiFromUint32(TNode<Uint32T> value) { | |||
| 759 | CSA_DCHECK(this, IntPtrLessThan(ChangeUint32ToWord(value),((void)0) | |||
| 760 | IntPtrConstant(Smi::kMaxValue)))((void)0); | |||
| 761 | return SmiFromInt32(Signed(value)); | |||
| 762 | } | |||
| 763 | ||||
| 764 | TNode<BoolT> CodeStubAssembler::IsValidPositiveSmi(TNode<IntPtrT> value) { | |||
| 765 | intptr_t constant_value; | |||
| 766 | if (TryToIntPtrConstant(value, &constant_value)) { | |||
| 767 | return (static_cast<uintptr_t>(constant_value) <= | |||
| 768 | static_cast<uintptr_t>(Smi::kMaxValue)) | |||
| 769 | ? Int32TrueConstant() | |||
| 770 | : Int32FalseConstant(); | |||
| 771 | } | |||
| 772 | ||||
| 773 | return UintPtrLessThanOrEqual(value, IntPtrConstant(Smi::kMaxValue)); | |||
| 774 | } | |||
| 775 | ||||
| 776 | TNode<Smi> CodeStubAssembler::SmiTag(TNode<IntPtrT> value) { | |||
| 777 | int32_t constant_value; | |||
| 778 | if (TryToInt32Constant(value, &constant_value) && | |||
| 779 | Smi::IsValid(constant_value)) { | |||
| 780 | return SmiConstant(constant_value); | |||
| 781 | } | |||
| 782 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 783 | return SmiFromInt32(TruncateIntPtrToInt32(value)); | |||
| 784 | } | |||
| 785 | TNode<Smi> smi = | |||
| 786 | BitcastWordToTaggedSigned(WordShl(value, SmiShiftBitsConstant())); | |||
| 787 | return smi; | |||
| 788 | } | |||
| 789 | ||||
| 790 | TNode<IntPtrT> CodeStubAssembler::SmiUntag(TNode<Smi> value) { | |||
| 791 | intptr_t constant_value; | |||
| 792 | if (TryToIntPtrConstant(value, &constant_value)) { | |||
| 793 | return IntPtrConstant(constant_value >> (kSmiShiftSize + kSmiTagSize)); | |||
| 794 | } | |||
| 795 | TNode<IntPtrT> raw_bits = BitcastTaggedToWordForTagAndSmiBits(value); | |||
| 796 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 797 | // Clear the upper half using sign-extension. | |||
| 798 | raw_bits = ChangeInt32ToIntPtr(TruncateIntPtrToInt32(raw_bits)); | |||
| 799 | } | |||
| 800 | return Signed(WordSarShiftOutZeros(raw_bits, SmiShiftBitsConstant())); | |||
| 801 | } | |||
| 802 | ||||
| 803 | TNode<Int32T> CodeStubAssembler::SmiToInt32(TNode<Smi> value) { | |||
| 804 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 805 | return Signed(Word32SarShiftOutZeros( | |||
| 806 | TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(value)), | |||
| 807 | SmiShiftBitsConstant32())); | |||
| 808 | } | |||
| 809 | TNode<IntPtrT> result = SmiUntag(value); | |||
| 810 | return TruncateIntPtrToInt32(result); | |||
| 811 | } | |||
| 812 | ||||
| 813 | TNode<Float64T> CodeStubAssembler::SmiToFloat64(TNode<Smi> value) { | |||
| 814 | return ChangeInt32ToFloat64(SmiToInt32(value)); | |||
| 815 | } | |||
| 816 | ||||
| 817 | TNode<Smi> CodeStubAssembler::SmiMax(TNode<Smi> a, TNode<Smi> b) { | |||
| 818 | return SelectConstant<Smi>(SmiLessThan(a, b), b, a); | |||
| 819 | } | |||
| 820 | ||||
| 821 | TNode<Smi> CodeStubAssembler::SmiMin(TNode<Smi> a, TNode<Smi> b) { | |||
| 822 | return SelectConstant<Smi>(SmiLessThan(a, b), a, b); | |||
| 823 | } | |||
| 824 | ||||
| 825 | TNode<IntPtrT> CodeStubAssembler::TryIntPtrAdd(TNode<IntPtrT> a, | |||
| 826 | TNode<IntPtrT> b, | |||
| 827 | Label* if_overflow) { | |||
| 828 | TNode<PairT<IntPtrT, BoolT>> pair = IntPtrAddWithOverflow(a, b); | |||
| 829 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 830 | GotoIf(overflow, if_overflow); | |||
| 831 | return Projection<0>(pair); | |||
| 832 | } | |||
| 833 | ||||
| 834 | TNode<IntPtrT> CodeStubAssembler::TryIntPtrSub(TNode<IntPtrT> a, | |||
| 835 | TNode<IntPtrT> b, | |||
| 836 | Label* if_overflow) { | |||
| 837 | TNode<PairT<IntPtrT, BoolT>> pair = IntPtrSubWithOverflow(a, b); | |||
| 838 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 839 | GotoIf(overflow, if_overflow); | |||
| 840 | return Projection<0>(pair); | |||
| 841 | } | |||
| 842 | ||||
| 843 | TNode<Int32T> CodeStubAssembler::TryInt32Mul(TNode<Int32T> a, TNode<Int32T> b, | |||
| 844 | Label* if_overflow) { | |||
| 845 | TNode<PairT<Int32T, BoolT>> pair = Int32MulWithOverflow(a, b); | |||
| 846 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 847 | GotoIf(overflow, if_overflow); | |||
| 848 | return Projection<0>(pair); | |||
| 849 | } | |||
| 850 | ||||
| 851 | TNode<Smi> CodeStubAssembler::TrySmiAdd(TNode<Smi> lhs, TNode<Smi> rhs, | |||
| 852 | Label* if_overflow) { | |||
| 853 | if (SmiValuesAre32Bits()) { | |||
| 854 | return BitcastWordToTaggedSigned( | |||
| 855 | TryIntPtrAdd(BitcastTaggedToWordForTagAndSmiBits(lhs), | |||
| 856 | BitcastTaggedToWordForTagAndSmiBits(rhs), if_overflow)); | |||
| 857 | } else { | |||
| 858 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 859 | TNode<PairT<Int32T, BoolT>> pair = Int32AddWithOverflow( | |||
| 860 | TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(lhs)), | |||
| 861 | TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(rhs))); | |||
| 862 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 863 | GotoIf(overflow, if_overflow); | |||
| 864 | TNode<Int32T> result = Projection<0>(pair); | |||
| 865 | return BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(result)); | |||
| 866 | } | |||
| 867 | } | |||
| 868 | ||||
| 869 | TNode<Smi> CodeStubAssembler::TrySmiSub(TNode<Smi> lhs, TNode<Smi> rhs, | |||
| 870 | Label* if_overflow) { | |||
| 871 | if (SmiValuesAre32Bits()) { | |||
| 872 | TNode<PairT<IntPtrT, BoolT>> pair = | |||
| 873 | IntPtrSubWithOverflow(BitcastTaggedToWordForTagAndSmiBits(lhs), | |||
| 874 | BitcastTaggedToWordForTagAndSmiBits(rhs)); | |||
| 875 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 876 | GotoIf(overflow, if_overflow); | |||
| 877 | TNode<IntPtrT> result = Projection<0>(pair); | |||
| 878 | return BitcastWordToTaggedSigned(result); | |||
| 879 | } else { | |||
| 880 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 881 | TNode<PairT<Int32T, BoolT>> pair = Int32SubWithOverflow( | |||
| 882 | TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(lhs)), | |||
| 883 | TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(rhs))); | |||
| 884 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 885 | GotoIf(overflow, if_overflow); | |||
| 886 | TNode<Int32T> result = Projection<0>(pair); | |||
| 887 | return BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(result)); | |||
| 888 | } | |||
| 889 | } | |||
| 890 | ||||
| 891 | TNode<Smi> CodeStubAssembler::TrySmiAbs(TNode<Smi> a, Label* if_overflow) { | |||
| 892 | if (SmiValuesAre32Bits()) { | |||
| 893 | TNode<PairT<IntPtrT, BoolT>> pair = | |||
| 894 | IntPtrAbsWithOverflow(BitcastTaggedToWordForTagAndSmiBits(a)); | |||
| 895 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 896 | GotoIf(overflow, if_overflow); | |||
| 897 | TNode<IntPtrT> result = Projection<0>(pair); | |||
| 898 | return BitcastWordToTaggedSigned(result); | |||
| 899 | } else { | |||
| 900 | CHECK(SmiValuesAre31Bits())do { if ((__builtin_expect(!!(!(SmiValuesAre31Bits())), 0))) { V8_Fatal("Check failed: %s.", "SmiValuesAre31Bits()"); } } while (false); | |||
| 901 | CHECK(IsInt32AbsWithOverflowSupported())do { if ((__builtin_expect(!!(!(IsInt32AbsWithOverflowSupported ())), 0))) { V8_Fatal("Check failed: %s.", "IsInt32AbsWithOverflowSupported()" ); } } while (false); | |||
| 902 | TNode<PairT<Int32T, BoolT>> pair = Int32AbsWithOverflow( | |||
| 903 | TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(a))); | |||
| 904 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 905 | GotoIf(overflow, if_overflow); | |||
| 906 | TNode<Int32T> result = Projection<0>(pair); | |||
| 907 | return BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(result)); | |||
| 908 | } | |||
| 909 | } | |||
| 910 | ||||
| 911 | TNode<Number> CodeStubAssembler::NumberMax(TNode<Number> a, TNode<Number> b) { | |||
| 912 | // TODO(danno): This could be optimized by specifically handling smi cases. | |||
| 913 | TVARIABLE(Number, result)TVariable<Number> result(this); | |||
| 914 | Label done(this), greater_than_equal_a(this), greater_than_equal_b(this); | |||
| 915 | GotoIfNumberGreaterThanOrEqual(a, b, &greater_than_equal_a); | |||
| 916 | GotoIfNumberGreaterThanOrEqual(b, a, &greater_than_equal_b); | |||
| 917 | result = NanConstant(); | |||
| 918 | Goto(&done); | |||
| 919 | BIND(&greater_than_equal_a)Bind(&greater_than_equal_a); | |||
| 920 | result = a; | |||
| 921 | Goto(&done); | |||
| 922 | BIND(&greater_than_equal_b)Bind(&greater_than_equal_b); | |||
| 923 | result = b; | |||
| 924 | Goto(&done); | |||
| 925 | BIND(&done)Bind(&done); | |||
| 926 | return result.value(); | |||
| 927 | } | |||
| 928 | ||||
| 929 | TNode<Number> CodeStubAssembler::NumberMin(TNode<Number> a, TNode<Number> b) { | |||
| 930 | // TODO(danno): This could be optimized by specifically handling smi cases. | |||
| 931 | TVARIABLE(Number, result)TVariable<Number> result(this); | |||
| 932 | Label done(this), greater_than_equal_a(this), greater_than_equal_b(this); | |||
| 933 | GotoIfNumberGreaterThanOrEqual(a, b, &greater_than_equal_a); | |||
| 934 | GotoIfNumberGreaterThanOrEqual(b, a, &greater_than_equal_b); | |||
| 935 | result = NanConstant(); | |||
| 936 | Goto(&done); | |||
| 937 | BIND(&greater_than_equal_a)Bind(&greater_than_equal_a); | |||
| 938 | result = b; | |||
| 939 | Goto(&done); | |||
| 940 | BIND(&greater_than_equal_b)Bind(&greater_than_equal_b); | |||
| 941 | result = a; | |||
| 942 | Goto(&done); | |||
| 943 | BIND(&done)Bind(&done); | |||
| 944 | return result.value(); | |||
| 945 | } | |||
| 946 | ||||
| 947 | TNode<Number> CodeStubAssembler::SmiMod(TNode<Smi> a, TNode<Smi> b) { | |||
| 948 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 949 | Label return_result(this, &var_result), | |||
| 950 | return_minuszero(this, Label::kDeferred), | |||
| 951 | return_nan(this, Label::kDeferred); | |||
| 952 | ||||
| 953 | // Untag {a} and {b}. | |||
| 954 | TNode<Int32T> int_a = SmiToInt32(a); | |||
| 955 | TNode<Int32T> int_b = SmiToInt32(b); | |||
| 956 | ||||
| 957 | // Return NaN if {b} is zero. | |||
| 958 | GotoIf(Word32Equal(int_b, Int32Constant(0)), &return_nan); | |||
| 959 | ||||
| 960 | // Check if {a} is non-negative. | |||
| 961 | Label if_aisnotnegative(this), if_aisnegative(this, Label::kDeferred); | |||
| 962 | Branch(Int32LessThanOrEqual(Int32Constant(0), int_a), &if_aisnotnegative, | |||
| 963 | &if_aisnegative); | |||
| 964 | ||||
| 965 | BIND(&if_aisnotnegative)Bind(&if_aisnotnegative); | |||
| 966 | { | |||
| 967 | // Fast case, don't need to check any other edge cases. | |||
| 968 | TNode<Int32T> r = Int32Mod(int_a, int_b); | |||
| 969 | var_result = SmiFromInt32(r); | |||
| 970 | Goto(&return_result); | |||
| 971 | } | |||
| 972 | ||||
| 973 | BIND(&if_aisnegative)Bind(&if_aisnegative); | |||
| 974 | { | |||
| 975 | if (SmiValuesAre32Bits()) { | |||
| 976 | // Check if {a} is kMinInt and {b} is -1 (only relevant if the | |||
| 977 | // kMinInt is actually representable as a Smi). | |||
| 978 | Label join(this); | |||
| 979 | GotoIfNot(Word32Equal(int_a, Int32Constant(kMinInt)), &join); | |||
| 980 | GotoIf(Word32Equal(int_b, Int32Constant(-1)), &return_minuszero); | |||
| 981 | Goto(&join); | |||
| 982 | BIND(&join)Bind(&join); | |||
| 983 | } | |||
| 984 | ||||
| 985 | // Perform the integer modulus operation. | |||
| 986 | TNode<Int32T> r = Int32Mod(int_a, int_b); | |||
| 987 | ||||
| 988 | // Check if {r} is zero, and if so return -0, because we have to | |||
| 989 | // take the sign of the left hand side {a}, which is negative. | |||
| 990 | GotoIf(Word32Equal(r, Int32Constant(0)), &return_minuszero); | |||
| 991 | ||||
| 992 | // The remainder {r} can be outside the valid Smi range on 32bit | |||
| 993 | // architectures, so we cannot just say SmiFromInt32(r) here. | |||
| 994 | var_result = ChangeInt32ToTagged(r); | |||
| 995 | Goto(&return_result); | |||
| 996 | } | |||
| 997 | ||||
| 998 | BIND(&return_minuszero)Bind(&return_minuszero); | |||
| 999 | var_result = MinusZeroConstant(); | |||
| 1000 | Goto(&return_result); | |||
| 1001 | ||||
| 1002 | BIND(&return_nan)Bind(&return_nan); | |||
| 1003 | var_result = NanConstant(); | |||
| 1004 | Goto(&return_result); | |||
| 1005 | ||||
| 1006 | BIND(&return_result)Bind(&return_result); | |||
| 1007 | return var_result.value(); | |||
| 1008 | } | |||
| 1009 | ||||
| 1010 | TNode<Number> CodeStubAssembler::SmiMul(TNode<Smi> a, TNode<Smi> b) { | |||
| 1011 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 1012 | TVARIABLE(Float64T, var_lhs_float64)TVariable<Float64T> var_lhs_float64(this); | |||
| 1013 | TVARIABLE(Float64T, var_rhs_float64)TVariable<Float64T> var_rhs_float64(this); | |||
| 1014 | Label return_result(this, &var_result); | |||
| 1015 | ||||
| 1016 | // Both {a} and {b} are Smis. Convert them to integers and multiply. | |||
| 1017 | TNode<Int32T> lhs32 = SmiToInt32(a); | |||
| 1018 | TNode<Int32T> rhs32 = SmiToInt32(b); | |||
| 1019 | auto pair = Int32MulWithOverflow(lhs32, rhs32); | |||
| 1020 | ||||
| 1021 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 1022 | ||||
| 1023 | // Check if the multiplication overflowed. | |||
| 1024 | Label if_overflow(this, Label::kDeferred), if_notoverflow(this); | |||
| 1025 | Branch(overflow, &if_overflow, &if_notoverflow); | |||
| 1026 | BIND(&if_notoverflow)Bind(&if_notoverflow); | |||
| 1027 | { | |||
| 1028 | // If the answer is zero, we may need to return -0.0, depending on the | |||
| 1029 | // input. | |||
| 1030 | Label answer_zero(this), answer_not_zero(this); | |||
| 1031 | TNode<Int32T> answer = Projection<0>(pair); | |||
| 1032 | TNode<Int32T> zero = Int32Constant(0); | |||
| 1033 | Branch(Word32Equal(answer, zero), &answer_zero, &answer_not_zero); | |||
| 1034 | BIND(&answer_not_zero)Bind(&answer_not_zero); | |||
| 1035 | { | |||
| 1036 | var_result = ChangeInt32ToTagged(answer); | |||
| 1037 | Goto(&return_result); | |||
| 1038 | } | |||
| 1039 | BIND(&answer_zero)Bind(&answer_zero); | |||
| 1040 | { | |||
| 1041 | TNode<Int32T> or_result = Word32Or(lhs32, rhs32); | |||
| 1042 | Label if_should_be_negative_zero(this), if_should_be_zero(this); | |||
| 1043 | Branch(Int32LessThan(or_result, zero), &if_should_be_negative_zero, | |||
| 1044 | &if_should_be_zero); | |||
| 1045 | BIND(&if_should_be_negative_zero)Bind(&if_should_be_negative_zero); | |||
| 1046 | { | |||
| 1047 | var_result = MinusZeroConstant(); | |||
| 1048 | Goto(&return_result); | |||
| 1049 | } | |||
| 1050 | BIND(&if_should_be_zero)Bind(&if_should_be_zero); | |||
| 1051 | { | |||
| 1052 | var_result = SmiConstant(0); | |||
| 1053 | Goto(&return_result); | |||
| 1054 | } | |||
| 1055 | } | |||
| 1056 | } | |||
| 1057 | BIND(&if_overflow)Bind(&if_overflow); | |||
| 1058 | { | |||
| 1059 | var_lhs_float64 = SmiToFloat64(a); | |||
| 1060 | var_rhs_float64 = SmiToFloat64(b); | |||
| 1061 | TNode<Float64T> value = | |||
| 1062 | Float64Mul(var_lhs_float64.value(), var_rhs_float64.value()); | |||
| 1063 | var_result = AllocateHeapNumberWithValue(value); | |||
| 1064 | Goto(&return_result); | |||
| 1065 | } | |||
| 1066 | ||||
| 1067 | BIND(&return_result)Bind(&return_result); | |||
| 1068 | return var_result.value(); | |||
| 1069 | } | |||
| 1070 | ||||
| 1071 | TNode<Smi> CodeStubAssembler::TrySmiDiv(TNode<Smi> dividend, TNode<Smi> divisor, | |||
| 1072 | Label* bailout) { | |||
| 1073 | // Both {a} and {b} are Smis. Bailout to floating point division if {divisor} | |||
| 1074 | // is zero. | |||
| 1075 | GotoIf(TaggedEqual(divisor, SmiConstant(0)), bailout); | |||
| 1076 | ||||
| 1077 | // Do floating point division if {dividend} is zero and {divisor} is | |||
| 1078 | // negative. | |||
| 1079 | Label dividend_is_zero(this), dividend_is_not_zero(this); | |||
| 1080 | Branch(TaggedEqual(dividend, SmiConstant(0)), ÷nd_is_zero, | |||
| 1081 | ÷nd_is_not_zero); | |||
| 1082 | ||||
| 1083 | BIND(÷nd_is_zero)Bind(÷nd_is_zero); | |||
| 1084 | { | |||
| 1085 | GotoIf(SmiLessThan(divisor, SmiConstant(0)), bailout); | |||
| 1086 | Goto(÷nd_is_not_zero); | |||
| 1087 | } | |||
| 1088 | BIND(÷nd_is_not_zero)Bind(÷nd_is_not_zero); | |||
| 1089 | ||||
| 1090 | TNode<Int32T> untagged_divisor = SmiToInt32(divisor); | |||
| 1091 | TNode<Int32T> untagged_dividend = SmiToInt32(dividend); | |||
| 1092 | ||||
| 1093 | // Do floating point division if {dividend} is kMinInt (or kMinInt - 1 | |||
| 1094 | // if the Smi size is 31) and {divisor} is -1. | |||
| 1095 | Label divisor_is_minus_one(this), divisor_is_not_minus_one(this); | |||
| 1096 | Branch(Word32Equal(untagged_divisor, Int32Constant(-1)), | |||
| 1097 | &divisor_is_minus_one, &divisor_is_not_minus_one); | |||
| 1098 | ||||
| 1099 | BIND(&divisor_is_minus_one)Bind(&divisor_is_minus_one); | |||
| 1100 | { | |||
| 1101 | GotoIf(Word32Equal( | |||
| 1102 | untagged_dividend, | |||
| 1103 | Int32Constant(kSmiValueSize == 32 ? kMinInt : (kMinInt >> 1))), | |||
| 1104 | bailout); | |||
| 1105 | Goto(&divisor_is_not_minus_one); | |||
| 1106 | } | |||
| 1107 | BIND(&divisor_is_not_minus_one)Bind(&divisor_is_not_minus_one); | |||
| 1108 | ||||
| 1109 | TNode<Int32T> untagged_result = Int32Div(untagged_dividend, untagged_divisor); | |||
| 1110 | TNode<Int32T> truncated = Int32Mul(untagged_result, untagged_divisor); | |||
| 1111 | ||||
| 1112 | // Do floating point division if the remainder is not 0. | |||
| 1113 | GotoIf(Word32NotEqual(untagged_dividend, truncated), bailout); | |||
| 1114 | ||||
| 1115 | return SmiFromInt32(untagged_result); | |||
| 1116 | } | |||
| 1117 | ||||
| 1118 | TNode<Smi> CodeStubAssembler::SmiLexicographicCompare(TNode<Smi> x, | |||
| 1119 | TNode<Smi> y) { | |||
| 1120 | TNode<ExternalReference> smi_lexicographic_compare = | |||
| 1121 | ExternalConstant(ExternalReference::smi_lexicographic_compare_function()); | |||
| 1122 | TNode<ExternalReference> isolate_ptr = | |||
| 1123 | ExternalConstant(ExternalReference::isolate_address(isolate())); | |||
| 1124 | return CAST(CallCFunction(smi_lexicographic_compare, MachineType::AnyTagged(),Cast(CallCFunction(smi_lexicographic_compare, MachineType::AnyTagged (), std::make_pair(MachineType::Pointer(), isolate_ptr), std:: make_pair(MachineType::AnyTagged(), x), std::make_pair(MachineType ::AnyTagged(), y))) | |||
| 1125 | std::make_pair(MachineType::Pointer(), isolate_ptr),Cast(CallCFunction(smi_lexicographic_compare, MachineType::AnyTagged (), std::make_pair(MachineType::Pointer(), isolate_ptr), std:: make_pair(MachineType::AnyTagged(), x), std::make_pair(MachineType ::AnyTagged(), y))) | |||
| 1126 | std::make_pair(MachineType::AnyTagged(), x),Cast(CallCFunction(smi_lexicographic_compare, MachineType::AnyTagged (), std::make_pair(MachineType::Pointer(), isolate_ptr), std:: make_pair(MachineType::AnyTagged(), x), std::make_pair(MachineType ::AnyTagged(), y))) | |||
| 1127 | std::make_pair(MachineType::AnyTagged(), y)))Cast(CallCFunction(smi_lexicographic_compare, MachineType::AnyTagged (), std::make_pair(MachineType::Pointer(), isolate_ptr), std:: make_pair(MachineType::AnyTagged(), x), std::make_pair(MachineType ::AnyTagged(), y))); | |||
| 1128 | } | |||
| 1129 | ||||
| 1130 | TNode<Int32T> CodeStubAssembler::TruncateWordToInt32(TNode<WordT> value) { | |||
| 1131 | if (Is64()) { | |||
| 1132 | return TruncateInt64ToInt32(ReinterpretCast<Int64T>(value)); | |||
| 1133 | } | |||
| 1134 | return ReinterpretCast<Int32T>(value); | |||
| 1135 | } | |||
| 1136 | ||||
| 1137 | TNode<Int32T> CodeStubAssembler::TruncateIntPtrToInt32(TNode<IntPtrT> value) { | |||
| 1138 | if (Is64()) { | |||
| 1139 | return TruncateInt64ToInt32(ReinterpretCast<Int64T>(value)); | |||
| 1140 | } | |||
| 1141 | return ReinterpretCast<Int32T>(value); | |||
| 1142 | } | |||
| 1143 | ||||
| 1144 | TNode<BoolT> CodeStubAssembler::TaggedIsSmi(TNode<MaybeObject> a) { | |||
| 1145 | STATIC_ASSERT(kSmiTagMask < kMaxUInt32)static_assert(kSmiTagMask < kMaxUInt32, "kSmiTagMask < kMaxUInt32" ); | |||
| 1146 | return Word32Equal( | |||
| 1147 | Word32And(TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(a)), | |||
| 1148 | Int32Constant(kSmiTagMask)), | |||
| 1149 | Int32Constant(0)); | |||
| 1150 | } | |||
| 1151 | ||||
| 1152 | TNode<BoolT> CodeStubAssembler::TaggedIsNotSmi(TNode<MaybeObject> a) { | |||
| 1153 | return Word32BinaryNot(TaggedIsSmi(a)); | |||
| 1154 | } | |||
| 1155 | ||||
| 1156 | TNode<BoolT> CodeStubAssembler::TaggedIsPositiveSmi(TNode<Object> a) { | |||
| 1157 | #if defined(V8_HOST_ARCH_32_BIT) || defined(V8_31BIT_SMIS_ON_64BIT_ARCH) | |||
| 1158 | return Word32Equal( | |||
| 1159 | Word32And( | |||
| 1160 | TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(a)), | |||
| 1161 | Uint32Constant(static_cast<uint32_t>(kSmiTagMask | kSmiSignMask))), | |||
| 1162 | Int32Constant(0)); | |||
| 1163 | #else | |||
| 1164 | return WordEqual(WordAnd(BitcastTaggedToWordForTagAndSmiBits(a), | |||
| 1165 | IntPtrConstant(kSmiTagMask | kSmiSignMask)), | |||
| 1166 | IntPtrConstant(0)); | |||
| 1167 | #endif | |||
| 1168 | } | |||
| 1169 | ||||
| 1170 | TNode<BoolT> CodeStubAssembler::WordIsAligned(TNode<WordT> word, | |||
| 1171 | size_t alignment) { | |||
| 1172 | DCHECK(base::bits::IsPowerOfTwo(alignment))((void) 0); | |||
| 1173 | DCHECK_LE(alignment, kMaxUInt32)((void) 0); | |||
| 1174 | return Word32Equal( | |||
| 1175 | Int32Constant(0), | |||
| 1176 | Word32And(TruncateWordToInt32(word), | |||
| 1177 | Uint32Constant(static_cast<uint32_t>(alignment) - 1))); | |||
| 1178 | } | |||
| 1179 | ||||
| 1180 | #if DEBUG | |||
| 1181 | void CodeStubAssembler::Bind(Label* label, AssemblerDebugInfo debug_info) { | |||
| 1182 | CodeAssembler::Bind(label, debug_info); | |||
| 1183 | } | |||
| 1184 | #endif // DEBUG | |||
| 1185 | ||||
| 1186 | void CodeStubAssembler::Bind(Label* label) { CodeAssembler::Bind(label); } | |||
| 1187 | ||||
| 1188 | TNode<Float64T> CodeStubAssembler::LoadDoubleWithHoleCheck( | |||
| 1189 | TNode<FixedDoubleArray> array, TNode<IntPtrT> index, Label* if_hole) { | |||
| 1190 | return LoadFixedDoubleArrayElement(array, index, if_hole); | |||
| 1191 | } | |||
| 1192 | ||||
| 1193 | void CodeStubAssembler::BranchIfJSReceiver(TNode<Object> object, Label* if_true, | |||
| 1194 | Label* if_false) { | |||
| 1195 | GotoIf(TaggedIsSmi(object), if_false); | |||
| 1196 | STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE)static_assert(LAST_JS_RECEIVER_TYPE == LAST_TYPE, "LAST_JS_RECEIVER_TYPE == LAST_TYPE" ); | |||
| 1197 | Branch(IsJSReceiver(CAST(object)Cast(object)), if_true, if_false); | |||
| 1198 | } | |||
| 1199 | ||||
| 1200 | void CodeStubAssembler::GotoIfForceSlowPath(Label* if_true) { | |||
| 1201 | #ifdef V8_ENABLE_FORCE_SLOW_PATH | |||
| 1202 | const TNode<ExternalReference> force_slow_path_addr = | |||
| 1203 | ExternalConstant(ExternalReference::force_slow_path(isolate())); | |||
| 1204 | const TNode<Uint8T> force_slow = Load<Uint8T>(force_slow_path_addr); | |||
| 1205 | ||||
| 1206 | GotoIf(force_slow, if_true); | |||
| 1207 | #endif | |||
| 1208 | } | |||
| 1209 | ||||
| 1210 | TNode<HeapObject> CodeStubAssembler::AllocateRaw(TNode<IntPtrT> size_in_bytes, | |||
| 1211 | AllocationFlags flags, | |||
| 1212 | TNode<RawPtrT> top_address, | |||
| 1213 | TNode<RawPtrT> limit_address) { | |||
| 1214 | Label if_out_of_memory(this, Label::kDeferred); | |||
| 1215 | ||||
| 1216 | // TODO(jgruber,jkummerow): Extract the slow paths (= probably everything | |||
| 1217 | // but bump pointer allocation) into a builtin to save code space. The | |||
| 1218 | // size_in_bytes check may be moved there as well since a non-smi | |||
| 1219 | // size_in_bytes probably doesn't fit into the bump pointer region | |||
| 1220 | // (double-check that). | |||
| 1221 | ||||
| 1222 | intptr_t size_in_bytes_constant; | |||
| 1223 | bool size_in_bytes_is_constant = false; | |||
| 1224 | if (TryToIntPtrConstant(size_in_bytes, &size_in_bytes_constant)) { | |||
| 1225 | size_in_bytes_is_constant = true; | |||
| 1226 | CHECK(Internals::IsValidSmi(size_in_bytes_constant))do { if ((__builtin_expect(!!(!(Internals::IsValidSmi(size_in_bytes_constant ))), 0))) { V8_Fatal("Check failed: %s.", "Internals::IsValidSmi(size_in_bytes_constant)" ); } } while (false); | |||
| 1227 | CHECK_GT(size_in_bytes_constant, 0)do { bool _cmp = ::v8::base::CmpGTImpl< typename ::v8::base ::pass_value_or_ref<decltype(size_in_bytes_constant)>:: type, typename ::v8::base::pass_value_or_ref<decltype(0)> ::type>((size_in_bytes_constant), (0)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "size_in_bytes_constant" " " ">" " " "0"); } } while (false); } while (false); | |||
| 1228 | } else { | |||
| 1229 | GotoIfNot(IsValidPositiveSmi(size_in_bytes), &if_out_of_memory); | |||
| 1230 | } | |||
| 1231 | ||||
| 1232 | TNode<RawPtrT> top = Load<RawPtrT>(top_address); | |||
| 1233 | TNode<RawPtrT> limit = Load<RawPtrT>(limit_address); | |||
| 1234 | ||||
| 1235 | // If there's not enough space, call the runtime. | |||
| 1236 | TVARIABLE(Object, result)TVariable<Object> result(this); | |||
| 1237 | Label runtime_call(this, Label::kDeferred), no_runtime_call(this), out(this); | |||
| 1238 | ||||
| 1239 | bool needs_double_alignment = flags & AllocationFlag::kDoubleAlignment; | |||
| 1240 | bool allow_large_object_allocation = | |||
| 1241 | flags & AllocationFlag::kAllowLargeObjectAllocation; | |||
| 1242 | ||||
| 1243 | if (allow_large_object_allocation) { | |||
| 1244 | Label next(this); | |||
| 1245 | GotoIf(IsRegularHeapObjectSize(size_in_bytes), &next); | |||
| 1246 | ||||
| 1247 | TNode<Smi> runtime_flags = SmiConstant(Smi::FromInt( | |||
| 1248 | AllocateDoubleAlignFlag::encode(needs_double_alignment) | | |||
| 1249 | AllowLargeObjectAllocationFlag::encode(allow_large_object_allocation))); | |||
| 1250 | result = | |||
| 1251 | CallRuntime(Runtime::kAllocateInYoungGeneration, NoContextConstant(), | |||
| 1252 | SmiTag(size_in_bytes), runtime_flags); | |||
| 1253 | Goto(&out); | |||
| 1254 | ||||
| 1255 | BIND(&next)Bind(&next); | |||
| 1256 | } | |||
| 1257 | ||||
| 1258 | TVARIABLE(IntPtrT, adjusted_size, size_in_bytes)TVariable<IntPtrT> adjusted_size(size_in_bytes, this); | |||
| 1259 | ||||
| 1260 | if (needs_double_alignment) { | |||
| 1261 | Label next(this); | |||
| 1262 | GotoIfNot(WordAnd(top, IntPtrConstant(kDoubleAlignmentMask)), &next); | |||
| 1263 | ||||
| 1264 | adjusted_size = IntPtrAdd(size_in_bytes, IntPtrConstant(4)); | |||
| 1265 | Goto(&next); | |||
| 1266 | ||||
| 1267 | BIND(&next)Bind(&next); | |||
| 1268 | } | |||
| 1269 | ||||
| 1270 | TNode<IntPtrT> new_top = | |||
| 1271 | IntPtrAdd(UncheckedCast<IntPtrT>(top), adjusted_size.value()); | |||
| 1272 | ||||
| 1273 | Branch(UintPtrGreaterThanOrEqual(new_top, limit), &runtime_call, | |||
| 1274 | &no_runtime_call); | |||
| 1275 | ||||
| 1276 | BIND(&runtime_call)Bind(&runtime_call); | |||
| 1277 | { | |||
| 1278 | TNode<Smi> runtime_flags = SmiConstant(Smi::FromInt( | |||
| 1279 | AllocateDoubleAlignFlag::encode(needs_double_alignment) | | |||
| 1280 | AllowLargeObjectAllocationFlag::encode(allow_large_object_allocation))); | |||
| 1281 | if (flags & AllocationFlag::kPretenured) { | |||
| 1282 | result = | |||
| 1283 | CallRuntime(Runtime::kAllocateInOldGeneration, NoContextConstant(), | |||
| 1284 | SmiTag(size_in_bytes), runtime_flags); | |||
| 1285 | } else { | |||
| 1286 | result = | |||
| 1287 | CallRuntime(Runtime::kAllocateInYoungGeneration, NoContextConstant(), | |||
| 1288 | SmiTag(size_in_bytes), runtime_flags); | |||
| 1289 | } | |||
| 1290 | Goto(&out); | |||
| 1291 | } | |||
| 1292 | ||||
| 1293 | // When there is enough space, return `top' and bump it up. | |||
| 1294 | BIND(&no_runtime_call)Bind(&no_runtime_call); | |||
| 1295 | { | |||
| 1296 | StoreNoWriteBarrier(MachineType::PointerRepresentation(), top_address, | |||
| 1297 | new_top); | |||
| 1298 | ||||
| 1299 | TVARIABLE(IntPtrT, address, UncheckedCast<IntPtrT>(top))TVariable<IntPtrT> address(UncheckedCast<IntPtrT> (top), this); | |||
| 1300 | ||||
| 1301 | if (needs_double_alignment) { | |||
| 1302 | Label next(this); | |||
| 1303 | GotoIf(IntPtrEqual(adjusted_size.value(), size_in_bytes), &next); | |||
| 1304 | ||||
| 1305 | // Store a filler and increase the address by 4. | |||
| 1306 | StoreNoWriteBarrier(MachineRepresentation::kTagged, top, | |||
| 1307 | OnePointerFillerMapConstant()); | |||
| 1308 | address = IntPtrAdd(UncheckedCast<IntPtrT>(top), IntPtrConstant(4)); | |||
| 1309 | Goto(&next); | |||
| 1310 | ||||
| 1311 | BIND(&next)Bind(&next); | |||
| 1312 | } | |||
| 1313 | ||||
| 1314 | result = BitcastWordToTagged( | |||
| 1315 | IntPtrAdd(address.value(), IntPtrConstant(kHeapObjectTag))); | |||
| 1316 | Goto(&out); | |||
| 1317 | } | |||
| 1318 | ||||
| 1319 | if (!size_in_bytes_is_constant) { | |||
| 1320 | BIND(&if_out_of_memory)Bind(&if_out_of_memory); | |||
| 1321 | CallRuntime(Runtime::kFatalProcessOutOfMemoryInAllocateRaw, | |||
| 1322 | NoContextConstant()); | |||
| 1323 | Unreachable(); | |||
| 1324 | } | |||
| 1325 | ||||
| 1326 | BIND(&out)Bind(&out); | |||
| 1327 | return UncheckedCast<HeapObject>(result.value()); | |||
| 1328 | } | |||
| 1329 | ||||
| 1330 | TNode<HeapObject> CodeStubAssembler::AllocateRawUnaligned( | |||
| 1331 | TNode<IntPtrT> size_in_bytes, AllocationFlags flags, | |||
| 1332 | TNode<RawPtrT> top_address, TNode<RawPtrT> limit_address) { | |||
| 1333 | DCHECK_EQ(flags & AllocationFlag::kDoubleAlignment, 0)((void) 0); | |||
| 1334 | return AllocateRaw(size_in_bytes, flags, top_address, limit_address); | |||
| ||||
| 1335 | } | |||
| 1336 | ||||
| 1337 | TNode<HeapObject> CodeStubAssembler::AllocateRawDoubleAligned( | |||
| 1338 | TNode<IntPtrT> size_in_bytes, AllocationFlags flags, | |||
| 1339 | TNode<RawPtrT> top_address, TNode<RawPtrT> limit_address) { | |||
| 1340 | #if defined(V8_HOST_ARCH_32_BIT) | |||
| 1341 | return AllocateRaw(size_in_bytes, flags | AllocationFlag::kDoubleAlignment, | |||
| 1342 | top_address, limit_address); | |||
| 1343 | #elif defined(V8_HOST_ARCH_64_BIT1) | |||
| 1344 | #ifdef V8_COMPRESS_POINTERS | |||
| 1345 | // TODO(ishell, v8:8875): Consider using aligned allocations once the | |||
| 1346 | // allocation alignment inconsistency is fixed. For now we keep using | |||
| 1347 | // unaligned access since both x64 and arm64 architectures (where pointer | |||
| 1348 | // compression is supported) allow unaligned access to doubles and full words. | |||
| 1349 | #endif // V8_COMPRESS_POINTERS | |||
| 1350 | // Allocation on 64 bit machine is naturally double aligned | |||
| 1351 | return AllocateRaw(size_in_bytes, flags & ~AllocationFlag::kDoubleAlignment, | |||
| 1352 | top_address, limit_address); | |||
| 1353 | #else | |||
| 1354 | #error Architecture not supported | |||
| 1355 | #endif | |||
| 1356 | } | |||
| 1357 | ||||
| 1358 | TNode<HeapObject> CodeStubAssembler::AllocateInNewSpace( | |||
| 1359 | TNode<IntPtrT> size_in_bytes, AllocationFlags flags) { | |||
| 1360 | DCHECK(flags == AllocationFlag::kNone ||((void) 0) | |||
| 1361 | flags == AllocationFlag::kDoubleAlignment)((void) 0); | |||
| 1362 | CSA_DCHECK(this, IsRegularHeapObjectSize(size_in_bytes))((void)0); | |||
| 1363 | return Allocate(size_in_bytes, flags); | |||
| 1364 | } | |||
| 1365 | ||||
| 1366 | TNode<HeapObject> CodeStubAssembler::Allocate(TNode<IntPtrT> size_in_bytes, | |||
| 1367 | AllocationFlags flags) { | |||
| 1368 | Comment("Allocate"); | |||
| 1369 | if (FLAG_single_generation) flags |= AllocationFlag::kPretenured; | |||
| 1370 | bool const new_space = !(flags & AllocationFlag::kPretenured); | |||
| 1371 | bool const allow_large_objects = | |||
| 1372 | flags & AllocationFlag::kAllowLargeObjectAllocation; | |||
| 1373 | if (!allow_large_objects) { | |||
| 1374 | intptr_t size_constant; | |||
| 1375 | if (TryToIntPtrConstant(size_in_bytes, &size_constant)) { | |||
| 1376 | CHECK_LE(size_constant, kMaxRegularHeapObjectSize)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(size_constant)>::type, typename ::v8::base::pass_value_or_ref<decltype(kMaxRegularHeapObjectSize )>::type>((size_constant), (kMaxRegularHeapObjectSize)) ; do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "size_constant" " " "<=" " " "kMaxRegularHeapObjectSize" ); } } while (false); } while (false); | |||
| 1377 | } else { | |||
| 1378 | CSA_DCHECK(this, IsRegularHeapObjectSize(size_in_bytes))((void)0); | |||
| 1379 | } | |||
| 1380 | } | |||
| 1381 | if (!(flags & AllocationFlag::kDoubleAlignment)) { | |||
| 1382 | return OptimizedAllocate( | |||
| 1383 | size_in_bytes, | |||
| 1384 | new_space ? AllocationType::kYoung : AllocationType::kOld, | |||
| 1385 | allow_large_objects ? AllowLargeObjects::kTrue | |||
| 1386 | : AllowLargeObjects::kFalse); | |||
| 1387 | } | |||
| 1388 | TNode<ExternalReference> top_address = ExternalConstant( | |||
| 1389 | new_space | |||
| 1390 | ? ExternalReference::new_space_allocation_top_address(isolate()) | |||
| 1391 | : ExternalReference::old_space_allocation_top_address(isolate())); | |||
| 1392 | ||||
| 1393 | #ifdef DEBUG | |||
| 1394 | // New space is optional and if disabled both top and limit return | |||
| 1395 | // kNullAddress. | |||
| 1396 | if (ExternalReference::new_space_allocation_top_address(isolate()) | |||
| 1397 | .address() != kNullAddress) { | |||
| 1398 | Address raw_top_address = | |||
| 1399 | ExternalReference::new_space_allocation_top_address(isolate()) | |||
| 1400 | .address(); | |||
| 1401 | Address raw_limit_address = | |||
| 1402 | ExternalReference::new_space_allocation_limit_address(isolate()) | |||
| 1403 | .address(); | |||
| 1404 | ||||
| 1405 | CHECK_EQ(kSystemPointerSize, raw_limit_address - raw_top_address)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(kSystemPointerSize)>::type , typename ::v8::base::pass_value_or_ref<decltype(raw_limit_address - raw_top_address)>::type>((kSystemPointerSize), (raw_limit_address - raw_top_address)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "kSystemPointerSize" " " "==" " " "raw_limit_address - raw_top_address"); } } while ( false); } while (false); | |||
| 1406 | } | |||
| 1407 | ||||
| 1408 | DCHECK_EQ(kSystemPointerSize,((void) 0) | |||
| 1409 | ExternalReference::old_space_allocation_limit_address(isolate())((void) 0) | |||
| 1410 | .address() -((void) 0) | |||
| 1411 | ExternalReference::old_space_allocation_top_address(isolate())((void) 0) | |||
| 1412 | .address())((void) 0); | |||
| 1413 | #endif | |||
| 1414 | ||||
| 1415 | TNode<IntPtrT> limit_address = | |||
| 1416 | IntPtrAdd(ReinterpretCast<IntPtrT>(top_address), | |||
| 1417 | IntPtrConstant(kSystemPointerSize)); | |||
| 1418 | ||||
| 1419 | if (flags & AllocationFlag::kDoubleAlignment) { | |||
| 1420 | return AllocateRawDoubleAligned(size_in_bytes, flags, | |||
| 1421 | ReinterpretCast<RawPtrT>(top_address), | |||
| 1422 | ReinterpretCast<RawPtrT>(limit_address)); | |||
| 1423 | } else { | |||
| 1424 | return AllocateRawUnaligned(size_in_bytes, flags, | |||
| 1425 | ReinterpretCast<RawPtrT>(top_address), | |||
| 1426 | ReinterpretCast<RawPtrT>(limit_address)); | |||
| 1427 | } | |||
| 1428 | } | |||
| 1429 | ||||
| 1430 | TNode<HeapObject> CodeStubAssembler::AllocateInNewSpace(int size_in_bytes, | |||
| 1431 | AllocationFlags flags) { | |||
| 1432 | CHECK(flags == AllocationFlag::kNone ||do { if ((__builtin_expect(!!(!(flags == AllocationFlag::kNone || flags == AllocationFlag::kDoubleAlignment)), 0))) { V8_Fatal ("Check failed: %s.", "flags == AllocationFlag::kNone || flags == AllocationFlag::kDoubleAlignment" ); } } while (false) | |||
| 1433 | flags == AllocationFlag::kDoubleAlignment)do { if ((__builtin_expect(!!(!(flags == AllocationFlag::kNone || flags == AllocationFlag::kDoubleAlignment)), 0))) { V8_Fatal ("Check failed: %s.", "flags == AllocationFlag::kNone || flags == AllocationFlag::kDoubleAlignment" ); } } while (false); | |||
| 1434 | DCHECK_LE(size_in_bytes, kMaxRegularHeapObjectSize)((void) 0); | |||
| 1435 | return CodeStubAssembler::Allocate(IntPtrConstant(size_in_bytes), flags); | |||
| 1436 | } | |||
| 1437 | ||||
| 1438 | TNode<HeapObject> CodeStubAssembler::Allocate(int size_in_bytes, | |||
| 1439 | AllocationFlags flags) { | |||
| 1440 | return CodeStubAssembler::Allocate(IntPtrConstant(size_in_bytes), flags); | |||
| 1441 | } | |||
| 1442 | ||||
| 1443 | TNode<BoolT> CodeStubAssembler::IsRegularHeapObjectSize(TNode<IntPtrT> size) { | |||
| 1444 | return UintPtrLessThanOrEqual(size, | |||
| 1445 | IntPtrConstant(kMaxRegularHeapObjectSize)); | |||
| 1446 | } | |||
| 1447 | ||||
| 1448 | #if V8_ENABLE_WEBASSEMBLY1 | |||
| 1449 | TNode<HeapObject> CodeStubAssembler::AllocateWasmArray( | |||
| 1450 | TNode<IntPtrT> size_in_bytes, int initialization) { | |||
| 1451 | TNode<HeapObject> array = | |||
| 1452 | Allocate(size_in_bytes, AllocationFlag::kAllowLargeObjectAllocation); | |||
| 1453 | if (initialization == kUninitialized) return array; | |||
| 1454 | ||||
| 1455 | TNode<IntPtrT> array_address = BitcastTaggedToWord(array); | |||
| 1456 | TNode<IntPtrT> start = IntPtrAdd( | |||
| 1457 | array_address, IntPtrConstant(WasmArray::kHeaderSize - kHeapObjectTag)); | |||
| 1458 | TNode<IntPtrT> limit = IntPtrAdd( | |||
| 1459 | array_address, IntPtrSub(size_in_bytes, IntPtrConstant(kHeapObjectTag))); | |||
| 1460 | ||||
| 1461 | TNode<Object> value; | |||
| 1462 | if (initialization == kInitializeToZero) { | |||
| 1463 | // A pointer-sized zero pattern is just what we need for numeric Wasm | |||
| 1464 | // arrays (their object size is rounded up to a multiple of kPointerSize). | |||
| 1465 | value = SmiConstant(0); | |||
| 1466 | } else if (initialization == kInitializeToNull) { | |||
| 1467 | value = NullConstant(); | |||
| 1468 | } else { | |||
| 1469 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 1470 | } | |||
| 1471 | StoreFieldsNoWriteBarrier(start, limit, value); | |||
| 1472 | return array; | |||
| 1473 | } | |||
| 1474 | #endif // V8_ENABLE_WEBASSEMBLY | |||
| 1475 | ||||
| 1476 | void CodeStubAssembler::BranchIfToBooleanIsTrue(TNode<Object> value, | |||
| 1477 | Label* if_true, | |||
| 1478 | Label* if_false) { | |||
| 1479 | Label if_smi(this), if_notsmi(this), if_heapnumber(this, Label::kDeferred), | |||
| 1480 | if_bigint(this, Label::kDeferred); | |||
| 1481 | // Rule out false {value}. | |||
| 1482 | GotoIf(TaggedEqual(value, FalseConstant()), if_false); | |||
| 1483 | ||||
| 1484 | // Check if {value} is a Smi or a HeapObject. | |||
| 1485 | Branch(TaggedIsSmi(value), &if_smi, &if_notsmi); | |||
| 1486 | ||||
| 1487 | BIND(&if_smi)Bind(&if_smi); | |||
| 1488 | { | |||
| 1489 | // The {value} is a Smi, only need to check against zero. | |||
| 1490 | BranchIfSmiEqual(CAST(value)Cast(value), SmiConstant(0), if_false, if_true); | |||
| 1491 | } | |||
| 1492 | ||||
| 1493 | BIND(&if_notsmi)Bind(&if_notsmi); | |||
| 1494 | { | |||
| 1495 | TNode<HeapObject> value_heapobject = CAST(value)Cast(value); | |||
| 1496 | ||||
| 1497 | // Check if {value} is the empty string. | |||
| 1498 | GotoIf(IsEmptyString(value_heapobject), if_false); | |||
| 1499 | ||||
| 1500 | // The {value} is a HeapObject, load its map. | |||
| 1501 | TNode<Map> value_map = LoadMap(value_heapobject); | |||
| 1502 | ||||
| 1503 | // Only null, undefined and document.all have the undetectable bit set, | |||
| 1504 | // so we can return false immediately when that bit is set. | |||
| 1505 | GotoIf(IsUndetectableMap(value_map), if_false); | |||
| 1506 | ||||
| 1507 | // We still need to handle numbers specially, but all other {value}s | |||
| 1508 | // that make it here yield true. | |||
| 1509 | GotoIf(IsHeapNumberMap(value_map), &if_heapnumber); | |||
| 1510 | Branch(IsBigInt(value_heapobject), &if_bigint, if_true); | |||
| 1511 | ||||
| 1512 | BIND(&if_heapnumber)Bind(&if_heapnumber); | |||
| 1513 | { | |||
| 1514 | // Load the floating point value of {value}. | |||
| 1515 | TNode<Float64T> value_value = | |||
| 1516 | LoadObjectField<Float64T>(value_heapobject, HeapNumber::kValueOffset); | |||
| 1517 | ||||
| 1518 | // Check if the floating point {value} is neither 0.0, -0.0 nor NaN. | |||
| 1519 | Branch(Float64LessThan(Float64Constant(0.0), Float64Abs(value_value)), | |||
| 1520 | if_true, if_false); | |||
| 1521 | } | |||
| 1522 | ||||
| 1523 | BIND(&if_bigint)Bind(&if_bigint); | |||
| 1524 | { | |||
| 1525 | TNode<BigInt> bigint = CAST(value)Cast(value); | |||
| 1526 | TNode<Word32T> bitfield = LoadBigIntBitfield(bigint); | |||
| 1527 | TNode<Uint32T> length = DecodeWord32<BigIntBase::LengthBits>(bitfield); | |||
| 1528 | Branch(Word32Equal(length, Int32Constant(0)), if_false, if_true); | |||
| 1529 | } | |||
| 1530 | } | |||
| 1531 | } | |||
| 1532 | ||||
| 1533 | TNode<RawPtrT> CodeStubAssembler::LoadSandboxedPointerFromObject( | |||
| 1534 | TNode<HeapObject> object, TNode<IntPtrT> field_offset) { | |||
| 1535 | #ifdef V8_SANDBOXED_POINTERS | |||
| 1536 | return ReinterpretCast<RawPtrT>( | |||
| 1537 | LoadObjectField<SandboxedPtrT>(object, field_offset)); | |||
| 1538 | #else | |||
| 1539 | return LoadObjectField<RawPtrT>(object, field_offset); | |||
| 1540 | #endif // V8_SANDBOXED_POINTERS | |||
| 1541 | } | |||
| 1542 | ||||
| 1543 | void CodeStubAssembler::StoreSandboxedPointerToObject(TNode<HeapObject> object, | |||
| 1544 | TNode<IntPtrT> offset, | |||
| 1545 | TNode<RawPtrT> pointer) { | |||
| 1546 | #ifdef V8_SANDBOXED_POINTERS | |||
| 1547 | TNode<SandboxedPtrT> sbx_ptr = ReinterpretCast<SandboxedPtrT>(pointer); | |||
| 1548 | ||||
| 1549 | // Ensure pointer points into the sandbox. | |||
| 1550 | TNode<ExternalReference> sandbox_base_address = | |||
| 1551 | ExternalConstant(ExternalReference::sandbox_base_address()); | |||
| 1552 | TNode<ExternalReference> sandbox_end_address = | |||
| 1553 | ExternalConstant(ExternalReference::sandbox_end_address()); | |||
| 1554 | TNode<UintPtrT> sandbox_base = Load<UintPtrT>(sandbox_base_address); | |||
| 1555 | TNode<UintPtrT> sandbox_end = Load<UintPtrT>(sandbox_end_address); | |||
| 1556 | CSA_CHECK(this, UintPtrGreaterThanOrEqual(sbx_ptr, sandbox_base))(this)->FastCheck(UintPtrGreaterThanOrEqual(sbx_ptr, sandbox_base )); | |||
| 1557 | CSA_CHECK(this, UintPtrLessThan(sbx_ptr, sandbox_end))(this)->FastCheck(UintPtrLessThan(sbx_ptr, sandbox_end)); | |||
| 1558 | ||||
| 1559 | StoreObjectFieldNoWriteBarrier<SandboxedPtrT>(object, offset, sbx_ptr); | |||
| 1560 | #else | |||
| 1561 | StoreObjectFieldNoWriteBarrier<RawPtrT>(object, offset, pointer); | |||
| 1562 | #endif // V8_SANDBOXED_POINTERS | |||
| 1563 | } | |||
| 1564 | ||||
| 1565 | TNode<RawPtrT> CodeStubAssembler::EmptyBackingStoreBufferConstant() { | |||
| 1566 | #ifdef V8_SANDBOXED_POINTERS | |||
| 1567 | // TODO(chromium:1218005) consider creating a LoadSandboxedPointerConstant() | |||
| 1568 | // if more of these constants are required later on. | |||
| 1569 | TNode<ExternalReference> empty_backing_store_buffer = | |||
| 1570 | ExternalConstant(ExternalReference::empty_backing_store_buffer()); | |||
| 1571 | return Load<RawPtrT>(empty_backing_store_buffer); | |||
| 1572 | #else | |||
| 1573 | return ReinterpretCast<RawPtrT>(IntPtrConstant(0)); | |||
| 1574 | #endif // V8_SANDBOXED_POINTERS | |||
| 1575 | } | |||
| 1576 | ||||
| 1577 | #ifdef V8_SANDBOXED_EXTERNAL_POINTERS | |||
| 1578 | TNode<ExternalPointerT> CodeStubAssembler::ChangeIndexToExternalPointer( | |||
| 1579 | TNode<Uint32T> index) { | |||
| 1580 | DCHECK_EQ(kExternalPointerSize, kUInt32Size)((void) 0); | |||
| 1581 | TNode<Uint32T> shifted_index = | |||
| 1582 | Word32Shl(index, Uint32Constant(kExternalPointerIndexShift)); | |||
| 1583 | return ReinterpretCast<ExternalPointerT>(shifted_index); | |||
| 1584 | } | |||
| 1585 | ||||
| 1586 | TNode<Uint32T> CodeStubAssembler::ChangeExternalPointerToIndex( | |||
| 1587 | TNode<ExternalPointerT> external_pointer) { | |||
| 1588 | DCHECK_EQ(kExternalPointerSize, kUInt32Size)((void) 0); | |||
| 1589 | TNode<Uint32T> shifted_index = ReinterpretCast<Uint32T>(external_pointer); | |||
| 1590 | return Word32Shr(shifted_index, Uint32Constant(kExternalPointerIndexShift)); | |||
| 1591 | } | |||
| 1592 | #endif // V8_SANDBOXED_EXTERNAL_POINTERS | |||
| 1593 | ||||
| 1594 | void CodeStubAssembler::InitializeExternalPointerField(TNode<HeapObject> object, | |||
| 1595 | TNode<IntPtrT> offset) { | |||
| 1596 | #ifdef V8_SANDBOXED_EXTERNAL_POINTERS | |||
| 1597 | TNode<ExternalReference> external_pointer_table_address = ExternalConstant( | |||
| 1598 | ExternalReference::external_pointer_table_address(isolate())); | |||
| 1599 | ||||
| 1600 | // We could implement the fast path for allocating from the freelist here, | |||
| 1601 | // however, this logic needs to be atomic and so requires CSA to expose | |||
| 1602 | // atomic operations. | |||
| 1603 | TNode<ExternalReference> table_allocate_function = ExternalConstant( | |||
| 1604 | ExternalReference::external_pointer_table_allocate_entry()); | |||
| 1605 | TNode<Uint32T> index = UncheckedCast<Uint32T>(CallCFunction( | |||
| 1606 | table_allocate_function, MachineType::Uint32(), | |||
| 1607 | std::make_pair(MachineType::Pointer(), external_pointer_table_address))); | |||
| 1608 | ||||
| 1609 | // Currently, we assume that the caller will immediately initialize the entry | |||
| 1610 | // through StoreExternalPointerToObject after allocating it. That way, we | |||
| 1611 | // avoid initializing the entry twice (once with nullptr, then again with the | |||
| 1612 | // real value). TODO(saelo) initialize the entry with zero here and switch | |||
| 1613 | // callers to a version that initializes the entry with a given pointer. | |||
| 1614 | ||||
| 1615 | TNode<ExternalPointerT> pointer = ChangeIndexToExternalPointer(index); | |||
| 1616 | StoreObjectFieldNoWriteBarrier<ExternalPointerT>(object, offset, pointer); | |||
| 1617 | #endif | |||
| 1618 | } | |||
| 1619 | ||||
| 1620 | TNode<RawPtrT> CodeStubAssembler::LoadExternalPointerFromObject( | |||
| 1621 | TNode<HeapObject> object, TNode<IntPtrT> offset, | |||
| 1622 | ExternalPointerTag external_pointer_tag) { | |||
| 1623 | #ifdef V8_SANDBOXED_EXTERNAL_POINTERS | |||
| 1624 | TNode<ExternalReference> external_pointer_table_address = ExternalConstant( | |||
| 1625 | ExternalReference::external_pointer_table_address(isolate())); | |||
| 1626 | TNode<RawPtrT> table = UncheckedCast<RawPtrT>( | |||
| 1627 | Load(MachineType::Pointer(), external_pointer_table_address, | |||
| 1628 | UintPtrConstant(Internals::kExternalPointerTableBufferOffset))); | |||
| 1629 | ||||
| 1630 | TNode<ExternalPointerT> encoded = | |||
| 1631 | LoadObjectField<ExternalPointerT>(object, offset); | |||
| 1632 | TNode<Uint32T> index = ChangeExternalPointerToIndex(encoded); | |||
| 1633 | // TODO(v8:10391): consider updating ElementOffsetFromIndex to generate code | |||
| 1634 | // that does one shift right instead of two shifts (right and then left). | |||
| 1635 | TNode<IntPtrT> table_offset = ElementOffsetFromIndex( | |||
| 1636 | ChangeUint32ToWord(index), SYSTEM_POINTER_ELEMENTS, 0); | |||
| 1637 | ||||
| 1638 | TNode<UintPtrT> entry = Load<UintPtrT>(table, table_offset); | |||
| 1639 | if (external_pointer_tag != 0) { | |||
| 1640 | TNode<UintPtrT> tag = UintPtrConstant(~external_pointer_tag); | |||
| 1641 | entry = UncheckedCast<UintPtrT>(WordAnd(entry, tag)); | |||
| 1642 | } | |||
| 1643 | return UncheckedCast<RawPtrT>(UncheckedCast<WordT>(entry)); | |||
| 1644 | #else | |||
| 1645 | return LoadObjectField<RawPtrT>(object, offset); | |||
| 1646 | #endif // V8_SANDBOXED_EXTERNAL_POINTERS | |||
| 1647 | } | |||
| 1648 | ||||
| 1649 | void CodeStubAssembler::StoreExternalPointerToObject( | |||
| 1650 | TNode<HeapObject> object, TNode<IntPtrT> offset, TNode<RawPtrT> pointer, | |||
| 1651 | ExternalPointerTag external_pointer_tag) { | |||
| 1652 | #ifdef V8_SANDBOXED_EXTERNAL_POINTERS | |||
| 1653 | TNode<ExternalReference> external_pointer_table_address = ExternalConstant( | |||
| 1654 | ExternalReference::external_pointer_table_address(isolate())); | |||
| 1655 | TNode<RawPtrT> table = UncheckedCast<RawPtrT>( | |||
| 1656 | Load(MachineType::Pointer(), external_pointer_table_address, | |||
| 1657 | UintPtrConstant(Internals::kExternalPointerTableBufferOffset))); | |||
| 1658 | ||||
| 1659 | TNode<ExternalPointerT> encoded = | |||
| 1660 | LoadObjectField<ExternalPointerT>(object, offset); | |||
| 1661 | TNode<Uint32T> index = ChangeExternalPointerToIndex(encoded); | |||
| 1662 | // TODO(v8:10391): consider updating ElementOffsetFromIndex to generate code | |||
| 1663 | // that does one shift right instead of two shifts (right and then left). | |||
| 1664 | TNode<IntPtrT> table_offset = ElementOffsetFromIndex( | |||
| 1665 | ChangeUint32ToWord(index), SYSTEM_POINTER_ELEMENTS, 0); | |||
| 1666 | ||||
| 1667 | TNode<UintPtrT> value = UncheckedCast<UintPtrT>(pointer); | |||
| 1668 | if (external_pointer_tag != 0) { | |||
| 1669 | TNode<UintPtrT> tag = UintPtrConstant(external_pointer_tag); | |||
| 1670 | value = UncheckedCast<UintPtrT>(WordOr(pointer, tag)); | |||
| 1671 | } | |||
| 1672 | StoreNoWriteBarrier(MachineType::PointerRepresentation(), table, table_offset, | |||
| 1673 | value); | |||
| 1674 | #else | |||
| 1675 | StoreObjectFieldNoWriteBarrier<RawPtrT>(object, offset, pointer); | |||
| 1676 | #endif // V8_SANDBOXED_EXTERNAL_POINTERS | |||
| 1677 | } | |||
| 1678 | ||||
| 1679 | TNode<Object> CodeStubAssembler::LoadFromParentFrame(int offset) { | |||
| 1680 | TNode<RawPtrT> frame_pointer = LoadParentFramePointer(); | |||
| 1681 | return LoadFullTagged(frame_pointer, IntPtrConstant(offset)); | |||
| 1682 | } | |||
| 1683 | ||||
| 1684 | TNode<Uint8T> CodeStubAssembler::LoadUint8Ptr(TNode<RawPtrT> ptr, | |||
| 1685 | TNode<IntPtrT> offset) { | |||
| 1686 | return Load<Uint8T>(IntPtrAdd(ReinterpretCast<IntPtrT>(ptr), offset)); | |||
| 1687 | } | |||
| 1688 | ||||
| 1689 | TNode<IntPtrT> CodeStubAssembler::LoadAndUntagObjectField( | |||
| 1690 | TNode<HeapObject> object, int offset) { | |||
| 1691 | // Please use LoadMap(object) instead. | |||
| 1692 | DCHECK_NE(offset, HeapObject::kMapOffset)((void) 0); | |||
| 1693 | if (SmiValuesAre32Bits()) { | |||
| 1694 | #if V8_TARGET_LITTLE_ENDIAN1 | |||
| 1695 | offset += 4; | |||
| 1696 | #endif | |||
| 1697 | return ChangeInt32ToIntPtr(LoadObjectField<Int32T>(object, offset)); | |||
| 1698 | } else { | |||
| 1699 | return SmiToIntPtr(LoadObjectField<Smi>(object, offset)); | |||
| 1700 | } | |||
| 1701 | } | |||
| 1702 | ||||
| 1703 | TNode<Int32T> CodeStubAssembler::LoadAndUntagToWord32ObjectField( | |||
| 1704 | TNode<HeapObject> object, int offset) { | |||
| 1705 | // Please use LoadMap(object) instead. | |||
| 1706 | DCHECK_NE(offset, HeapObject::kMapOffset)((void) 0); | |||
| 1707 | if (SmiValuesAre32Bits()) { | |||
| 1708 | #if V8_TARGET_LITTLE_ENDIAN1 | |||
| 1709 | offset += 4; | |||
| 1710 | #endif | |||
| 1711 | return LoadObjectField<Int32T>(object, offset); | |||
| 1712 | } else { | |||
| 1713 | return SmiToInt32(LoadObjectField<Smi>(object, offset)); | |||
| 1714 | } | |||
| 1715 | } | |||
| 1716 | ||||
| 1717 | TNode<Float64T> CodeStubAssembler::LoadHeapNumberValue( | |||
| 1718 | TNode<HeapObject> object) { | |||
| 1719 | CSA_DCHECK(this, Word32Or(IsHeapNumber(object), IsOddball(object)))((void)0); | |||
| 1720 | STATIC_ASSERT(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset)static_assert(HeapNumber::kValueOffset == Oddball::kToNumberRawOffset , "HeapNumber::kValueOffset == Oddball::kToNumberRawOffset"); | |||
| 1721 | return LoadObjectField<Float64T>(object, HeapNumber::kValueOffset); | |||
| 1722 | } | |||
| 1723 | ||||
| 1724 | TNode<Map> CodeStubAssembler::GetInstanceTypeMap(InstanceType instance_type) { | |||
| 1725 | Handle<Map> map_handle( | |||
| 1726 | Map::GetInstanceTypeMap(ReadOnlyRoots(isolate()), instance_type), | |||
| 1727 | isolate()); | |||
| 1728 | return HeapConstant(map_handle); | |||
| 1729 | } | |||
| 1730 | ||||
| 1731 | TNode<Map> CodeStubAssembler::LoadMap(TNode<HeapObject> object) { | |||
| 1732 | TNode<Map> map = LoadObjectField<Map>(object, HeapObject::kMapOffset); | |||
| 1733 | #ifdef V8_MAP_PACKING | |||
| 1734 | // Check the loaded map is unpacked. i.e. the lowest two bits != 0b10 | |||
| 1735 | CSA_DCHECK(this,((void)0) | |||
| 1736 | WordNotEqual(WordAnd(BitcastTaggedToWord(map),((void)0) | |||
| 1737 | IntPtrConstant(Internals::kMapWordXorMask)),((void)0) | |||
| 1738 | IntPtrConstant(Internals::kMapWordSignature)))((void)0); | |||
| 1739 | #endif | |||
| 1740 | return map; | |||
| 1741 | } | |||
| 1742 | ||||
| 1743 | TNode<Uint16T> CodeStubAssembler::LoadInstanceType(TNode<HeapObject> object) { | |||
| 1744 | return LoadMapInstanceType(LoadMap(object)); | |||
| 1745 | } | |||
| 1746 | ||||
| 1747 | TNode<BoolT> CodeStubAssembler::HasInstanceType(TNode<HeapObject> object, | |||
| 1748 | InstanceType instance_type) { | |||
| 1749 | return InstanceTypeEqual(LoadInstanceType(object), instance_type); | |||
| 1750 | } | |||
| 1751 | ||||
| 1752 | TNode<BoolT> CodeStubAssembler::DoesntHaveInstanceType( | |||
| 1753 | TNode<HeapObject> object, InstanceType instance_type) { | |||
| 1754 | return Word32NotEqual(LoadInstanceType(object), Int32Constant(instance_type)); | |||
| 1755 | } | |||
| 1756 | ||||
| 1757 | TNode<BoolT> CodeStubAssembler::TaggedDoesntHaveInstanceType( | |||
| 1758 | TNode<HeapObject> any_tagged, InstanceType type) { | |||
| 1759 | /* return Phi <TaggedIsSmi(val), DoesntHaveInstanceType(val, type)> */ | |||
| 1760 | TNode<BoolT> tagged_is_smi = TaggedIsSmi(any_tagged); | |||
| 1761 | return Select<BoolT>( | |||
| 1762 | tagged_is_smi, [=]() { return tagged_is_smi; }, | |||
| 1763 | [=]() { return DoesntHaveInstanceType(any_tagged, type); }); | |||
| 1764 | } | |||
| 1765 | ||||
| 1766 | TNode<BoolT> CodeStubAssembler::IsSpecialReceiverMap(TNode<Map> map) { | |||
| 1767 | TNode<BoolT> is_special = | |||
| 1768 | IsSpecialReceiverInstanceType(LoadMapInstanceType(map)); | |||
| 1769 | uint32_t mask = Map::Bits1::HasNamedInterceptorBit::kMask | | |||
| 1770 | Map::Bits1::IsAccessCheckNeededBit::kMask; | |||
| 1771 | USE(mask)do { ::v8::base::Use unused_tmp_array_for_use_macro[]{mask}; ( void)unused_tmp_array_for_use_macro; } while (false); | |||
| 1772 | // Interceptors or access checks imply special receiver. | |||
| 1773 | CSA_DCHECK(this,((void)0) | |||
| 1774 | SelectConstant<BoolT>(IsSetWord32(LoadMapBitField(map), mask),((void)0) | |||
| 1775 | is_special, Int32TrueConstant()))((void)0); | |||
| 1776 | return is_special; | |||
| 1777 | } | |||
| 1778 | ||||
| 1779 | TNode<Word32T> CodeStubAssembler::IsStringWrapperElementsKind(TNode<Map> map) { | |||
| 1780 | TNode<Int32T> kind = LoadMapElementsKind(map); | |||
| 1781 | return Word32Or( | |||
| 1782 | Word32Equal(kind, Int32Constant(FAST_STRING_WRAPPER_ELEMENTS)), | |||
| 1783 | Word32Equal(kind, Int32Constant(SLOW_STRING_WRAPPER_ELEMENTS))); | |||
| 1784 | } | |||
| 1785 | ||||
| 1786 | void CodeStubAssembler::GotoIfMapHasSlowProperties(TNode<Map> map, | |||
| 1787 | Label* if_slow) { | |||
| 1788 | GotoIf(IsStringWrapperElementsKind(map), if_slow); | |||
| 1789 | GotoIf(IsSpecialReceiverMap(map), if_slow); | |||
| 1790 | GotoIf(IsDictionaryMap(map), if_slow); | |||
| 1791 | } | |||
| 1792 | ||||
| 1793 | TNode<HeapObject> CodeStubAssembler::LoadFastProperties( | |||
| 1794 | TNode<JSReceiver> object) { | |||
| 1795 | CSA_SLOW_DCHECK(this, Word32BinaryNot(IsDictionaryMap(LoadMap(object))))((void)0); | |||
| 1796 | TNode<Object> properties = LoadJSReceiverPropertiesOrHash(object); | |||
| 1797 | return Select<HeapObject>( | |||
| 1798 | TaggedIsSmi(properties), [=] { return EmptyFixedArrayConstant(); }, | |||
| 1799 | [=] { return CAST(properties)Cast(properties); }); | |||
| 1800 | } | |||
| 1801 | ||||
| 1802 | TNode<HeapObject> CodeStubAssembler::LoadSlowProperties( | |||
| 1803 | TNode<JSReceiver> object) { | |||
| 1804 | CSA_SLOW_DCHECK(this, IsDictionaryMap(LoadMap(object)))((void)0); | |||
| 1805 | TNode<Object> properties = LoadJSReceiverPropertiesOrHash(object); | |||
| 1806 | NodeGenerator<HeapObject> make_empty = [=]() -> TNode<HeapObject> { | |||
| 1807 | if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOLfalse) { | |||
| 1808 | return EmptySwissPropertyDictionaryConstant(); | |||
| 1809 | } else { | |||
| 1810 | return EmptyPropertyDictionaryConstant(); | |||
| 1811 | } | |||
| 1812 | }; | |||
| 1813 | NodeGenerator<HeapObject> cast_properties = [=] { | |||
| 1814 | TNode<HeapObject> dict = CAST(properties)Cast(properties); | |||
| 1815 | if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOLfalse) { | |||
| 1816 | CSA_DCHECK(this, Word32Or(IsSwissNameDictionary(dict),((void)0) | |||
| 1817 | IsGlobalDictionary(dict)))((void)0); | |||
| 1818 | } else { | |||
| 1819 | CSA_DCHECK(this,((void)0) | |||
| 1820 | Word32Or(IsNameDictionary(dict), IsGlobalDictionary(dict)))((void)0); | |||
| 1821 | } | |||
| 1822 | return dict; | |||
| 1823 | }; | |||
| 1824 | return Select<HeapObject>(TaggedIsSmi(properties), make_empty, | |||
| 1825 | cast_properties); | |||
| 1826 | } | |||
| 1827 | ||||
| 1828 | TNode<Object> CodeStubAssembler::LoadJSArgumentsObjectLength( | |||
| 1829 | TNode<Context> context, TNode<JSArgumentsObject> array) { | |||
| 1830 | CSA_DCHECK(this, IsJSArgumentsObjectWithLength(context, array))((void)0); | |||
| 1831 | constexpr int offset = JSStrictArgumentsObject::kLengthOffset; | |||
| 1832 | STATIC_ASSERT(offset == JSSloppyArgumentsObject::kLengthOffset)static_assert(offset == JSSloppyArgumentsObject::kLengthOffset , "offset == JSSloppyArgumentsObject::kLengthOffset"); | |||
| 1833 | return LoadObjectField(array, offset); | |||
| 1834 | } | |||
| 1835 | ||||
| 1836 | TNode<Smi> CodeStubAssembler::LoadFastJSArrayLength(TNode<JSArray> array) { | |||
| 1837 | TNode<Number> length = LoadJSArrayLength(array); | |||
| 1838 | CSA_DCHECK(this, Word32Or(IsFastElementsKind(LoadElementsKind(array)),((void)0) | |||
| 1839 | IsElementsKindInRange(((void)0) | |||
| 1840 | LoadElementsKind(array),((void)0) | |||
| 1841 | FIRST_ANY_NONEXTENSIBLE_ELEMENTS_KIND,((void)0) | |||
| 1842 | LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND)))((void)0); | |||
| 1843 | // JSArray length is always a positive Smi for fast arrays. | |||
| 1844 | CSA_SLOW_DCHECK(this, TaggedIsPositiveSmi(length))((void)0); | |||
| 1845 | return CAST(length)Cast(length); | |||
| 1846 | } | |||
| 1847 | ||||
| 1848 | TNode<Smi> CodeStubAssembler::LoadFixedArrayBaseLength( | |||
| 1849 | TNode<FixedArrayBase> array) { | |||
| 1850 | CSA_SLOW_DCHECK(this, IsNotWeakFixedArraySubclass(array))((void)0); | |||
| 1851 | return LoadObjectField<Smi>(array, FixedArrayBase::kLengthOffset); | |||
| 1852 | } | |||
| 1853 | ||||
| 1854 | TNode<IntPtrT> CodeStubAssembler::LoadAndUntagFixedArrayBaseLength( | |||
| 1855 | TNode<FixedArrayBase> array) { | |||
| 1856 | return LoadAndUntagObjectField(array, FixedArrayBase::kLengthOffset); | |||
| 1857 | } | |||
| 1858 | ||||
| 1859 | TNode<IntPtrT> CodeStubAssembler::LoadFeedbackVectorLength( | |||
| 1860 | TNode<FeedbackVector> vector) { | |||
| 1861 | return ChangeInt32ToIntPtr( | |||
| 1862 | LoadObjectField<Int32T>(vector, FeedbackVector::kLengthOffset)); | |||
| 1863 | } | |||
| 1864 | ||||
| 1865 | TNode<Smi> CodeStubAssembler::LoadWeakFixedArrayLength( | |||
| 1866 | TNode<WeakFixedArray> array) { | |||
| 1867 | return LoadObjectField<Smi>(array, WeakFixedArray::kLengthOffset); | |||
| 1868 | } | |||
| 1869 | ||||
| 1870 | TNode<IntPtrT> CodeStubAssembler::LoadAndUntagWeakFixedArrayLength( | |||
| 1871 | TNode<WeakFixedArray> array) { | |||
| 1872 | return LoadAndUntagObjectField(array, WeakFixedArray::kLengthOffset); | |||
| 1873 | } | |||
| 1874 | ||||
| 1875 | TNode<Int32T> CodeStubAssembler::LoadNumberOfDescriptors( | |||
| 1876 | TNode<DescriptorArray> array) { | |||
| 1877 | return UncheckedCast<Int32T>(LoadObjectField<Int16T>( | |||
| 1878 | array, DescriptorArray::kNumberOfDescriptorsOffset)); | |||
| 1879 | } | |||
| 1880 | ||||
| 1881 | TNode<Int32T> CodeStubAssembler::LoadNumberOfOwnDescriptors(TNode<Map> map) { | |||
| 1882 | TNode<Uint32T> bit_field3 = LoadMapBitField3(map); | |||
| 1883 | return UncheckedCast<Int32T>( | |||
| 1884 | DecodeWord32<Map::Bits3::NumberOfOwnDescriptorsBits>(bit_field3)); | |||
| 1885 | } | |||
| 1886 | ||||
| 1887 | TNode<Int32T> CodeStubAssembler::LoadMapBitField(TNode<Map> map) { | |||
| 1888 | return UncheckedCast<Int32T>( | |||
| 1889 | LoadObjectField<Uint8T>(map, Map::kBitFieldOffset)); | |||
| 1890 | } | |||
| 1891 | ||||
| 1892 | TNode<Int32T> CodeStubAssembler::LoadMapBitField2(TNode<Map> map) { | |||
| 1893 | return UncheckedCast<Int32T>( | |||
| 1894 | LoadObjectField<Uint8T>(map, Map::kBitField2Offset)); | |||
| 1895 | } | |||
| 1896 | ||||
| 1897 | TNode<Uint32T> CodeStubAssembler::LoadMapBitField3(TNode<Map> map) { | |||
| 1898 | return LoadObjectField<Uint32T>(map, Map::kBitField3Offset); | |||
| 1899 | } | |||
| 1900 | ||||
| 1901 | TNode<Uint16T> CodeStubAssembler::LoadMapInstanceType(TNode<Map> map) { | |||
| 1902 | return LoadObjectField<Uint16T>(map, Map::kInstanceTypeOffset); | |||
| 1903 | } | |||
| 1904 | ||||
| 1905 | TNode<Int32T> CodeStubAssembler::LoadMapElementsKind(TNode<Map> map) { | |||
| 1906 | TNode<Int32T> bit_field2 = LoadMapBitField2(map); | |||
| 1907 | return Signed(DecodeWord32<Map::Bits2::ElementsKindBits>(bit_field2)); | |||
| 1908 | } | |||
| 1909 | ||||
| 1910 | TNode<Int32T> CodeStubAssembler::LoadElementsKind(TNode<HeapObject> object) { | |||
| 1911 | return LoadMapElementsKind(LoadMap(object)); | |||
| 1912 | } | |||
| 1913 | ||||
| 1914 | TNode<DescriptorArray> CodeStubAssembler::LoadMapDescriptors(TNode<Map> map) { | |||
| 1915 | return LoadObjectField<DescriptorArray>(map, Map::kInstanceDescriptorsOffset); | |||
| 1916 | } | |||
| 1917 | ||||
| 1918 | TNode<HeapObject> CodeStubAssembler::LoadMapPrototype(TNode<Map> map) { | |||
| 1919 | return LoadObjectField<HeapObject>(map, Map::kPrototypeOffset); | |||
| 1920 | } | |||
| 1921 | ||||
| 1922 | TNode<IntPtrT> CodeStubAssembler::LoadMapInstanceSizeInWords(TNode<Map> map) { | |||
| 1923 | return ChangeInt32ToIntPtr( | |||
| 1924 | LoadObjectField<Uint8T>(map, Map::kInstanceSizeInWordsOffset)); | |||
| 1925 | } | |||
| 1926 | ||||
| 1927 | TNode<IntPtrT> CodeStubAssembler::LoadMapInobjectPropertiesStartInWords( | |||
| 1928 | TNode<Map> map) { | |||
| 1929 | // See Map::GetInObjectPropertiesStartInWords() for details. | |||
| 1930 | CSA_DCHECK(this, IsJSObjectMap(map))((void)0); | |||
| 1931 | return ChangeInt32ToIntPtr(LoadObjectField<Uint8T>( | |||
| 1932 | map, Map::kInobjectPropertiesStartOrConstructorFunctionIndexOffset)); | |||
| 1933 | } | |||
| 1934 | ||||
| 1935 | TNode<IntPtrT> CodeStubAssembler::LoadMapConstructorFunctionIndex( | |||
| 1936 | TNode<Map> map) { | |||
| 1937 | // See Map::GetConstructorFunctionIndex() for details. | |||
| 1938 | CSA_DCHECK(this, IsPrimitiveInstanceType(LoadMapInstanceType(map)))((void)0); | |||
| 1939 | return ChangeInt32ToIntPtr(LoadObjectField<Uint8T>( | |||
| 1940 | map, Map::kInobjectPropertiesStartOrConstructorFunctionIndexOffset)); | |||
| 1941 | } | |||
| 1942 | ||||
| 1943 | TNode<Object> CodeStubAssembler::LoadMapConstructor(TNode<Map> map) { | |||
| 1944 | TVARIABLE(Object, result,TVariable<Object> result(LoadObjectField( map, Map::kConstructorOrBackPointerOrNativeContextOffset ), this) | |||
| 1945 | LoadObjectField(TVariable<Object> result(LoadObjectField( map, Map::kConstructorOrBackPointerOrNativeContextOffset ), this) | |||
| 1946 | map, Map::kConstructorOrBackPointerOrNativeContextOffset))TVariable<Object> result(LoadObjectField( map, Map::kConstructorOrBackPointerOrNativeContextOffset ), this); | |||
| 1947 | ||||
| 1948 | Label done(this), loop(this, &result); | |||
| 1949 | Goto(&loop); | |||
| 1950 | BIND(&loop)Bind(&loop); | |||
| 1951 | { | |||
| 1952 | GotoIf(TaggedIsSmi(result.value()), &done); | |||
| 1953 | TNode<BoolT> is_map_type = | |||
| 1954 | InstanceTypeEqual(LoadInstanceType(CAST(result.value())Cast(result.value())), MAP_TYPE); | |||
| 1955 | GotoIfNot(is_map_type, &done); | |||
| 1956 | result = | |||
| 1957 | LoadObjectField(CAST(result.value())Cast(result.value()), | |||
| 1958 | Map::kConstructorOrBackPointerOrNativeContextOffset); | |||
| 1959 | Goto(&loop); | |||
| 1960 | } | |||
| 1961 | BIND(&done)Bind(&done); | |||
| 1962 | return result.value(); | |||
| 1963 | } | |||
| 1964 | ||||
| 1965 | TNode<WordT> CodeStubAssembler::LoadMapEnumLength(TNode<Map> map) { | |||
| 1966 | TNode<Uint32T> bit_field3 = LoadMapBitField3(map); | |||
| 1967 | return DecodeWordFromWord32<Map::Bits3::EnumLengthBits>(bit_field3); | |||
| 1968 | } | |||
| 1969 | ||||
| 1970 | TNode<Object> CodeStubAssembler::LoadMapBackPointer(TNode<Map> map) { | |||
| 1971 | TNode<HeapObject> object = CAST(LoadObjectField(Cast(LoadObjectField( map, Map::kConstructorOrBackPointerOrNativeContextOffset )) | |||
| 1972 | map, Map::kConstructorOrBackPointerOrNativeContextOffset))Cast(LoadObjectField( map, Map::kConstructorOrBackPointerOrNativeContextOffset )); | |||
| 1973 | return Select<Object>( | |||
| 1974 | IsMap(object), [=] { return object; }, | |||
| 1975 | [=] { return UndefinedConstant(); }); | |||
| 1976 | } | |||
| 1977 | ||||
| 1978 | TNode<Uint32T> CodeStubAssembler::EnsureOnlyHasSimpleProperties( | |||
| 1979 | TNode<Map> map, TNode<Int32T> instance_type, Label* bailout) { | |||
| 1980 | // This check can have false positives, since it applies to any | |||
| 1981 | // JSPrimitiveWrapper type. | |||
| 1982 | GotoIf(IsCustomElementsReceiverInstanceType(instance_type), bailout); | |||
| 1983 | ||||
| 1984 | TNode<Uint32T> bit_field3 = LoadMapBitField3(map); | |||
| 1985 | GotoIf(IsSetWord32(bit_field3, Map::Bits3::IsDictionaryMapBit::kMask), | |||
| 1986 | bailout); | |||
| 1987 | ||||
| 1988 | return bit_field3; | |||
| 1989 | } | |||
| 1990 | ||||
| 1991 | TNode<IntPtrT> CodeStubAssembler::LoadJSReceiverIdentityHash( | |||
| 1992 | TNode<JSReceiver> receiver, Label* if_no_hash) { | |||
| 1993 | TVARIABLE(IntPtrT, var_hash)TVariable<IntPtrT> var_hash(this); | |||
| 1994 | Label done(this), if_smi(this), if_property_array(this), | |||
| 1995 | if_swiss_property_dictionary(this), if_property_dictionary(this), | |||
| 1996 | if_fixed_array(this); | |||
| 1997 | ||||
| 1998 | TNode<Object> properties_or_hash = | |||
| 1999 | LoadObjectField(receiver, JSReceiver::kPropertiesOrHashOffset); | |||
| 2000 | GotoIf(TaggedIsSmi(properties_or_hash), &if_smi); | |||
| 2001 | ||||
| 2002 | TNode<HeapObject> properties = CAST(properties_or_hash)Cast(properties_or_hash); | |||
| 2003 | TNode<Uint16T> properties_instance_type = LoadInstanceType(properties); | |||
| 2004 | ||||
| 2005 | GotoIf(InstanceTypeEqual(properties_instance_type, PROPERTY_ARRAY_TYPE), | |||
| 2006 | &if_property_array); | |||
| 2007 | if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOLfalse) { | |||
| 2008 | GotoIf( | |||
| 2009 | InstanceTypeEqual(properties_instance_type, SWISS_NAME_DICTIONARY_TYPE), | |||
| 2010 | &if_swiss_property_dictionary); | |||
| 2011 | } | |||
| 2012 | Branch(InstanceTypeEqual(properties_instance_type, NAME_DICTIONARY_TYPE), | |||
| 2013 | &if_property_dictionary, &if_fixed_array); | |||
| 2014 | ||||
| 2015 | BIND(&if_fixed_array)Bind(&if_fixed_array); | |||
| 2016 | { | |||
| 2017 | var_hash = IntPtrConstant(PropertyArray::kNoHashSentinel); | |||
| 2018 | Goto(&done); | |||
| 2019 | } | |||
| 2020 | ||||
| 2021 | BIND(&if_smi)Bind(&if_smi); | |||
| 2022 | { | |||
| 2023 | var_hash = SmiUntag(CAST(properties_or_hash)Cast(properties_or_hash)); | |||
| 2024 | Goto(&done); | |||
| 2025 | } | |||
| 2026 | ||||
| 2027 | BIND(&if_property_array)Bind(&if_property_array); | |||
| 2028 | { | |||
| 2029 | TNode<IntPtrT> length_and_hash = LoadAndUntagObjectField( | |||
| 2030 | properties, PropertyArray::kLengthAndHashOffset); | |||
| 2031 | var_hash = Signed(DecodeWord<PropertyArray::HashField>(length_and_hash)); | |||
| 2032 | Goto(&done); | |||
| 2033 | } | |||
| 2034 | if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOLfalse) { | |||
| 2035 | BIND(&if_swiss_property_dictionary)Bind(&if_swiss_property_dictionary); | |||
| 2036 | { | |||
| 2037 | var_hash = Signed( | |||
| 2038 | ChangeUint32ToWord(LoadSwissNameDictionaryHash(CAST(properties)Cast(properties)))); | |||
| 2039 | Goto(&done); | |||
| 2040 | } | |||
| 2041 | } | |||
| 2042 | ||||
| 2043 | BIND(&if_property_dictionary)Bind(&if_property_dictionary); | |||
| 2044 | { | |||
| 2045 | var_hash = SmiUntag(CAST(LoadFixedArrayElement(Cast(LoadFixedArrayElement( Cast(properties), NameDictionary:: kObjectHashIndex)) | |||
| 2046 | CAST(properties), NameDictionary::kObjectHashIndex))Cast(LoadFixedArrayElement( Cast(properties), NameDictionary:: kObjectHashIndex))); | |||
| 2047 | Goto(&done); | |||
| 2048 | } | |||
| 2049 | ||||
| 2050 | BIND(&done)Bind(&done); | |||
| 2051 | if (if_no_hash != nullptr) { | |||
| 2052 | GotoIf(IntPtrEqual(var_hash.value(), | |||
| 2053 | IntPtrConstant(PropertyArray::kNoHashSentinel)), | |||
| 2054 | if_no_hash); | |||
| 2055 | } | |||
| 2056 | return var_hash.value(); | |||
| 2057 | } | |||
| 2058 | ||||
| 2059 | TNode<Uint32T> CodeStubAssembler::LoadNameHashAssumeComputed(TNode<Name> name) { | |||
| 2060 | TNode<Uint32T> hash_field = LoadNameRawHashField(name); | |||
| 2061 | CSA_DCHECK(this, IsClearWord32(hash_field, Name::kHashNotComputedMask))((void)0); | |||
| 2062 | return DecodeWord32<Name::HashBits>(hash_field); | |||
| 2063 | } | |||
| 2064 | ||||
| 2065 | TNode<Uint32T> CodeStubAssembler::LoadNameHash(TNode<Name> name, | |||
| 2066 | Label* if_hash_not_computed) { | |||
| 2067 | TNode<Uint32T> raw_hash_field = LoadNameRawHashField(name); | |||
| 2068 | if (if_hash_not_computed != nullptr) { | |||
| 2069 | GotoIf(IsSetWord32(raw_hash_field, Name::kHashNotComputedMask), | |||
| 2070 | if_hash_not_computed); | |||
| 2071 | } | |||
| 2072 | return DecodeWord32<Name::HashBits>(raw_hash_field); | |||
| 2073 | } | |||
| 2074 | ||||
| 2075 | TNode<Smi> CodeStubAssembler::LoadStringLengthAsSmi(TNode<String> string) { | |||
| 2076 | return SmiFromIntPtr(LoadStringLengthAsWord(string)); | |||
| 2077 | } | |||
| 2078 | ||||
| 2079 | TNode<IntPtrT> CodeStubAssembler::LoadStringLengthAsWord(TNode<String> string) { | |||
| 2080 | return Signed(ChangeUint32ToWord(LoadStringLengthAsWord32(string))); | |||
| 2081 | } | |||
| 2082 | ||||
| 2083 | TNode<Uint32T> CodeStubAssembler::LoadStringLengthAsWord32( | |||
| 2084 | TNode<String> string) { | |||
| 2085 | return LoadObjectField<Uint32T>(string, String::kLengthOffset); | |||
| 2086 | } | |||
| 2087 | ||||
| 2088 | TNode<Object> CodeStubAssembler::LoadJSPrimitiveWrapperValue( | |||
| 2089 | TNode<JSPrimitiveWrapper> object) { | |||
| 2090 | return LoadObjectField(object, JSPrimitiveWrapper::kValueOffset); | |||
| 2091 | } | |||
| 2092 | ||||
| 2093 | void CodeStubAssembler::DispatchMaybeObject(TNode<MaybeObject> maybe_object, | |||
| 2094 | Label* if_smi, Label* if_cleared, | |||
| 2095 | Label* if_weak, Label* if_strong, | |||
| 2096 | TVariable<Object>* extracted) { | |||
| 2097 | Label inner_if_smi(this), inner_if_strong(this); | |||
| 2098 | ||||
| 2099 | GotoIf(TaggedIsSmi(maybe_object), &inner_if_smi); | |||
| 2100 | ||||
| 2101 | GotoIf(IsCleared(maybe_object), if_cleared); | |||
| 2102 | ||||
| 2103 | GotoIf(IsStrong(maybe_object), &inner_if_strong); | |||
| 2104 | ||||
| 2105 | *extracted = GetHeapObjectAssumeWeak(maybe_object); | |||
| 2106 | Goto(if_weak); | |||
| 2107 | ||||
| 2108 | BIND(&inner_if_smi)Bind(&inner_if_smi); | |||
| 2109 | *extracted = CAST(maybe_object)Cast(maybe_object); | |||
| 2110 | Goto(if_smi); | |||
| 2111 | ||||
| 2112 | BIND(&inner_if_strong)Bind(&inner_if_strong); | |||
| 2113 | *extracted = CAST(maybe_object)Cast(maybe_object); | |||
| 2114 | Goto(if_strong); | |||
| 2115 | } | |||
| 2116 | ||||
| 2117 | void CodeStubAssembler::DcheckHasValidMap(TNode<HeapObject> object) { | |||
| 2118 | #ifdef V8_MAP_PACKING | |||
| 2119 | // Test if the map is an unpacked and valid map | |||
| 2120 | CSA_DCHECK(this, IsMap(LoadMap(object)))((void)0); | |||
| 2121 | #endif | |||
| 2122 | } | |||
| 2123 | ||||
| 2124 | TNode<BoolT> CodeStubAssembler::IsStrong(TNode<MaybeObject> value) { | |||
| 2125 | return Word32Equal(Word32And(TruncateIntPtrToInt32( | |||
| 2126 | BitcastTaggedToWordForTagAndSmiBits(value)), | |||
| 2127 | Int32Constant(kHeapObjectTagMask)), | |||
| 2128 | Int32Constant(kHeapObjectTag)); | |||
| 2129 | } | |||
| 2130 | ||||
| 2131 | TNode<HeapObject> CodeStubAssembler::GetHeapObjectIfStrong( | |||
| 2132 | TNode<MaybeObject> value, Label* if_not_strong) { | |||
| 2133 | GotoIfNot(IsStrong(value), if_not_strong); | |||
| 2134 | return CAST(value)Cast(value); | |||
| 2135 | } | |||
| 2136 | ||||
| 2137 | TNode<BoolT> CodeStubAssembler::IsWeakOrCleared(TNode<MaybeObject> value) { | |||
| 2138 | return Word32Equal(Word32And(TruncateIntPtrToInt32( | |||
| 2139 | BitcastTaggedToWordForTagAndSmiBits(value)), | |||
| 2140 | Int32Constant(kHeapObjectTagMask)), | |||
| 2141 | Int32Constant(kWeakHeapObjectTag)); | |||
| 2142 | } | |||
| 2143 | ||||
| 2144 | TNode<BoolT> CodeStubAssembler::IsCleared(TNode<MaybeObject> value) { | |||
| 2145 | return Word32Equal(TruncateIntPtrToInt32(BitcastMaybeObjectToWord(value)), | |||
| 2146 | Int32Constant(kClearedWeakHeapObjectLower32)); | |||
| 2147 | } | |||
| 2148 | ||||
| 2149 | TNode<HeapObject> CodeStubAssembler::GetHeapObjectAssumeWeak( | |||
| 2150 | TNode<MaybeObject> value) { | |||
| 2151 | CSA_DCHECK(this, IsWeakOrCleared(value))((void)0); | |||
| 2152 | CSA_DCHECK(this, IsNotCleared(value))((void)0); | |||
| 2153 | return UncheckedCast<HeapObject>(BitcastWordToTagged(WordAnd( | |||
| 2154 | BitcastMaybeObjectToWord(value), IntPtrConstant(~kWeakHeapObjectMask)))); | |||
| 2155 | } | |||
| 2156 | ||||
| 2157 | TNode<HeapObject> CodeStubAssembler::GetHeapObjectAssumeWeak( | |||
| 2158 | TNode<MaybeObject> value, Label* if_cleared) { | |||
| 2159 | GotoIf(IsCleared(value), if_cleared); | |||
| 2160 | return GetHeapObjectAssumeWeak(value); | |||
| 2161 | } | |||
| 2162 | ||||
| 2163 | // This version generates | |||
| 2164 | // (maybe_object & ~mask) == value | |||
| 2165 | // It works for non-Smi |maybe_object| and for both Smi and HeapObject values | |||
| 2166 | // but requires a big constant for ~mask. | |||
| 2167 | TNode<BoolT> CodeStubAssembler::IsWeakReferenceToObject( | |||
| 2168 | TNode<MaybeObject> maybe_object, TNode<Object> value) { | |||
| 2169 | CSA_DCHECK(this, TaggedIsNotSmi(maybe_object))((void)0); | |||
| 2170 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 2171 | return Word32Equal( | |||
| 2172 | Word32And(TruncateWordToInt32(BitcastMaybeObjectToWord(maybe_object)), | |||
| 2173 | Uint32Constant(~static_cast<uint32_t>(kWeakHeapObjectMask))), | |||
| 2174 | TruncateWordToInt32(BitcastTaggedToWord(value))); | |||
| 2175 | } else { | |||
| 2176 | return WordEqual(WordAnd(BitcastMaybeObjectToWord(maybe_object), | |||
| 2177 | IntPtrConstant(~kWeakHeapObjectMask)), | |||
| 2178 | BitcastTaggedToWord(value)); | |||
| 2179 | } | |||
| 2180 | } | |||
| 2181 | ||||
| 2182 | // This version generates | |||
| 2183 | // maybe_object == (heap_object | mask) | |||
| 2184 | // It works for any |maybe_object| values and generates a better code because it | |||
| 2185 | // uses a small constant for mask. | |||
| 2186 | TNode<BoolT> CodeStubAssembler::IsWeakReferenceTo( | |||
| 2187 | TNode<MaybeObject> maybe_object, TNode<HeapObject> heap_object) { | |||
| 2188 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 2189 | return Word32Equal( | |||
| 2190 | TruncateWordToInt32(BitcastMaybeObjectToWord(maybe_object)), | |||
| 2191 | Word32Or(TruncateWordToInt32(BitcastTaggedToWord(heap_object)), | |||
| 2192 | Int32Constant(kWeakHeapObjectMask))); | |||
| 2193 | } else { | |||
| 2194 | return WordEqual(BitcastMaybeObjectToWord(maybe_object), | |||
| 2195 | WordOr(BitcastTaggedToWord(heap_object), | |||
| 2196 | IntPtrConstant(kWeakHeapObjectMask))); | |||
| 2197 | } | |||
| 2198 | } | |||
| 2199 | ||||
| 2200 | TNode<MaybeObject> CodeStubAssembler::MakeWeak(TNode<HeapObject> value) { | |||
| 2201 | return ReinterpretCast<MaybeObject>(BitcastWordToTagged( | |||
| 2202 | WordOr(BitcastTaggedToWord(value), IntPtrConstant(kWeakHeapObjectTag)))); | |||
| 2203 | } | |||
| 2204 | ||||
| 2205 | template <> | |||
| 2206 | TNode<IntPtrT> CodeStubAssembler::LoadArrayLength(TNode<FixedArray> array) { | |||
| 2207 | return LoadAndUntagFixedArrayBaseLength(array); | |||
| 2208 | } | |||
| 2209 | ||||
| 2210 | template <> | |||
| 2211 | TNode<IntPtrT> CodeStubAssembler::LoadArrayLength(TNode<WeakFixedArray> array) { | |||
| 2212 | return LoadAndUntagWeakFixedArrayLength(array); | |||
| 2213 | } | |||
| 2214 | ||||
| 2215 | template <> | |||
| 2216 | TNode<IntPtrT> CodeStubAssembler::LoadArrayLength(TNode<PropertyArray> array) { | |||
| 2217 | return LoadPropertyArrayLength(array); | |||
| 2218 | } | |||
| 2219 | ||||
| 2220 | template <> | |||
| 2221 | TNode<IntPtrT> CodeStubAssembler::LoadArrayLength( | |||
| 2222 | TNode<DescriptorArray> array) { | |||
| 2223 | return IntPtrMul(ChangeInt32ToIntPtr(LoadNumberOfDescriptors(array)), | |||
| 2224 | IntPtrConstant(DescriptorArray::kEntrySize)); | |||
| 2225 | } | |||
| 2226 | ||||
| 2227 | template <> | |||
| 2228 | TNode<IntPtrT> CodeStubAssembler::LoadArrayLength( | |||
| 2229 | TNode<TransitionArray> array) { | |||
| 2230 | return LoadAndUntagWeakFixedArrayLength(array); | |||
| 2231 | } | |||
| 2232 | ||||
| 2233 | template <typename Array, typename TIndex, typename TValue> | |||
| 2234 | TNode<TValue> CodeStubAssembler::LoadArrayElement(TNode<Array> array, | |||
| 2235 | int array_header_size, | |||
| 2236 | TNode<TIndex> index_node, | |||
| 2237 | int additional_offset) { | |||
| 2238 | // TODO(v8:9708): Do we want to keep both IntPtrT and UintPtrT variants? | |||
| 2239 | static_assert(std::is_same<TIndex, Smi>::value || | |||
| 2240 | std::is_same<TIndex, UintPtrT>::value || | |||
| 2241 | std::is_same<TIndex, IntPtrT>::value, | |||
| 2242 | "Only Smi, UintPtrT or IntPtrT indices are allowed"); | |||
| 2243 | CSA_DCHECK(this, IntPtrGreaterThanOrEqual(ParameterToIntPtr(index_node),((void)0) | |||
| 2244 | IntPtrConstant(0)))((void)0); | |||
| 2245 | DCHECK(IsAligned(additional_offset, kTaggedSize))((void) 0); | |||
| 2246 | int32_t header_size = array_header_size + additional_offset - kHeapObjectTag; | |||
| 2247 | TNode<IntPtrT> offset = | |||
| 2248 | ElementOffsetFromIndex(index_node, HOLEY_ELEMENTS, header_size); | |||
| 2249 | CSA_DCHECK(this, IsOffsetInBounds(offset, LoadArrayLength(array),((void)0) | |||
| 2250 | array_header_size))((void)0); | |||
| 2251 | constexpr MachineType machine_type = MachineTypeOf<TValue>::value; | |||
| 2252 | return UncheckedCast<TValue>(LoadFromObject(machine_type, array, offset)); | |||
| 2253 | } | |||
| 2254 | ||||
| 2255 | template V8_EXPORT_PRIVATE TNode<MaybeObject> | |||
| 2256 | CodeStubAssembler::LoadArrayElement<TransitionArray, IntPtrT>( | |||
| 2257 | TNode<TransitionArray>, int, TNode<IntPtrT>, int); | |||
| 2258 | ||||
| 2259 | template <typename TIndex> | |||
| 2260 | TNode<Object> CodeStubAssembler::LoadFixedArrayElement( | |||
| 2261 | TNode<FixedArray> object, TNode<TIndex> index, int additional_offset, | |||
| 2262 | CheckBounds check_bounds) { | |||
| 2263 | // TODO(v8:9708): Do we want to keep both IntPtrT and UintPtrT variants? | |||
| 2264 | static_assert(std::is_same<TIndex, Smi>::value || | |||
| 2265 | std::is_same<TIndex, UintPtrT>::value || | |||
| 2266 | std::is_same<TIndex, IntPtrT>::value, | |||
| 2267 | "Only Smi, UintPtrT or IntPtrT indexes are allowed"); | |||
| 2268 | CSA_DCHECK(this, IsFixedArraySubclass(object))((void)0); | |||
| 2269 | CSA_DCHECK(this, IsNotWeakFixedArraySubclass(object))((void)0); | |||
| 2270 | ||||
| 2271 | if (NeedsBoundsCheck(check_bounds)) { | |||
| 2272 | FixedArrayBoundsCheck(object, index, additional_offset); | |||
| 2273 | } | |||
| 2274 | TNode<MaybeObject> element = LoadArrayElement(object, FixedArray::kHeaderSize, | |||
| 2275 | index, additional_offset); | |||
| 2276 | return CAST(element)Cast(element); | |||
| 2277 | } | |||
| 2278 | ||||
| 2279 | template V8_EXPORT_PRIVATE TNode<Object> | |||
| 2280 | CodeStubAssembler::LoadFixedArrayElement<Smi>(TNode<FixedArray>, TNode<Smi>, | |||
| 2281 | int, CheckBounds); | |||
| 2282 | template V8_EXPORT_PRIVATE TNode<Object> | |||
| 2283 | CodeStubAssembler::LoadFixedArrayElement<UintPtrT>(TNode<FixedArray>, | |||
| 2284 | TNode<UintPtrT>, int, | |||
| 2285 | CheckBounds); | |||
| 2286 | template V8_EXPORT_PRIVATE TNode<Object> | |||
| 2287 | CodeStubAssembler::LoadFixedArrayElement<IntPtrT>(TNode<FixedArray>, | |||
| 2288 | TNode<IntPtrT>, int, | |||
| 2289 | CheckBounds); | |||
| 2290 | ||||
| 2291 | void CodeStubAssembler::FixedArrayBoundsCheck(TNode<FixedArrayBase> array, | |||
| 2292 | TNode<Smi> index, | |||
| 2293 | int additional_offset) { | |||
| 2294 | if (!FLAG_fixed_array_bounds_checks) return; | |||
| 2295 | DCHECK(IsAligned(additional_offset, kTaggedSize))((void) 0); | |||
| 2296 | TNode<Smi> effective_index; | |||
| 2297 | Smi constant_index; | |||
| 2298 | bool index_is_constant = TryToSmiConstant(index, &constant_index); | |||
| 2299 | if (index_is_constant) { | |||
| 2300 | effective_index = SmiConstant(Smi::ToInt(constant_index) + | |||
| 2301 | additional_offset / kTaggedSize); | |||
| 2302 | } else { | |||
| 2303 | effective_index = | |||
| 2304 | SmiAdd(index, SmiConstant(additional_offset / kTaggedSize)); | |||
| 2305 | } | |||
| 2306 | CSA_CHECK(this, SmiBelow(effective_index, LoadFixedArrayBaseLength(array)))(this)->FastCheck(SmiBelow(effective_index, LoadFixedArrayBaseLength (array))); | |||
| 2307 | } | |||
| 2308 | ||||
| 2309 | void CodeStubAssembler::FixedArrayBoundsCheck(TNode<FixedArrayBase> array, | |||
| 2310 | TNode<IntPtrT> index, | |||
| 2311 | int additional_offset) { | |||
| 2312 | if (!FLAG_fixed_array_bounds_checks) return; | |||
| 2313 | DCHECK(IsAligned(additional_offset, kTaggedSize))((void) 0); | |||
| 2314 | // IntPtrAdd does constant-folding automatically. | |||
| 2315 | TNode<IntPtrT> effective_index = | |||
| 2316 | IntPtrAdd(index, IntPtrConstant(additional_offset / kTaggedSize)); | |||
| 2317 | CSA_CHECK(this, UintPtrLessThan(effective_index,(this)->FastCheck(UintPtrLessThan(effective_index, LoadAndUntagFixedArrayBaseLength (array))) | |||
| 2318 | LoadAndUntagFixedArrayBaseLength(array)))(this)->FastCheck(UintPtrLessThan(effective_index, LoadAndUntagFixedArrayBaseLength (array))); | |||
| 2319 | } | |||
| 2320 | ||||
| 2321 | TNode<Object> CodeStubAssembler::LoadPropertyArrayElement( | |||
| 2322 | TNode<PropertyArray> object, TNode<IntPtrT> index) { | |||
| 2323 | int additional_offset = 0; | |||
| 2324 | return CAST(LoadArrayElement(object, PropertyArray::kHeaderSize, index,Cast(LoadArrayElement(object, PropertyArray::kHeaderSize, index , additional_offset)) | |||
| 2325 | additional_offset))Cast(LoadArrayElement(object, PropertyArray::kHeaderSize, index , additional_offset)); | |||
| 2326 | } | |||
| 2327 | ||||
| 2328 | TNode<IntPtrT> CodeStubAssembler::LoadPropertyArrayLength( | |||
| 2329 | TNode<PropertyArray> object) { | |||
| 2330 | TNode<IntPtrT> value = | |||
| 2331 | LoadAndUntagObjectField(object, PropertyArray::kLengthAndHashOffset); | |||
| 2332 | return Signed(DecodeWord<PropertyArray::LengthField>(value)); | |||
| 2333 | } | |||
| 2334 | ||||
| 2335 | TNode<RawPtrT> CodeStubAssembler::LoadJSTypedArrayDataPtr( | |||
| 2336 | TNode<JSTypedArray> typed_array) { | |||
| 2337 | // Data pointer = external_pointer + static_cast<Tagged_t>(base_pointer). | |||
| 2338 | TNode<RawPtrT> external_pointer = | |||
| 2339 | LoadJSTypedArrayExternalPointerPtr(typed_array); | |||
| 2340 | ||||
| 2341 | TNode<IntPtrT> base_pointer; | |||
| 2342 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 2343 | TNode<Int32T> compressed_base = | |||
| 2344 | LoadObjectField<Int32T>(typed_array, JSTypedArray::kBasePointerOffset); | |||
| 2345 | // Zero-extend TaggedT to WordT according to current compression scheme | |||
| 2346 | // so that the addition with |external_pointer| (which already contains | |||
| 2347 | // compensated offset value) below will decompress the tagged value. | |||
| 2348 | // See JSTypedArray::ExternalPointerCompensationForOnHeapArray() for | |||
| 2349 | // details. | |||
| 2350 | base_pointer = Signed(ChangeUint32ToWord(compressed_base)); | |||
| 2351 | } else { | |||
| 2352 | base_pointer = | |||
| 2353 | LoadObjectField<IntPtrT>(typed_array, JSTypedArray::kBasePointerOffset); | |||
| 2354 | } | |||
| 2355 | return RawPtrAdd(external_pointer, base_pointer); | |||
| 2356 | } | |||
| 2357 | ||||
| 2358 | TNode<BigInt> CodeStubAssembler::LoadFixedBigInt64ArrayElementAsTagged( | |||
| 2359 | TNode<RawPtrT> data_pointer, TNode<IntPtrT> offset) { | |||
| 2360 | if (Is64()) { | |||
| 2361 | TNode<IntPtrT> value = Load<IntPtrT>(data_pointer, offset); | |||
| 2362 | return BigIntFromInt64(value); | |||
| 2363 | } else { | |||
| 2364 | DCHECK(!Is64())((void) 0); | |||
| 2365 | #if defined(V8_TARGET_BIG_ENDIAN) | |||
| 2366 | TNode<IntPtrT> high = Load<IntPtrT>(data_pointer, offset); | |||
| 2367 | TNode<IntPtrT> low = Load<IntPtrT>( | |||
| 2368 | data_pointer, IntPtrAdd(offset, IntPtrConstant(kSystemPointerSize))); | |||
| 2369 | #else | |||
| 2370 | TNode<IntPtrT> low = Load<IntPtrT>(data_pointer, offset); | |||
| 2371 | TNode<IntPtrT> high = Load<IntPtrT>( | |||
| 2372 | data_pointer, IntPtrAdd(offset, IntPtrConstant(kSystemPointerSize))); | |||
| 2373 | #endif | |||
| 2374 | return BigIntFromInt32Pair(low, high); | |||
| 2375 | } | |||
| 2376 | } | |||
| 2377 | ||||
| 2378 | TNode<BigInt> CodeStubAssembler::BigIntFromInt32Pair(TNode<IntPtrT> low, | |||
| 2379 | TNode<IntPtrT> high) { | |||
| 2380 | DCHECK(!Is64())((void) 0); | |||
| 2381 | TVARIABLE(BigInt, var_result)TVariable<BigInt> var_result(this); | |||
| 2382 | TVARIABLE(Word32T, var_sign, Int32Constant(BigInt::SignBits::encode(false)))TVariable<Word32T> var_sign(Int32Constant(BigInt::SignBits ::encode(false)), this); | |||
| 2383 | TVARIABLE(IntPtrT, var_high, high)TVariable<IntPtrT> var_high(high, this); | |||
| 2384 | TVARIABLE(IntPtrT, var_low, low)TVariable<IntPtrT> var_low(low, this); | |||
| 2385 | Label high_zero(this), negative(this), allocate_one_digit(this), | |||
| 2386 | allocate_two_digits(this), if_zero(this), done(this); | |||
| 2387 | ||||
| 2388 | GotoIf(IntPtrEqual(var_high.value(), IntPtrConstant(0)), &high_zero); | |||
| 2389 | Branch(IntPtrLessThan(var_high.value(), IntPtrConstant(0)), &negative, | |||
| 2390 | &allocate_two_digits); | |||
| 2391 | ||||
| 2392 | BIND(&high_zero)Bind(&high_zero); | |||
| 2393 | Branch(IntPtrEqual(var_low.value(), IntPtrConstant(0)), &if_zero, | |||
| 2394 | &allocate_one_digit); | |||
| 2395 | ||||
| 2396 | BIND(&negative)Bind(&negative); | |||
| 2397 | { | |||
| 2398 | var_sign = Int32Constant(BigInt::SignBits::encode(true)); | |||
| 2399 | // We must negate the value by computing "0 - (high|low)", performing | |||
| 2400 | // both parts of the subtraction separately and manually taking care | |||
| 2401 | // of the carry bit (which is 1 iff low != 0). | |||
| 2402 | var_high = IntPtrSub(IntPtrConstant(0), var_high.value()); | |||
| 2403 | Label carry(this), no_carry(this); | |||
| 2404 | Branch(IntPtrEqual(var_low.value(), IntPtrConstant(0)), &no_carry, &carry); | |||
| 2405 | BIND(&carry)Bind(&carry); | |||
| 2406 | var_high = IntPtrSub(var_high.value(), IntPtrConstant(1)); | |||
| 2407 | Goto(&no_carry); | |||
| 2408 | BIND(&no_carry)Bind(&no_carry); | |||
| 2409 | var_low = IntPtrSub(IntPtrConstant(0), var_low.value()); | |||
| 2410 | // var_high was non-zero going into this block, but subtracting the | |||
| 2411 | // carry bit from it could bring us back onto the "one digit" path. | |||
| 2412 | Branch(IntPtrEqual(var_high.value(), IntPtrConstant(0)), | |||
| 2413 | &allocate_one_digit, &allocate_two_digits); | |||
| 2414 | } | |||
| 2415 | ||||
| 2416 | BIND(&allocate_one_digit)Bind(&allocate_one_digit); | |||
| 2417 | { | |||
| 2418 | var_result = AllocateRawBigInt(IntPtrConstant(1)); | |||
| 2419 | StoreBigIntBitfield(var_result.value(), | |||
| 2420 | Word32Or(var_sign.value(), | |||
| 2421 | Int32Constant(BigInt::LengthBits::encode(1)))); | |||
| 2422 | StoreBigIntDigit(var_result.value(), 0, Unsigned(var_low.value())); | |||
| 2423 | Goto(&done); | |||
| 2424 | } | |||
| 2425 | ||||
| 2426 | BIND(&allocate_two_digits)Bind(&allocate_two_digits); | |||
| 2427 | { | |||
| 2428 | var_result = AllocateRawBigInt(IntPtrConstant(2)); | |||
| 2429 | StoreBigIntBitfield(var_result.value(), | |||
| 2430 | Word32Or(var_sign.value(), | |||
| 2431 | Int32Constant(BigInt::LengthBits::encode(2)))); | |||
| 2432 | StoreBigIntDigit(var_result.value(), 0, Unsigned(var_low.value())); | |||
| 2433 | StoreBigIntDigit(var_result.value(), 1, Unsigned(var_high.value())); | |||
| 2434 | Goto(&done); | |||
| 2435 | } | |||
| 2436 | ||||
| 2437 | BIND(&if_zero)Bind(&if_zero); | |||
| 2438 | var_result = AllocateBigInt(IntPtrConstant(0)); | |||
| 2439 | Goto(&done); | |||
| 2440 | ||||
| 2441 | BIND(&done)Bind(&done); | |||
| 2442 | return var_result.value(); | |||
| 2443 | } | |||
| 2444 | ||||
| 2445 | TNode<BigInt> CodeStubAssembler::BigIntFromInt64(TNode<IntPtrT> value) { | |||
| 2446 | DCHECK(Is64())((void) 0); | |||
| 2447 | TVARIABLE(BigInt, var_result)TVariable<BigInt> var_result(this); | |||
| 2448 | Label done(this), if_positive(this), if_negative(this), if_zero(this); | |||
| 2449 | GotoIf(IntPtrEqual(value, IntPtrConstant(0)), &if_zero); | |||
| 2450 | var_result = AllocateRawBigInt(IntPtrConstant(1)); | |||
| 2451 | Branch(IntPtrGreaterThan(value, IntPtrConstant(0)), &if_positive, | |||
| 2452 | &if_negative); | |||
| 2453 | ||||
| 2454 | BIND(&if_positive)Bind(&if_positive); | |||
| 2455 | { | |||
| 2456 | StoreBigIntBitfield(var_result.value(), | |||
| 2457 | Int32Constant(BigInt::SignBits::encode(false) | | |||
| 2458 | BigInt::LengthBits::encode(1))); | |||
| 2459 | StoreBigIntDigit(var_result.value(), 0, Unsigned(value)); | |||
| 2460 | Goto(&done); | |||
| 2461 | } | |||
| 2462 | ||||
| 2463 | BIND(&if_negative)Bind(&if_negative); | |||
| 2464 | { | |||
| 2465 | StoreBigIntBitfield(var_result.value(), | |||
| 2466 | Int32Constant(BigInt::SignBits::encode(true) | | |||
| 2467 | BigInt::LengthBits::encode(1))); | |||
| 2468 | StoreBigIntDigit(var_result.value(), 0, | |||
| 2469 | Unsigned(IntPtrSub(IntPtrConstant(0), value))); | |||
| 2470 | Goto(&done); | |||
| 2471 | } | |||
| 2472 | ||||
| 2473 | BIND(&if_zero)Bind(&if_zero); | |||
| 2474 | { | |||
| 2475 | var_result = AllocateBigInt(IntPtrConstant(0)); | |||
| 2476 | Goto(&done); | |||
| 2477 | } | |||
| 2478 | ||||
| 2479 | BIND(&done)Bind(&done); | |||
| 2480 | return var_result.value(); | |||
| 2481 | } | |||
| 2482 | ||||
| 2483 | TNode<BigInt> CodeStubAssembler::LoadFixedBigUint64ArrayElementAsTagged( | |||
| 2484 | TNode<RawPtrT> data_pointer, TNode<IntPtrT> offset) { | |||
| 2485 | Label if_zero(this), done(this); | |||
| 2486 | if (Is64()) { | |||
| 2487 | TNode<UintPtrT> value = Load<UintPtrT>(data_pointer, offset); | |||
| 2488 | return BigIntFromUint64(value); | |||
| 2489 | } else { | |||
| 2490 | DCHECK(!Is64())((void) 0); | |||
| 2491 | #if defined(V8_TARGET_BIG_ENDIAN) | |||
| 2492 | TNode<UintPtrT> high = Load<UintPtrT>(data_pointer, offset); | |||
| 2493 | TNode<UintPtrT> low = Load<UintPtrT>( | |||
| 2494 | data_pointer, IntPtrAdd(offset, IntPtrConstant(kSystemPointerSize))); | |||
| 2495 | #else | |||
| 2496 | TNode<UintPtrT> low = Load<UintPtrT>(data_pointer, offset); | |||
| 2497 | TNode<UintPtrT> high = Load<UintPtrT>( | |||
| 2498 | data_pointer, IntPtrAdd(offset, IntPtrConstant(kSystemPointerSize))); | |||
| 2499 | #endif | |||
| 2500 | return BigIntFromUint32Pair(low, high); | |||
| 2501 | } | |||
| 2502 | } | |||
| 2503 | ||||
| 2504 | TNode<BigInt> CodeStubAssembler::BigIntFromUint32Pair(TNode<UintPtrT> low, | |||
| 2505 | TNode<UintPtrT> high) { | |||
| 2506 | DCHECK(!Is64())((void) 0); | |||
| 2507 | TVARIABLE(BigInt, var_result)TVariable<BigInt> var_result(this); | |||
| 2508 | Label high_zero(this), if_zero(this), done(this); | |||
| 2509 | ||||
| 2510 | GotoIf(IntPtrEqual(high, IntPtrConstant(0)), &high_zero); | |||
| 2511 | var_result = AllocateBigInt(IntPtrConstant(2)); | |||
| 2512 | StoreBigIntDigit(var_result.value(), 0, low); | |||
| 2513 | StoreBigIntDigit(var_result.value(), 1, high); | |||
| 2514 | Goto(&done); | |||
| 2515 | ||||
| 2516 | BIND(&high_zero)Bind(&high_zero); | |||
| 2517 | GotoIf(IntPtrEqual(low, IntPtrConstant(0)), &if_zero); | |||
| 2518 | var_result = AllocateBigInt(IntPtrConstant(1)); | |||
| 2519 | StoreBigIntDigit(var_result.value(), 0, low); | |||
| 2520 | Goto(&done); | |||
| 2521 | ||||
| 2522 | BIND(&if_zero)Bind(&if_zero); | |||
| 2523 | var_result = AllocateBigInt(IntPtrConstant(0)); | |||
| 2524 | Goto(&done); | |||
| 2525 | ||||
| 2526 | BIND(&done)Bind(&done); | |||
| 2527 | return var_result.value(); | |||
| 2528 | } | |||
| 2529 | ||||
| 2530 | TNode<BigInt> CodeStubAssembler::BigIntFromUint64(TNode<UintPtrT> value) { | |||
| 2531 | DCHECK(Is64())((void) 0); | |||
| 2532 | TVARIABLE(BigInt, var_result)TVariable<BigInt> var_result(this); | |||
| 2533 | Label done(this), if_zero(this); | |||
| 2534 | GotoIf(IntPtrEqual(value, IntPtrConstant(0)), &if_zero); | |||
| 2535 | var_result = AllocateBigInt(IntPtrConstant(1)); | |||
| 2536 | StoreBigIntDigit(var_result.value(), 0, value); | |||
| 2537 | Goto(&done); | |||
| 2538 | ||||
| 2539 | BIND(&if_zero)Bind(&if_zero); | |||
| 2540 | var_result = AllocateBigInt(IntPtrConstant(0)); | |||
| 2541 | Goto(&done); | |||
| 2542 | BIND(&done)Bind(&done); | |||
| 2543 | return var_result.value(); | |||
| 2544 | } | |||
| 2545 | ||||
| 2546 | TNode<Numeric> CodeStubAssembler::LoadFixedTypedArrayElementAsTagged( | |||
| 2547 | TNode<RawPtrT> data_pointer, TNode<UintPtrT> index, | |||
| 2548 | ElementsKind elements_kind) { | |||
| 2549 | TNode<IntPtrT> offset = | |||
| 2550 | ElementOffsetFromIndex(Signed(index), elements_kind, 0); | |||
| 2551 | switch (elements_kind) { | |||
| 2552 | case UINT8_ELEMENTS: /* fall through */ | |||
| 2553 | case UINT8_CLAMPED_ELEMENTS: | |||
| 2554 | return SmiFromInt32(Load<Uint8T>(data_pointer, offset)); | |||
| 2555 | case INT8_ELEMENTS: | |||
| 2556 | return SmiFromInt32(Load<Int8T>(data_pointer, offset)); | |||
| 2557 | case UINT16_ELEMENTS: | |||
| 2558 | return SmiFromInt32(Load<Uint16T>(data_pointer, offset)); | |||
| 2559 | case INT16_ELEMENTS: | |||
| 2560 | return SmiFromInt32(Load<Int16T>(data_pointer, offset)); | |||
| 2561 | case UINT32_ELEMENTS: | |||
| 2562 | return ChangeUint32ToTagged(Load<Uint32T>(data_pointer, offset)); | |||
| 2563 | case INT32_ELEMENTS: | |||
| 2564 | return ChangeInt32ToTagged(Load<Int32T>(data_pointer, offset)); | |||
| 2565 | case FLOAT32_ELEMENTS: | |||
| 2566 | return AllocateHeapNumberWithValue( | |||
| 2567 | ChangeFloat32ToFloat64(Load<Float32T>(data_pointer, offset))); | |||
| 2568 | case FLOAT64_ELEMENTS: | |||
| 2569 | return AllocateHeapNumberWithValue(Load<Float64T>(data_pointer, offset)); | |||
| 2570 | case BIGINT64_ELEMENTS: | |||
| 2571 | return LoadFixedBigInt64ArrayElementAsTagged(data_pointer, offset); | |||
| 2572 | case BIGUINT64_ELEMENTS: | |||
| 2573 | return LoadFixedBigUint64ArrayElementAsTagged(data_pointer, offset); | |||
| 2574 | default: | |||
| 2575 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 2576 | } | |||
| 2577 | } | |||
| 2578 | ||||
| 2579 | TNode<Numeric> CodeStubAssembler::LoadFixedTypedArrayElementAsTagged( | |||
| 2580 | TNode<RawPtrT> data_pointer, TNode<UintPtrT> index, | |||
| 2581 | TNode<Int32T> elements_kind) { | |||
| 2582 | TVARIABLE(Numeric, var_result)TVariable<Numeric> var_result(this); | |||
| 2583 | Label done(this), if_unknown_type(this, Label::kDeferred); | |||
| 2584 | int32_t elements_kinds[] = { | |||
| 2585 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) TYPE##_ELEMENTS, | |||
| 2586 | TYPED_ARRAYS(TYPED_ARRAY_CASE)TYPED_ARRAY_CASE(Uint8, uint8, UINT8, uint8_t) TYPED_ARRAY_CASE (Int8, int8, INT8, int8_t) TYPED_ARRAY_CASE(Uint16, uint16, UINT16 , uint16_t) TYPED_ARRAY_CASE(Int16, int16, INT16, int16_t) TYPED_ARRAY_CASE (Uint32, uint32, UINT32, uint32_t) TYPED_ARRAY_CASE(Int32, int32 , INT32, int32_t) TYPED_ARRAY_CASE(Float32, float32, FLOAT32, float) TYPED_ARRAY_CASE(Float64, float64, FLOAT64, double) TYPED_ARRAY_CASE (Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t) TYPED_ARRAY_CASE (BigUint64, biguint64, BIGUINT64, uint64_t) TYPED_ARRAY_CASE( BigInt64, bigint64, BIGINT64, int64_t) RAB_GSAB_TYPED_ARRAYS(TYPED_ARRAY_CASE)TYPED_ARRAY_CASE(RabGsabUint8, rab_gsab_uint8, RAB_GSAB_UINT8 , uint8_t) TYPED_ARRAY_CASE(RabGsabInt8, rab_gsab_int8, RAB_GSAB_INT8 , int8_t) TYPED_ARRAY_CASE(RabGsabUint16, rab_gsab_uint16, RAB_GSAB_UINT16 , uint16_t) TYPED_ARRAY_CASE(RabGsabInt16, rab_gsab_int16, RAB_GSAB_INT16 , int16_t) TYPED_ARRAY_CASE(RabGsabUint32, rab_gsab_uint32, RAB_GSAB_UINT32 , uint32_t) TYPED_ARRAY_CASE(RabGsabInt32, rab_gsab_int32, RAB_GSAB_INT32 , int32_t) TYPED_ARRAY_CASE(RabGsabFloat32, rab_gsab_float32, RAB_GSAB_FLOAT32, float) TYPED_ARRAY_CASE(RabGsabFloat64, rab_gsab_float64 , RAB_GSAB_FLOAT64, double) TYPED_ARRAY_CASE(RabGsabUint8Clamped , rab_gsab_uint8_clamped, RAB_GSAB_UINT8_CLAMPED, uint8_t) TYPED_ARRAY_CASE (RabGsabBigUint64, rab_gsab_biguint64, RAB_GSAB_BIGUINT64, uint64_t ) TYPED_ARRAY_CASE(RabGsabBigInt64, rab_gsab_bigint64, RAB_GSAB_BIGINT64 , int64_t) | |||
| 2587 | #undef TYPED_ARRAY_CASE | |||
| 2588 | }; | |||
| 2589 | ||||
| 2590 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) Label if_##type##array(this); | |||
| 2591 | TYPED_ARRAYS(TYPED_ARRAY_CASE)TYPED_ARRAY_CASE(Uint8, uint8, UINT8, uint8_t) TYPED_ARRAY_CASE (Int8, int8, INT8, int8_t) TYPED_ARRAY_CASE(Uint16, uint16, UINT16 , uint16_t) TYPED_ARRAY_CASE(Int16, int16, INT16, int16_t) TYPED_ARRAY_CASE (Uint32, uint32, UINT32, uint32_t) TYPED_ARRAY_CASE(Int32, int32 , INT32, int32_t) TYPED_ARRAY_CASE(Float32, float32, FLOAT32, float) TYPED_ARRAY_CASE(Float64, float64, FLOAT64, double) TYPED_ARRAY_CASE (Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t) TYPED_ARRAY_CASE (BigUint64, biguint64, BIGUINT64, uint64_t) TYPED_ARRAY_CASE( BigInt64, bigint64, BIGINT64, int64_t) | |||
| 2592 | #undef TYPED_ARRAY_CASE | |||
| 2593 | ||||
| 2594 | Label* elements_kind_labels[] = { | |||
| 2595 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) &if_##type##array, | |||
| 2596 | TYPED_ARRAYS(TYPED_ARRAY_CASE)TYPED_ARRAY_CASE(Uint8, uint8, UINT8, uint8_t) TYPED_ARRAY_CASE (Int8, int8, INT8, int8_t) TYPED_ARRAY_CASE(Uint16, uint16, UINT16 , uint16_t) TYPED_ARRAY_CASE(Int16, int16, INT16, int16_t) TYPED_ARRAY_CASE (Uint32, uint32, UINT32, uint32_t) TYPED_ARRAY_CASE(Int32, int32 , INT32, int32_t) TYPED_ARRAY_CASE(Float32, float32, FLOAT32, float) TYPED_ARRAY_CASE(Float64, float64, FLOAT64, double) TYPED_ARRAY_CASE (Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t) TYPED_ARRAY_CASE (BigUint64, biguint64, BIGUINT64, uint64_t) TYPED_ARRAY_CASE( BigInt64, bigint64, BIGINT64, int64_t) | |||
| 2597 | // The same labels again for RAB / GSAB. We dispatch RAB / GSAB elements | |||
| 2598 | // kinds to the corresponding non-RAB / GSAB elements kinds. | |||
| 2599 | TYPED_ARRAYS(TYPED_ARRAY_CASE)TYPED_ARRAY_CASE(Uint8, uint8, UINT8, uint8_t) TYPED_ARRAY_CASE (Int8, int8, INT8, int8_t) TYPED_ARRAY_CASE(Uint16, uint16, UINT16 , uint16_t) TYPED_ARRAY_CASE(Int16, int16, INT16, int16_t) TYPED_ARRAY_CASE (Uint32, uint32, UINT32, uint32_t) TYPED_ARRAY_CASE(Int32, int32 , INT32, int32_t) TYPED_ARRAY_CASE(Float32, float32, FLOAT32, float) TYPED_ARRAY_CASE(Float64, float64, FLOAT64, double) TYPED_ARRAY_CASE (Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t) TYPED_ARRAY_CASE (BigUint64, biguint64, BIGUINT64, uint64_t) TYPED_ARRAY_CASE( BigInt64, bigint64, BIGINT64, int64_t) | |||
| 2600 | #undef TYPED_ARRAY_CASE | |||
| 2601 | }; | |||
| 2602 | STATIC_ASSERT(arraysize(elements_kinds) == arraysize(elements_kind_labels))static_assert((sizeof(ArraySizeHelper(elements_kinds))) == (sizeof (ArraySizeHelper(elements_kind_labels))), "arraysize(elements_kinds) == arraysize(elements_kind_labels)" ); | |||
| 2603 | ||||
| 2604 | Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels, | |||
| 2605 | arraysize(elements_kinds)(sizeof(ArraySizeHelper(elements_kinds)))); | |||
| 2606 | ||||
| 2607 | BIND(&if_unknown_type)Bind(&if_unknown_type); | |||
| 2608 | Unreachable(); | |||
| 2609 | ||||
| 2610 | #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \ | |||
| 2611 | BIND(&if_##type##array)Bind(&if_##type##array); \ | |||
| 2612 | { \ | |||
| 2613 | var_result = LoadFixedTypedArrayElementAsTagged(data_pointer, index, \ | |||
| 2614 | TYPE##_ELEMENTS); \ | |||
| 2615 | Goto(&done); \ | |||
| 2616 | } | |||
| 2617 | TYPED_ARRAYS(TYPED_ARRAY_CASE)TYPED_ARRAY_CASE(Uint8, uint8, UINT8, uint8_t) TYPED_ARRAY_CASE (Int8, int8, INT8, int8_t) TYPED_ARRAY_CASE(Uint16, uint16, UINT16 , uint16_t) TYPED_ARRAY_CASE(Int16, int16, INT16, int16_t) TYPED_ARRAY_CASE (Uint32, uint32, UINT32, uint32_t) TYPED_ARRAY_CASE(Int32, int32 , INT32, int32_t) TYPED_ARRAY_CASE(Float32, float32, FLOAT32, float) TYPED_ARRAY_CASE(Float64, float64, FLOAT64, double) TYPED_ARRAY_CASE (Uint8Clamped, uint8_clamped, UINT8_CLAMPED, uint8_t) TYPED_ARRAY_CASE (BigUint64, biguint64, BIGUINT64, uint64_t) TYPED_ARRAY_CASE( BigInt64, bigint64, BIGINT64, int64_t) | |||
| 2618 | #undef TYPED_ARRAY_CASE | |||
| 2619 | ||||
| 2620 | BIND(&done)Bind(&done); | |||
| 2621 | return var_result.value(); | |||
| 2622 | } | |||
| 2623 | ||||
| 2624 | template <typename TIndex> | |||
| 2625 | TNode<MaybeObject> CodeStubAssembler::LoadFeedbackVectorSlot( | |||
| 2626 | TNode<FeedbackVector> feedback_vector, TNode<TIndex> slot, | |||
| 2627 | int additional_offset) { | |||
| 2628 | int32_t header_size = FeedbackVector::kRawFeedbackSlotsOffset + | |||
| 2629 | additional_offset - kHeapObjectTag; | |||
| 2630 | TNode<IntPtrT> offset = | |||
| 2631 | ElementOffsetFromIndex(slot, HOLEY_ELEMENTS, header_size); | |||
| 2632 | CSA_SLOW_DCHECK(((void)0) | |||
| 2633 | this, IsOffsetInBounds(offset, LoadFeedbackVectorLength(feedback_vector),((void)0) | |||
| 2634 | FeedbackVector::kHeaderSize))((void)0); | |||
| 2635 | return Load<MaybeObject>(feedback_vector, offset); | |||
| 2636 | } | |||
| 2637 | ||||
| 2638 | template TNode<MaybeObject> CodeStubAssembler::LoadFeedbackVectorSlot( | |||
| 2639 | TNode<FeedbackVector> feedback_vector, TNode<TaggedIndex> slot, | |||
| 2640 | int additional_offset); | |||
| 2641 | template TNode<MaybeObject> CodeStubAssembler::LoadFeedbackVectorSlot( | |||
| 2642 | TNode<FeedbackVector> feedback_vector, TNode<IntPtrT> slot, | |||
| 2643 | int additional_offset); | |||
| 2644 | template TNode<MaybeObject> CodeStubAssembler::LoadFeedbackVectorSlot( | |||
| 2645 | TNode<FeedbackVector> feedback_vector, TNode<UintPtrT> slot, | |||
| 2646 | int additional_offset); | |||
| 2647 | ||||
| 2648 | template <typename Array> | |||
| 2649 | TNode<Int32T> CodeStubAssembler::LoadAndUntagToWord32ArrayElement( | |||
| 2650 | TNode<Array> object, int array_header_size, TNode<IntPtrT> index, | |||
| 2651 | int additional_offset) { | |||
| 2652 | DCHECK(IsAligned(additional_offset, kTaggedSize))((void) 0); | |||
| 2653 | int endian_correction = 0; | |||
| 2654 | #if V8_TARGET_LITTLE_ENDIAN1 | |||
| 2655 | if (SmiValuesAre32Bits()) endian_correction = 4; | |||
| 2656 | #endif | |||
| 2657 | int32_t header_size = array_header_size + additional_offset - kHeapObjectTag + | |||
| 2658 | endian_correction; | |||
| 2659 | TNode<IntPtrT> offset = | |||
| 2660 | ElementOffsetFromIndex(index, HOLEY_ELEMENTS, header_size); | |||
| 2661 | CSA_DCHECK(this, IsOffsetInBounds(offset, LoadArrayLength(object),((void)0) | |||
| 2662 | array_header_size + endian_correction))((void)0); | |||
| 2663 | if (SmiValuesAre32Bits()) { | |||
| 2664 | return Load<Int32T>(object, offset); | |||
| 2665 | } else { | |||
| 2666 | return SmiToInt32(Load<Smi>(object, offset)); | |||
| 2667 | } | |||
| 2668 | } | |||
| 2669 | ||||
| 2670 | TNode<Int32T> CodeStubAssembler::LoadAndUntagToWord32FixedArrayElement( | |||
| 2671 | TNode<FixedArray> object, TNode<IntPtrT> index, int additional_offset) { | |||
| 2672 | CSA_SLOW_DCHECK(this, IsFixedArraySubclass(object))((void)0); | |||
| 2673 | return LoadAndUntagToWord32ArrayElement(object, FixedArray::kHeaderSize, | |||
| 2674 | index, additional_offset); | |||
| 2675 | } | |||
| 2676 | ||||
| 2677 | TNode<MaybeObject> CodeStubAssembler::LoadWeakFixedArrayElement( | |||
| 2678 | TNode<WeakFixedArray> object, TNode<IntPtrT> index, int additional_offset) { | |||
| 2679 | return LoadArrayElement(object, WeakFixedArray::kHeaderSize, index, | |||
| 2680 | additional_offset); | |||
| 2681 | } | |||
| 2682 | ||||
| 2683 | TNode<Float64T> CodeStubAssembler::LoadFixedDoubleArrayElement( | |||
| 2684 | TNode<FixedDoubleArray> object, TNode<IntPtrT> index, Label* if_hole, | |||
| 2685 | MachineType machine_type) { | |||
| 2686 | int32_t header_size = FixedDoubleArray::kHeaderSize - kHeapObjectTag; | |||
| 2687 | TNode<IntPtrT> offset = | |||
| 2688 | ElementOffsetFromIndex(index, HOLEY_DOUBLE_ELEMENTS, header_size); | |||
| 2689 | CSA_DCHECK(this, IsOffsetInBounds(((void)0) | |||
| 2690 | offset, LoadAndUntagFixedArrayBaseLength(object),((void)0) | |||
| 2691 | FixedDoubleArray::kHeaderSize, HOLEY_DOUBLE_ELEMENTS))((void)0); | |||
| 2692 | return LoadDoubleWithHoleCheck(object, offset, if_hole, machine_type); | |||
| 2693 | } | |||
| 2694 | ||||
| 2695 | TNode<Object> CodeStubAssembler::LoadFixedArrayBaseElementAsTagged( | |||
| 2696 | TNode<FixedArrayBase> elements, TNode<IntPtrT> index, | |||
| 2697 | TNode<Int32T> elements_kind, Label* if_accessor, Label* if_hole) { | |||
| 2698 | TVARIABLE(Object, var_result)TVariable<Object> var_result(this); | |||
| 2699 | Label done(this), if_packed(this), if_holey(this), if_packed_double(this), | |||
| 2700 | if_holey_double(this), if_dictionary(this, Label::kDeferred); | |||
| 2701 | ||||
| 2702 | int32_t kinds[] = { | |||
| 2703 | // Handled by if_packed. | |||
| 2704 | PACKED_SMI_ELEMENTS, PACKED_ELEMENTS, PACKED_NONEXTENSIBLE_ELEMENTS, | |||
| 2705 | PACKED_SEALED_ELEMENTS, PACKED_FROZEN_ELEMENTS, | |||
| 2706 | // Handled by if_holey. | |||
| 2707 | HOLEY_SMI_ELEMENTS, HOLEY_ELEMENTS, HOLEY_NONEXTENSIBLE_ELEMENTS, | |||
| 2708 | HOLEY_SEALED_ELEMENTS, HOLEY_FROZEN_ELEMENTS, | |||
| 2709 | // Handled by if_packed_double. | |||
| 2710 | PACKED_DOUBLE_ELEMENTS, | |||
| 2711 | // Handled by if_holey_double. | |||
| 2712 | HOLEY_DOUBLE_ELEMENTS}; | |||
| 2713 | Label* labels[] = {// PACKED_{SMI,}_ELEMENTS | |||
| 2714 | &if_packed, &if_packed, &if_packed, &if_packed, &if_packed, | |||
| 2715 | // HOLEY_{SMI,}_ELEMENTS | |||
| 2716 | &if_holey, &if_holey, &if_holey, &if_holey, &if_holey, | |||
| 2717 | // PACKED_DOUBLE_ELEMENTS | |||
| 2718 | &if_packed_double, | |||
| 2719 | // HOLEY_DOUBLE_ELEMENTS | |||
| 2720 | &if_holey_double}; | |||
| 2721 | Switch(elements_kind, &if_dictionary, kinds, labels, arraysize(kinds)(sizeof(ArraySizeHelper(kinds)))); | |||
| 2722 | ||||
| 2723 | BIND(&if_packed)Bind(&if_packed); | |||
| 2724 | { | |||
| 2725 | var_result = LoadFixedArrayElement(CAST(elements)Cast(elements), index, 0); | |||
| 2726 | Goto(&done); | |||
| 2727 | } | |||
| 2728 | ||||
| 2729 | BIND(&if_holey)Bind(&if_holey); | |||
| 2730 | { | |||
| 2731 | var_result = LoadFixedArrayElement(CAST(elements)Cast(elements), index); | |||
| 2732 | Branch(TaggedEqual(var_result.value(), TheHoleConstant()), if_hole, &done); | |||
| 2733 | } | |||
| 2734 | ||||
| 2735 | BIND(&if_packed_double)Bind(&if_packed_double); | |||
| 2736 | { | |||
| 2737 | var_result = AllocateHeapNumberWithValue( | |||
| 2738 | LoadFixedDoubleArrayElement(CAST(elements)Cast(elements), index)); | |||
| 2739 | Goto(&done); | |||
| 2740 | } | |||
| 2741 | ||||
| 2742 | BIND(&if_holey_double)Bind(&if_holey_double); | |||
| 2743 | { | |||
| 2744 | var_result = AllocateHeapNumberWithValue( | |||
| 2745 | LoadFixedDoubleArrayElement(CAST(elements)Cast(elements), index, if_hole)); | |||
| 2746 | Goto(&done); | |||
| 2747 | } | |||
| 2748 | ||||
| 2749 | BIND(&if_dictionary)Bind(&if_dictionary); | |||
| 2750 | { | |||
| 2751 | CSA_DCHECK(this, IsDictionaryElementsKind(elements_kind))((void)0); | |||
| 2752 | var_result = BasicLoadNumberDictionaryElement(CAST(elements)Cast(elements), index, | |||
| 2753 | if_accessor, if_hole); | |||
| 2754 | Goto(&done); | |||
| 2755 | } | |||
| 2756 | ||||
| 2757 | BIND(&done)Bind(&done); | |||
| 2758 | return var_result.value(); | |||
| 2759 | } | |||
| 2760 | ||||
| 2761 | TNode<BoolT> CodeStubAssembler::IsDoubleHole(TNode<Object> base, | |||
| 2762 | TNode<IntPtrT> offset) { | |||
| 2763 | // TODO(ishell): Compare only the upper part for the hole once the | |||
| 2764 | // compiler is able to fold addition of already complex |offset| with | |||
| 2765 | // |kIeeeDoubleExponentWordOffset| into one addressing mode. | |||
| 2766 | if (Is64()) { | |||
| 2767 | TNode<Uint64T> element = Load<Uint64T>(base, offset); | |||
| 2768 | return Word64Equal(element, Int64Constant(kHoleNanInt64)); | |||
| 2769 | } else { | |||
| 2770 | TNode<Uint32T> element_upper = Load<Uint32T>( | |||
| 2771 | base, IntPtrAdd(offset, IntPtrConstant(kIeeeDoubleExponentWordOffset))); | |||
| 2772 | return Word32Equal(element_upper, Int32Constant(kHoleNanUpper32)); | |||
| 2773 | } | |||
| 2774 | } | |||
| 2775 | ||||
| 2776 | TNode<Float64T> CodeStubAssembler::LoadDoubleWithHoleCheck( | |||
| 2777 | TNode<Object> base, TNode<IntPtrT> offset, Label* if_hole, | |||
| 2778 | MachineType machine_type) { | |||
| 2779 | if (if_hole) { | |||
| 2780 | GotoIf(IsDoubleHole(base, offset), if_hole); | |||
| 2781 | } | |||
| 2782 | if (machine_type.IsNone()) { | |||
| 2783 | // This means the actual value is not needed. | |||
| 2784 | return TNode<Float64T>(); | |||
| 2785 | } | |||
| 2786 | return UncheckedCast<Float64T>(Load(machine_type, base, offset)); | |||
| 2787 | } | |||
| 2788 | ||||
| 2789 | TNode<ScopeInfo> CodeStubAssembler::LoadScopeInfo(TNode<Context> context) { | |||
| 2790 | return CAST(LoadContextElement(context, Context::SCOPE_INFO_INDEX))Cast(LoadContextElement(context, Context::SCOPE_INFO_INDEX)); | |||
| 2791 | } | |||
| 2792 | ||||
| 2793 | TNode<BoolT> CodeStubAssembler::LoadScopeInfoHasExtensionField( | |||
| 2794 | TNode<ScopeInfo> scope_info) { | |||
| 2795 | TNode<IntPtrT> value = | |||
| 2796 | LoadAndUntagObjectField(scope_info, ScopeInfo::kFlagsOffset); | |||
| 2797 | return IsSetWord<ScopeInfo::HasContextExtensionSlotBit>(value); | |||
| 2798 | } | |||
| 2799 | ||||
| 2800 | void CodeStubAssembler::StoreContextElementNoWriteBarrier( | |||
| 2801 | TNode<Context> context, int slot_index, TNode<Object> value) { | |||
| 2802 | int offset = Context::SlotOffset(slot_index); | |||
| 2803 | StoreNoWriteBarrier(MachineRepresentation::kTagged, context, | |||
| 2804 | IntPtrConstant(offset), value); | |||
| 2805 | } | |||
| 2806 | ||||
| 2807 | TNode<NativeContext> CodeStubAssembler::LoadNativeContext( | |||
| 2808 | TNode<Context> context) { | |||
| 2809 | TNode<Map> map = LoadMap(context); | |||
| 2810 | return CAST(LoadObjectField(Cast(LoadObjectField( map, Map::kConstructorOrBackPointerOrNativeContextOffset )) | |||
| 2811 | map, Map::kConstructorOrBackPointerOrNativeContextOffset))Cast(LoadObjectField( map, Map::kConstructorOrBackPointerOrNativeContextOffset )); | |||
| 2812 | } | |||
| 2813 | ||||
| 2814 | TNode<Context> CodeStubAssembler::LoadModuleContext(TNode<Context> context) { | |||
| 2815 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 2816 | TNode<Map> module_map = CAST(Cast(LoadContextElement(native_context, Context::MODULE_CONTEXT_MAP_INDEX )) | |||
| 2817 | LoadContextElement(native_context, Context::MODULE_CONTEXT_MAP_INDEX))Cast(LoadContextElement(native_context, Context::MODULE_CONTEXT_MAP_INDEX )); | |||
| 2818 | TVariable<Object> cur_context(context, this); | |||
| 2819 | ||||
| 2820 | Label context_found(this); | |||
| 2821 | ||||
| 2822 | Label context_search(this, &cur_context); | |||
| 2823 | ||||
| 2824 | // Loop until cur_context->map() is module_map. | |||
| 2825 | Goto(&context_search); | |||
| 2826 | BIND(&context_search)Bind(&context_search); | |||
| 2827 | { | |||
| 2828 | CSA_DCHECK(this, Word32BinaryNot(((void)0) | |||
| 2829 | TaggedEqual(cur_context.value(), native_context)))((void)0); | |||
| 2830 | GotoIf(TaggedEqual(LoadMap(CAST(cur_context.value())Cast(cur_context.value())), module_map), | |||
| 2831 | &context_found); | |||
| 2832 | ||||
| 2833 | cur_context = | |||
| 2834 | LoadContextElement(CAST(cur_context.value())Cast(cur_context.value()), Context::PREVIOUS_INDEX); | |||
| 2835 | Goto(&context_search); | |||
| 2836 | } | |||
| 2837 | ||||
| 2838 | BIND(&context_found)Bind(&context_found); | |||
| 2839 | return UncheckedCast<Context>(cur_context.value()); | |||
| 2840 | } | |||
| 2841 | ||||
| 2842 | TNode<Object> CodeStubAssembler::GetImportMetaObject(TNode<Context> context) { | |||
| 2843 | const TNode<Context> module_context = LoadModuleContext(context); | |||
| 2844 | const TNode<HeapObject> module = | |||
| 2845 | CAST(LoadContextElement(module_context, Context::EXTENSION_INDEX))Cast(LoadContextElement(module_context, Context::EXTENSION_INDEX )); | |||
| 2846 | const TNode<Object> import_meta = | |||
| 2847 | LoadObjectField(module, SourceTextModule::kImportMetaOffset); | |||
| 2848 | ||||
| 2849 | TVARIABLE(Object, return_value, import_meta)TVariable<Object> return_value(import_meta, this); | |||
| 2850 | ||||
| 2851 | Label end(this); | |||
| 2852 | GotoIfNot(IsTheHole(import_meta), &end); | |||
| 2853 | ||||
| 2854 | return_value = CallRuntime(Runtime::kGetImportMetaObject, context); | |||
| 2855 | Goto(&end); | |||
| 2856 | ||||
| 2857 | BIND(&end)Bind(&end); | |||
| 2858 | return return_value.value(); | |||
| 2859 | } | |||
| 2860 | ||||
| 2861 | TNode<Map> CodeStubAssembler::LoadObjectFunctionInitialMap( | |||
| 2862 | TNode<NativeContext> native_context) { | |||
| 2863 | TNode<JSFunction> object_function = | |||
| 2864 | CAST(LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX))Cast(LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX )); | |||
| 2865 | return CAST(LoadJSFunctionPrototypeOrInitialMap(object_function))Cast(LoadJSFunctionPrototypeOrInitialMap(object_function)); | |||
| 2866 | } | |||
| 2867 | ||||
| 2868 | TNode<Map> CodeStubAssembler::LoadSlowObjectWithNullPrototypeMap( | |||
| 2869 | TNode<NativeContext> native_context) { | |||
| 2870 | TNode<Map> map = CAST(LoadContextElement(Cast(LoadContextElement( native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP )) | |||
| 2871 | native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP))Cast(LoadContextElement( native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP )); | |||
| 2872 | return map; | |||
| 2873 | } | |||
| 2874 | ||||
| 2875 | TNode<Map> CodeStubAssembler::LoadJSArrayElementsMap( | |||
| 2876 | TNode<Int32T> kind, TNode<NativeContext> native_context) { | |||
| 2877 | CSA_DCHECK(this, IsFastElementsKind(kind))((void)0); | |||
| 2878 | TNode<IntPtrT> offset = | |||
| 2879 | IntPtrAdd(IntPtrConstant(Context::FIRST_JS_ARRAY_MAP_SLOT), | |||
| 2880 | ChangeInt32ToIntPtr(kind)); | |||
| 2881 | return UncheckedCast<Map>(LoadContextElement(native_context, offset)); | |||
| 2882 | } | |||
| 2883 | ||||
| 2884 | TNode<Map> CodeStubAssembler::LoadJSArrayElementsMap( | |||
| 2885 | ElementsKind kind, TNode<NativeContext> native_context) { | |||
| 2886 | return UncheckedCast<Map>( | |||
| 2887 | LoadContextElement(native_context, Context::ArrayMapIndex(kind))); | |||
| 2888 | } | |||
| 2889 | ||||
| 2890 | TNode<BoolT> CodeStubAssembler::IsGeneratorFunction( | |||
| 2891 | TNode<JSFunction> function) { | |||
| 2892 | const TNode<SharedFunctionInfo> shared_function_info = | |||
| 2893 | LoadObjectField<SharedFunctionInfo>( | |||
| 2894 | function, JSFunction::kSharedFunctionInfoOffset); | |||
| 2895 | ||||
| 2896 | const TNode<Uint32T> function_kind = | |||
| 2897 | DecodeWord32<SharedFunctionInfo::FunctionKindBits>( | |||
| 2898 | LoadObjectField<Uint32T>(shared_function_info, | |||
| 2899 | SharedFunctionInfo::kFlagsOffset)); | |||
| 2900 | ||||
| 2901 | // See IsGeneratorFunction(FunctionKind kind). | |||
| 2902 | return IsInRange( | |||
| 2903 | function_kind, | |||
| 2904 | static_cast<uint32_t>(FunctionKind::kAsyncConciseGeneratorMethod), | |||
| 2905 | static_cast<uint32_t>(FunctionKind::kConciseGeneratorMethod)); | |||
| 2906 | } | |||
| 2907 | ||||
| 2908 | TNode<BoolT> CodeStubAssembler::IsJSFunctionWithPrototypeSlot( | |||
| 2909 | TNode<HeapObject> object) { | |||
| 2910 | // Only JSFunction maps may have HasPrototypeSlotBit set. | |||
| 2911 | return IsSetWord32<Map::Bits1::HasPrototypeSlotBit>( | |||
| 2912 | LoadMapBitField(LoadMap(object))); | |||
| 2913 | } | |||
| 2914 | ||||
| 2915 | void CodeStubAssembler::BranchIfHasPrototypeProperty( | |||
| 2916 | TNode<JSFunction> function, TNode<Int32T> function_map_bit_field, | |||
| 2917 | Label* if_true, Label* if_false) { | |||
| 2918 | // (has_prototype_slot() && IsConstructor()) || | |||
| 2919 | // IsGeneratorFunction(shared()->kind()) | |||
| 2920 | uint32_t mask = Map::Bits1::HasPrototypeSlotBit::kMask | | |||
| 2921 | Map::Bits1::IsConstructorBit::kMask; | |||
| 2922 | ||||
| 2923 | GotoIf(IsAllSetWord32(function_map_bit_field, mask), if_true); | |||
| 2924 | Branch(IsGeneratorFunction(function), if_true, if_false); | |||
| 2925 | } | |||
| 2926 | ||||
| 2927 | void CodeStubAssembler::GotoIfPrototypeRequiresRuntimeLookup( | |||
| 2928 | TNode<JSFunction> function, TNode<Map> map, Label* runtime) { | |||
| 2929 | // !has_prototype_property() || has_non_instance_prototype() | |||
| 2930 | TNode<Int32T> map_bit_field = LoadMapBitField(map); | |||
| 2931 | Label next_check(this); | |||
| 2932 | BranchIfHasPrototypeProperty(function, map_bit_field, &next_check, runtime); | |||
| 2933 | BIND(&next_check)Bind(&next_check); | |||
| 2934 | GotoIf(IsSetWord32<Map::Bits1::HasNonInstancePrototypeBit>(map_bit_field), | |||
| 2935 | runtime); | |||
| 2936 | } | |||
| 2937 | ||||
| 2938 | TNode<HeapObject> CodeStubAssembler::LoadJSFunctionPrototype( | |||
| 2939 | TNode<JSFunction> function, Label* if_bailout) { | |||
| 2940 | CSA_DCHECK(this, IsFunctionWithPrototypeSlotMap(LoadMap(function)))((void)0); | |||
| 2941 | CSA_DCHECK(this, IsClearWord32<Map::Bits1::HasNonInstancePrototypeBit>(((void)0) | |||
| 2942 | LoadMapBitField(LoadMap(function))))((void)0); | |||
| 2943 | TNode<HeapObject> proto_or_map = LoadObjectField<HeapObject>( | |||
| 2944 | function, JSFunction::kPrototypeOrInitialMapOffset); | |||
| 2945 | GotoIf(IsTheHole(proto_or_map), if_bailout); | |||
| 2946 | ||||
| 2947 | TVARIABLE(HeapObject, var_result, proto_or_map)TVariable<HeapObject> var_result(proto_or_map, this); | |||
| 2948 | Label done(this, &var_result); | |||
| 2949 | GotoIfNot(IsMap(proto_or_map), &done); | |||
| 2950 | ||||
| 2951 | var_result = LoadMapPrototype(CAST(proto_or_map)Cast(proto_or_map)); | |||
| 2952 | Goto(&done); | |||
| 2953 | ||||
| 2954 | BIND(&done)Bind(&done); | |||
| 2955 | return var_result.value(); | |||
| 2956 | } | |||
| 2957 | ||||
| 2958 | TNode<BytecodeArray> CodeStubAssembler::LoadSharedFunctionInfoBytecodeArray( | |||
| 2959 | TNode<SharedFunctionInfo> shared) { | |||
| 2960 | TNode<HeapObject> function_data = LoadObjectField<HeapObject>( | |||
| 2961 | shared, SharedFunctionInfo::kFunctionDataOffset); | |||
| 2962 | ||||
| 2963 | TVARIABLE(HeapObject, var_result, function_data)TVariable<HeapObject> var_result(function_data, this); | |||
| 2964 | ||||
| 2965 | Label check_for_interpreter_data(this, &var_result); | |||
| 2966 | Label done(this, &var_result); | |||
| 2967 | ||||
| 2968 | GotoIfNot(HasInstanceType(var_result.value(), CODET_TYPE), | |||
| 2969 | &check_for_interpreter_data); | |||
| 2970 | { | |||
| 2971 | TNode<CodeT> code = CAST(var_result.value())Cast(var_result.value()); | |||
| 2972 | #ifdef DEBUG | |||
| 2973 | TNode<Int32T> code_flags = | |||
| 2974 | LoadObjectField<Int32T>(code, CodeT::kFlagsOffset); | |||
| 2975 | CSA_DCHECK(((void)0) | |||
| 2976 | this, Word32Equal(DecodeWord32<CodeT::KindField>(code_flags),((void)0) | |||
| 2977 | Int32Constant(static_cast<int>(CodeKind::BASELINE))))((void)0); | |||
| 2978 | #endif // DEBUG | |||
| 2979 | TNode<HeapObject> baseline_data = LoadObjectField<HeapObject>( | |||
| 2980 | FromCodeT(code), Code::kDeoptimizationDataOrInterpreterDataOffset); | |||
| 2981 | var_result = baseline_data; | |||
| 2982 | } | |||
| 2983 | Goto(&check_for_interpreter_data); | |||
| 2984 | ||||
| 2985 | BIND(&check_for_interpreter_data)Bind(&check_for_interpreter_data); | |||
| 2986 | ||||
| 2987 | GotoIfNot(HasInstanceType(var_result.value(), INTERPRETER_DATA_TYPE), &done); | |||
| 2988 | TNode<BytecodeArray> bytecode_array = LoadObjectField<BytecodeArray>( | |||
| 2989 | var_result.value(), InterpreterData::kBytecodeArrayOffset); | |||
| 2990 | var_result = bytecode_array; | |||
| 2991 | Goto(&done); | |||
| 2992 | ||||
| 2993 | BIND(&done)Bind(&done); | |||
| 2994 | return CAST(var_result.value())Cast(var_result.value()); | |||
| 2995 | } | |||
| 2996 | ||||
| 2997 | void CodeStubAssembler::StoreObjectByteNoWriteBarrier(TNode<HeapObject> object, | |||
| 2998 | int offset, | |||
| 2999 | TNode<Word32T> value) { | |||
| 3000 | StoreNoWriteBarrier(MachineRepresentation::kWord8, object, | |||
| 3001 | IntPtrConstant(offset - kHeapObjectTag), value); | |||
| 3002 | } | |||
| 3003 | ||||
| 3004 | void CodeStubAssembler::StoreHeapNumberValue(TNode<HeapNumber> object, | |||
| 3005 | TNode<Float64T> value) { | |||
| 3006 | StoreObjectFieldNoWriteBarrier(object, HeapNumber::kValueOffset, value); | |||
| 3007 | } | |||
| 3008 | ||||
| 3009 | void CodeStubAssembler::StoreObjectField(TNode<HeapObject> object, int offset, | |||
| 3010 | TNode<Smi> value) { | |||
| 3011 | StoreObjectFieldNoWriteBarrier(object, offset, value); | |||
| 3012 | } | |||
| 3013 | ||||
| 3014 | void CodeStubAssembler::StoreObjectField(TNode<HeapObject> object, | |||
| 3015 | TNode<IntPtrT> offset, | |||
| 3016 | TNode<Smi> value) { | |||
| 3017 | StoreObjectFieldNoWriteBarrier(object, offset, value); | |||
| 3018 | } | |||
| 3019 | ||||
| 3020 | void CodeStubAssembler::StoreObjectField(TNode<HeapObject> object, int offset, | |||
| 3021 | TNode<Object> value) { | |||
| 3022 | DCHECK_NE(HeapObject::kMapOffset, offset)((void) 0); // Use StoreMap instead. | |||
| 3023 | OptimizedStoreField(MachineRepresentation::kTagged, | |||
| 3024 | UncheckedCast<HeapObject>(object), offset, value); | |||
| 3025 | } | |||
| 3026 | ||||
| 3027 | void CodeStubAssembler::StoreObjectField(TNode<HeapObject> object, | |||
| 3028 | TNode<IntPtrT> offset, | |||
| 3029 | TNode<Object> value) { | |||
| 3030 | int const_offset; | |||
| 3031 | if (TryToInt32Constant(offset, &const_offset)) { | |||
| 3032 | StoreObjectField(object, const_offset, value); | |||
| 3033 | } else { | |||
| 3034 | Store(object, IntPtrSub(offset, IntPtrConstant(kHeapObjectTag)), value); | |||
| 3035 | } | |||
| 3036 | } | |||
| 3037 | ||||
| 3038 | void CodeStubAssembler::UnsafeStoreObjectFieldNoWriteBarrier( | |||
| 3039 | TNode<HeapObject> object, int offset, TNode<Object> value) { | |||
| 3040 | DCHECK_NE(HeapObject::kMapOffset, offset)((void) 0); // Use StoreMap instead. | |||
| 3041 | OptimizedStoreFieldUnsafeNoWriteBarrier(MachineRepresentation::kTagged, | |||
| 3042 | object, offset, value); | |||
| 3043 | } | |||
| 3044 | ||||
| 3045 | void CodeStubAssembler::StoreJSSharedStructInObjectField( | |||
| 3046 | TNode<HeapObject> object, TNode<IntPtrT> offset, TNode<Object> value) { | |||
| 3047 | CSA_DCHECK(this, IsJSSharedStruct(object))((void)0); | |||
| 3048 | // JSSharedStructs are allocated in the shared old space, which is currently | |||
| 3049 | // collected by stopping the world, so the incremental write barrier is not | |||
| 3050 | // needed. They can only store Smis and other HeapObjects in the shared old | |||
| 3051 | // space, so the generational write barrier is also not needed. | |||
| 3052 | // TODO(v8:12547): Add a safer, shared variant of NoWriteBarrier instead of | |||
| 3053 | // using Unsafe. | |||
| 3054 | int const_offset; | |||
| 3055 | if (TryToInt32Constant(offset, &const_offset)) { | |||
| 3056 | UnsafeStoreObjectFieldNoWriteBarrier(object, const_offset, value); | |||
| 3057 | } else { | |||
| 3058 | UnsafeStoreNoWriteBarrier(MachineRepresentation::kTagged, object, | |||
| 3059 | IntPtrSub(offset, IntPtrConstant(kHeapObjectTag)), | |||
| 3060 | value); | |||
| 3061 | } | |||
| 3062 | } | |||
| 3063 | ||||
| 3064 | void CodeStubAssembler::StoreMap(TNode<HeapObject> object, TNode<Map> map) { | |||
| 3065 | OptimizedStoreMap(object, map); | |||
| 3066 | DcheckHasValidMap(object); | |||
| 3067 | } | |||
| 3068 | ||||
| 3069 | void CodeStubAssembler::StoreMapNoWriteBarrier(TNode<HeapObject> object, | |||
| 3070 | RootIndex map_root_index) { | |||
| 3071 | StoreMapNoWriteBarrier(object, CAST(LoadRoot(map_root_index))Cast(LoadRoot(map_root_index))); | |||
| 3072 | } | |||
| 3073 | ||||
| 3074 | void CodeStubAssembler::StoreMapNoWriteBarrier(TNode<HeapObject> object, | |||
| 3075 | TNode<Map> map) { | |||
| 3076 | OptimizedStoreMap(object, map); | |||
| 3077 | DcheckHasValidMap(object); | |||
| 3078 | } | |||
| 3079 | ||||
| 3080 | void CodeStubAssembler::StoreObjectFieldRoot(TNode<HeapObject> object, | |||
| 3081 | int offset, RootIndex root_index) { | |||
| 3082 | TNode<Object> root = LoadRoot(root_index); | |||
| 3083 | if (offset == HeapObject::kMapOffset) { | |||
| 3084 | StoreMap(object, CAST(root)Cast(root)); | |||
| 3085 | } else if (RootsTable::IsImmortalImmovable(root_index)) { | |||
| 3086 | StoreObjectFieldNoWriteBarrier(object, offset, root); | |||
| 3087 | } else { | |||
| 3088 | StoreObjectField(object, offset, root); | |||
| 3089 | } | |||
| 3090 | } | |||
| 3091 | ||||
| 3092 | template <typename TIndex> | |||
| 3093 | void CodeStubAssembler::StoreFixedArrayOrPropertyArrayElement( | |||
| 3094 | TNode<UnionT<FixedArray, PropertyArray>> object, TNode<TIndex> index_node, | |||
| 3095 | TNode<Object> value, WriteBarrierMode barrier_mode, int additional_offset) { | |||
| 3096 | // TODO(v8:9708): Do we want to keep both IntPtrT and UintPtrT variants? | |||
| 3097 | static_assert(std::is_same<TIndex, Smi>::value || | |||
| 3098 | std::is_same<TIndex, UintPtrT>::value || | |||
| 3099 | std::is_same<TIndex, IntPtrT>::value, | |||
| 3100 | "Only Smi, UintPtrT or IntPtrT index is allowed"); | |||
| 3101 | DCHECK(barrier_mode == SKIP_WRITE_BARRIER ||((void) 0) | |||
| 3102 | barrier_mode == UNSAFE_SKIP_WRITE_BARRIER ||((void) 0) | |||
| 3103 | barrier_mode == UPDATE_WRITE_BARRIER ||((void) 0) | |||
| 3104 | barrier_mode == UPDATE_EPHEMERON_KEY_WRITE_BARRIER)((void) 0); | |||
| 3105 | DCHECK(IsAligned(additional_offset, kTaggedSize))((void) 0); | |||
| 3106 | STATIC_ASSERT(static_cast<int>(FixedArray::kHeaderSize) ==static_assert(static_cast<int>(FixedArray::kHeaderSize) == static_cast<int>(PropertyArray::kHeaderSize), "static_cast<int>(FixedArray::kHeaderSize) == static_cast<int>(PropertyArray::kHeaderSize)" ) | |||
| 3107 | static_cast<int>(PropertyArray::kHeaderSize))static_assert(static_cast<int>(FixedArray::kHeaderSize) == static_cast<int>(PropertyArray::kHeaderSize), "static_cast<int>(FixedArray::kHeaderSize) == static_cast<int>(PropertyArray::kHeaderSize)" ); | |||
| 3108 | int header_size = | |||
| 3109 | FixedArray::kHeaderSize + additional_offset - kHeapObjectTag; | |||
| 3110 | TNode<IntPtrT> offset = | |||
| 3111 | ElementOffsetFromIndex(index_node, HOLEY_ELEMENTS, header_size); | |||
| 3112 | STATIC_ASSERT(static_cast<int>(FixedArrayBase::kLengthOffset) ==static_assert(static_cast<int>(FixedArrayBase::kLengthOffset ) == static_cast<int>(WeakFixedArray::kLengthOffset), "static_cast<int>(FixedArrayBase::kLengthOffset) == static_cast<int>(WeakFixedArray::kLengthOffset)" ) | |||
| 3113 | static_cast<int>(WeakFixedArray::kLengthOffset))static_assert(static_cast<int>(FixedArrayBase::kLengthOffset ) == static_cast<int>(WeakFixedArray::kLengthOffset), "static_cast<int>(FixedArrayBase::kLengthOffset) == static_cast<int>(WeakFixedArray::kLengthOffset)" ); | |||
| 3114 | STATIC_ASSERT(static_cast<int>(FixedArrayBase::kLengthOffset) ==static_assert(static_cast<int>(FixedArrayBase::kLengthOffset ) == static_cast<int>(PropertyArray::kLengthAndHashOffset ), "static_cast<int>(FixedArrayBase::kLengthOffset) == static_cast<int>(PropertyArray::kLengthAndHashOffset)" ) | |||
| 3115 | static_cast<int>(PropertyArray::kLengthAndHashOffset))static_assert(static_cast<int>(FixedArrayBase::kLengthOffset ) == static_cast<int>(PropertyArray::kLengthAndHashOffset ), "static_cast<int>(FixedArrayBase::kLengthOffset) == static_cast<int>(PropertyArray::kLengthAndHashOffset)" ); | |||
| 3116 | // Check that index_node + additional_offset <= object.length. | |||
| 3117 | // TODO(cbruni): Use proper LoadXXLength helpers | |||
| 3118 | CSA_DCHECK(((void)0) | |||
| 3119 | this,((void)0) | |||
| 3120 | IsOffsetInBounds(((void)0) | |||
| 3121 | offset,((void)0) | |||
| 3122 | Select<IntPtrT>(((void)0) | |||
| 3123 | IsPropertyArray(object),((void)0) | |||
| 3124 | [=] {((void)0) | |||
| 3125 | TNode<IntPtrT> length_and_hash = LoadAndUntagObjectField(((void)0) | |||
| 3126 | object, PropertyArray::kLengthAndHashOffset);((void)0) | |||
| 3127 | return Signed(((void)0) | |||
| 3128 | DecodeWord<PropertyArray::LengthField>(length_and_hash));((void)0) | |||
| 3129 | },((void)0) | |||
| 3130 | [=] {((void)0) | |||
| 3131 | return LoadAndUntagObjectField(object,((void)0) | |||
| 3132 | FixedArrayBase::kLengthOffset);((void)0) | |||
| 3133 | }),((void)0) | |||
| 3134 | FixedArray::kHeaderSize))((void)0); | |||
| 3135 | if (barrier_mode == SKIP_WRITE_BARRIER) { | |||
| 3136 | StoreNoWriteBarrier(MachineRepresentation::kTagged, object, offset, value); | |||
| 3137 | } else if (barrier_mode == UNSAFE_SKIP_WRITE_BARRIER) { | |||
| 3138 | UnsafeStoreNoWriteBarrier(MachineRepresentation::kTagged, object, offset, | |||
| 3139 | value); | |||
| 3140 | } else if (barrier_mode == UPDATE_EPHEMERON_KEY_WRITE_BARRIER) { | |||
| 3141 | StoreEphemeronKey(object, offset, value); | |||
| 3142 | } else { | |||
| 3143 | Store(object, offset, value); | |||
| 3144 | } | |||
| 3145 | } | |||
| 3146 | ||||
| 3147 | template V8_EXPORT_PRIVATE void | |||
| 3148 | CodeStubAssembler::StoreFixedArrayOrPropertyArrayElement<Smi>( | |||
| 3149 | TNode<UnionT<FixedArray, PropertyArray>>, TNode<Smi>, TNode<Object>, | |||
| 3150 | WriteBarrierMode, int); | |||
| 3151 | ||||
| 3152 | template V8_EXPORT_PRIVATE void | |||
| 3153 | CodeStubAssembler::StoreFixedArrayOrPropertyArrayElement<IntPtrT>( | |||
| 3154 | TNode<UnionT<FixedArray, PropertyArray>>, TNode<IntPtrT>, TNode<Object>, | |||
| 3155 | WriteBarrierMode, int); | |||
| 3156 | ||||
| 3157 | template V8_EXPORT_PRIVATE void | |||
| 3158 | CodeStubAssembler::StoreFixedArrayOrPropertyArrayElement<UintPtrT>( | |||
| 3159 | TNode<UnionT<FixedArray, PropertyArray>>, TNode<UintPtrT>, TNode<Object>, | |||
| 3160 | WriteBarrierMode, int); | |||
| 3161 | ||||
| 3162 | template <typename TIndex> | |||
| 3163 | void CodeStubAssembler::StoreFixedDoubleArrayElement( | |||
| 3164 | TNode<FixedDoubleArray> object, TNode<TIndex> index, TNode<Float64T> value, | |||
| 3165 | CheckBounds check_bounds) { | |||
| 3166 | // TODO(v8:9708): Do we want to keep both IntPtrT and UintPtrT variants? | |||
| 3167 | static_assert(std::is_same<TIndex, Smi>::value || | |||
| 3168 | std::is_same<TIndex, UintPtrT>::value || | |||
| 3169 | std::is_same<TIndex, IntPtrT>::value, | |||
| 3170 | "Only Smi, UintPtrT or IntPtrT index is allowed"); | |||
| 3171 | if (NeedsBoundsCheck(check_bounds)) { | |||
| 3172 | FixedArrayBoundsCheck(object, index, 0); | |||
| 3173 | } | |||
| 3174 | TNode<IntPtrT> offset = ElementOffsetFromIndex( | |||
| 3175 | index, PACKED_DOUBLE_ELEMENTS, FixedArray::kHeaderSize - kHeapObjectTag); | |||
| 3176 | MachineRepresentation rep = MachineRepresentation::kFloat64; | |||
| 3177 | // Make sure we do not store signalling NaNs into double arrays. | |||
| 3178 | TNode<Float64T> value_silenced = Float64SilenceNaN(value); | |||
| 3179 | StoreNoWriteBarrier(rep, object, offset, value_silenced); | |||
| 3180 | } | |||
| 3181 | ||||
| 3182 | // Export the Smi version which is used outside of code-stub-assembler. | |||
| 3183 | template V8_EXPORT_PRIVATE void CodeStubAssembler::StoreFixedDoubleArrayElement< | |||
| 3184 | Smi>(TNode<FixedDoubleArray>, TNode<Smi>, TNode<Float64T>, CheckBounds); | |||
| 3185 | ||||
| 3186 | void CodeStubAssembler::StoreFeedbackVectorSlot( | |||
| 3187 | TNode<FeedbackVector> feedback_vector, TNode<UintPtrT> slot, | |||
| 3188 | TNode<AnyTaggedT> value, WriteBarrierMode barrier_mode, | |||
| 3189 | int additional_offset) { | |||
| 3190 | DCHECK(IsAligned(additional_offset, kTaggedSize))((void) 0); | |||
| 3191 | DCHECK(barrier_mode == SKIP_WRITE_BARRIER ||((void) 0) | |||
| 3192 | barrier_mode == UNSAFE_SKIP_WRITE_BARRIER ||((void) 0) | |||
| 3193 | barrier_mode == UPDATE_WRITE_BARRIER)((void) 0); | |||
| 3194 | int header_size = FeedbackVector::kRawFeedbackSlotsOffset + | |||
| 3195 | additional_offset - kHeapObjectTag; | |||
| 3196 | TNode<IntPtrT> offset = | |||
| 3197 | ElementOffsetFromIndex(Signed(slot), HOLEY_ELEMENTS, header_size); | |||
| 3198 | // Check that slot <= feedback_vector.length. | |||
| 3199 | CSA_DCHECK(this,((void)0) | |||
| 3200 | IsOffsetInBounds(offset, LoadFeedbackVectorLength(feedback_vector),((void)0) | |||
| 3201 | FeedbackVector::kHeaderSize),((void)0) | |||
| 3202 | SmiFromIntPtr(offset), feedback_vector)((void)0); | |||
| 3203 | if (barrier_mode == SKIP_WRITE_BARRIER) { | |||
| 3204 | StoreNoWriteBarrier(MachineRepresentation::kTagged, feedback_vector, offset, | |||
| 3205 | value); | |||
| 3206 | } else if (barrier_mode == UNSAFE_SKIP_WRITE_BARRIER) { | |||
| 3207 | UnsafeStoreNoWriteBarrier(MachineRepresentation::kTagged, feedback_vector, | |||
| 3208 | offset, value); | |||
| 3209 | } else { | |||
| 3210 | Store(feedback_vector, offset, value); | |||
| 3211 | } | |||
| 3212 | } | |||
| 3213 | ||||
| 3214 | TNode<Int32T> CodeStubAssembler::EnsureArrayPushable(TNode<Context> context, | |||
| 3215 | TNode<Map> map, | |||
| 3216 | Label* bailout) { | |||
| 3217 | // Disallow pushing onto prototypes. It might be the JSArray prototype. | |||
| 3218 | // Disallow pushing onto non-extensible objects. | |||
| 3219 | Comment("Disallow pushing onto prototypes"); | |||
| 3220 | GotoIfNot(IsExtensibleNonPrototypeMap(map), bailout); | |||
| 3221 | ||||
| 3222 | EnsureArrayLengthWritable(context, map, bailout); | |||
| 3223 | ||||
| 3224 | TNode<Uint32T> kind = | |||
| 3225 | DecodeWord32<Map::Bits2::ElementsKindBits>(LoadMapBitField2(map)); | |||
| 3226 | return Signed(kind); | |||
| 3227 | } | |||
| 3228 | ||||
| 3229 | void CodeStubAssembler::PossiblyGrowElementsCapacity( | |||
| 3230 | ElementsKind kind, TNode<HeapObject> array, TNode<BInt> length, | |||
| 3231 | TVariable<FixedArrayBase>* var_elements, TNode<BInt> growth, | |||
| 3232 | Label* bailout) { | |||
| 3233 | Label fits(this, var_elements); | |||
| 3234 | TNode<BInt> capacity = | |||
| 3235 | TaggedToParameter<BInt>(LoadFixedArrayBaseLength(var_elements->value())); | |||
| 3236 | ||||
| 3237 | TNode<BInt> new_length = IntPtrOrSmiAdd(growth, length); | |||
| 3238 | GotoIfNot(IntPtrOrSmiGreaterThan(new_length, capacity), &fits); | |||
| 3239 | TNode<BInt> new_capacity = CalculateNewElementsCapacity(new_length); | |||
| 3240 | *var_elements = GrowElementsCapacity(array, var_elements->value(), kind, kind, | |||
| 3241 | capacity, new_capacity, bailout); | |||
| 3242 | Goto(&fits); | |||
| 3243 | BIND(&fits)Bind(&fits); | |||
| 3244 | } | |||
| 3245 | ||||
| 3246 | TNode<Smi> CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, | |||
| 3247 | TNode<JSArray> array, | |||
| 3248 | CodeStubArguments* args, | |||
| 3249 | TVariable<IntPtrT>* arg_index, | |||
| 3250 | Label* bailout) { | |||
| 3251 | Comment("BuildAppendJSArray: ", ElementsKindToString(kind)); | |||
| 3252 | Label pre_bailout(this); | |||
| 3253 | Label success(this); | |||
| 3254 | TVARIABLE(Smi, var_tagged_length)TVariable<Smi> var_tagged_length(this); | |||
| 3255 | TVARIABLE(BInt, var_length, SmiToBInt(LoadFastJSArrayLength(array)))TVariable<BInt> var_length(SmiToBInt(LoadFastJSArrayLength (array)), this); | |||
| 3256 | TVARIABLE(FixedArrayBase, var_elements, LoadElements(array))TVariable<FixedArrayBase> var_elements(LoadElements(array ), this); | |||
| 3257 | ||||
| 3258 | // Resize the capacity of the fixed array if it doesn't fit. | |||
| 3259 | TNode<IntPtrT> first = arg_index->value(); | |||
| 3260 | TNode<BInt> growth = | |||
| 3261 | IntPtrToBInt(IntPtrSub(args->GetLengthWithoutReceiver(), first)); | |||
| 3262 | PossiblyGrowElementsCapacity(kind, array, var_length.value(), &var_elements, | |||
| 3263 | growth, &pre_bailout); | |||
| 3264 | ||||
| 3265 | // Push each argument onto the end of the array now that there is enough | |||
| 3266 | // capacity. | |||
| 3267 | CodeStubAssembler::VariableList push_vars({&var_length}, zone()); | |||
| 3268 | TNode<FixedArrayBase> elements = var_elements.value(); | |||
| 3269 | args->ForEach( | |||
| 3270 | push_vars, | |||
| 3271 | [&](TNode<Object> arg) { | |||
| 3272 | TryStoreArrayElement(kind, &pre_bailout, elements, var_length.value(), | |||
| 3273 | arg); | |||
| 3274 | Increment(&var_length); | |||
| 3275 | }, | |||
| 3276 | first); | |||
| 3277 | { | |||
| 3278 | TNode<Smi> length = BIntToSmi(var_length.value()); | |||
| 3279 | var_tagged_length = length; | |||
| 3280 | StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); | |||
| 3281 | Goto(&success); | |||
| 3282 | } | |||
| 3283 | ||||
| 3284 | BIND(&pre_bailout)Bind(&pre_bailout); | |||
| 3285 | { | |||
| 3286 | TNode<Smi> length = ParameterToTagged(var_length.value()); | |||
| 3287 | var_tagged_length = length; | |||
| 3288 | TNode<Smi> diff = SmiSub(length, LoadFastJSArrayLength(array)); | |||
| 3289 | StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); | |||
| 3290 | *arg_index = IntPtrAdd(arg_index->value(), SmiUntag(diff)); | |||
| 3291 | Goto(bailout); | |||
| 3292 | } | |||
| 3293 | ||||
| 3294 | BIND(&success)Bind(&success); | |||
| 3295 | return var_tagged_length.value(); | |||
| 3296 | } | |||
| 3297 | ||||
| 3298 | void CodeStubAssembler::TryStoreArrayElement(ElementsKind kind, Label* bailout, | |||
| 3299 | TNode<FixedArrayBase> elements, | |||
| 3300 | TNode<BInt> index, | |||
| 3301 | TNode<Object> value) { | |||
| 3302 | if (IsSmiElementsKind(kind)) { | |||
| 3303 | GotoIf(TaggedIsNotSmi(value), bailout); | |||
| 3304 | } else if (IsDoubleElementsKind(kind)) { | |||
| 3305 | GotoIfNotNumber(value, bailout); | |||
| 3306 | } | |||
| 3307 | ||||
| 3308 | if (IsDoubleElementsKind(kind)) { | |||
| 3309 | StoreElement(elements, kind, index, ChangeNumberToFloat64(CAST(value)Cast(value))); | |||
| 3310 | } else { | |||
| 3311 | StoreElement(elements, kind, index, value); | |||
| 3312 | } | |||
| 3313 | } | |||
| 3314 | ||||
| 3315 | void CodeStubAssembler::BuildAppendJSArray(ElementsKind kind, | |||
| 3316 | TNode<JSArray> array, | |||
| 3317 | TNode<Object> value, | |||
| 3318 | Label* bailout) { | |||
| 3319 | Comment("BuildAppendJSArray: ", ElementsKindToString(kind)); | |||
| 3320 | TVARIABLE(BInt, var_length, SmiToBInt(LoadFastJSArrayLength(array)))TVariable<BInt> var_length(SmiToBInt(LoadFastJSArrayLength (array)), this); | |||
| 3321 | TVARIABLE(FixedArrayBase, var_elements, LoadElements(array))TVariable<FixedArrayBase> var_elements(LoadElements(array ), this); | |||
| 3322 | ||||
| 3323 | // Resize the capacity of the fixed array if it doesn't fit. | |||
| 3324 | TNode<BInt> growth = IntPtrOrSmiConstant<BInt>(1); | |||
| 3325 | PossiblyGrowElementsCapacity(kind, array, var_length.value(), &var_elements, | |||
| 3326 | growth, bailout); | |||
| 3327 | ||||
| 3328 | // Push each argument onto the end of the array now that there is enough | |||
| 3329 | // capacity. | |||
| 3330 | TryStoreArrayElement(kind, bailout, var_elements.value(), var_length.value(), | |||
| 3331 | value); | |||
| 3332 | Increment(&var_length); | |||
| 3333 | ||||
| 3334 | TNode<Smi> length = BIntToSmi(var_length.value()); | |||
| 3335 | StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); | |||
| 3336 | } | |||
| 3337 | ||||
| 3338 | TNode<Cell> CodeStubAssembler::AllocateCellWithValue(TNode<Object> value, | |||
| 3339 | WriteBarrierMode mode) { | |||
| 3340 | TNode<HeapObject> result = Allocate(Cell::kSize, AllocationFlag::kNone); | |||
| 3341 | StoreMapNoWriteBarrier(result, RootIndex::kCellMap); | |||
| 3342 | TNode<Cell> cell = CAST(result)Cast(result); | |||
| 3343 | StoreCellValue(cell, value, mode); | |||
| 3344 | return cell; | |||
| 3345 | } | |||
| 3346 | ||||
| 3347 | TNode<Object> CodeStubAssembler::LoadCellValue(TNode<Cell> cell) { | |||
| 3348 | return LoadObjectField(cell, Cell::kValueOffset); | |||
| 3349 | } | |||
| 3350 | ||||
| 3351 | void CodeStubAssembler::StoreCellValue(TNode<Cell> cell, TNode<Object> value, | |||
| 3352 | WriteBarrierMode mode) { | |||
| 3353 | DCHECK(mode == SKIP_WRITE_BARRIER || mode == UPDATE_WRITE_BARRIER)((void) 0); | |||
| 3354 | ||||
| 3355 | if (mode == UPDATE_WRITE_BARRIER) { | |||
| 3356 | StoreObjectField(cell, Cell::kValueOffset, value); | |||
| 3357 | } else { | |||
| 3358 | StoreObjectFieldNoWriteBarrier(cell, Cell::kValueOffset, value); | |||
| 3359 | } | |||
| 3360 | } | |||
| 3361 | ||||
| 3362 | TNode<HeapNumber> CodeStubAssembler::AllocateHeapNumber() { | |||
| 3363 | TNode<HeapObject> result = Allocate(HeapNumber::kSize, AllocationFlag::kNone); | |||
| 3364 | RootIndex heap_map_index = RootIndex::kHeapNumberMap; | |||
| 3365 | StoreMapNoWriteBarrier(result, heap_map_index); | |||
| 3366 | return UncheckedCast<HeapNumber>(result); | |||
| 3367 | } | |||
| 3368 | ||||
| 3369 | TNode<HeapNumber> CodeStubAssembler::AllocateHeapNumberWithValue( | |||
| 3370 | TNode<Float64T> value) { | |||
| 3371 | TNode<HeapNumber> result = AllocateHeapNumber(); | |||
| 3372 | StoreHeapNumberValue(result, value); | |||
| 3373 | return result; | |||
| 3374 | } | |||
| 3375 | ||||
| 3376 | TNode<Object> CodeStubAssembler::CloneIfMutablePrimitive(TNode<Object> object) { | |||
| 3377 | TVARIABLE(Object, result, object)TVariable<Object> result(object, this); | |||
| 3378 | Label done(this); | |||
| 3379 | ||||
| 3380 | GotoIf(TaggedIsSmi(object), &done); | |||
| 3381 | // TODO(leszeks): Read the field descriptor to decide if this heap number is | |||
| 3382 | // mutable or not. | |||
| 3383 | GotoIfNot(IsHeapNumber(UncheckedCast<HeapObject>(object)), &done); | |||
| 3384 | { | |||
| 3385 | // Mutable heap number found --- allocate a clone. | |||
| 3386 | TNode<Float64T> value = | |||
| 3387 | LoadHeapNumberValue(UncheckedCast<HeapNumber>(object)); | |||
| 3388 | result = AllocateHeapNumberWithValue(value); | |||
| 3389 | Goto(&done); | |||
| 3390 | } | |||
| 3391 | ||||
| 3392 | BIND(&done)Bind(&done); | |||
| 3393 | return result.value(); | |||
| 3394 | } | |||
| 3395 | ||||
| 3396 | TNode<BigInt> CodeStubAssembler::AllocateBigInt(TNode<IntPtrT> length) { | |||
| 3397 | TNode<BigInt> result = AllocateRawBigInt(length); | |||
| 3398 | StoreBigIntBitfield(result, | |||
| 3399 | Word32Shl(TruncateIntPtrToInt32(length), | |||
| 3400 | Int32Constant(BigInt::LengthBits::kShift))); | |||
| 3401 | return result; | |||
| 3402 | } | |||
| 3403 | ||||
| 3404 | TNode<BigInt> CodeStubAssembler::AllocateRawBigInt(TNode<IntPtrT> length) { | |||
| 3405 | TNode<IntPtrT> size = | |||
| 3406 | IntPtrAdd(IntPtrConstant(BigInt::kHeaderSize), | |||
| 3407 | Signed(WordShl(length, kSystemPointerSizeLog2))); | |||
| 3408 | TNode<HeapObject> raw_result = | |||
| 3409 | Allocate(size, AllocationFlag::kAllowLargeObjectAllocation); | |||
| 3410 | StoreMapNoWriteBarrier(raw_result, RootIndex::kBigIntMap); | |||
| 3411 | if (FIELD_SIZE(BigInt::kOptionalPaddingOffset)(BigInt::kOptionalPaddingOffsetEnd + 1 - BigInt::kOptionalPaddingOffset ) != 0) { | |||
| 3412 | DCHECK_EQ(4, FIELD_SIZE(BigInt::kOptionalPaddingOffset))((void) 0); | |||
| 3413 | StoreObjectFieldNoWriteBarrier(raw_result, BigInt::kOptionalPaddingOffset, | |||
| 3414 | Int32Constant(0)); | |||
| 3415 | } | |||
| 3416 | return UncheckedCast<BigInt>(raw_result); | |||
| 3417 | } | |||
| 3418 | ||||
| 3419 | void CodeStubAssembler::StoreBigIntBitfield(TNode<BigInt> bigint, | |||
| 3420 | TNode<Word32T> bitfield) { | |||
| 3421 | StoreObjectFieldNoWriteBarrier(bigint, BigInt::kBitfieldOffset, bitfield); | |||
| 3422 | } | |||
| 3423 | ||||
| 3424 | void CodeStubAssembler::StoreBigIntDigit(TNode<BigInt> bigint, | |||
| 3425 | intptr_t digit_index, | |||
| 3426 | TNode<UintPtrT> digit) { | |||
| 3427 | CHECK_LE(0, digit_index)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(0)>::type, typename ::v8:: base::pass_value_or_ref<decltype(digit_index)>::type> ((0), (digit_index)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "0" " " "<=" " " "digit_index" ); } } while (false); } while (false); | |||
| 3428 | CHECK_LT(digit_index, BigInt::kMaxLength)do { bool _cmp = ::v8::base::CmpLTImpl< typename ::v8::base ::pass_value_or_ref<decltype(digit_index)>::type, typename ::v8::base::pass_value_or_ref<decltype(BigInt::kMaxLength )>::type>((digit_index), (BigInt::kMaxLength)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "digit_index" " " "<" " " "BigInt::kMaxLength"); } } while (false); } while (false); | |||
| 3429 | StoreObjectFieldNoWriteBarrier( | |||
| 3430 | bigint, | |||
| 3431 | BigInt::kDigitsOffset + | |||
| 3432 | static_cast<int>(digit_index) * kSystemPointerSize, | |||
| 3433 | digit); | |||
| 3434 | } | |||
| 3435 | ||||
| 3436 | void CodeStubAssembler::StoreBigIntDigit(TNode<BigInt> bigint, | |||
| 3437 | TNode<IntPtrT> digit_index, | |||
| 3438 | TNode<UintPtrT> digit) { | |||
| 3439 | TNode<IntPtrT> offset = | |||
| 3440 | IntPtrAdd(IntPtrConstant(BigInt::kDigitsOffset), | |||
| 3441 | IntPtrMul(digit_index, IntPtrConstant(kSystemPointerSize))); | |||
| 3442 | StoreObjectFieldNoWriteBarrier(bigint, offset, digit); | |||
| 3443 | } | |||
| 3444 | ||||
| 3445 | TNode<Word32T> CodeStubAssembler::LoadBigIntBitfield(TNode<BigInt> bigint) { | |||
| 3446 | return UncheckedCast<Word32T>( | |||
| 3447 | LoadObjectField<Uint32T>(bigint, BigInt::kBitfieldOffset)); | |||
| 3448 | } | |||
| 3449 | ||||
| 3450 | TNode<UintPtrT> CodeStubAssembler::LoadBigIntDigit(TNode<BigInt> bigint, | |||
| 3451 | intptr_t digit_index) { | |||
| 3452 | CHECK_LE(0, digit_index)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(0)>::type, typename ::v8:: base::pass_value_or_ref<decltype(digit_index)>::type> ((0), (digit_index)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "0" " " "<=" " " "digit_index" ); } } while (false); } while (false); | |||
| 3453 | CHECK_LT(digit_index, BigInt::kMaxLength)do { bool _cmp = ::v8::base::CmpLTImpl< typename ::v8::base ::pass_value_or_ref<decltype(digit_index)>::type, typename ::v8::base::pass_value_or_ref<decltype(BigInt::kMaxLength )>::type>((digit_index), (BigInt::kMaxLength)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "digit_index" " " "<" " " "BigInt::kMaxLength"); } } while (false); } while (false); | |||
| 3454 | return LoadObjectField<UintPtrT>( | |||
| 3455 | bigint, BigInt::kDigitsOffset + | |||
| 3456 | static_cast<int>(digit_index) * kSystemPointerSize); | |||
| 3457 | } | |||
| 3458 | ||||
| 3459 | TNode<UintPtrT> CodeStubAssembler::LoadBigIntDigit(TNode<BigInt> bigint, | |||
| 3460 | TNode<IntPtrT> digit_index) { | |||
| 3461 | TNode<IntPtrT> offset = | |||
| 3462 | IntPtrAdd(IntPtrConstant(BigInt::kDigitsOffset), | |||
| 3463 | IntPtrMul(digit_index, IntPtrConstant(kSystemPointerSize))); | |||
| 3464 | return LoadObjectField<UintPtrT>(bigint, offset); | |||
| 3465 | } | |||
| 3466 | ||||
| 3467 | TNode<ByteArray> CodeStubAssembler::AllocateNonEmptyByteArray( | |||
| 3468 | TNode<UintPtrT> length, AllocationFlags flags) { | |||
| 3469 | CSA_DCHECK(this, WordNotEqual(length, IntPtrConstant(0)))((void)0); | |||
| 3470 | ||||
| 3471 | Comment("AllocateNonEmptyByteArray"); | |||
| 3472 | TVARIABLE(Object, var_result)TVariable<Object> var_result(this); | |||
| 3473 | ||||
| 3474 | TNode<IntPtrT> raw_size = | |||
| 3475 | GetArrayAllocationSize(Signed(length), UINT8_ELEMENTS, | |||
| 3476 | ByteArray::kHeaderSize + kObjectAlignmentMask); | |||
| 3477 | TNode<IntPtrT> size = | |||
| 3478 | WordAnd(raw_size, IntPtrConstant(~kObjectAlignmentMask)); | |||
| 3479 | ||||
| 3480 | TNode<HeapObject> result = Allocate(size, flags); | |||
| 3481 | ||||
| 3482 | DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kByteArrayMap))((void) 0); | |||
| 3483 | StoreMapNoWriteBarrier(result, RootIndex::kByteArrayMap); | |||
| 3484 | StoreObjectFieldNoWriteBarrier(result, ByteArray::kLengthOffset, | |||
| 3485 | SmiTag(Signed(length))); | |||
| 3486 | ||||
| 3487 | return CAST(result)Cast(result); | |||
| 3488 | } | |||
| 3489 | ||||
| 3490 | TNode<ByteArray> CodeStubAssembler::AllocateByteArray(TNode<UintPtrT> length, | |||
| 3491 | AllocationFlags flags) { | |||
| 3492 | // TODO(ishell): unify with AllocateNonEmptyByteArray(). | |||
| 3493 | ||||
| 3494 | Comment("AllocateByteArray"); | |||
| 3495 | TVARIABLE(Object, var_result)TVariable<Object> var_result(this); | |||
| 3496 | ||||
| 3497 | // Compute the ByteArray size and check if it fits into new space. | |||
| 3498 | Label if_lengthiszero(this), if_sizeissmall(this), | |||
| 3499 | if_notsizeissmall(this, Label::kDeferred), if_join(this); | |||
| 3500 | GotoIf(WordEqual(length, UintPtrConstant(0)), &if_lengthiszero); | |||
| 3501 | ||||
| 3502 | TNode<IntPtrT> raw_size = | |||
| 3503 | GetArrayAllocationSize(Signed(length), UINT8_ELEMENTS, | |||
| 3504 | ByteArray::kHeaderSize + kObjectAlignmentMask); | |||
| 3505 | TNode<IntPtrT> size = | |||
| 3506 | WordAnd(raw_size, IntPtrConstant(~kObjectAlignmentMask)); | |||
| 3507 | Branch(IntPtrLessThanOrEqual(size, IntPtrConstant(kMaxRegularHeapObjectSize)), | |||
| 3508 | &if_sizeissmall, &if_notsizeissmall); | |||
| 3509 | ||||
| 3510 | BIND(&if_sizeissmall)Bind(&if_sizeissmall); | |||
| 3511 | { | |||
| 3512 | // Just allocate the ByteArray in new space. | |||
| 3513 | TNode<HeapObject> result = | |||
| 3514 | AllocateInNewSpace(UncheckedCast<IntPtrT>(size), flags); | |||
| 3515 | DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kByteArrayMap))((void) 0); | |||
| 3516 | StoreMapNoWriteBarrier(result, RootIndex::kByteArrayMap); | |||
| 3517 | StoreObjectFieldNoWriteBarrier(result, ByteArray::kLengthOffset, | |||
| 3518 | SmiTag(Signed(length))); | |||
| 3519 | var_result = result; | |||
| 3520 | Goto(&if_join); | |||
| 3521 | } | |||
| 3522 | ||||
| 3523 | BIND(&if_notsizeissmall)Bind(&if_notsizeissmall); | |||
| 3524 | { | |||
| 3525 | // We might need to allocate in large object space, go to the runtime. | |||
| 3526 | TNode<Object> result = | |||
| 3527 | CallRuntime(Runtime::kAllocateByteArray, NoContextConstant(), | |||
| 3528 | ChangeUintPtrToTagged(length)); | |||
| 3529 | var_result = result; | |||
| 3530 | Goto(&if_join); | |||
| 3531 | } | |||
| 3532 | ||||
| 3533 | BIND(&if_lengthiszero)Bind(&if_lengthiszero); | |||
| 3534 | { | |||
| 3535 | var_result = EmptyByteArrayConstant(); | |||
| 3536 | Goto(&if_join); | |||
| 3537 | } | |||
| 3538 | ||||
| 3539 | BIND(&if_join)Bind(&if_join); | |||
| 3540 | return CAST(var_result.value())Cast(var_result.value()); | |||
| 3541 | } | |||
| 3542 | ||||
| 3543 | TNode<String> CodeStubAssembler::AllocateSeqOneByteString( | |||
| 3544 | uint32_t length, AllocationFlags flags) { | |||
| 3545 | Comment("AllocateSeqOneByteString"); | |||
| 3546 | if (length == 0) { | |||
| 3547 | return EmptyStringConstant(); | |||
| 3548 | } | |||
| 3549 | TNode<HeapObject> result = Allocate(SeqOneByteString::SizeFor(length), flags); | |||
| 3550 | DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kOneByteStringMap))((void) 0); | |||
| 3551 | StoreMapNoWriteBarrier(result, RootIndex::kOneByteStringMap); | |||
| 3552 | StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset, | |||
| 3553 | Uint32Constant(length)); | |||
| 3554 | StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kRawHashFieldOffset, | |||
| 3555 | Int32Constant(String::kEmptyHashField)); | |||
| 3556 | return CAST(result)Cast(result); | |||
| 3557 | } | |||
| 3558 | ||||
| 3559 | TNode<BoolT> CodeStubAssembler::IsZeroOrContext(TNode<Object> object) { | |||
| 3560 | return Select<BoolT>( | |||
| 3561 | TaggedEqual(object, SmiConstant(0)), [=] { return Int32TrueConstant(); }, | |||
| 3562 | [=] { return IsContext(CAST(object)Cast(object)); }); | |||
| 3563 | } | |||
| 3564 | ||||
| 3565 | TNode<String> CodeStubAssembler::AllocateSeqTwoByteString( | |||
| 3566 | uint32_t length, AllocationFlags flags) { | |||
| 3567 | Comment("AllocateSeqTwoByteString"); | |||
| 3568 | if (length == 0) { | |||
| 3569 | return EmptyStringConstant(); | |||
| 3570 | } | |||
| 3571 | TNode<HeapObject> result = Allocate(SeqTwoByteString::SizeFor(length), flags); | |||
| 3572 | DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kStringMap))((void) 0); | |||
| 3573 | StoreMapNoWriteBarrier(result, RootIndex::kStringMap); | |||
| 3574 | StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kLengthOffset, | |||
| 3575 | Uint32Constant(length)); | |||
| 3576 | StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kRawHashFieldOffset, | |||
| 3577 | Int32Constant(String::kEmptyHashField)); | |||
| 3578 | return CAST(result)Cast(result); | |||
| 3579 | } | |||
| 3580 | ||||
| 3581 | TNode<String> CodeStubAssembler::AllocateSlicedString(RootIndex map_root_index, | |||
| 3582 | TNode<Uint32T> length, | |||
| 3583 | TNode<String> parent, | |||
| 3584 | TNode<Smi> offset) { | |||
| 3585 | DCHECK(map_root_index == RootIndex::kSlicedOneByteStringMap ||((void) 0) | |||
| 3586 | map_root_index == RootIndex::kSlicedStringMap)((void) 0); | |||
| 3587 | TNode<HeapObject> result = Allocate(SlicedString::kSize); | |||
| 3588 | DCHECK(RootsTable::IsImmortalImmovable(map_root_index))((void) 0); | |||
| 3589 | StoreMapNoWriteBarrier(result, map_root_index); | |||
| 3590 | StoreObjectFieldNoWriteBarrier(result, SlicedString::kRawHashFieldOffset, | |||
| 3591 | Int32Constant(String::kEmptyHashField)); | |||
| 3592 | StoreObjectFieldNoWriteBarrier(result, SlicedString::kLengthOffset, length); | |||
| 3593 | StoreObjectFieldNoWriteBarrier(result, SlicedString::kParentOffset, parent); | |||
| 3594 | StoreObjectFieldNoWriteBarrier(result, SlicedString::kOffsetOffset, offset); | |||
| 3595 | return CAST(result)Cast(result); | |||
| 3596 | } | |||
| 3597 | ||||
| 3598 | TNode<String> CodeStubAssembler::AllocateSlicedOneByteString( | |||
| 3599 | TNode<Uint32T> length, TNode<String> parent, TNode<Smi> offset) { | |||
| 3600 | return AllocateSlicedString(RootIndex::kSlicedOneByteStringMap, length, | |||
| 3601 | parent, offset); | |||
| 3602 | } | |||
| 3603 | ||||
| 3604 | TNode<String> CodeStubAssembler::AllocateSlicedTwoByteString( | |||
| 3605 | TNode<Uint32T> length, TNode<String> parent, TNode<Smi> offset) { | |||
| 3606 | return AllocateSlicedString(RootIndex::kSlicedStringMap, length, parent, | |||
| 3607 | offset); | |||
| 3608 | } | |||
| 3609 | ||||
| 3610 | TNode<NameDictionary> CodeStubAssembler::AllocateNameDictionary( | |||
| 3611 | int at_least_space_for) { | |||
| 3612 | return AllocateNameDictionary(IntPtrConstant(at_least_space_for)); | |||
| 3613 | } | |||
| 3614 | ||||
| 3615 | TNode<NameDictionary> CodeStubAssembler::AllocateNameDictionary( | |||
| 3616 | TNode<IntPtrT> at_least_space_for, AllocationFlags flags) { | |||
| 3617 | CSA_DCHECK(this, UintPtrLessThanOrEqual(((void)0) | |||
| 3618 | at_least_space_for,((void)0) | |||
| 3619 | IntPtrConstant(NameDictionary::kMaxCapacity)))((void)0); | |||
| 3620 | TNode<IntPtrT> capacity = HashTableComputeCapacity(at_least_space_for); | |||
| 3621 | return AllocateNameDictionaryWithCapacity(capacity, flags); | |||
| 3622 | } | |||
| 3623 | ||||
| 3624 | TNode<NameDictionary> CodeStubAssembler::AllocateNameDictionaryWithCapacity( | |||
| 3625 | TNode<IntPtrT> capacity, AllocationFlags flags) { | |||
| 3626 | CSA_DCHECK(this, WordIsPowerOfTwo(capacity))((void)0); | |||
| 3627 | CSA_DCHECK(this, IntPtrGreaterThan(capacity, IntPtrConstant(0)))((void)0); | |||
| 3628 | TNode<IntPtrT> length = EntryToIndex<NameDictionary>(capacity); | |||
| 3629 | TNode<IntPtrT> store_size = IntPtrAdd( | |||
| 3630 | TimesTaggedSize(length), IntPtrConstant(NameDictionary::kHeaderSize)); | |||
| 3631 | ||||
| 3632 | TNode<NameDictionary> result = | |||
| 3633 | UncheckedCast<NameDictionary>(Allocate(store_size, flags)); | |||
| 3634 | ||||
| 3635 | // Initialize FixedArray fields. | |||
| 3636 | { | |||
| 3637 | DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kNameDictionaryMap))((void) 0); | |||
| 3638 | StoreMapNoWriteBarrier(result, RootIndex::kNameDictionaryMap); | |||
| 3639 | StoreObjectFieldNoWriteBarrier(result, FixedArray::kLengthOffset, | |||
| 3640 | SmiFromIntPtr(length)); | |||
| 3641 | } | |||
| 3642 | ||||
| 3643 | // Initialized HashTable fields. | |||
| 3644 | { | |||
| 3645 | TNode<Smi> zero = SmiConstant(0); | |||
| 3646 | StoreFixedArrayElement(result, NameDictionary::kNumberOfElementsIndex, zero, | |||
| 3647 | SKIP_WRITE_BARRIER); | |||
| 3648 | StoreFixedArrayElement(result, | |||
| 3649 | NameDictionary::kNumberOfDeletedElementsIndex, zero, | |||
| 3650 | SKIP_WRITE_BARRIER); | |||
| 3651 | StoreFixedArrayElement(result, NameDictionary::kCapacityIndex, | |||
| 3652 | SmiTag(capacity), SKIP_WRITE_BARRIER); | |||
| 3653 | // Initialize Dictionary fields. | |||
| 3654 | StoreFixedArrayElement(result, NameDictionary::kNextEnumerationIndexIndex, | |||
| 3655 | SmiConstant(PropertyDetails::kInitialIndex), | |||
| 3656 | SKIP_WRITE_BARRIER); | |||
| 3657 | StoreFixedArrayElement(result, NameDictionary::kObjectHashIndex, | |||
| 3658 | SmiConstant(PropertyArray::kNoHashSentinel), | |||
| 3659 | SKIP_WRITE_BARRIER); | |||
| 3660 | } | |||
| 3661 | ||||
| 3662 | // Initialize NameDictionary elements. | |||
| 3663 | { | |||
| 3664 | TNode<IntPtrT> result_word = BitcastTaggedToWord(result); | |||
| 3665 | TNode<IntPtrT> start_address = IntPtrAdd( | |||
| 3666 | result_word, IntPtrConstant(NameDictionary::OffsetOfElementAt( | |||
| 3667 | NameDictionary::kElementsStartIndex) - | |||
| 3668 | kHeapObjectTag)); | |||
| 3669 | TNode<IntPtrT> end_address = IntPtrAdd( | |||
| 3670 | result_word, IntPtrSub(store_size, IntPtrConstant(kHeapObjectTag))); | |||
| 3671 | ||||
| 3672 | TNode<Oddball> filler = UndefinedConstant(); | |||
| 3673 | DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kUndefinedValue))((void) 0); | |||
| 3674 | ||||
| 3675 | StoreFieldsNoWriteBarrier(start_address, end_address, filler); | |||
| 3676 | } | |||
| 3677 | ||||
| 3678 | return result; | |||
| 3679 | } | |||
| 3680 | ||||
| 3681 | TNode<NameDictionary> CodeStubAssembler::CopyNameDictionary( | |||
| 3682 | TNode<NameDictionary> dictionary, Label* large_object_fallback) { | |||
| 3683 | Comment("Copy boilerplate property dict"); | |||
| 3684 | TNode<IntPtrT> capacity = SmiUntag(GetCapacity<NameDictionary>(dictionary)); | |||
| 3685 | CSA_DCHECK(this, IntPtrGreaterThanOrEqual(capacity, IntPtrConstant(0)))((void)0); | |||
| 3686 | GotoIf(UintPtrGreaterThan( | |||
| 3687 | capacity, IntPtrConstant(NameDictionary::kMaxRegularCapacity)), | |||
| 3688 | large_object_fallback); | |||
| 3689 | TNode<NameDictionary> properties = | |||
| 3690 | AllocateNameDictionaryWithCapacity(capacity); | |||
| 3691 | TNode<IntPtrT> length = SmiUntag(LoadFixedArrayBaseLength(dictionary)); | |||
| 3692 | CopyFixedArrayElements(PACKED_ELEMENTS, dictionary, properties, length, | |||
| 3693 | SKIP_WRITE_BARRIER); | |||
| 3694 | return properties; | |||
| 3695 | } | |||
| 3696 | ||||
| 3697 | template <typename CollectionType> | |||
| 3698 | TNode<CollectionType> CodeStubAssembler::AllocateOrderedHashTable( | |||
| 3699 | TNode<IntPtrT> capacity) { | |||
| 3700 | capacity = IntPtrRoundUpToPowerOfTwo32(capacity); | |||
| 3701 | capacity = | |||
| 3702 | IntPtrMax(capacity, IntPtrConstant(CollectionType::kInitialCapacity)); | |||
| 3703 | return AllocateOrderedHashTableWithCapacity<CollectionType>(capacity); | |||
| 3704 | } | |||
| 3705 | ||||
| 3706 | template <typename CollectionType> | |||
| 3707 | TNode<CollectionType> CodeStubAssembler::AllocateOrderedHashTableWithCapacity( | |||
| 3708 | TNode<IntPtrT> capacity) { | |||
| 3709 | CSA_DCHECK(this, WordIsPowerOfTwo(capacity))((void)0); | |||
| 3710 | CSA_DCHECK(this,((void)0) | |||
| 3711 | IntPtrGreaterThanOrEqual(((void)0) | |||
| 3712 | capacity, IntPtrConstant(CollectionType::kInitialCapacity)))((void)0); | |||
| 3713 | CSA_DCHECK(this,((void)0) | |||
| 3714 | IntPtrLessThanOrEqual(((void)0) | |||
| 3715 | capacity, IntPtrConstant(CollectionType::MaxCapacity())))((void)0); | |||
| 3716 | ||||
| 3717 | STATIC_ASSERT(CollectionType::kLoadFactor == 2)static_assert(CollectionType::kLoadFactor == 2, "CollectionType::kLoadFactor == 2" ); | |||
| 3718 | TNode<IntPtrT> bucket_count = Signed(WordShr(capacity, IntPtrConstant(1))); | |||
| 3719 | TNode<IntPtrT> data_table_length = | |||
| 3720 | IntPtrMul(capacity, IntPtrConstant(CollectionType::kEntrySize)); | |||
| 3721 | ||||
| 3722 | TNode<IntPtrT> data_table_start_index = IntPtrAdd( | |||
| 3723 | IntPtrConstant(CollectionType::HashTableStartIndex()), bucket_count); | |||
| 3724 | TNode<IntPtrT> fixed_array_length = | |||
| 3725 | IntPtrAdd(data_table_start_index, data_table_length); | |||
| 3726 | ||||
| 3727 | // Allocate the table and add the proper map. | |||
| 3728 | const ElementsKind elements_kind = HOLEY_ELEMENTS; | |||
| 3729 | TNode<Map> fixed_array_map = | |||
| 3730 | HeapConstant(CollectionType::GetMap(ReadOnlyRoots(isolate()))); | |||
| 3731 | TNode<CollectionType> table = CAST(AllocateFixedArray(Cast(AllocateFixedArray( elements_kind, fixed_array_length, AllocationFlag ::kAllowLargeObjectAllocation, fixed_array_map)) | |||
| 3732 | elements_kind, fixed_array_length,Cast(AllocateFixedArray( elements_kind, fixed_array_length, AllocationFlag ::kAllowLargeObjectAllocation, fixed_array_map)) | |||
| 3733 | AllocationFlag::kAllowLargeObjectAllocation, fixed_array_map))Cast(AllocateFixedArray( elements_kind, fixed_array_length, AllocationFlag ::kAllowLargeObjectAllocation, fixed_array_map)); | |||
| 3734 | ||||
| 3735 | Comment("Initialize the OrderedHashTable fields."); | |||
| 3736 | const WriteBarrierMode barrier_mode = SKIP_WRITE_BARRIER; | |||
| 3737 | UnsafeStoreFixedArrayElement(table, CollectionType::NumberOfElementsIndex(), | |||
| 3738 | SmiConstant(0), barrier_mode); | |||
| 3739 | UnsafeStoreFixedArrayElement(table, | |||
| 3740 | CollectionType::NumberOfDeletedElementsIndex(), | |||
| 3741 | SmiConstant(0), barrier_mode); | |||
| 3742 | UnsafeStoreFixedArrayElement(table, CollectionType::NumberOfBucketsIndex(), | |||
| 3743 | SmiFromIntPtr(bucket_count), barrier_mode); | |||
| 3744 | ||||
| 3745 | TNode<IntPtrT> object_address = BitcastTaggedToWord(table); | |||
| 3746 | ||||
| 3747 | STATIC_ASSERT(CollectionType::HashTableStartIndex() ==static_assert(CollectionType::HashTableStartIndex() == CollectionType ::NumberOfBucketsIndex() + 1, "CollectionType::HashTableStartIndex() == CollectionType::NumberOfBucketsIndex() + 1" ) | |||
| 3748 | CollectionType::NumberOfBucketsIndex() + 1)static_assert(CollectionType::HashTableStartIndex() == CollectionType ::NumberOfBucketsIndex() + 1, "CollectionType::HashTableStartIndex() == CollectionType::NumberOfBucketsIndex() + 1" ); | |||
| 3749 | ||||
| 3750 | TNode<Smi> not_found_sentinel = SmiConstant(CollectionType::kNotFound); | |||
| 3751 | ||||
| 3752 | intptr_t const_capacity; | |||
| 3753 | if (TryToIntPtrConstant(capacity, &const_capacity) && | |||
| 3754 | const_capacity == CollectionType::kInitialCapacity) { | |||
| 3755 | int const_bucket_count = | |||
| 3756 | static_cast<int>(const_capacity / CollectionType::kLoadFactor); | |||
| 3757 | int const_data_table_length = | |||
| 3758 | static_cast<int>(const_capacity * CollectionType::kEntrySize); | |||
| 3759 | int const_data_table_start_index = static_cast<int>( | |||
| 3760 | CollectionType::HashTableStartIndex() + const_bucket_count); | |||
| 3761 | ||||
| 3762 | Comment("Fill the buckets with kNotFound (constant capacity)."); | |||
| 3763 | for (int i = 0; i < const_bucket_count; i++) { | |||
| 3764 | UnsafeStoreFixedArrayElement(table, | |||
| 3765 | CollectionType::HashTableStartIndex() + i, | |||
| 3766 | not_found_sentinel, barrier_mode); | |||
| 3767 | } | |||
| 3768 | ||||
| 3769 | Comment("Fill the data table with undefined (constant capacity)."); | |||
| 3770 | for (int i = 0; i < const_data_table_length; i++) { | |||
| 3771 | UnsafeStoreFixedArrayElement(table, const_data_table_start_index + i, | |||
| 3772 | UndefinedConstant(), barrier_mode); | |||
| 3773 | } | |||
| 3774 | } else { | |||
| 3775 | Comment("Fill the buckets with kNotFound."); | |||
| 3776 | TNode<IntPtrT> buckets_start_address = | |||
| 3777 | IntPtrAdd(object_address, | |||
| 3778 | IntPtrConstant(FixedArray::OffsetOfElementAt( | |||
| 3779 | CollectionType::HashTableStartIndex()) - | |||
| 3780 | kHeapObjectTag)); | |||
| 3781 | TNode<IntPtrT> buckets_end_address = | |||
| 3782 | IntPtrAdd(buckets_start_address, TimesTaggedSize(bucket_count)); | |||
| 3783 | ||||
| 3784 | StoreFieldsNoWriteBarrier(buckets_start_address, buckets_end_address, | |||
| 3785 | not_found_sentinel); | |||
| 3786 | ||||
| 3787 | Comment("Fill the data table with undefined."); | |||
| 3788 | TNode<IntPtrT> data_start_address = buckets_end_address; | |||
| 3789 | TNode<IntPtrT> data_end_address = IntPtrAdd( | |||
| 3790 | object_address, | |||
| 3791 | IntPtrAdd(IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag), | |||
| 3792 | TimesTaggedSize(fixed_array_length))); | |||
| 3793 | ||||
| 3794 | StoreFieldsNoWriteBarrier(data_start_address, data_end_address, | |||
| 3795 | UndefinedConstant()); | |||
| 3796 | ||||
| 3797 | #ifdef DEBUG | |||
| 3798 | TNode<IntPtrT> ptr_diff = | |||
| 3799 | IntPtrSub(data_end_address, buckets_start_address); | |||
| 3800 | TNode<IntPtrT> array_length = LoadAndUntagFixedArrayBaseLength(table); | |||
| 3801 | TNode<IntPtrT> array_data_fields = IntPtrSub( | |||
| 3802 | array_length, IntPtrConstant(CollectionType::HashTableStartIndex())); | |||
| 3803 | TNode<IntPtrT> expected_end = | |||
| 3804 | IntPtrAdd(data_start_address, | |||
| 3805 | TimesTaggedSize(IntPtrMul( | |||
| 3806 | capacity, IntPtrConstant(CollectionType::kEntrySize)))); | |||
| 3807 | ||||
| 3808 | CSA_DCHECK(this, IntPtrEqual(ptr_diff, TimesTaggedSize(array_data_fields)))((void)0); | |||
| 3809 | CSA_DCHECK(this, IntPtrEqual(expected_end, data_end_address))((void)0); | |||
| 3810 | #endif | |||
| 3811 | } | |||
| 3812 | ||||
| 3813 | return table; | |||
| 3814 | } | |||
| 3815 | ||||
| 3816 | TNode<OrderedNameDictionary> CodeStubAssembler::AllocateOrderedNameDictionary( | |||
| 3817 | TNode<IntPtrT> capacity) { | |||
| 3818 | TNode<OrderedNameDictionary> table = | |||
| 3819 | AllocateOrderedHashTable<OrderedNameDictionary>(capacity); | |||
| 3820 | StoreFixedArrayElement(table, OrderedNameDictionary::PrefixIndex(), | |||
| 3821 | SmiConstant(PropertyArray::kNoHashSentinel), | |||
| 3822 | SKIP_WRITE_BARRIER); | |||
| 3823 | return table; | |||
| 3824 | } | |||
| 3825 | ||||
| 3826 | TNode<OrderedNameDictionary> CodeStubAssembler::AllocateOrderedNameDictionary( | |||
| 3827 | int capacity) { | |||
| 3828 | return AllocateOrderedNameDictionary(IntPtrConstant(capacity)); | |||
| 3829 | } | |||
| 3830 | ||||
| 3831 | TNode<OrderedHashSet> CodeStubAssembler::AllocateOrderedHashSet() { | |||
| 3832 | return AllocateOrderedHashTableWithCapacity<OrderedHashSet>( | |||
| 3833 | IntPtrConstant(OrderedHashSet::kInitialCapacity)); | |||
| 3834 | } | |||
| 3835 | ||||
| 3836 | TNode<OrderedHashMap> CodeStubAssembler::AllocateOrderedHashMap() { | |||
| 3837 | return AllocateOrderedHashTableWithCapacity<OrderedHashMap>( | |||
| 3838 | IntPtrConstant(OrderedHashMap::kInitialCapacity)); | |||
| 3839 | } | |||
| 3840 | ||||
| 3841 | TNode<JSObject> CodeStubAssembler::AllocateJSObjectFromMap( | |||
| 3842 | TNode<Map> map, base::Optional<TNode<HeapObject>> properties, | |||
| 3843 | base::Optional<TNode<FixedArray>> elements, AllocationFlags flags, | |||
| 3844 | SlackTrackingMode slack_tracking_mode) { | |||
| 3845 | CSA_DCHECK(this, Word32BinaryNot(IsJSFunctionMap(map)))((void)0); | |||
| 3846 | CSA_DCHECK(this, Word32BinaryNot(InstanceTypeEqual(LoadMapInstanceType(map),((void)0) | |||
| 3847 | JS_GLOBAL_OBJECT_TYPE)))((void)0); | |||
| 3848 | TNode<IntPtrT> instance_size = | |||
| 3849 | TimesTaggedSize(LoadMapInstanceSizeInWords(map)); | |||
| 3850 | TNode<HeapObject> object = AllocateInNewSpace(instance_size, flags); | |||
| 3851 | StoreMapNoWriteBarrier(object, map); | |||
| 3852 | InitializeJSObjectFromMap(object, map, instance_size, properties, elements, | |||
| 3853 | slack_tracking_mode); | |||
| 3854 | return CAST(object)Cast(object); | |||
| 3855 | } | |||
| 3856 | ||||
| 3857 | void CodeStubAssembler::InitializeJSObjectFromMap( | |||
| 3858 | TNode<HeapObject> object, TNode<Map> map, TNode<IntPtrT> instance_size, | |||
| 3859 | base::Optional<TNode<HeapObject>> properties, | |||
| 3860 | base::Optional<TNode<FixedArray>> elements, | |||
| 3861 | SlackTrackingMode slack_tracking_mode) { | |||
| 3862 | // This helper assumes that the object is in new-space, as guarded by the | |||
| 3863 | // check in AllocatedJSObjectFromMap. | |||
| 3864 | if (!properties) { | |||
| 3865 | CSA_DCHECK(this, Word32BinaryNot(IsDictionaryMap((map))))((void)0); | |||
| 3866 | StoreObjectFieldRoot(object, JSObject::kPropertiesOrHashOffset, | |||
| 3867 | RootIndex::kEmptyFixedArray); | |||
| 3868 | } else { | |||
| 3869 | CSA_DCHECK(this, Word32Or(Word32Or(Word32Or(IsPropertyArray(*properties),((void)0) | |||
| 3870 | IsNameDictionary(*properties)),((void)0) | |||
| 3871 | IsSwissNameDictionary(*properties)),((void)0) | |||
| 3872 | IsEmptyFixedArray(*properties)))((void)0); | |||
| 3873 | StoreObjectFieldNoWriteBarrier(object, JSObject::kPropertiesOrHashOffset, | |||
| 3874 | *properties); | |||
| 3875 | } | |||
| 3876 | if (!elements) { | |||
| 3877 | StoreObjectFieldRoot(object, JSObject::kElementsOffset, | |||
| 3878 | RootIndex::kEmptyFixedArray); | |||
| 3879 | } else { | |||
| 3880 | StoreObjectFieldNoWriteBarrier(object, JSObject::kElementsOffset, | |||
| 3881 | *elements); | |||
| 3882 | } | |||
| 3883 | if (slack_tracking_mode == kNoSlackTracking) { | |||
| 3884 | InitializeJSObjectBodyNoSlackTracking(object, map, instance_size); | |||
| 3885 | } else { | |||
| 3886 | DCHECK_EQ(slack_tracking_mode, kWithSlackTracking)((void) 0); | |||
| 3887 | InitializeJSObjectBodyWithSlackTracking(object, map, instance_size); | |||
| 3888 | } | |||
| 3889 | } | |||
| 3890 | ||||
| 3891 | void CodeStubAssembler::InitializeJSObjectBodyNoSlackTracking( | |||
| 3892 | TNode<HeapObject> object, TNode<Map> map, TNode<IntPtrT> instance_size, | |||
| 3893 | int start_offset) { | |||
| 3894 | STATIC_ASSERT(Map::kNoSlackTracking == 0)static_assert(Map::kNoSlackTracking == 0, "Map::kNoSlackTracking == 0" ); | |||
| 3895 | CSA_DCHECK(this, IsClearWord32<Map::Bits3::ConstructionCounterBits>(((void)0) | |||
| 3896 | LoadMapBitField3(map)))((void)0); | |||
| 3897 | InitializeFieldsWithRoot(object, IntPtrConstant(start_offset), instance_size, | |||
| 3898 | RootIndex::kUndefinedValue); | |||
| 3899 | } | |||
| 3900 | ||||
| 3901 | void CodeStubAssembler::InitializeJSObjectBodyWithSlackTracking( | |||
| 3902 | TNode<HeapObject> object, TNode<Map> map, TNode<IntPtrT> instance_size) { | |||
| 3903 | Comment("InitializeJSObjectBodyNoSlackTracking"); | |||
| 3904 | ||||
| 3905 | // Perform in-object slack tracking if requested. | |||
| 3906 | int start_offset = JSObject::kHeaderSize; | |||
| 3907 | TNode<Uint32T> bit_field3 = LoadMapBitField3(map); | |||
| 3908 | Label end(this), slack_tracking(this), complete(this, Label::kDeferred); | |||
| 3909 | STATIC_ASSERT(Map::kNoSlackTracking == 0)static_assert(Map::kNoSlackTracking == 0, "Map::kNoSlackTracking == 0" ); | |||
| 3910 | GotoIf(IsSetWord32<Map::Bits3::ConstructionCounterBits>(bit_field3), | |||
| 3911 | &slack_tracking); | |||
| 3912 | Comment("No slack tracking"); | |||
| 3913 | InitializeJSObjectBodyNoSlackTracking(object, map, instance_size); | |||
| 3914 | Goto(&end); | |||
| 3915 | ||||
| 3916 | BIND(&slack_tracking)Bind(&slack_tracking); | |||
| 3917 | { | |||
| 3918 | Comment("Decrease construction counter"); | |||
| 3919 | // Slack tracking is only done on initial maps. | |||
| 3920 | CSA_DCHECK(this, IsUndefined(LoadMapBackPointer(map)))((void)0); | |||
| 3921 | STATIC_ASSERT(Map::Bits3::ConstructionCounterBits::kLastUsedBit == 31)static_assert(Map::Bits3::ConstructionCounterBits::kLastUsedBit == 31, "Map::Bits3::ConstructionCounterBits::kLastUsedBit == 31" ); | |||
| 3922 | TNode<Word32T> new_bit_field3 = Int32Sub( | |||
| 3923 | bit_field3, | |||
| 3924 | Int32Constant(1 << Map::Bits3::ConstructionCounterBits::kShift)); | |||
| 3925 | StoreObjectFieldNoWriteBarrier(map, Map::kBitField3Offset, new_bit_field3); | |||
| 3926 | STATIC_ASSERT(Map::kSlackTrackingCounterEnd == 1)static_assert(Map::kSlackTrackingCounterEnd == 1, "Map::kSlackTrackingCounterEnd == 1" ); | |||
| 3927 | ||||
| 3928 | // The object still has in-object slack therefore the |unsed_or_unused| | |||
| 3929 | // field contain the "used" value. | |||
| 3930 | TNode<IntPtrT> used_size = | |||
| 3931 | Signed(TimesTaggedSize(ChangeUint32ToWord(LoadObjectField<Uint8T>( | |||
| 3932 | map, Map::kUsedOrUnusedInstanceSizeInWordsOffset)))); | |||
| 3933 | ||||
| 3934 | Comment("iInitialize filler fields"); | |||
| 3935 | InitializeFieldsWithRoot(object, used_size, instance_size, | |||
| 3936 | RootIndex::kOnePointerFillerMap); | |||
| 3937 | ||||
| 3938 | Comment("Initialize undefined fields"); | |||
| 3939 | InitializeFieldsWithRoot(object, IntPtrConstant(start_offset), used_size, | |||
| 3940 | RootIndex::kUndefinedValue); | |||
| 3941 | ||||
| 3942 | STATIC_ASSERT(Map::kNoSlackTracking == 0)static_assert(Map::kNoSlackTracking == 0, "Map::kNoSlackTracking == 0" ); | |||
| 3943 | GotoIf(IsClearWord32<Map::Bits3::ConstructionCounterBits>(new_bit_field3), | |||
| 3944 | &complete); | |||
| 3945 | Goto(&end); | |||
| 3946 | } | |||
| 3947 | ||||
| 3948 | // Finalize the instance size. | |||
| 3949 | BIND(&complete)Bind(&complete); | |||
| 3950 | { | |||
| 3951 | // ComplextInobjectSlackTracking doesn't allocate and thus doesn't need a | |||
| 3952 | // context. | |||
| 3953 | CallRuntime(Runtime::kCompleteInobjectSlackTrackingForMap, | |||
| 3954 | NoContextConstant(), map); | |||
| 3955 | Goto(&end); | |||
| 3956 | } | |||
| 3957 | ||||
| 3958 | BIND(&end)Bind(&end); | |||
| 3959 | } | |||
| 3960 | ||||
| 3961 | void CodeStubAssembler::StoreFieldsNoWriteBarrier(TNode<IntPtrT> start_address, | |||
| 3962 | TNode<IntPtrT> end_address, | |||
| 3963 | TNode<Object> value) { | |||
| 3964 | Comment("StoreFieldsNoWriteBarrier"); | |||
| 3965 | CSA_DCHECK(this, WordIsAligned(start_address, kTaggedSize))((void)0); | |||
| 3966 | CSA_DCHECK(this, WordIsAligned(end_address, kTaggedSize))((void)0); | |||
| 3967 | BuildFastLoop<IntPtrT>( | |||
| 3968 | start_address, end_address, | |||
| 3969 | [=](TNode<IntPtrT> current) { | |||
| 3970 | UnsafeStoreNoWriteBarrier(MachineRepresentation::kTagged, current, | |||
| 3971 | value); | |||
| 3972 | }, | |||
| 3973 | kTaggedSize, IndexAdvanceMode::kPost); | |||
| 3974 | } | |||
| 3975 | ||||
| 3976 | void CodeStubAssembler::MakeFixedArrayCOW(TNode<FixedArray> array) { | |||
| 3977 | CSA_DCHECK(this, IsFixedArrayMap(LoadMap(array)))((void)0); | |||
| 3978 | Label done(this); | |||
| 3979 | // The empty fixed array is not modifiable anyway. And we shouldn't change its | |||
| 3980 | // Map. | |||
| 3981 | GotoIf(TaggedEqual(array, EmptyFixedArrayConstant()), &done); | |||
| 3982 | StoreMap(array, FixedCOWArrayMapConstant()); | |||
| 3983 | Goto(&done); | |||
| 3984 | BIND(&done)Bind(&done); | |||
| 3985 | } | |||
| 3986 | ||||
| 3987 | TNode<BoolT> CodeStubAssembler::IsValidFastJSArrayCapacity( | |||
| 3988 | TNode<IntPtrT> capacity) { | |||
| 3989 | return UintPtrLessThanOrEqual(capacity, | |||
| 3990 | UintPtrConstant(JSArray::kMaxFastArrayLength)); | |||
| 3991 | } | |||
| 3992 | ||||
| 3993 | TNode<JSArray> CodeStubAssembler::AllocateJSArray( | |||
| 3994 | TNode<Map> array_map, TNode<FixedArrayBase> elements, TNode<Smi> length, | |||
| 3995 | base::Optional<TNode<AllocationSite>> allocation_site, | |||
| 3996 | int array_header_size) { | |||
| 3997 | Comment("begin allocation of JSArray passing in elements"); | |||
| 3998 | CSA_SLOW_DCHECK(this, TaggedIsPositiveSmi(length))((void)0); | |||
| 3999 | ||||
| 4000 | int base_size = array_header_size; | |||
| 4001 | if (allocation_site) { | |||
| 4002 | DCHECK(V8_ALLOCATION_SITE_TRACKING_BOOL)((void) 0); | |||
| 4003 | base_size += AllocationMemento::kSize; | |||
| 4004 | } | |||
| 4005 | ||||
| 4006 | TNode<IntPtrT> size = IntPtrConstant(base_size); | |||
| 4007 | TNode<JSArray> result = | |||
| 4008 | AllocateUninitializedJSArray(array_map, length, allocation_site, size); | |||
| 4009 | StoreObjectFieldNoWriteBarrier(result, JSArray::kElementsOffset, elements); | |||
| 4010 | return result; | |||
| 4011 | } | |||
| 4012 | ||||
| 4013 | namespace { | |||
| 4014 | ||||
| 4015 | // To prevent GC between the array and elements allocation, the elements | |||
| 4016 | // object allocation is folded together with the js-array allocation. | |||
| 4017 | TNode<FixedArrayBase> InnerAllocateElements(CodeStubAssembler* csa, | |||
| 4018 | TNode<JSArray> js_array, | |||
| 4019 | int offset) { | |||
| 4020 | return csa->UncheckedCast<FixedArrayBase>( | |||
| 4021 | csa->BitcastWordToTagged(csa->IntPtrAdd( | |||
| 4022 | csa->BitcastTaggedToWord(js_array), csa->IntPtrConstant(offset)))); | |||
| 4023 | } | |||
| 4024 | ||||
| 4025 | } // namespace | |||
| 4026 | ||||
| 4027 | std::pair<TNode<JSArray>, TNode<FixedArrayBase>> | |||
| 4028 | CodeStubAssembler::AllocateUninitializedJSArrayWithElements( | |||
| 4029 | ElementsKind kind, TNode<Map> array_map, TNode<Smi> length, | |||
| 4030 | base::Optional<TNode<AllocationSite>> allocation_site, | |||
| 4031 | TNode<IntPtrT> capacity, AllocationFlags allocation_flags, | |||
| 4032 | int array_header_size) { | |||
| 4033 | Comment("begin allocation of JSArray with elements"); | |||
| 4034 | CHECK_EQ(allocation_flags & ~AllocationFlag::kAllowLargeObjectAllocation, 0)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(allocation_flags & ~AllocationFlag ::kAllowLargeObjectAllocation)>::type, typename ::v8::base ::pass_value_or_ref<decltype(0)>::type>((allocation_flags & ~AllocationFlag::kAllowLargeObjectAllocation), (0)); do { if ((__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "allocation_flags & ~AllocationFlag::kAllowLargeObjectAllocation" " " "==" " " "0"); } } while (false); } while (false); | |||
| 4035 | CSA_SLOW_DCHECK(this, TaggedIsPositiveSmi(length))((void)0); | |||
| 4036 | ||||
| 4037 | TVARIABLE(JSArray, array)TVariable<JSArray> array(this); | |||
| 4038 | TVARIABLE(FixedArrayBase, elements)TVariable<FixedArrayBase> elements(this); | |||
| 4039 | ||||
| 4040 | Label out(this), empty(this), nonempty(this); | |||
| 4041 | ||||
| 4042 | int capacity_int; | |||
| 4043 | if (TryToInt32Constant(capacity, &capacity_int)) { | |||
| 4044 | if (capacity_int == 0) { | |||
| 4045 | TNode<FixedArray> empty_array = EmptyFixedArrayConstant(); | |||
| 4046 | array = AllocateJSArray(array_map, empty_array, length, allocation_site, | |||
| 4047 | array_header_size); | |||
| 4048 | return {array.value(), empty_array}; | |||
| 4049 | } else { | |||
| 4050 | Goto(&nonempty); | |||
| 4051 | } | |||
| 4052 | } else { | |||
| 4053 | Branch(WordEqual(capacity, IntPtrConstant(0)), &empty, &nonempty); | |||
| 4054 | ||||
| 4055 | BIND(&empty)Bind(&empty); | |||
| 4056 | { | |||
| 4057 | TNode<FixedArray> empty_array = EmptyFixedArrayConstant(); | |||
| 4058 | array = AllocateJSArray(array_map, empty_array, length, allocation_site, | |||
| 4059 | array_header_size); | |||
| 4060 | elements = empty_array; | |||
| 4061 | Goto(&out); | |||
| 4062 | } | |||
| 4063 | } | |||
| 4064 | ||||
| 4065 | BIND(&nonempty)Bind(&nonempty); | |||
| 4066 | { | |||
| 4067 | int base_size = array_header_size; | |||
| 4068 | if (allocation_site) { | |||
| 4069 | DCHECK(V8_ALLOCATION_SITE_TRACKING_BOOL)((void) 0); | |||
| 4070 | base_size += AllocationMemento::kSize; | |||
| 4071 | } | |||
| 4072 | ||||
| 4073 | const int elements_offset = base_size; | |||
| 4074 | ||||
| 4075 | // Compute space for elements | |||
| 4076 | base_size += FixedArray::kHeaderSize; | |||
| 4077 | TNode<IntPtrT> size = ElementOffsetFromIndex(capacity, kind, base_size); | |||
| 4078 | ||||
| 4079 | // For very large arrays in which the requested allocation exceeds the | |||
| 4080 | // maximal size of a regular heap object, we cannot use the allocation | |||
| 4081 | // folding trick. Instead, we first allocate the elements in large object | |||
| 4082 | // space, and then allocate the JSArray (and possibly the allocation | |||
| 4083 | // memento) in new space. | |||
| 4084 | if (allocation_flags & AllocationFlag::kAllowLargeObjectAllocation) { | |||
| 4085 | Label next(this); | |||
| 4086 | GotoIf(IsRegularHeapObjectSize(size), &next); | |||
| 4087 | ||||
| 4088 | CSA_CHECK(this, IsValidFastJSArrayCapacity(capacity))(this)->FastCheck(IsValidFastJSArrayCapacity(capacity)); | |||
| 4089 | ||||
| 4090 | // Allocate and initialize the elements first. Full initialization is | |||
| 4091 | // needed because the upcoming JSArray allocation could trigger GC. | |||
| 4092 | elements = AllocateFixedArray(kind, capacity, allocation_flags); | |||
| 4093 | ||||
| 4094 | if (IsDoubleElementsKind(kind)) { | |||
| 4095 | FillFixedDoubleArrayWithZero(CAST(elements.value())Cast(elements.value()), capacity); | |||
| 4096 | } else { | |||
| 4097 | FillFixedArrayWithSmiZero(CAST(elements.value())Cast(elements.value()), capacity); | |||
| 4098 | } | |||
| 4099 | ||||
| 4100 | // The JSArray and possibly allocation memento next. Note that | |||
| 4101 | // allocation_flags are *not* passed on here and the resulting JSArray | |||
| 4102 | // will always be in new space. | |||
| 4103 | array = AllocateJSArray(array_map, elements.value(), length, | |||
| 4104 | allocation_site, array_header_size); | |||
| 4105 | ||||
| 4106 | Goto(&out); | |||
| 4107 | ||||
| 4108 | BIND(&next)Bind(&next); | |||
| 4109 | } | |||
| 4110 | // Fold all objects into a single new space allocation. | |||
| 4111 | array = | |||
| 4112 | AllocateUninitializedJSArray(array_map, length, allocation_site, size); | |||
| 4113 | elements = InnerAllocateElements(this, array.value(), elements_offset); | |||
| 4114 | ||||
| 4115 | StoreObjectFieldNoWriteBarrier(array.value(), JSObject::kElementsOffset, | |||
| 4116 | elements.value()); | |||
| 4117 | ||||
| 4118 | // Setup elements object. | |||
| 4119 | STATIC_ASSERT(FixedArrayBase::kHeaderSize == 2 * kTaggedSize)static_assert(FixedArrayBase::kHeaderSize == 2 * kTaggedSize, "FixedArrayBase::kHeaderSize == 2 * kTaggedSize"); | |||
| 4120 | RootIndex elements_map_index = IsDoubleElementsKind(kind) | |||
| 4121 | ? RootIndex::kFixedDoubleArrayMap | |||
| 4122 | : RootIndex::kFixedArrayMap; | |||
| 4123 | DCHECK(RootsTable::IsImmortalImmovable(elements_map_index))((void) 0); | |||
| 4124 | StoreMapNoWriteBarrier(elements.value(), elements_map_index); | |||
| 4125 | ||||
| 4126 | CSA_DCHECK(this, WordNotEqual(capacity, IntPtrConstant(0)))((void)0); | |||
| 4127 | TNode<Smi> capacity_smi = SmiTag(capacity); | |||
| 4128 | StoreObjectFieldNoWriteBarrier(elements.value(), FixedArray::kLengthOffset, | |||
| 4129 | capacity_smi); | |||
| 4130 | Goto(&out); | |||
| 4131 | } | |||
| 4132 | ||||
| 4133 | BIND(&out)Bind(&out); | |||
| 4134 | return {array.value(), elements.value()}; | |||
| 4135 | } | |||
| 4136 | ||||
| 4137 | TNode<JSArray> CodeStubAssembler::AllocateUninitializedJSArray( | |||
| 4138 | TNode<Map> array_map, TNode<Smi> length, | |||
| 4139 | base::Optional<TNode<AllocationSite>> allocation_site, | |||
| 4140 | TNode<IntPtrT> size_in_bytes) { | |||
| 4141 | CSA_SLOW_DCHECK(this, TaggedIsPositiveSmi(length))((void)0); | |||
| 4142 | ||||
| 4143 | // Allocate space for the JSArray and the elements FixedArray in one go. | |||
| 4144 | TNode<HeapObject> array = AllocateInNewSpace(size_in_bytes); | |||
| 4145 | ||||
| 4146 | StoreMapNoWriteBarrier(array, array_map); | |||
| 4147 | StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); | |||
| 4148 | StoreObjectFieldRoot(array, JSArray::kPropertiesOrHashOffset, | |||
| 4149 | RootIndex::kEmptyFixedArray); | |||
| 4150 | ||||
| 4151 | if (allocation_site) { | |||
| 4152 | DCHECK(V8_ALLOCATION_SITE_TRACKING_BOOL)((void) 0); | |||
| 4153 | InitializeAllocationMemento(array, IntPtrConstant(JSArray::kHeaderSize), | |||
| 4154 | *allocation_site); | |||
| 4155 | } | |||
| 4156 | ||||
| 4157 | return CAST(array)Cast(array); | |||
| 4158 | } | |||
| 4159 | ||||
| 4160 | TNode<JSArray> CodeStubAssembler::AllocateJSArray( | |||
| 4161 | ElementsKind kind, TNode<Map> array_map, TNode<IntPtrT> capacity, | |||
| 4162 | TNode<Smi> length, base::Optional<TNode<AllocationSite>> allocation_site, | |||
| 4163 | AllocationFlags allocation_flags) { | |||
| 4164 | CSA_SLOW_DCHECK(this, TaggedIsPositiveSmi(length))((void)0); | |||
| 4165 | ||||
| 4166 | TNode<JSArray> array; | |||
| 4167 | TNode<FixedArrayBase> elements; | |||
| 4168 | ||||
| 4169 | std::tie(array, elements) = AllocateUninitializedJSArrayWithElements( | |||
| 4170 | kind, array_map, length, allocation_site, capacity, allocation_flags); | |||
| 4171 | ||||
| 4172 | Label out(this), nonempty(this); | |||
| 4173 | ||||
| 4174 | Branch(WordEqual(capacity, IntPtrConstant(0)), &out, &nonempty); | |||
| 4175 | ||||
| 4176 | BIND(&nonempty)Bind(&nonempty); | |||
| 4177 | { | |||
| 4178 | FillFixedArrayWithValue(kind, elements, IntPtrConstant(0), capacity, | |||
| 4179 | RootIndex::kTheHoleValue); | |||
| 4180 | Goto(&out); | |||
| 4181 | } | |||
| 4182 | ||||
| 4183 | BIND(&out)Bind(&out); | |||
| 4184 | return array; | |||
| 4185 | } | |||
| 4186 | ||||
| 4187 | TNode<JSArray> CodeStubAssembler::ExtractFastJSArray(TNode<Context> context, | |||
| 4188 | TNode<JSArray> array, | |||
| 4189 | TNode<BInt> begin, | |||
| 4190 | TNode<BInt> count) { | |||
| 4191 | TNode<Map> original_array_map = LoadMap(array); | |||
| 4192 | TNode<Int32T> elements_kind = LoadMapElementsKind(original_array_map); | |||
| 4193 | ||||
| 4194 | // Use the canonical map for the Array's ElementsKind | |||
| 4195 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 4196 | TNode<Map> array_map = LoadJSArrayElementsMap(elements_kind, native_context); | |||
| 4197 | ||||
| 4198 | TNode<FixedArrayBase> new_elements = ExtractFixedArray( | |||
| 4199 | LoadElements(array), base::Optional<TNode<BInt>>(begin), | |||
| 4200 | base::Optional<TNode<BInt>>(count), | |||
| 4201 | base::Optional<TNode<BInt>>(base::nullopt), | |||
| 4202 | ExtractFixedArrayFlag::kAllFixedArrays, nullptr, elements_kind); | |||
| 4203 | ||||
| 4204 | TNode<JSArray> result = AllocateJSArray( | |||
| 4205 | array_map, new_elements, ParameterToTagged(count), base::nullopt); | |||
| 4206 | return result; | |||
| 4207 | } | |||
| 4208 | ||||
| 4209 | TNode<JSArray> CodeStubAssembler::CloneFastJSArray( | |||
| 4210 | TNode<Context> context, TNode<JSArray> array, | |||
| 4211 | base::Optional<TNode<AllocationSite>> allocation_site, | |||
| 4212 | HoleConversionMode convert_holes) { | |||
| 4213 | // TODO(dhai): we should be able to assert IsFastJSArray(array) here, but this | |||
| 4214 | // function is also used to copy boilerplates even when the no-elements | |||
| 4215 | // protector is invalid. This function should be renamed to reflect its uses. | |||
| 4216 | ||||
| 4217 | TNode<Number> length = LoadJSArrayLength(array); | |||
| 4218 | TNode<FixedArrayBase> new_elements; | |||
| 4219 | TVARIABLE(FixedArrayBase, var_new_elements)TVariable<FixedArrayBase> var_new_elements(this); | |||
| 4220 | TVARIABLE(Int32T, var_elements_kind, LoadMapElementsKind(LoadMap(array)))TVariable<Int32T> var_elements_kind(LoadMapElementsKind (LoadMap(array)), this); | |||
| 4221 | ||||
| 4222 | Label allocate_jsarray(this), holey_extract(this), | |||
| 4223 | allocate_jsarray_main(this); | |||
| 4224 | ||||
| 4225 | bool need_conversion = | |||
| 4226 | convert_holes == HoleConversionMode::kConvertToUndefined; | |||
| 4227 | if (need_conversion) { | |||
| 4228 | // We need to take care of holes, if the array is of holey elements kind. | |||
| 4229 | GotoIf(IsHoleyFastElementsKindForRead(var_elements_kind.value()), | |||
| 4230 | &holey_extract); | |||
| 4231 | } | |||
| 4232 | ||||
| 4233 | // Simple extraction that preserves holes. | |||
| 4234 | new_elements = ExtractFixedArray( | |||
| 4235 | LoadElements(array), | |||
| 4236 | base::Optional<TNode<BInt>>(IntPtrOrSmiConstant<BInt>(0)), | |||
| 4237 | base::Optional<TNode<BInt>>(TaggedToParameter<BInt>(CAST(length)Cast(length))), | |||
| 4238 | base::Optional<TNode<BInt>>(base::nullopt), | |||
| 4239 | ExtractFixedArrayFlag::kAllFixedArraysDontCopyCOW, nullptr, | |||
| 4240 | var_elements_kind.value()); | |||
| 4241 | var_new_elements = new_elements; | |||
| 4242 | Goto(&allocate_jsarray); | |||
| 4243 | ||||
| 4244 | if (need_conversion) { | |||
| 4245 | BIND(&holey_extract)Bind(&holey_extract); | |||
| 4246 | // Convert holes to undefined. | |||
| 4247 | TVARIABLE(BoolT, var_holes_converted, Int32FalseConstant())TVariable<BoolT> var_holes_converted(Int32FalseConstant (), this); | |||
| 4248 | // Copy |array|'s elements store. The copy will be compatible with the | |||
| 4249 | // original elements kind unless there are holes in the source. Any holes | |||
| 4250 | // get converted to undefined, hence in that case the copy is compatible | |||
| 4251 | // only with PACKED_ELEMENTS and HOLEY_ELEMENTS, and we will choose | |||
| 4252 | // PACKED_ELEMENTS. Also, if we want to replace holes, we must not use | |||
| 4253 | // ExtractFixedArrayFlag::kDontCopyCOW. | |||
| 4254 | new_elements = ExtractFixedArray( | |||
| 4255 | LoadElements(array), | |||
| 4256 | base::Optional<TNode<BInt>>(IntPtrOrSmiConstant<BInt>(0)), | |||
| 4257 | base::Optional<TNode<BInt>>(TaggedToParameter<BInt>(CAST(length)Cast(length))), | |||
| 4258 | base::Optional<TNode<BInt>>(base::nullopt), | |||
| 4259 | ExtractFixedArrayFlag::kAllFixedArrays, &var_holes_converted); | |||
| 4260 | var_new_elements = new_elements; | |||
| 4261 | // If the array type didn't change, use the original elements kind. | |||
| 4262 | GotoIfNot(var_holes_converted.value(), &allocate_jsarray); | |||
| 4263 | // Otherwise use PACKED_ELEMENTS for the target's elements kind. | |||
| 4264 | var_elements_kind = Int32Constant(PACKED_ELEMENTS); | |||
| 4265 | Goto(&allocate_jsarray); | |||
| 4266 | } | |||
| 4267 | ||||
| 4268 | BIND(&allocate_jsarray)Bind(&allocate_jsarray); | |||
| 4269 | ||||
| 4270 | // Handle any nonextensible elements kinds | |||
| 4271 | CSA_DCHECK(this, IsElementsKindLessThanOrEqual(((void)0) | |||
| 4272 | var_elements_kind.value(),((void)0) | |||
| 4273 | LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND))((void)0); | |||
| 4274 | GotoIf(IsElementsKindLessThanOrEqual(var_elements_kind.value(), | |||
| 4275 | LAST_FAST_ELEMENTS_KIND), | |||
| 4276 | &allocate_jsarray_main); | |||
| 4277 | var_elements_kind = Int32Constant(PACKED_ELEMENTS); | |||
| 4278 | Goto(&allocate_jsarray_main); | |||
| 4279 | ||||
| 4280 | BIND(&allocate_jsarray_main)Bind(&allocate_jsarray_main); | |||
| 4281 | // Use the cannonical map for the chosen elements kind. | |||
| 4282 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 4283 | TNode<Map> array_map = | |||
| 4284 | LoadJSArrayElementsMap(var_elements_kind.value(), native_context); | |||
| 4285 | ||||
| 4286 | TNode<JSArray> result = AllocateJSArray(array_map, var_new_elements.value(), | |||
| 4287 | CAST(length)Cast(length), allocation_site); | |||
| 4288 | return result; | |||
| 4289 | } | |||
| 4290 | ||||
| 4291 | template <typename TIndex> | |||
| 4292 | TNode<FixedArrayBase> CodeStubAssembler::AllocateFixedArray( | |||
| 4293 | ElementsKind kind, TNode<TIndex> capacity, AllocationFlags flags, | |||
| 4294 | base::Optional<TNode<Map>> fixed_array_map) { | |||
| 4295 | static_assert( | |||
| 4296 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 4297 | "Only Smi or IntPtrT capacity is allowed"); | |||
| 4298 | Comment("AllocateFixedArray"); | |||
| 4299 | CSA_DCHECK(this,((void)0) | |||
| 4300 | IntPtrOrSmiGreaterThan(capacity, IntPtrOrSmiConstant<TIndex>(0)))((void)0); | |||
| 4301 | ||||
| 4302 | const intptr_t kMaxLength = IsDoubleElementsKind(kind) | |||
| 4303 | ? FixedDoubleArray::kMaxLength | |||
| 4304 | : FixedArray::kMaxLength; | |||
| 4305 | intptr_t capacity_constant; | |||
| 4306 | if (ToParameterConstant(capacity, &capacity_constant)) { | |||
| 4307 | CHECK_LE(capacity_constant, kMaxLength)do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(capacity_constant)>::type, typename ::v8::base::pass_value_or_ref<decltype(kMaxLength )>::type>((capacity_constant), (kMaxLength)); do { if ( (__builtin_expect(!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s." , "capacity_constant" " " "<=" " " "kMaxLength"); } } while (false); } while (false); | |||
| 4308 | } else { | |||
| 4309 | Label if_out_of_memory(this, Label::kDeferred), next(this); | |||
| 4310 | Branch(IntPtrOrSmiGreaterThan(capacity, IntPtrOrSmiConstant<TIndex>( | |||
| 4311 | static_cast<int>(kMaxLength))), | |||
| 4312 | &if_out_of_memory, &next); | |||
| 4313 | ||||
| 4314 | BIND(&if_out_of_memory)Bind(&if_out_of_memory); | |||
| 4315 | CallRuntime(Runtime::kFatalProcessOutOfMemoryInvalidArrayLength, | |||
| 4316 | NoContextConstant()); | |||
| 4317 | Unreachable(); | |||
| 4318 | ||||
| 4319 | BIND(&next)Bind(&next); | |||
| 4320 | } | |||
| 4321 | ||||
| 4322 | TNode<IntPtrT> total_size = GetFixedArrayAllocationSize(capacity, kind); | |||
| 4323 | ||||
| 4324 | if (IsDoubleElementsKind(kind)) flags |= AllocationFlag::kDoubleAlignment; | |||
| 4325 | // Allocate both array and elements object, and initialize the JSArray. | |||
| 4326 | TNode<HeapObject> array = Allocate(total_size, flags); | |||
| 4327 | if (fixed_array_map) { | |||
| 4328 | // Conservatively only skip the write barrier if there are no allocation | |||
| 4329 | // flags, this ensures that the object hasn't ended up in LOS. Note that the | |||
| 4330 | // fixed array map is currently always immortal and technically wouldn't | |||
| 4331 | // need the write barrier even in LOS, but it's better to not take chances | |||
| 4332 | // in case this invariant changes later, since it's difficult to enforce | |||
| 4333 | // locally here. | |||
| 4334 | if (flags == AllocationFlag::kNone) { | |||
| 4335 | StoreMapNoWriteBarrier(array, *fixed_array_map); | |||
| 4336 | } else { | |||
| 4337 | StoreMap(array, *fixed_array_map); | |||
| 4338 | } | |||
| 4339 | } else { | |||
| 4340 | RootIndex map_index = IsDoubleElementsKind(kind) | |||
| 4341 | ? RootIndex::kFixedDoubleArrayMap | |||
| 4342 | : RootIndex::kFixedArrayMap; | |||
| 4343 | DCHECK(RootsTable::IsImmortalImmovable(map_index))((void) 0); | |||
| 4344 | StoreMapNoWriteBarrier(array, map_index); | |||
| 4345 | } | |||
| 4346 | StoreObjectFieldNoWriteBarrier(array, FixedArrayBase::kLengthOffset, | |||
| 4347 | ParameterToTagged(capacity)); | |||
| 4348 | return UncheckedCast<FixedArrayBase>(array); | |||
| 4349 | } | |||
| 4350 | ||||
| 4351 | // There is no need to export the Smi version since it is only used inside | |||
| 4352 | // code-stub-assembler. | |||
| 4353 | template V8_EXPORT_PRIVATE TNode<FixedArrayBase> | |||
| 4354 | CodeStubAssembler::AllocateFixedArray<IntPtrT>(ElementsKind, TNode<IntPtrT>, | |||
| 4355 | AllocationFlags, | |||
| 4356 | base::Optional<TNode<Map>>); | |||
| 4357 | ||||
| 4358 | template <typename TIndex> | |||
| 4359 | TNode<FixedArray> CodeStubAssembler::ExtractToFixedArray( | |||
| 4360 | TNode<FixedArrayBase> source, TNode<TIndex> first, TNode<TIndex> count, | |||
| 4361 | TNode<TIndex> capacity, TNode<Map> source_map, ElementsKind from_kind, | |||
| 4362 | AllocationFlags allocation_flags, ExtractFixedArrayFlags extract_flags, | |||
| 4363 | HoleConversionMode convert_holes, TVariable<BoolT>* var_holes_converted, | |||
| 4364 | base::Optional<TNode<Int32T>> source_elements_kind) { | |||
| 4365 | static_assert( | |||
| 4366 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 4367 | "Only Smi or IntPtrT first, count, and capacity are allowed"); | |||
| 4368 | ||||
| 4369 | DCHECK(extract_flags & ExtractFixedArrayFlag::kFixedArrays)((void) 0); | |||
| 4370 | CSA_DCHECK(this,((void)0) | |||
| 4371 | IntPtrOrSmiNotEqual(IntPtrOrSmiConstant<TIndex>(0), capacity))((void)0); | |||
| 4372 | CSA_DCHECK(this, TaggedEqual(source_map, LoadMap(source)))((void)0); | |||
| 4373 | ||||
| 4374 | TVARIABLE(FixedArrayBase, var_result)TVariable<FixedArrayBase> var_result(this); | |||
| 4375 | TVARIABLE(Map, var_target_map, source_map)TVariable<Map> var_target_map(source_map, this); | |||
| 4376 | ||||
| 4377 | Label done(this, {&var_result}), is_cow(this), | |||
| 4378 | new_space_handler(this, {&var_target_map}); | |||
| 4379 | ||||
| 4380 | // If source_map is either FixedDoubleArrayMap, or FixedCOWArrayMap but | |||
| 4381 | // we can't just use COW, use FixedArrayMap as the target map. Otherwise, use | |||
| 4382 | // source_map as the target map. | |||
| 4383 | if (IsDoubleElementsKind(from_kind)) { | |||
| 4384 | CSA_DCHECK(this, IsFixedDoubleArrayMap(source_map))((void)0); | |||
| 4385 | var_target_map = FixedArrayMapConstant(); | |||
| 4386 | Goto(&new_space_handler); | |||
| 4387 | } else { | |||
| 4388 | CSA_DCHECK(this, Word32BinaryNot(IsFixedDoubleArrayMap(source_map)))((void)0); | |||
| 4389 | Branch(TaggedEqual(var_target_map.value(), FixedCOWArrayMapConstant()), | |||
| 4390 | &is_cow, &new_space_handler); | |||
| 4391 | ||||
| 4392 | BIND(&is_cow)Bind(&is_cow); | |||
| 4393 | { | |||
| 4394 | // |source| is a COW array, so we don't actually need to allocate a new | |||
| 4395 | // array unless: | |||
| 4396 | // 1) |extract_flags| forces us to, or | |||
| 4397 | // 2) we're asked to extract only part of the |source| (|first| != 0). | |||
| 4398 | if (extract_flags & ExtractFixedArrayFlag::kDontCopyCOW) { | |||
| 4399 | Branch(IntPtrOrSmiNotEqual(IntPtrOrSmiConstant<TIndex>(0), first), | |||
| 4400 | &new_space_handler, [&] { | |||
| 4401 | var_result = source; | |||
| 4402 | Goto(&done); | |||
| 4403 | }); | |||
| 4404 | } else { | |||
| 4405 | var_target_map = FixedArrayMapConstant(); | |||
| 4406 | Goto(&new_space_handler); | |||
| 4407 | } | |||
| 4408 | } | |||
| 4409 | } | |||
| 4410 | ||||
| 4411 | BIND(&new_space_handler)Bind(&new_space_handler); | |||
| 4412 | { | |||
| 4413 | Comment("Copy FixedArray in young generation"); | |||
| 4414 | // We use PACKED_ELEMENTS to tell AllocateFixedArray and | |||
| 4415 | // CopyFixedArrayElements that we want a FixedArray. | |||
| 4416 | const ElementsKind to_kind = PACKED_ELEMENTS; | |||
| 4417 | TNode<FixedArrayBase> to_elements = AllocateFixedArray( | |||
| 4418 | to_kind, capacity, allocation_flags, var_target_map.value()); | |||
| 4419 | var_result = to_elements; | |||
| 4420 | ||||
| 4421 | #ifndef V8_ENABLE_SINGLE_GENERATION | |||
| 4422 | #ifdef DEBUG | |||
| 4423 | TNode<IntPtrT> object_word = BitcastTaggedToWord(to_elements); | |||
| 4424 | TNode<IntPtrT> object_page = PageFromAddress(object_word); | |||
| 4425 | TNode<IntPtrT> page_flags = | |||
| 4426 | Load<IntPtrT>(object_page, IntPtrConstant(Page::kFlagsOffset)); | |||
| 4427 | CSA_DCHECK(((void)0) | |||
| 4428 | this,((void)0) | |||
| 4429 | WordNotEqual(((void)0) | |||
| 4430 | WordAnd(page_flags,((void)0) | |||
| 4431 | IntPtrConstant(MemoryChunk::kIsInYoungGenerationMask)),((void)0) | |||
| 4432 | IntPtrConstant(0)))((void)0); | |||
| 4433 | #endif | |||
| 4434 | #endif | |||
| 4435 | ||||
| 4436 | if (convert_holes == HoleConversionMode::kDontConvert && | |||
| 4437 | !IsDoubleElementsKind(from_kind)) { | |||
| 4438 | // We can use CopyElements (memcpy) because we don't need to replace or | |||
| 4439 | // convert any values. Since {to_elements} is in new-space, CopyElements | |||
| 4440 | // will efficiently use memcpy. | |||
| 4441 | FillFixedArrayWithValue(to_kind, to_elements, count, capacity, | |||
| 4442 | RootIndex::kTheHoleValue); | |||
| 4443 | CopyElements(to_kind, to_elements, IntPtrConstant(0), source, | |||
| 4444 | ParameterToIntPtr(first), ParameterToIntPtr(count), | |||
| 4445 | SKIP_WRITE_BARRIER); | |||
| 4446 | } else { | |||
| 4447 | CopyFixedArrayElements(from_kind, source, to_kind, to_elements, first, | |||
| 4448 | count, capacity, SKIP_WRITE_BARRIER, convert_holes, | |||
| 4449 | var_holes_converted); | |||
| 4450 | } | |||
| 4451 | Goto(&done); | |||
| 4452 | } | |||
| 4453 | ||||
| 4454 | BIND(&done)Bind(&done); | |||
| 4455 | return UncheckedCast<FixedArray>(var_result.value()); | |||
| 4456 | } | |||
| 4457 | ||||
| 4458 | template <typename TIndex> | |||
| 4459 | TNode<FixedArrayBase> CodeStubAssembler::ExtractFixedDoubleArrayFillingHoles( | |||
| 4460 | TNode<FixedArrayBase> from_array, TNode<TIndex> first, TNode<TIndex> count, | |||
| 4461 | TNode<TIndex> capacity, TNode<Map> fixed_array_map, | |||
| 4462 | TVariable<BoolT>* var_holes_converted, AllocationFlags allocation_flags, | |||
| 4463 | ExtractFixedArrayFlags extract_flags) { | |||
| 4464 | static_assert( | |||
| 4465 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 4466 | "Only Smi or IntPtrT first, count, and capacity are allowed"); | |||
| 4467 | ||||
| 4468 | DCHECK_NE(var_holes_converted, nullptr)((void) 0); | |||
| 4469 | CSA_DCHECK(this, IsFixedDoubleArrayMap(fixed_array_map))((void)0); | |||
| 4470 | ||||
| 4471 | TVARIABLE(FixedArrayBase, var_result)TVariable<FixedArrayBase> var_result(this); | |||
| 4472 | const ElementsKind kind = PACKED_DOUBLE_ELEMENTS; | |||
| 4473 | TNode<FixedArrayBase> to_elements = | |||
| 4474 | AllocateFixedArray(kind, capacity, allocation_flags, fixed_array_map); | |||
| 4475 | var_result = to_elements; | |||
| 4476 | // We first try to copy the FixedDoubleArray to a new FixedDoubleArray. | |||
| 4477 | // |var_holes_converted| is set to False preliminarily. | |||
| 4478 | *var_holes_converted = Int32FalseConstant(); | |||
| 4479 | ||||
| 4480 | // The construction of the loop and the offsets for double elements is | |||
| 4481 | // extracted from CopyFixedArrayElements. | |||
| 4482 | CSA_SLOW_DCHECK(this, IsFixedArrayWithKindOrEmpty(from_array, kind))((void)0); | |||
| 4483 | STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize)static_assert(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize , "FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize"); | |||
| 4484 | ||||
| 4485 | Comment("[ ExtractFixedDoubleArrayFillingHoles"); | |||
| 4486 | ||||
| 4487 | // This copy can trigger GC, so we pre-initialize the array with holes. | |||
| 4488 | FillFixedArrayWithValue(kind, to_elements, IntPtrOrSmiConstant<TIndex>(0), | |||
| 4489 | capacity, RootIndex::kTheHoleValue); | |||
| 4490 | ||||
| 4491 | const int first_element_offset = FixedArray::kHeaderSize - kHeapObjectTag; | |||
| 4492 | TNode<IntPtrT> first_from_element_offset = | |||
| 4493 | ElementOffsetFromIndex(first, kind, 0); | |||
| 4494 | TNode<IntPtrT> limit_offset = IntPtrAdd(first_from_element_offset, | |||
| 4495 | IntPtrConstant(first_element_offset)); | |||
| 4496 | TVARIABLE(IntPtrT, var_from_offset,TVariable<IntPtrT> var_from_offset(ElementOffsetFromIndex (IntPtrOrSmiAdd(first, count), kind, first_element_offset), this ) | |||
| 4497 | ElementOffsetFromIndex(IntPtrOrSmiAdd(first, count), kind,TVariable<IntPtrT> var_from_offset(ElementOffsetFromIndex (IntPtrOrSmiAdd(first, count), kind, first_element_offset), this ) | |||
| 4498 | first_element_offset))TVariable<IntPtrT> var_from_offset(ElementOffsetFromIndex (IntPtrOrSmiAdd(first, count), kind, first_element_offset), this ); | |||
| 4499 | ||||
| 4500 | Label decrement(this, {&var_from_offset}), done(this); | |||
| 4501 | TNode<IntPtrT> to_array_adjusted = | |||
| 4502 | IntPtrSub(BitcastTaggedToWord(to_elements), first_from_element_offset); | |||
| 4503 | ||||
| 4504 | Branch(WordEqual(var_from_offset.value(), limit_offset), &done, &decrement); | |||
| 4505 | ||||
| 4506 | BIND(&decrement)Bind(&decrement); | |||
| 4507 | { | |||
| 4508 | TNode<IntPtrT> from_offset = | |||
| 4509 | IntPtrSub(var_from_offset.value(), IntPtrConstant(kDoubleSize)); | |||
| 4510 | var_from_offset = from_offset; | |||
| 4511 | ||||
| 4512 | TNode<IntPtrT> to_offset = from_offset; | |||
| 4513 | ||||
| 4514 | Label if_hole(this); | |||
| 4515 | ||||
| 4516 | TNode<Float64T> value = LoadDoubleWithHoleCheck( | |||
| 4517 | from_array, var_from_offset.value(), &if_hole, MachineType::Float64()); | |||
| 4518 | ||||
| 4519 | StoreNoWriteBarrier(MachineRepresentation::kFloat64, to_array_adjusted, | |||
| 4520 | to_offset, value); | |||
| 4521 | ||||
| 4522 | TNode<BoolT> compare = WordNotEqual(from_offset, limit_offset); | |||
| 4523 | Branch(compare, &decrement, &done); | |||
| 4524 | ||||
| 4525 | BIND(&if_hole)Bind(&if_hole); | |||
| 4526 | // We are unlucky: there are holes! We need to restart the copy, this time | |||
| 4527 | // we will copy the FixedDoubleArray to a new FixedArray with undefined | |||
| 4528 | // replacing holes. We signal this to the caller through | |||
| 4529 | // |var_holes_converted|. | |||
| 4530 | *var_holes_converted = Int32TrueConstant(); | |||
| 4531 | to_elements = | |||
| 4532 | ExtractToFixedArray(from_array, first, count, capacity, fixed_array_map, | |||
| 4533 | kind, allocation_flags, extract_flags, | |||
| 4534 | HoleConversionMode::kConvertToUndefined); | |||
| 4535 | var_result = to_elements; | |||
| 4536 | Goto(&done); | |||
| 4537 | } | |||
| 4538 | ||||
| 4539 | BIND(&done)Bind(&done); | |||
| 4540 | Comment("] ExtractFixedDoubleArrayFillingHoles"); | |||
| 4541 | return var_result.value(); | |||
| 4542 | } | |||
| 4543 | ||||
| 4544 | template <typename TIndex> | |||
| 4545 | TNode<FixedArrayBase> CodeStubAssembler::ExtractFixedArray( | |||
| 4546 | TNode<FixedArrayBase> source, base::Optional<TNode<TIndex>> first, | |||
| 4547 | base::Optional<TNode<TIndex>> count, base::Optional<TNode<TIndex>> capacity, | |||
| 4548 | ExtractFixedArrayFlags extract_flags, TVariable<BoolT>* var_holes_converted, | |||
| 4549 | base::Optional<TNode<Int32T>> source_elements_kind) { | |||
| 4550 | static_assert( | |||
| 4551 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 4552 | "Only Smi or IntPtrT first, count, and capacity are allowed"); | |||
| 4553 | DCHECK(extract_flags & ExtractFixedArrayFlag::kFixedArrays ||((void) 0) | |||
| 4554 | extract_flags & ExtractFixedArrayFlag::kFixedDoubleArrays)((void) 0); | |||
| 4555 | // If we want to replace holes, ExtractFixedArrayFlag::kDontCopyCOW should | |||
| 4556 | // not be used, because that disables the iteration which detects holes. | |||
| 4557 | DCHECK_IMPLIES(var_holes_converted != nullptr,((void) 0) | |||
| 4558 | !(extract_flags & ExtractFixedArrayFlag::kDontCopyCOW))((void) 0); | |||
| 4559 | HoleConversionMode convert_holes = | |||
| 4560 | var_holes_converted != nullptr ? HoleConversionMode::kConvertToUndefined | |||
| 4561 | : HoleConversionMode::kDontConvert; | |||
| 4562 | TVARIABLE(FixedArrayBase, var_result)TVariable<FixedArrayBase> var_result(this); | |||
| 4563 | auto allocation_flags = AllocationFlag::kAllowLargeObjectAllocation; | |||
| 4564 | if (!first) { | |||
| 4565 | first = IntPtrOrSmiConstant<TIndex>(0); | |||
| 4566 | } | |||
| 4567 | if (!count) { | |||
| 4568 | count = IntPtrOrSmiSub( | |||
| 4569 | TaggedToParameter<TIndex>(LoadFixedArrayBaseLength(source)), *first); | |||
| 4570 | ||||
| 4571 | CSA_DCHECK(this, IntPtrOrSmiLessThanOrEqual(IntPtrOrSmiConstant<TIndex>(0),((void)0) | |||
| 4572 | *count))((void)0); | |||
| 4573 | } | |||
| 4574 | if (!capacity) { | |||
| 4575 | capacity = *count; | |||
| 4576 | } else { | |||
| 4577 | CSA_DCHECK(this, Word32BinaryNot(IntPtrOrSmiGreaterThan(((void)0) | |||
| 4578 | IntPtrOrSmiAdd(*first, *count), *capacity)))((void)0); | |||
| 4579 | } | |||
| 4580 | ||||
| 4581 | Label if_fixed_double_array(this), empty(this), done(this, &var_result); | |||
| 4582 | TNode<Map> source_map = LoadMap(source); | |||
| 4583 | GotoIf(IntPtrOrSmiEqual(IntPtrOrSmiConstant<TIndex>(0), *capacity), &empty); | |||
| 4584 | ||||
| 4585 | if (extract_flags & ExtractFixedArrayFlag::kFixedDoubleArrays) { | |||
| 4586 | if (extract_flags & ExtractFixedArrayFlag::kFixedArrays) { | |||
| 4587 | GotoIf(IsFixedDoubleArrayMap(source_map), &if_fixed_double_array); | |||
| 4588 | } else { | |||
| 4589 | CSA_DCHECK(this, IsFixedDoubleArrayMap(source_map))((void)0); | |||
| 4590 | } | |||
| 4591 | } | |||
| 4592 | ||||
| 4593 | if (extract_flags & ExtractFixedArrayFlag::kFixedArrays) { | |||
| 4594 | // Here we can only get |source| as FixedArray, never FixedDoubleArray. | |||
| 4595 | // PACKED_ELEMENTS is used to signify that the source is a FixedArray. | |||
| 4596 | TNode<FixedArray> to_elements = ExtractToFixedArray( | |||
| 4597 | source, *first, *count, *capacity, source_map, PACKED_ELEMENTS, | |||
| 4598 | allocation_flags, extract_flags, convert_holes, var_holes_converted, | |||
| 4599 | source_elements_kind); | |||
| 4600 | var_result = to_elements; | |||
| 4601 | Goto(&done); | |||
| 4602 | } | |||
| 4603 | ||||
| 4604 | if (extract_flags & ExtractFixedArrayFlag::kFixedDoubleArrays) { | |||
| 4605 | BIND(&if_fixed_double_array)Bind(&if_fixed_double_array); | |||
| 4606 | Comment("Copy FixedDoubleArray"); | |||
| 4607 | ||||
| 4608 | if (convert_holes == HoleConversionMode::kConvertToUndefined) { | |||
| 4609 | TNode<FixedArrayBase> to_elements = ExtractFixedDoubleArrayFillingHoles( | |||
| 4610 | source, *first, *count, *capacity, source_map, var_holes_converted, | |||
| 4611 | allocation_flags, extract_flags); | |||
| 4612 | var_result = to_elements; | |||
| 4613 | } else { | |||
| 4614 | // We use PACKED_DOUBLE_ELEMENTS to signify that both the source and | |||
| 4615 | // the target are FixedDoubleArray. That it is PACKED or HOLEY does not | |||
| 4616 | // matter. | |||
| 4617 | ElementsKind kind = PACKED_DOUBLE_ELEMENTS; | |||
| 4618 | TNode<FixedArrayBase> to_elements = | |||
| 4619 | AllocateFixedArray(kind, *capacity, allocation_flags, source_map); | |||
| 4620 | FillFixedArrayWithValue(kind, to_elements, *count, *capacity, | |||
| 4621 | RootIndex::kTheHoleValue); | |||
| 4622 | CopyElements(kind, to_elements, IntPtrConstant(0), source, | |||
| 4623 | ParameterToIntPtr(*first), ParameterToIntPtr(*count)); | |||
| 4624 | var_result = to_elements; | |||
| 4625 | } | |||
| 4626 | ||||
| 4627 | Goto(&done); | |||
| 4628 | } | |||
| 4629 | ||||
| 4630 | BIND(&empty)Bind(&empty); | |||
| 4631 | { | |||
| 4632 | Comment("Copy empty array"); | |||
| 4633 | ||||
| 4634 | var_result = EmptyFixedArrayConstant(); | |||
| 4635 | Goto(&done); | |||
| 4636 | } | |||
| 4637 | ||||
| 4638 | BIND(&done)Bind(&done); | |||
| 4639 | return var_result.value(); | |||
| 4640 | } | |||
| 4641 | ||||
| 4642 | template V8_EXPORT_PRIVATE TNode<FixedArrayBase> | |||
| 4643 | CodeStubAssembler::ExtractFixedArray<Smi>( | |||
| 4644 | TNode<FixedArrayBase>, base::Optional<TNode<Smi>>, | |||
| 4645 | base::Optional<TNode<Smi>>, base::Optional<TNode<Smi>>, | |||
| 4646 | ExtractFixedArrayFlags, TVariable<BoolT>*, base::Optional<TNode<Int32T>>); | |||
| 4647 | ||||
| 4648 | template V8_EXPORT_PRIVATE TNode<FixedArrayBase> | |||
| 4649 | CodeStubAssembler::ExtractFixedArray<IntPtrT>( | |||
| 4650 | TNode<FixedArrayBase>, base::Optional<TNode<IntPtrT>>, | |||
| 4651 | base::Optional<TNode<IntPtrT>>, base::Optional<TNode<IntPtrT>>, | |||
| 4652 | ExtractFixedArrayFlags, TVariable<BoolT>*, base::Optional<TNode<Int32T>>); | |||
| 4653 | ||||
| 4654 | void CodeStubAssembler::InitializePropertyArrayLength( | |||
| 4655 | TNode<PropertyArray> property_array, TNode<IntPtrT> length) { | |||
| 4656 | CSA_DCHECK(this, IntPtrGreaterThan(length, IntPtrConstant(0)))((void)0); | |||
| 4657 | CSA_DCHECK(this,((void)0) | |||
| 4658 | IntPtrLessThanOrEqual(((void)0) | |||
| 4659 | length, IntPtrConstant(PropertyArray::LengthField::kMax)))((void)0); | |||
| 4660 | StoreObjectFieldNoWriteBarrier( | |||
| 4661 | property_array, PropertyArray::kLengthAndHashOffset, SmiTag(length)); | |||
| 4662 | } | |||
| 4663 | ||||
| 4664 | TNode<PropertyArray> CodeStubAssembler::AllocatePropertyArray( | |||
| 4665 | TNode<IntPtrT> capacity) { | |||
| 4666 | CSA_DCHECK(this, IntPtrGreaterThan(capacity, IntPtrConstant(0)))((void)0); | |||
| 4667 | TNode<IntPtrT> total_size = GetPropertyArrayAllocationSize(capacity); | |||
| 4668 | ||||
| 4669 | TNode<HeapObject> array = Allocate(total_size, AllocationFlag::kNone); | |||
| 4670 | RootIndex map_index = RootIndex::kPropertyArrayMap; | |||
| 4671 | DCHECK(RootsTable::IsImmortalImmovable(map_index))((void) 0); | |||
| 4672 | StoreMapNoWriteBarrier(array, map_index); | |||
| 4673 | TNode<PropertyArray> property_array = CAST(array)Cast(array); | |||
| 4674 | InitializePropertyArrayLength(property_array, capacity); | |||
| 4675 | return property_array; | |||
| 4676 | } | |||
| 4677 | ||||
| 4678 | void CodeStubAssembler::FillPropertyArrayWithUndefined( | |||
| 4679 | TNode<PropertyArray> array, TNode<IntPtrT> from_index, | |||
| 4680 | TNode<IntPtrT> to_index) { | |||
| 4681 | ElementsKind kind = PACKED_ELEMENTS; | |||
| 4682 | TNode<Oddball> value = UndefinedConstant(); | |||
| 4683 | BuildFastArrayForEach( | |||
| 4684 | array, kind, from_index, to_index, | |||
| 4685 | [this, value](TNode<HeapObject> array, TNode<IntPtrT> offset) { | |||
| 4686 | StoreNoWriteBarrier(MachineRepresentation::kTagged, array, offset, | |||
| 4687 | value); | |||
| 4688 | }); | |||
| 4689 | } | |||
| 4690 | ||||
| 4691 | template <typename TIndex> | |||
| 4692 | void CodeStubAssembler::FillFixedArrayWithValue(ElementsKind kind, | |||
| 4693 | TNode<FixedArrayBase> array, | |||
| 4694 | TNode<TIndex> from_index, | |||
| 4695 | TNode<TIndex> to_index, | |||
| 4696 | RootIndex value_root_index) { | |||
| 4697 | static_assert( | |||
| 4698 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 4699 | "Only Smi or IntPtrT from and to are allowed"); | |||
| 4700 | CSA_SLOW_DCHECK(this, IsFixedArrayWithKind(array, kind))((void)0); | |||
| 4701 | DCHECK(value_root_index == RootIndex::kTheHoleValue ||((void) 0) | |||
| 4702 | value_root_index == RootIndex::kUndefinedValue)((void) 0); | |||
| 4703 | ||||
| 4704 | // Determine the value to initialize the {array} based | |||
| 4705 | // on the {value_root_index} and the elements {kind}. | |||
| 4706 | TNode<Object> value = LoadRoot(value_root_index); | |||
| 4707 | TNode<Float64T> float_value; | |||
| 4708 | if (IsDoubleElementsKind(kind)) { | |||
| 4709 | float_value = LoadHeapNumberValue(CAST(value)Cast(value)); | |||
| 4710 | } | |||
| 4711 | ||||
| 4712 | BuildFastArrayForEach( | |||
| 4713 | array, kind, from_index, to_index, | |||
| 4714 | [this, value, float_value, kind](TNode<HeapObject> array, | |||
| 4715 | TNode<IntPtrT> offset) { | |||
| 4716 | if (IsDoubleElementsKind(kind)) { | |||
| 4717 | StoreNoWriteBarrier(MachineRepresentation::kFloat64, array, offset, | |||
| 4718 | float_value); | |||
| 4719 | } else { | |||
| 4720 | StoreNoWriteBarrier(MachineRepresentation::kTagged, array, offset, | |||
| 4721 | value); | |||
| 4722 | } | |||
| 4723 | }); | |||
| 4724 | } | |||
| 4725 | ||||
| 4726 | template V8_EXPORT_PRIVATE void | |||
| 4727 | CodeStubAssembler::FillFixedArrayWithValue<IntPtrT>(ElementsKind, | |||
| 4728 | TNode<FixedArrayBase>, | |||
| 4729 | TNode<IntPtrT>, | |||
| 4730 | TNode<IntPtrT>, | |||
| 4731 | RootIndex); | |||
| 4732 | template V8_EXPORT_PRIVATE void CodeStubAssembler::FillFixedArrayWithValue<Smi>( | |||
| 4733 | ElementsKind, TNode<FixedArrayBase>, TNode<Smi>, TNode<Smi>, RootIndex); | |||
| 4734 | ||||
| 4735 | void CodeStubAssembler::StoreDoubleHole(TNode<HeapObject> object, | |||
| 4736 | TNode<IntPtrT> offset) { | |||
| 4737 | TNode<UintPtrT> double_hole = | |||
| 4738 | Is64() ? ReinterpretCast<UintPtrT>(Int64Constant(kHoleNanInt64)) | |||
| 4739 | : ReinterpretCast<UintPtrT>(Int32Constant(kHoleNanLower32)); | |||
| 4740 | // TODO(danno): When we have a Float32/Float64 wrapper class that | |||
| 4741 | // preserves double bits during manipulation, remove this code/change | |||
| 4742 | // this to an indexed Float64 store. | |||
| 4743 | if (Is64()) { | |||
| 4744 | StoreNoWriteBarrier(MachineRepresentation::kWord64, object, offset, | |||
| 4745 | double_hole); | |||
| 4746 | } else { | |||
| 4747 | StoreNoWriteBarrier(MachineRepresentation::kWord32, object, offset, | |||
| 4748 | double_hole); | |||
| 4749 | StoreNoWriteBarrier(MachineRepresentation::kWord32, object, | |||
| 4750 | IntPtrAdd(offset, IntPtrConstant(kInt32Size)), | |||
| 4751 | double_hole); | |||
| 4752 | } | |||
| 4753 | } | |||
| 4754 | ||||
| 4755 | void CodeStubAssembler::StoreFixedDoubleArrayHole(TNode<FixedDoubleArray> array, | |||
| 4756 | TNode<IntPtrT> index) { | |||
| 4757 | TNode<IntPtrT> offset = ElementOffsetFromIndex( | |||
| 4758 | index, PACKED_DOUBLE_ELEMENTS, FixedArray::kHeaderSize - kHeapObjectTag); | |||
| 4759 | CSA_DCHECK(this, IsOffsetInBounds(((void)0) | |||
| 4760 | offset, LoadAndUntagFixedArrayBaseLength(array),((void)0) | |||
| 4761 | FixedDoubleArray::kHeaderSize, PACKED_DOUBLE_ELEMENTS))((void)0); | |||
| 4762 | StoreDoubleHole(array, offset); | |||
| 4763 | } | |||
| 4764 | ||||
| 4765 | void CodeStubAssembler::FillFixedArrayWithSmiZero(TNode<FixedArray> array, | |||
| 4766 | TNode<IntPtrT> length) { | |||
| 4767 | CSA_DCHECK(this, WordEqual(length, LoadAndUntagFixedArrayBaseLength(array)))((void)0); | |||
| 4768 | ||||
| 4769 | TNode<IntPtrT> byte_length = TimesTaggedSize(length); | |||
| 4770 | CSA_DCHECK(this, UintPtrLessThan(length, byte_length))((void)0); | |||
| 4771 | ||||
| 4772 | static const int32_t fa_base_data_offset = | |||
| 4773 | FixedArray::kHeaderSize - kHeapObjectTag; | |||
| 4774 | TNode<IntPtrT> backing_store = IntPtrAdd(BitcastTaggedToWord(array), | |||
| 4775 | IntPtrConstant(fa_base_data_offset)); | |||
| 4776 | ||||
| 4777 | // Call out to memset to perform initialization. | |||
| 4778 | TNode<ExternalReference> memset = | |||
| 4779 | ExternalConstant(ExternalReference::libc_memset_function()); | |||
| 4780 | STATIC_ASSERT(kSizetSize == kIntptrSize)static_assert(kSizetSize == kIntptrSize, "kSizetSize == kIntptrSize" ); | |||
| 4781 | CallCFunction(memset, MachineType::Pointer(), | |||
| 4782 | std::make_pair(MachineType::Pointer(), backing_store), | |||
| 4783 | std::make_pair(MachineType::IntPtr(), IntPtrConstant(0)), | |||
| 4784 | std::make_pair(MachineType::UintPtr(), byte_length)); | |||
| 4785 | } | |||
| 4786 | ||||
| 4787 | void CodeStubAssembler::FillFixedDoubleArrayWithZero( | |||
| 4788 | TNode<FixedDoubleArray> array, TNode<IntPtrT> length) { | |||
| 4789 | CSA_DCHECK(this, WordEqual(length, LoadAndUntagFixedArrayBaseLength(array)))((void)0); | |||
| 4790 | ||||
| 4791 | TNode<IntPtrT> byte_length = TimesDoubleSize(length); | |||
| 4792 | CSA_DCHECK(this, UintPtrLessThan(length, byte_length))((void)0); | |||
| 4793 | ||||
| 4794 | static const int32_t fa_base_data_offset = | |||
| 4795 | FixedDoubleArray::kHeaderSize - kHeapObjectTag; | |||
| 4796 | TNode<IntPtrT> backing_store = IntPtrAdd(BitcastTaggedToWord(array), | |||
| 4797 | IntPtrConstant(fa_base_data_offset)); | |||
| 4798 | ||||
| 4799 | // Call out to memset to perform initialization. | |||
| 4800 | TNode<ExternalReference> memset = | |||
| 4801 | ExternalConstant(ExternalReference::libc_memset_function()); | |||
| 4802 | STATIC_ASSERT(kSizetSize == kIntptrSize)static_assert(kSizetSize == kIntptrSize, "kSizetSize == kIntptrSize" ); | |||
| 4803 | CallCFunction(memset, MachineType::Pointer(), | |||
| 4804 | std::make_pair(MachineType::Pointer(), backing_store), | |||
| 4805 | std::make_pair(MachineType::IntPtr(), IntPtrConstant(0)), | |||
| 4806 | std::make_pair(MachineType::UintPtr(), byte_length)); | |||
| 4807 | } | |||
| 4808 | ||||
| 4809 | void CodeStubAssembler::JumpIfPointersFromHereAreInteresting( | |||
| 4810 | TNode<Object> object, Label* interesting) { | |||
| 4811 | Label finished(this); | |||
| 4812 | TNode<IntPtrT> object_word = BitcastTaggedToWord(object); | |||
| 4813 | TNode<IntPtrT> object_page = PageFromAddress(object_word); | |||
| 4814 | TNode<IntPtrT> page_flags = UncheckedCast<IntPtrT>(Load( | |||
| 4815 | MachineType::IntPtr(), object_page, IntPtrConstant(Page::kFlagsOffset))); | |||
| 4816 | Branch( | |||
| 4817 | WordEqual(WordAnd(page_flags, | |||
| 4818 | IntPtrConstant( | |||
| 4819 | MemoryChunk::kPointersFromHereAreInterestingMask)), | |||
| 4820 | IntPtrConstant(0)), | |||
| 4821 | &finished, interesting); | |||
| 4822 | BIND(&finished)Bind(&finished); | |||
| 4823 | } | |||
| 4824 | ||||
| 4825 | void CodeStubAssembler::MoveElements(ElementsKind kind, | |||
| 4826 | TNode<FixedArrayBase> elements, | |||
| 4827 | TNode<IntPtrT> dst_index, | |||
| 4828 | TNode<IntPtrT> src_index, | |||
| 4829 | TNode<IntPtrT> length) { | |||
| 4830 | Label finished(this); | |||
| 4831 | Label needs_barrier(this); | |||
| 4832 | #ifdef V8_DISABLE_WRITE_BARRIERS | |||
| 4833 | const bool needs_barrier_check = false; | |||
| 4834 | #else | |||
| 4835 | const bool needs_barrier_check = !IsDoubleElementsKind(kind); | |||
| 4836 | #endif // V8_DISABLE_WRITE_BARRIERS | |||
| 4837 | ||||
| 4838 | DCHECK(IsFastElementsKind(kind))((void) 0); | |||
| 4839 | CSA_DCHECK(this, IsFixedArrayWithKind(elements, kind))((void)0); | |||
| 4840 | CSA_DCHECK(this,((void)0) | |||
| 4841 | IntPtrLessThanOrEqual(IntPtrAdd(dst_index, length),((void)0) | |||
| 4842 | LoadAndUntagFixedArrayBaseLength(elements)))((void)0); | |||
| 4843 | CSA_DCHECK(this,((void)0) | |||
| 4844 | IntPtrLessThanOrEqual(IntPtrAdd(src_index, length),((void)0) | |||
| 4845 | LoadAndUntagFixedArrayBaseLength(elements)))((void)0); | |||
| 4846 | ||||
| 4847 | // The write barrier can be ignored if {dst_elements} is in new space, or if | |||
| 4848 | // the elements pointer is FixedDoubleArray. | |||
| 4849 | if (needs_barrier_check) { | |||
| 4850 | JumpIfPointersFromHereAreInteresting(elements, &needs_barrier); | |||
| 4851 | } | |||
| 4852 | ||||
| 4853 | const TNode<IntPtrT> source_byte_length = | |||
| 4854 | IntPtrMul(length, IntPtrConstant(ElementsKindToByteSize(kind))); | |||
| 4855 | static const int32_t fa_base_data_offset = | |||
| 4856 | FixedArrayBase::kHeaderSize - kHeapObjectTag; | |||
| 4857 | TNode<IntPtrT> elements_intptr = BitcastTaggedToWord(elements); | |||
| 4858 | TNode<IntPtrT> target_data_ptr = | |||
| 4859 | IntPtrAdd(elements_intptr, | |||
| 4860 | ElementOffsetFromIndex(dst_index, kind, fa_base_data_offset)); | |||
| 4861 | TNode<IntPtrT> source_data_ptr = | |||
| 4862 | IntPtrAdd(elements_intptr, | |||
| 4863 | ElementOffsetFromIndex(src_index, kind, fa_base_data_offset)); | |||
| 4864 | TNode<ExternalReference> memmove = | |||
| 4865 | ExternalConstant(ExternalReference::libc_memmove_function()); | |||
| 4866 | CallCFunction(memmove, MachineType::Pointer(), | |||
| 4867 | std::make_pair(MachineType::Pointer(), target_data_ptr), | |||
| 4868 | std::make_pair(MachineType::Pointer(), source_data_ptr), | |||
| 4869 | std::make_pair(MachineType::UintPtr(), source_byte_length)); | |||
| 4870 | ||||
| 4871 | if (needs_barrier_check) { | |||
| 4872 | Goto(&finished); | |||
| 4873 | ||||
| 4874 | BIND(&needs_barrier)Bind(&needs_barrier); | |||
| 4875 | { | |||
| 4876 | const TNode<IntPtrT> begin = src_index; | |||
| 4877 | const TNode<IntPtrT> end = IntPtrAdd(begin, length); | |||
| 4878 | ||||
| 4879 | // If dst_index is less than src_index, then walk forward. | |||
| 4880 | const TNode<IntPtrT> delta = | |||
| 4881 | IntPtrMul(IntPtrSub(dst_index, begin), | |||
| 4882 | IntPtrConstant(ElementsKindToByteSize(kind))); | |||
| 4883 | auto loop_body = [&](TNode<HeapObject> array, TNode<IntPtrT> offset) { | |||
| 4884 | const TNode<AnyTaggedT> element = Load<AnyTaggedT>(array, offset); | |||
| 4885 | const TNode<WordT> delta_offset = IntPtrAdd(offset, delta); | |||
| 4886 | Store(array, delta_offset, element); | |||
| 4887 | }; | |||
| 4888 | ||||
| 4889 | Label iterate_forward(this); | |||
| 4890 | Label iterate_backward(this); | |||
| 4891 | Branch(IntPtrLessThan(delta, IntPtrConstant(0)), &iterate_forward, | |||
| 4892 | &iterate_backward); | |||
| 4893 | BIND(&iterate_forward)Bind(&iterate_forward); | |||
| 4894 | { | |||
| 4895 | // Make a loop for the stores. | |||
| 4896 | BuildFastArrayForEach(elements, kind, begin, end, loop_body, | |||
| 4897 | ForEachDirection::kForward); | |||
| 4898 | Goto(&finished); | |||
| 4899 | } | |||
| 4900 | ||||
| 4901 | BIND(&iterate_backward)Bind(&iterate_backward); | |||
| 4902 | { | |||
| 4903 | BuildFastArrayForEach(elements, kind, begin, end, loop_body, | |||
| 4904 | ForEachDirection::kReverse); | |||
| 4905 | Goto(&finished); | |||
| 4906 | } | |||
| 4907 | } | |||
| 4908 | BIND(&finished)Bind(&finished); | |||
| 4909 | } | |||
| 4910 | } | |||
| 4911 | ||||
| 4912 | void CodeStubAssembler::CopyElements(ElementsKind kind, | |||
| 4913 | TNode<FixedArrayBase> dst_elements, | |||
| 4914 | TNode<IntPtrT> dst_index, | |||
| 4915 | TNode<FixedArrayBase> src_elements, | |||
| 4916 | TNode<IntPtrT> src_index, | |||
| 4917 | TNode<IntPtrT> length, | |||
| 4918 | WriteBarrierMode write_barrier) { | |||
| 4919 | Label finished(this); | |||
| 4920 | Label needs_barrier(this); | |||
| 4921 | #ifdef V8_DISABLE_WRITE_BARRIERS | |||
| 4922 | const bool needs_barrier_check = false; | |||
| 4923 | #else | |||
| 4924 | const bool needs_barrier_check = !IsDoubleElementsKind(kind); | |||
| 4925 | #endif // V8_DISABLE_WRITE_BARRIERS | |||
| 4926 | ||||
| 4927 | DCHECK(IsFastElementsKind(kind))((void) 0); | |||
| 4928 | CSA_DCHECK(this, IsFixedArrayWithKind(dst_elements, kind))((void)0); | |||
| 4929 | CSA_DCHECK(this, IsFixedArrayWithKind(src_elements, kind))((void)0); | |||
| 4930 | CSA_DCHECK(this, IntPtrLessThanOrEqual(((void)0) | |||
| 4931 | IntPtrAdd(dst_index, length),((void)0) | |||
| 4932 | LoadAndUntagFixedArrayBaseLength(dst_elements)))((void)0); | |||
| 4933 | CSA_DCHECK(this, IntPtrLessThanOrEqual(((void)0) | |||
| 4934 | IntPtrAdd(src_index, length),((void)0) | |||
| 4935 | LoadAndUntagFixedArrayBaseLength(src_elements)))((void)0); | |||
| 4936 | CSA_DCHECK(this, Word32Or(TaggedNotEqual(dst_elements, src_elements),((void)0) | |||
| 4937 | IntPtrEqual(length, IntPtrConstant(0))))((void)0); | |||
| 4938 | ||||
| 4939 | // The write barrier can be ignored if {dst_elements} is in new space, or if | |||
| 4940 | // the elements pointer is FixedDoubleArray. | |||
| 4941 | if (needs_barrier_check) { | |||
| 4942 | JumpIfPointersFromHereAreInteresting(dst_elements, &needs_barrier); | |||
| 4943 | } | |||
| 4944 | ||||
| 4945 | TNode<IntPtrT> source_byte_length = | |||
| 4946 | IntPtrMul(length, IntPtrConstant(ElementsKindToByteSize(kind))); | |||
| 4947 | static const int32_t fa_base_data_offset = | |||
| 4948 | FixedArrayBase::kHeaderSize - kHeapObjectTag; | |||
| 4949 | TNode<IntPtrT> src_offset_start = | |||
| 4950 | ElementOffsetFromIndex(src_index, kind, fa_base_data_offset); | |||
| 4951 | TNode<IntPtrT> dst_offset_start = | |||
| 4952 | ElementOffsetFromIndex(dst_index, kind, fa_base_data_offset); | |||
| 4953 | TNode<IntPtrT> src_elements_intptr = BitcastTaggedToWord(src_elements); | |||
| 4954 | TNode<IntPtrT> source_data_ptr = | |||
| 4955 | IntPtrAdd(src_elements_intptr, src_offset_start); | |||
| 4956 | TNode<IntPtrT> dst_elements_intptr = BitcastTaggedToWord(dst_elements); | |||
| 4957 | TNode<IntPtrT> dst_data_ptr = | |||
| 4958 | IntPtrAdd(dst_elements_intptr, dst_offset_start); | |||
| 4959 | TNode<ExternalReference> memcpy = | |||
| 4960 | ExternalConstant(ExternalReference::libc_memcpy_function()); | |||
| 4961 | CallCFunction(memcpy, MachineType::Pointer(), | |||
| 4962 | std::make_pair(MachineType::Pointer(), dst_data_ptr), | |||
| 4963 | std::make_pair(MachineType::Pointer(), source_data_ptr), | |||
| 4964 | std::make_pair(MachineType::UintPtr(), source_byte_length)); | |||
| 4965 | ||||
| 4966 | if (needs_barrier_check) { | |||
| 4967 | Goto(&finished); | |||
| 4968 | ||||
| 4969 | BIND(&needs_barrier)Bind(&needs_barrier); | |||
| 4970 | { | |||
| 4971 | const TNode<IntPtrT> begin = src_index; | |||
| 4972 | const TNode<IntPtrT> end = IntPtrAdd(begin, length); | |||
| 4973 | const TNode<IntPtrT> delta = | |||
| 4974 | IntPtrMul(IntPtrSub(dst_index, src_index), | |||
| 4975 | IntPtrConstant(ElementsKindToByteSize(kind))); | |||
| 4976 | BuildFastArrayForEach( | |||
| 4977 | src_elements, kind, begin, end, | |||
| 4978 | [&](TNode<HeapObject> array, TNode<IntPtrT> offset) { | |||
| 4979 | const TNode<AnyTaggedT> element = Load<AnyTaggedT>(array, offset); | |||
| 4980 | const TNode<WordT> delta_offset = IntPtrAdd(offset, delta); | |||
| 4981 | if (write_barrier == SKIP_WRITE_BARRIER) { | |||
| 4982 | StoreNoWriteBarrier(MachineRepresentation::kTagged, dst_elements, | |||
| 4983 | delta_offset, element); | |||
| 4984 | } else { | |||
| 4985 | Store(dst_elements, delta_offset, element); | |||
| 4986 | } | |||
| 4987 | }, | |||
| 4988 | ForEachDirection::kForward); | |||
| 4989 | Goto(&finished); | |||
| 4990 | } | |||
| 4991 | BIND(&finished)Bind(&finished); | |||
| 4992 | } | |||
| 4993 | } | |||
| 4994 | ||||
| 4995 | template <typename TIndex> | |||
| 4996 | void CodeStubAssembler::CopyFixedArrayElements( | |||
| 4997 | ElementsKind from_kind, TNode<FixedArrayBase> from_array, | |||
| 4998 | ElementsKind to_kind, TNode<FixedArrayBase> to_array, | |||
| 4999 | TNode<TIndex> first_element, TNode<TIndex> element_count, | |||
| 5000 | TNode<TIndex> capacity, WriteBarrierMode barrier_mode, | |||
| 5001 | HoleConversionMode convert_holes, TVariable<BoolT>* var_holes_converted) { | |||
| 5002 | DCHECK_IMPLIES(var_holes_converted != nullptr,((void) 0) | |||
| 5003 | convert_holes == HoleConversionMode::kConvertToUndefined)((void) 0); | |||
| 5004 | CSA_SLOW_DCHECK(this, IsFixedArrayWithKindOrEmpty(from_array, from_kind))((void)0); | |||
| 5005 | CSA_SLOW_DCHECK(this, IsFixedArrayWithKindOrEmpty(to_array, to_kind))((void)0); | |||
| 5006 | STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize)static_assert(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize , "FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize"); | |||
| 5007 | static_assert( | |||
| 5008 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 5009 | "Only Smi or IntPtrT indices are allowed"); | |||
| 5010 | ||||
| 5011 | const int first_element_offset = FixedArray::kHeaderSize - kHeapObjectTag; | |||
| 5012 | Comment("[ CopyFixedArrayElements"); | |||
| 5013 | ||||
| 5014 | // Typed array elements are not supported. | |||
| 5015 | DCHECK(!IsTypedArrayElementsKind(from_kind))((void) 0); | |||
| 5016 | DCHECK(!IsTypedArrayElementsKind(to_kind))((void) 0); | |||
| 5017 | ||||
| 5018 | Label done(this); | |||
| 5019 | bool from_double_elements = IsDoubleElementsKind(from_kind); | |||
| 5020 | bool to_double_elements = IsDoubleElementsKind(to_kind); | |||
| 5021 | bool doubles_to_objects_conversion = | |||
| 5022 | IsDoubleElementsKind(from_kind) && IsObjectElementsKind(to_kind); | |||
| 5023 | bool needs_write_barrier = | |||
| 5024 | doubles_to_objects_conversion || | |||
| 5025 | (barrier_mode == UPDATE_WRITE_BARRIER && IsObjectElementsKind(to_kind)); | |||
| 5026 | bool element_offset_matches = | |||
| 5027 | !needs_write_barrier && | |||
| 5028 | (kTaggedSize == kDoubleSize || | |||
| 5029 | IsDoubleElementsKind(from_kind) == IsDoubleElementsKind(to_kind)); | |||
| 5030 | TNode<UintPtrT> double_hole = | |||
| 5031 | Is64() ? ReinterpretCast<UintPtrT>(Int64Constant(kHoleNanInt64)) | |||
| 5032 | : ReinterpretCast<UintPtrT>(Int32Constant(kHoleNanLower32)); | |||
| 5033 | ||||
| 5034 | // If copying might trigger a GC, we pre-initialize the FixedArray such that | |||
| 5035 | // it's always in a consistent state. | |||
| 5036 | if (convert_holes == HoleConversionMode::kConvertToUndefined) { | |||
| 5037 | DCHECK(IsObjectElementsKind(to_kind))((void) 0); | |||
| 5038 | // Use undefined for the part that we copy and holes for the rest. | |||
| 5039 | // Later if we run into a hole in the source we can just skip the writing | |||
| 5040 | // to the target and are still guaranteed that we get an undefined. | |||
| 5041 | FillFixedArrayWithValue(to_kind, to_array, IntPtrOrSmiConstant<TIndex>(0), | |||
| 5042 | element_count, RootIndex::kUndefinedValue); | |||
| 5043 | FillFixedArrayWithValue(to_kind, to_array, element_count, capacity, | |||
| 5044 | RootIndex::kTheHoleValue); | |||
| 5045 | } else if (doubles_to_objects_conversion) { | |||
| 5046 | // Pre-initialized the target with holes so later if we run into a hole in | |||
| 5047 | // the source we can just skip the writing to the target. | |||
| 5048 | FillFixedArrayWithValue(to_kind, to_array, IntPtrOrSmiConstant<TIndex>(0), | |||
| 5049 | capacity, RootIndex::kTheHoleValue); | |||
| 5050 | } else if (element_count != capacity) { | |||
| 5051 | FillFixedArrayWithValue(to_kind, to_array, element_count, capacity, | |||
| 5052 | RootIndex::kTheHoleValue); | |||
| 5053 | } | |||
| 5054 | ||||
| 5055 | TNode<IntPtrT> first_from_element_offset = | |||
| 5056 | ElementOffsetFromIndex(first_element, from_kind, 0); | |||
| 5057 | TNode<IntPtrT> limit_offset = Signed(IntPtrAdd( | |||
| 5058 | first_from_element_offset, IntPtrConstant(first_element_offset))); | |||
| 5059 | TVARIABLE(IntPtrT, var_from_offset,TVariable<IntPtrT> var_from_offset(ElementOffsetFromIndex (IntPtrOrSmiAdd(first_element, element_count), from_kind, first_element_offset ), this) | |||
| 5060 | ElementOffsetFromIndex(IntPtrOrSmiAdd(first_element, element_count),TVariable<IntPtrT> var_from_offset(ElementOffsetFromIndex (IntPtrOrSmiAdd(first_element, element_count), from_kind, first_element_offset ), this) | |||
| 5061 | from_kind, first_element_offset))TVariable<IntPtrT> var_from_offset(ElementOffsetFromIndex (IntPtrOrSmiAdd(first_element, element_count), from_kind, first_element_offset ), this); | |||
| 5062 | // This second variable is used only when the element sizes of source and | |||
| 5063 | // destination arrays do not match. | |||
| 5064 | TVARIABLE(IntPtrT, var_to_offset)TVariable<IntPtrT> var_to_offset(this); | |||
| 5065 | if (element_offset_matches) { | |||
| 5066 | var_to_offset = var_from_offset.value(); | |||
| 5067 | } else { | |||
| 5068 | var_to_offset = | |||
| 5069 | ElementOffsetFromIndex(element_count, to_kind, first_element_offset); | |||
| 5070 | } | |||
| 5071 | ||||
| 5072 | VariableList vars({&var_from_offset, &var_to_offset}, zone()); | |||
| 5073 | if (var_holes_converted != nullptr) vars.push_back(var_holes_converted); | |||
| 5074 | Label decrement(this, vars); | |||
| 5075 | ||||
| 5076 | TNode<IntPtrT> to_array_adjusted = | |||
| 5077 | element_offset_matches | |||
| 5078 | ? IntPtrSub(BitcastTaggedToWord(to_array), first_from_element_offset) | |||
| 5079 | : ReinterpretCast<IntPtrT>(to_array); | |||
| 5080 | ||||
| 5081 | Branch(WordEqual(var_from_offset.value(), limit_offset), &done, &decrement); | |||
| 5082 | ||||
| 5083 | BIND(&decrement)Bind(&decrement); | |||
| 5084 | { | |||
| 5085 | TNode<IntPtrT> from_offset = Signed(IntPtrSub( | |||
| 5086 | var_from_offset.value(), | |||
| 5087 | IntPtrConstant(from_double_elements ? kDoubleSize : kTaggedSize))); | |||
| 5088 | var_from_offset = from_offset; | |||
| 5089 | ||||
| 5090 | TNode<IntPtrT> to_offset; | |||
| 5091 | if (element_offset_matches) { | |||
| 5092 | to_offset = from_offset; | |||
| 5093 | } else { | |||
| 5094 | to_offset = IntPtrSub( | |||
| 5095 | var_to_offset.value(), | |||
| 5096 | IntPtrConstant(to_double_elements ? kDoubleSize : kTaggedSize)); | |||
| 5097 | var_to_offset = to_offset; | |||
| 5098 | } | |||
| 5099 | ||||
| 5100 | Label next_iter(this), store_double_hole(this), signal_hole(this); | |||
| 5101 | Label* if_hole; | |||
| 5102 | if (convert_holes == HoleConversionMode::kConvertToUndefined) { | |||
| 5103 | // The target elements array is already preinitialized with undefined | |||
| 5104 | // so we only need to signal that a hole was found and continue the loop. | |||
| 5105 | if_hole = &signal_hole; | |||
| 5106 | } else if (doubles_to_objects_conversion) { | |||
| 5107 | // The target elements array is already preinitialized with holes, so we | |||
| 5108 | // can just proceed with the next iteration. | |||
| 5109 | if_hole = &next_iter; | |||
| 5110 | } else if (IsDoubleElementsKind(to_kind)) { | |||
| 5111 | if_hole = &store_double_hole; | |||
| 5112 | } else { | |||
| 5113 | // In all the other cases don't check for holes and copy the data as is. | |||
| 5114 | if_hole = nullptr; | |||
| 5115 | } | |||
| 5116 | ||||
| 5117 | if (to_double_elements) { | |||
| 5118 | DCHECK(!needs_write_barrier)((void) 0); | |||
| 5119 | TNode<Float64T> value = LoadElementAndPrepareForStore<Float64T>( | |||
| 5120 | from_array, var_from_offset.value(), from_kind, to_kind, if_hole); | |||
| 5121 | StoreNoWriteBarrier(MachineRepresentation::kFloat64, to_array_adjusted, | |||
| 5122 | to_offset, value); | |||
| 5123 | } else { | |||
| 5124 | TNode<Object> value = LoadElementAndPrepareForStore<Object>( | |||
| 5125 | from_array, var_from_offset.value(), from_kind, to_kind, if_hole); | |||
| 5126 | if (needs_write_barrier) { | |||
| 5127 | CHECK_EQ(to_array, to_array_adjusted)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(to_array)>::type, typename ::v8::base::pass_value_or_ref<decltype(to_array_adjusted) >::type>((to_array), (to_array_adjusted)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "to_array" " " "==" " " "to_array_adjusted"); } } while (false); } while (false); | |||
| 5128 | Store(to_array_adjusted, to_offset, value); | |||
| 5129 | } else { | |||
| 5130 | UnsafeStoreNoWriteBarrier(MachineRepresentation::kTagged, | |||
| 5131 | to_array_adjusted, to_offset, value); | |||
| 5132 | } | |||
| 5133 | } | |||
| 5134 | ||||
| 5135 | Goto(&next_iter); | |||
| 5136 | ||||
| 5137 | if (if_hole == &store_double_hole) { | |||
| 5138 | BIND(&store_double_hole)Bind(&store_double_hole); | |||
| 5139 | // Don't use doubles to store the hole double, since manipulating the | |||
| 5140 | // signaling NaN used for the hole in C++, e.g. with bit_cast, will | |||
| 5141 | // change its value on ia32 (the x87 stack is used to return values | |||
| 5142 | // and stores to the stack silently clear the signalling bit). | |||
| 5143 | // | |||
| 5144 | // TODO(danno): When we have a Float32/Float64 wrapper class that | |||
| 5145 | // preserves double bits during manipulation, remove this code/change | |||
| 5146 | // this to an indexed Float64 store. | |||
| 5147 | if (Is64()) { | |||
| 5148 | StoreNoWriteBarrier(MachineRepresentation::kWord64, to_array_adjusted, | |||
| 5149 | to_offset, double_hole); | |||
| 5150 | } else { | |||
| 5151 | StoreNoWriteBarrier(MachineRepresentation::kWord32, to_array_adjusted, | |||
| 5152 | to_offset, double_hole); | |||
| 5153 | StoreNoWriteBarrier(MachineRepresentation::kWord32, to_array_adjusted, | |||
| 5154 | IntPtrAdd(to_offset, IntPtrConstant(kInt32Size)), | |||
| 5155 | double_hole); | |||
| 5156 | } | |||
| 5157 | Goto(&next_iter); | |||
| 5158 | } else if (if_hole == &signal_hole) { | |||
| 5159 | // This case happens only when IsObjectElementsKind(to_kind). | |||
| 5160 | BIND(&signal_hole)Bind(&signal_hole); | |||
| 5161 | if (var_holes_converted != nullptr) { | |||
| 5162 | *var_holes_converted = Int32TrueConstant(); | |||
| 5163 | } | |||
| 5164 | Goto(&next_iter); | |||
| 5165 | } | |||
| 5166 | ||||
| 5167 | BIND(&next_iter)Bind(&next_iter); | |||
| 5168 | TNode<BoolT> compare = WordNotEqual(from_offset, limit_offset); | |||
| 5169 | Branch(compare, &decrement, &done); | |||
| 5170 | } | |||
| 5171 | ||||
| 5172 | BIND(&done)Bind(&done); | |||
| 5173 | Comment("] CopyFixedArrayElements"); | |||
| 5174 | } | |||
| 5175 | ||||
| 5176 | TNode<FixedArray> CodeStubAssembler::HeapObjectToFixedArray( | |||
| 5177 | TNode<HeapObject> base, Label* cast_fail) { | |||
| 5178 | Label fixed_array(this); | |||
| 5179 | TNode<Map> map = LoadMap(base); | |||
| 5180 | GotoIf(TaggedEqual(map, FixedArrayMapConstant()), &fixed_array); | |||
| 5181 | GotoIf(TaggedNotEqual(map, FixedCOWArrayMapConstant()), cast_fail); | |||
| 5182 | Goto(&fixed_array); | |||
| 5183 | BIND(&fixed_array)Bind(&fixed_array); | |||
| 5184 | return UncheckedCast<FixedArray>(base); | |||
| 5185 | } | |||
| 5186 | ||||
| 5187 | void CodeStubAssembler::CopyPropertyArrayValues(TNode<HeapObject> from_array, | |||
| 5188 | TNode<PropertyArray> to_array, | |||
| 5189 | TNode<IntPtrT> property_count, | |||
| 5190 | WriteBarrierMode barrier_mode, | |||
| 5191 | DestroySource destroy_source) { | |||
| 5192 | CSA_SLOW_DCHECK(this, Word32Or(IsPropertyArray(from_array),((void)0) | |||
| 5193 | IsEmptyFixedArray(from_array)))((void)0); | |||
| 5194 | Comment("[ CopyPropertyArrayValues"); | |||
| 5195 | ||||
| 5196 | bool needs_write_barrier = barrier_mode == UPDATE_WRITE_BARRIER; | |||
| 5197 | ||||
| 5198 | if (destroy_source == DestroySource::kNo) { | |||
| 5199 | // PropertyArray may contain mutable HeapNumbers, which will be cloned on | |||
| 5200 | // the heap, requiring a write barrier. | |||
| 5201 | needs_write_barrier = true; | |||
| 5202 | } | |||
| 5203 | ||||
| 5204 | TNode<IntPtrT> start = IntPtrConstant(0); | |||
| 5205 | ElementsKind kind = PACKED_ELEMENTS; | |||
| 5206 | BuildFastArrayForEach( | |||
| 5207 | from_array, kind, start, property_count, | |||
| 5208 | [this, to_array, needs_write_barrier, destroy_source]( | |||
| 5209 | TNode<HeapObject> array, TNode<IntPtrT> offset) { | |||
| 5210 | TNode<AnyTaggedT> value = Load<AnyTaggedT>(array, offset); | |||
| 5211 | ||||
| 5212 | if (destroy_source == DestroySource::kNo) { | |||
| 5213 | value = CloneIfMutablePrimitive(CAST(value)Cast(value)); | |||
| 5214 | } | |||
| 5215 | ||||
| 5216 | if (needs_write_barrier) { | |||
| 5217 | Store(to_array, offset, value); | |||
| 5218 | } else { | |||
| 5219 | StoreNoWriteBarrier(MachineRepresentation::kTagged, to_array, offset, | |||
| 5220 | value); | |||
| 5221 | } | |||
| 5222 | }); | |||
| 5223 | ||||
| 5224 | #ifdef DEBUG | |||
| 5225 | // Zap {from_array} if the copying above has made it invalid. | |||
| 5226 | if (destroy_source == DestroySource::kYes) { | |||
| 5227 | Label did_zap(this); | |||
| 5228 | GotoIf(IsEmptyFixedArray(from_array), &did_zap); | |||
| 5229 | FillPropertyArrayWithUndefined(CAST(from_array)Cast(from_array), start, property_count); | |||
| 5230 | ||||
| 5231 | Goto(&did_zap); | |||
| 5232 | BIND(&did_zap)Bind(&did_zap); | |||
| 5233 | } | |||
| 5234 | #endif | |||
| 5235 | Comment("] CopyPropertyArrayValues"); | |||
| 5236 | } | |||
| 5237 | ||||
| 5238 | TNode<FixedArrayBase> CodeStubAssembler::CloneFixedArray( | |||
| 5239 | TNode<FixedArrayBase> source, ExtractFixedArrayFlags flags) { | |||
| 5240 | return ExtractFixedArray( | |||
| 5241 | source, base::Optional<TNode<BInt>>(IntPtrOrSmiConstant<BInt>(0)), | |||
| 5242 | base::Optional<TNode<BInt>>(base::nullopt), | |||
| 5243 | base::Optional<TNode<BInt>>(base::nullopt), flags); | |||
| 5244 | } | |||
| 5245 | ||||
| 5246 | template <> | |||
| 5247 | TNode<Object> CodeStubAssembler::LoadElementAndPrepareForStore( | |||
| 5248 | TNode<FixedArrayBase> array, TNode<IntPtrT> offset, ElementsKind from_kind, | |||
| 5249 | ElementsKind to_kind, Label* if_hole) { | |||
| 5250 | CSA_DCHECK(this, IsFixedArrayWithKind(array, from_kind))((void)0); | |||
| 5251 | DCHECK(!IsDoubleElementsKind(to_kind))((void) 0); | |||
| 5252 | if (IsDoubleElementsKind(from_kind)) { | |||
| 5253 | TNode<Float64T> value = | |||
| 5254 | LoadDoubleWithHoleCheck(array, offset, if_hole, MachineType::Float64()); | |||
| 5255 | return AllocateHeapNumberWithValue(value); | |||
| 5256 | } else { | |||
| 5257 | TNode<Object> value = Load<Object>(array, offset); | |||
| 5258 | if (if_hole) { | |||
| 5259 | GotoIf(TaggedEqual(value, TheHoleConstant()), if_hole); | |||
| 5260 | } | |||
| 5261 | return value; | |||
| 5262 | } | |||
| 5263 | } | |||
| 5264 | ||||
| 5265 | template <> | |||
| 5266 | TNode<Float64T> CodeStubAssembler::LoadElementAndPrepareForStore( | |||
| 5267 | TNode<FixedArrayBase> array, TNode<IntPtrT> offset, ElementsKind from_kind, | |||
| 5268 | ElementsKind to_kind, Label* if_hole) { | |||
| 5269 | CSA_DCHECK(this, IsFixedArrayWithKind(array, from_kind))((void)0); | |||
| 5270 | DCHECK(IsDoubleElementsKind(to_kind))((void) 0); | |||
| 5271 | if (IsDoubleElementsKind(from_kind)) { | |||
| 5272 | return LoadDoubleWithHoleCheck(array, offset, if_hole, | |||
| 5273 | MachineType::Float64()); | |||
| 5274 | } else { | |||
| 5275 | TNode<Object> value = Load<Object>(array, offset); | |||
| 5276 | if (if_hole) { | |||
| 5277 | GotoIf(TaggedEqual(value, TheHoleConstant()), if_hole); | |||
| 5278 | } | |||
| 5279 | if (IsSmiElementsKind(from_kind)) { | |||
| 5280 | return SmiToFloat64(CAST(value)Cast(value)); | |||
| 5281 | } | |||
| 5282 | return LoadHeapNumberValue(CAST(value)Cast(value)); | |||
| 5283 | } | |||
| 5284 | } | |||
| 5285 | ||||
| 5286 | template <typename TIndex> | |||
| 5287 | TNode<TIndex> CodeStubAssembler::CalculateNewElementsCapacity( | |||
| 5288 | TNode<TIndex> old_capacity) { | |||
| 5289 | static_assert( | |||
| 5290 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 5291 | "Only Smi or IntPtrT old_capacity is allowed"); | |||
| 5292 | Comment("TryGrowElementsCapacity"); | |||
| 5293 | TNode<TIndex> half_old_capacity = WordOrSmiShr(old_capacity, 1); | |||
| 5294 | TNode<TIndex> new_capacity = IntPtrOrSmiAdd(half_old_capacity, old_capacity); | |||
| 5295 | TNode<TIndex> padding = | |||
| 5296 | IntPtrOrSmiConstant<TIndex>(JSObject::kMinAddedElementsCapacity); | |||
| 5297 | return IntPtrOrSmiAdd(new_capacity, padding); | |||
| 5298 | } | |||
| 5299 | ||||
| 5300 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 5301 | CodeStubAssembler::CalculateNewElementsCapacity<IntPtrT>(TNode<IntPtrT>); | |||
| 5302 | template V8_EXPORT_PRIVATE TNode<Smi> | |||
| 5303 | CodeStubAssembler::CalculateNewElementsCapacity<Smi>(TNode<Smi>); | |||
| 5304 | ||||
| 5305 | TNode<FixedArrayBase> CodeStubAssembler::TryGrowElementsCapacity( | |||
| 5306 | TNode<HeapObject> object, TNode<FixedArrayBase> elements, ElementsKind kind, | |||
| 5307 | TNode<Smi> key, Label* bailout) { | |||
| 5308 | CSA_SLOW_DCHECK(this, IsFixedArrayWithKindOrEmpty(elements, kind))((void)0); | |||
| 5309 | TNode<Smi> capacity = LoadFixedArrayBaseLength(elements); | |||
| 5310 | ||||
| 5311 | return TryGrowElementsCapacity(object, elements, kind, | |||
| 5312 | TaggedToParameter<BInt>(key), | |||
| 5313 | TaggedToParameter<BInt>(capacity), bailout); | |||
| 5314 | } | |||
| 5315 | ||||
| 5316 | template <typename TIndex> | |||
| 5317 | TNode<FixedArrayBase> CodeStubAssembler::TryGrowElementsCapacity( | |||
| 5318 | TNode<HeapObject> object, TNode<FixedArrayBase> elements, ElementsKind kind, | |||
| 5319 | TNode<TIndex> key, TNode<TIndex> capacity, Label* bailout) { | |||
| 5320 | static_assert( | |||
| 5321 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 5322 | "Only Smi or IntPtrT key and capacity nodes are allowed"); | |||
| 5323 | Comment("TryGrowElementsCapacity"); | |||
| 5324 | CSA_SLOW_DCHECK(this, IsFixedArrayWithKindOrEmpty(elements, kind))((void)0); | |||
| 5325 | ||||
| 5326 | // If the gap growth is too big, fall back to the runtime. | |||
| 5327 | TNode<TIndex> max_gap = IntPtrOrSmiConstant<TIndex>(JSObject::kMaxGap); | |||
| 5328 | TNode<TIndex> max_capacity = IntPtrOrSmiAdd(capacity, max_gap); | |||
| 5329 | GotoIf(UintPtrOrSmiGreaterThanOrEqual(key, max_capacity), bailout); | |||
| 5330 | ||||
| 5331 | // Calculate the capacity of the new backing store. | |||
| 5332 | TNode<TIndex> new_capacity = CalculateNewElementsCapacity( | |||
| 5333 | IntPtrOrSmiAdd(key, IntPtrOrSmiConstant<TIndex>(1))); | |||
| 5334 | ||||
| 5335 | return GrowElementsCapacity(object, elements, kind, kind, capacity, | |||
| 5336 | new_capacity, bailout); | |||
| 5337 | } | |||
| 5338 | ||||
| 5339 | template <typename TIndex> | |||
| 5340 | TNode<FixedArrayBase> CodeStubAssembler::GrowElementsCapacity( | |||
| 5341 | TNode<HeapObject> object, TNode<FixedArrayBase> elements, | |||
| 5342 | ElementsKind from_kind, ElementsKind to_kind, TNode<TIndex> capacity, | |||
| 5343 | TNode<TIndex> new_capacity, Label* bailout) { | |||
| 5344 | static_assert( | |||
| 5345 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 5346 | "Only Smi or IntPtrT capacities are allowed"); | |||
| 5347 | Comment("[ GrowElementsCapacity"); | |||
| 5348 | CSA_SLOW_DCHECK(this, IsFixedArrayWithKindOrEmpty(elements, from_kind))((void)0); | |||
| 5349 | ||||
| 5350 | // If size of the allocation for the new capacity doesn't fit in a page | |||
| 5351 | // that we can bump-pointer allocate from, fall back to the runtime. | |||
| 5352 | int max_size = FixedArrayBase::GetMaxLengthForNewSpaceAllocation(to_kind); | |||
| 5353 | GotoIf(UintPtrOrSmiGreaterThanOrEqual(new_capacity, | |||
| 5354 | IntPtrOrSmiConstant<TIndex>(max_size)), | |||
| 5355 | bailout); | |||
| 5356 | ||||
| 5357 | // Allocate the new backing store. | |||
| 5358 | TNode<FixedArrayBase> new_elements = | |||
| 5359 | AllocateFixedArray(to_kind, new_capacity); | |||
| 5360 | ||||
| 5361 | // Copy the elements from the old elements store to the new. | |||
| 5362 | // The size-check above guarantees that the |new_elements| is allocated | |||
| 5363 | // in new space so we can skip the write barrier. | |||
| 5364 | CopyFixedArrayElements(from_kind, elements, to_kind, new_elements, capacity, | |||
| 5365 | new_capacity, SKIP_WRITE_BARRIER); | |||
| 5366 | ||||
| 5367 | StoreObjectField(object, JSObject::kElementsOffset, new_elements); | |||
| 5368 | Comment("] GrowElementsCapacity"); | |||
| 5369 | return new_elements; | |||
| 5370 | } | |||
| 5371 | ||||
| 5372 | template TNode<FixedArrayBase> CodeStubAssembler::GrowElementsCapacity<IntPtrT>( | |||
| 5373 | TNode<HeapObject>, TNode<FixedArrayBase>, ElementsKind, ElementsKind, | |||
| 5374 | TNode<IntPtrT>, TNode<IntPtrT>, compiler::CodeAssemblerLabel*); | |||
| 5375 | ||||
| 5376 | namespace { | |||
| 5377 | ||||
| 5378 | // Helper function for folded memento allocation. | |||
| 5379 | // Memento objects are designed to be put right after the objects they are | |||
| 5380 | // tracking on. So memento allocations have to be folded together with previous | |||
| 5381 | // object allocations. | |||
| 5382 | TNode<HeapObject> InnerAllocateMemento(CodeStubAssembler* csa, | |||
| 5383 | TNode<HeapObject> previous, | |||
| 5384 | TNode<IntPtrT> offset) { | |||
| 5385 | return csa->UncheckedCast<HeapObject>(csa->BitcastWordToTagged( | |||
| 5386 | csa->IntPtrAdd(csa->BitcastTaggedToWord(previous), offset))); | |||
| 5387 | } | |||
| 5388 | ||||
| 5389 | } // namespace | |||
| 5390 | ||||
| 5391 | void CodeStubAssembler::InitializeAllocationMemento( | |||
| 5392 | TNode<HeapObject> base, TNode<IntPtrT> base_allocation_size, | |||
| 5393 | TNode<AllocationSite> allocation_site) { | |||
| 5394 | DCHECK(V8_ALLOCATION_SITE_TRACKING_BOOL)((void) 0); | |||
| 5395 | Comment("[Initialize AllocationMemento"); | |||
| 5396 | TNode<HeapObject> memento = | |||
| 5397 | InnerAllocateMemento(this, base, base_allocation_size); | |||
| 5398 | StoreMapNoWriteBarrier(memento, RootIndex::kAllocationMementoMap); | |||
| 5399 | StoreObjectFieldNoWriteBarrier( | |||
| 5400 | memento, AllocationMemento::kAllocationSiteOffset, allocation_site); | |||
| 5401 | if (FLAG_allocation_site_pretenuring) { | |||
| 5402 | TNode<Int32T> count = LoadObjectField<Int32T>( | |||
| 5403 | allocation_site, AllocationSite::kPretenureCreateCountOffset); | |||
| 5404 | ||||
| 5405 | TNode<Int32T> incremented_count = Int32Add(count, Int32Constant(1)); | |||
| 5406 | StoreObjectFieldNoWriteBarrier(allocation_site, | |||
| 5407 | AllocationSite::kPretenureCreateCountOffset, | |||
| 5408 | incremented_count); | |||
| 5409 | } | |||
| 5410 | Comment("]"); | |||
| 5411 | } | |||
| 5412 | ||||
| 5413 | TNode<IntPtrT> CodeStubAssembler::TryTaggedToInt32AsIntPtr( | |||
| 5414 | TNode<Object> acc, Label* if_not_possible) { | |||
| 5415 | TVARIABLE(IntPtrT, acc_intptr)TVariable<IntPtrT> acc_intptr(this); | |||
| 5416 | Label is_not_smi(this), have_int32(this); | |||
| 5417 | ||||
| 5418 | GotoIfNot(TaggedIsSmi(acc), &is_not_smi); | |||
| 5419 | acc_intptr = SmiUntag(CAST(acc)Cast(acc)); | |||
| 5420 | Goto(&have_int32); | |||
| 5421 | ||||
| 5422 | BIND(&is_not_smi)Bind(&is_not_smi); | |||
| 5423 | GotoIfNot(IsHeapNumber(CAST(acc)Cast(acc)), if_not_possible); | |||
| 5424 | TNode<Float64T> value = LoadHeapNumberValue(CAST(acc)Cast(acc)); | |||
| 5425 | TNode<Int32T> value32 = RoundFloat64ToInt32(value); | |||
| 5426 | TNode<Float64T> value64 = ChangeInt32ToFloat64(value32); | |||
| 5427 | GotoIfNot(Float64Equal(value, value64), if_not_possible); | |||
| 5428 | acc_intptr = ChangeInt32ToIntPtr(value32); | |||
| 5429 | Goto(&have_int32); | |||
| 5430 | ||||
| 5431 | BIND(&have_int32)Bind(&have_int32); | |||
| 5432 | return acc_intptr.value(); | |||
| 5433 | } | |||
| 5434 | ||||
| 5435 | TNode<Float64T> CodeStubAssembler::TryTaggedToFloat64( | |||
| 5436 | TNode<Object> value, Label* if_valueisnotnumber) { | |||
| 5437 | return Select<Float64T>( | |||
| 5438 | TaggedIsSmi(value), [&]() { return SmiToFloat64(CAST(value)Cast(value)); }, | |||
| 5439 | [&]() { | |||
| 5440 | GotoIfNot(IsHeapNumber(CAST(value)Cast(value)), if_valueisnotnumber); | |||
| 5441 | return LoadHeapNumberValue(CAST(value)Cast(value)); | |||
| 5442 | }); | |||
| 5443 | } | |||
| 5444 | ||||
| 5445 | TNode<Float64T> CodeStubAssembler::TruncateTaggedToFloat64( | |||
| 5446 | TNode<Context> context, TNode<Object> value) { | |||
| 5447 | // We might need to loop once due to ToNumber conversion. | |||
| 5448 | TVARIABLE(Object, var_value, value)TVariable<Object> var_value(value, this); | |||
| 5449 | TVARIABLE(Float64T, var_result)TVariable<Float64T> var_result(this); | |||
| 5450 | Label loop(this, &var_value), done_loop(this, &var_result); | |||
| 5451 | Goto(&loop); | |||
| 5452 | BIND(&loop)Bind(&loop); | |||
| 5453 | { | |||
| 5454 | Label if_valueisnotnumber(this, Label::kDeferred); | |||
| 5455 | ||||
| 5456 | // Load the current {value}. | |||
| 5457 | value = var_value.value(); | |||
| 5458 | ||||
| 5459 | // Convert {value} to Float64 if it is a number and convert it to a number | |||
| 5460 | // otherwise. | |||
| 5461 | var_result = TryTaggedToFloat64(value, &if_valueisnotnumber); | |||
| 5462 | Goto(&done_loop); | |||
| 5463 | ||||
| 5464 | BIND(&if_valueisnotnumber)Bind(&if_valueisnotnumber); | |||
| 5465 | { | |||
| 5466 | // Convert the {value} to a Number first. | |||
| 5467 | var_value = CallBuiltin(Builtin::kNonNumberToNumber, context, value); | |||
| 5468 | Goto(&loop); | |||
| 5469 | } | |||
| 5470 | } | |||
| 5471 | BIND(&done_loop)Bind(&done_loop); | |||
| 5472 | return var_result.value(); | |||
| 5473 | } | |||
| 5474 | ||||
| 5475 | TNode<Word32T> CodeStubAssembler::TruncateTaggedToWord32(TNode<Context> context, | |||
| 5476 | TNode<Object> value) { | |||
| 5477 | TVARIABLE(Word32T, var_result)TVariable<Word32T> var_result(this); | |||
| 5478 | Label done(this); | |||
| 5479 | TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumber>( | |||
| 5480 | context, value, &done, &var_result, IsKnownTaggedPointer::kNo); | |||
| 5481 | BIND(&done)Bind(&done); | |||
| 5482 | return var_result.value(); | |||
| 5483 | } | |||
| 5484 | ||||
| 5485 | // Truncate {value} to word32 and jump to {if_number} if it is a Number, | |||
| 5486 | // or find that it is a BigInt and jump to {if_bigint}. | |||
| 5487 | void CodeStubAssembler::TaggedToWord32OrBigInt( | |||
| 5488 | TNode<Context> context, TNode<Object> value, Label* if_number, | |||
| 5489 | TVariable<Word32T>* var_word32, Label* if_bigint, | |||
| 5490 | TVariable<BigInt>* var_maybe_bigint) { | |||
| 5491 | TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumeric>( | |||
| 5492 | context, value, if_number, var_word32, IsKnownTaggedPointer::kNo, | |||
| 5493 | if_bigint, var_maybe_bigint); | |||
| 5494 | } | |||
| 5495 | ||||
| 5496 | // Truncate {value} to word32 and jump to {if_number} if it is a Number, | |||
| 5497 | // or find that it is a BigInt and jump to {if_bigint}. In either case, | |||
| 5498 | // store the type feedback in {var_feedback}. | |||
| 5499 | void CodeStubAssembler::TaggedToWord32OrBigIntWithFeedback( | |||
| 5500 | TNode<Context> context, TNode<Object> value, Label* if_number, | |||
| 5501 | TVariable<Word32T>* var_word32, Label* if_bigint, | |||
| 5502 | TVariable<BigInt>* var_maybe_bigint, TVariable<Smi>* var_feedback) { | |||
| 5503 | TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumeric>( | |||
| 5504 | context, value, if_number, var_word32, IsKnownTaggedPointer::kNo, | |||
| 5505 | if_bigint, var_maybe_bigint, var_feedback); | |||
| 5506 | } | |||
| 5507 | ||||
| 5508 | // Truncate {pointer} to word32 and jump to {if_number} if it is a Number, | |||
| 5509 | // or find that it is a BigInt and jump to {if_bigint}. In either case, | |||
| 5510 | // store the type feedback in {var_feedback}. | |||
| 5511 | void CodeStubAssembler::TaggedPointerToWord32OrBigIntWithFeedback( | |||
| 5512 | TNode<Context> context, TNode<HeapObject> pointer, Label* if_number, | |||
| 5513 | TVariable<Word32T>* var_word32, Label* if_bigint, | |||
| 5514 | TVariable<BigInt>* var_maybe_bigint, TVariable<Smi>* var_feedback) { | |||
| 5515 | TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumeric>( | |||
| 5516 | context, pointer, if_number, var_word32, IsKnownTaggedPointer::kYes, | |||
| 5517 | if_bigint, var_maybe_bigint, var_feedback); | |||
| 5518 | } | |||
| 5519 | ||||
| 5520 | template <Object::Conversion conversion> | |||
| 5521 | void CodeStubAssembler::TaggedToWord32OrBigIntImpl( | |||
| 5522 | TNode<Context> context, TNode<Object> value, Label* if_number, | |||
| 5523 | TVariable<Word32T>* var_word32, | |||
| 5524 | IsKnownTaggedPointer is_known_tagged_pointer, Label* if_bigint, | |||
| 5525 | TVariable<BigInt>* var_maybe_bigint, TVariable<Smi>* var_feedback) { | |||
| 5526 | // We might need to loop after conversion. | |||
| 5527 | TVARIABLE(Object, var_value, value)TVariable<Object> var_value(value, this); | |||
| 5528 | OverwriteFeedback(var_feedback, BinaryOperationFeedback::kNone); | |||
| 5529 | VariableList loop_vars({&var_value}, zone()); | |||
| 5530 | if (var_feedback != nullptr) loop_vars.push_back(var_feedback); | |||
| 5531 | Label loop(this, loop_vars); | |||
| 5532 | if (is_known_tagged_pointer == IsKnownTaggedPointer::kNo) { | |||
| 5533 | GotoIf(TaggedIsNotSmi(value), &loop); | |||
| 5534 | ||||
| 5535 | // {value} is a Smi. | |||
| 5536 | *var_word32 = SmiToInt32(CAST(value)Cast(value)); | |||
| 5537 | CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall); | |||
| 5538 | Goto(if_number); | |||
| 5539 | } else { | |||
| 5540 | Goto(&loop); | |||
| 5541 | } | |||
| 5542 | BIND(&loop)Bind(&loop); | |||
| 5543 | { | |||
| 5544 | value = var_value.value(); | |||
| 5545 | Label not_smi(this), is_heap_number(this), is_oddball(this), | |||
| 5546 | is_bigint(this), check_if_smi(this); | |||
| 5547 | ||||
| 5548 | TNode<HeapObject> value_heap_object = CAST(value)Cast(value); | |||
| 5549 | TNode<Map> map = LoadMap(value_heap_object); | |||
| 5550 | GotoIf(IsHeapNumberMap(map), &is_heap_number); | |||
| 5551 | TNode<Uint16T> instance_type = LoadMapInstanceType(map); | |||
| 5552 | if (conversion == Object::Conversion::kToNumeric) { | |||
| 5553 | GotoIf(IsBigIntInstanceType(instance_type), &is_bigint); | |||
| 5554 | } | |||
| 5555 | ||||
| 5556 | // Not HeapNumber (or BigInt if conversion == kToNumeric). | |||
| 5557 | { | |||
| 5558 | if (var_feedback != nullptr) { | |||
| 5559 | // We do not require an Or with earlier feedback here because once we | |||
| 5560 | // convert the value to a Numeric, we cannot reach this path. We can | |||
| 5561 | // only reach this path on the first pass when the feedback is kNone. | |||
| 5562 | CSA_DCHECK(this, SmiEqual(var_feedback->value(),((void)0) | |||
| 5563 | SmiConstant(BinaryOperationFeedback::kNone)))((void)0); | |||
| 5564 | } | |||
| 5565 | GotoIf(InstanceTypeEqual(instance_type, ODDBALL_TYPE), &is_oddball); | |||
| 5566 | // Not an oddball either -> convert. | |||
| 5567 | auto builtin = conversion == Object::Conversion::kToNumeric | |||
| 5568 | ? Builtin::kNonNumberToNumeric | |||
| 5569 | : Builtin::kNonNumberToNumber; | |||
| 5570 | var_value = CallBuiltin(builtin, context, value); | |||
| 5571 | OverwriteFeedback(var_feedback, BinaryOperationFeedback::kAny); | |||
| 5572 | Goto(&check_if_smi); | |||
| 5573 | ||||
| 5574 | BIND(&is_oddball)Bind(&is_oddball); | |||
| 5575 | var_value = LoadObjectField(value_heap_object, Oddball::kToNumberOffset); | |||
| 5576 | OverwriteFeedback(var_feedback, | |||
| 5577 | BinaryOperationFeedback::kNumberOrOddball); | |||
| 5578 | Goto(&check_if_smi); | |||
| 5579 | } | |||
| 5580 | ||||
| 5581 | BIND(&is_heap_number)Bind(&is_heap_number); | |||
| 5582 | *var_word32 = TruncateHeapNumberValueToWord32(CAST(value)Cast(value)); | |||
| 5583 | CombineFeedback(var_feedback, BinaryOperationFeedback::kNumber); | |||
| 5584 | Goto(if_number); | |||
| 5585 | ||||
| 5586 | if (conversion == Object::Conversion::kToNumeric) { | |||
| 5587 | BIND(&is_bigint)Bind(&is_bigint); | |||
| 5588 | *var_maybe_bigint = CAST(value)Cast(value); | |||
| 5589 | CombineFeedback(var_feedback, BinaryOperationFeedback::kBigInt); | |||
| 5590 | Goto(if_bigint); | |||
| 5591 | } | |||
| 5592 | ||||
| 5593 | BIND(&check_if_smi)Bind(&check_if_smi); | |||
| 5594 | value = var_value.value(); | |||
| 5595 | GotoIf(TaggedIsNotSmi(value), &loop); | |||
| 5596 | ||||
| 5597 | // {value} is a Smi. | |||
| 5598 | *var_word32 = SmiToInt32(CAST(value)Cast(value)); | |||
| 5599 | CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall); | |||
| 5600 | Goto(if_number); | |||
| 5601 | } | |||
| 5602 | } | |||
| 5603 | ||||
| 5604 | TNode<Int32T> CodeStubAssembler::TruncateNumberToWord32(TNode<Number> number) { | |||
| 5605 | TVARIABLE(Int32T, var_result)TVariable<Int32T> var_result(this); | |||
| 5606 | Label done(this), if_heapnumber(this); | |||
| 5607 | GotoIfNot(TaggedIsSmi(number), &if_heapnumber); | |||
| 5608 | var_result = SmiToInt32(CAST(number)Cast(number)); | |||
| 5609 | Goto(&done); | |||
| 5610 | ||||
| 5611 | BIND(&if_heapnumber)Bind(&if_heapnumber); | |||
| 5612 | TNode<Float64T> value = LoadHeapNumberValue(CAST(number)Cast(number)); | |||
| 5613 | var_result = Signed(TruncateFloat64ToWord32(value)); | |||
| 5614 | Goto(&done); | |||
| 5615 | ||||
| 5616 | BIND(&done)Bind(&done); | |||
| 5617 | return var_result.value(); | |||
| 5618 | } | |||
| 5619 | ||||
| 5620 | TNode<Int32T> CodeStubAssembler::TruncateHeapNumberValueToWord32( | |||
| 5621 | TNode<HeapNumber> object) { | |||
| 5622 | TNode<Float64T> value = LoadHeapNumberValue(object); | |||
| 5623 | return Signed(TruncateFloat64ToWord32(value)); | |||
| 5624 | } | |||
| 5625 | ||||
| 5626 | void CodeStubAssembler::TryHeapNumberToSmi(TNode<HeapNumber> number, | |||
| 5627 | TVariable<Smi>* var_result_smi, | |||
| 5628 | Label* if_smi) { | |||
| 5629 | TNode<Float64T> value = LoadHeapNumberValue(number); | |||
| 5630 | TryFloat64ToSmi(value, var_result_smi, if_smi); | |||
| 5631 | } | |||
| 5632 | ||||
| 5633 | void CodeStubAssembler::TryFloat32ToSmi(TNode<Float32T> value, | |||
| 5634 | TVariable<Smi>* var_result_smi, | |||
| 5635 | Label* if_smi) { | |||
| 5636 | TNode<Int32T> ivalue = TruncateFloat32ToInt32(value); | |||
| 5637 | TNode<Float32T> fvalue = RoundInt32ToFloat32(ivalue); | |||
| 5638 | ||||
| 5639 | Label if_int32(this), if_heap_number(this); | |||
| 5640 | ||||
| 5641 | GotoIfNot(Float32Equal(value, fvalue), &if_heap_number); | |||
| 5642 | GotoIfNot(Word32Equal(ivalue, Int32Constant(0)), &if_int32); | |||
| 5643 | Branch(Int32LessThan(UncheckedCast<Int32T>(BitcastFloat32ToInt32(value)), | |||
| 5644 | Int32Constant(0)), | |||
| 5645 | &if_heap_number, &if_int32); | |||
| 5646 | ||||
| 5647 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 5648 | BIND(&if_int32)Bind(&if_int32); | |||
| 5649 | { | |||
| 5650 | if (SmiValuesAre32Bits()) { | |||
| 5651 | *var_result_smi = SmiTag(ChangeInt32ToIntPtr(ivalue)); | |||
| 5652 | } else { | |||
| 5653 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 5654 | TNode<PairT<Int32T, BoolT>> pair = Int32AddWithOverflow(ivalue, ivalue); | |||
| 5655 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 5656 | GotoIf(overflow, &if_heap_number); | |||
| 5657 | *var_result_smi = | |||
| 5658 | BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(Projection<0>(pair))); | |||
| 5659 | } | |||
| 5660 | Goto(if_smi); | |||
| 5661 | } | |||
| 5662 | BIND(&if_heap_number)Bind(&if_heap_number); | |||
| 5663 | } | |||
| 5664 | ||||
| 5665 | void CodeStubAssembler::TryFloat64ToSmi(TNode<Float64T> value, | |||
| 5666 | TVariable<Smi>* var_result_smi, | |||
| 5667 | Label* if_smi) { | |||
| 5668 | TNode<Int32T> value32 = RoundFloat64ToInt32(value); | |||
| 5669 | TNode<Float64T> value64 = ChangeInt32ToFloat64(value32); | |||
| 5670 | ||||
| 5671 | Label if_int32(this), if_heap_number(this, Label::kDeferred); | |||
| 5672 | ||||
| 5673 | GotoIfNot(Float64Equal(value, value64), &if_heap_number); | |||
| 5674 | GotoIfNot(Word32Equal(value32, Int32Constant(0)), &if_int32); | |||
| 5675 | Branch(Int32LessThan(UncheckedCast<Int32T>(Float64ExtractHighWord32(value)), | |||
| 5676 | Int32Constant(0)), | |||
| 5677 | &if_heap_number, &if_int32); | |||
| 5678 | ||||
| 5679 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 5680 | BIND(&if_int32)Bind(&if_int32); | |||
| 5681 | { | |||
| 5682 | if (SmiValuesAre32Bits()) { | |||
| 5683 | *var_result_smi = SmiTag(ChangeInt32ToIntPtr(value32)); | |||
| 5684 | } else { | |||
| 5685 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 5686 | TNode<PairT<Int32T, BoolT>> pair = Int32AddWithOverflow(value32, value32); | |||
| 5687 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 5688 | GotoIf(overflow, &if_heap_number); | |||
| 5689 | *var_result_smi = | |||
| 5690 | BitcastWordToTaggedSigned(ChangeInt32ToIntPtr(Projection<0>(pair))); | |||
| 5691 | } | |||
| 5692 | Goto(if_smi); | |||
| 5693 | } | |||
| 5694 | BIND(&if_heap_number)Bind(&if_heap_number); | |||
| 5695 | } | |||
| 5696 | ||||
| 5697 | TNode<Number> CodeStubAssembler::ChangeFloat32ToTagged(TNode<Float32T> value) { | |||
| 5698 | Label if_smi(this), done(this); | |||
| 5699 | TVARIABLE(Smi, var_smi_result)TVariable<Smi> var_smi_result(this); | |||
| 5700 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 5701 | TryFloat32ToSmi(value, &var_smi_result, &if_smi); | |||
| 5702 | ||||
| 5703 | var_result = AllocateHeapNumberWithValue(ChangeFloat32ToFloat64(value)); | |||
| 5704 | Goto(&done); | |||
| 5705 | ||||
| 5706 | BIND(&if_smi)Bind(&if_smi); | |||
| 5707 | { | |||
| 5708 | var_result = var_smi_result.value(); | |||
| 5709 | Goto(&done); | |||
| 5710 | } | |||
| 5711 | BIND(&done)Bind(&done); | |||
| 5712 | return var_result.value(); | |||
| 5713 | } | |||
| 5714 | ||||
| 5715 | TNode<Number> CodeStubAssembler::ChangeFloat64ToTagged(TNode<Float64T> value) { | |||
| 5716 | Label if_smi(this), done(this); | |||
| 5717 | TVARIABLE(Smi, var_smi_result)TVariable<Smi> var_smi_result(this); | |||
| 5718 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 5719 | TryFloat64ToSmi(value, &var_smi_result, &if_smi); | |||
| 5720 | ||||
| 5721 | var_result = AllocateHeapNumberWithValue(value); | |||
| 5722 | Goto(&done); | |||
| 5723 | ||||
| 5724 | BIND(&if_smi)Bind(&if_smi); | |||
| 5725 | { | |||
| 5726 | var_result = var_smi_result.value(); | |||
| 5727 | Goto(&done); | |||
| 5728 | } | |||
| 5729 | BIND(&done)Bind(&done); | |||
| 5730 | return var_result.value(); | |||
| 5731 | } | |||
| 5732 | ||||
| 5733 | TNode<Number> CodeStubAssembler::ChangeInt32ToTagged(TNode<Int32T> value) { | |||
| 5734 | if (SmiValuesAre32Bits()) { | |||
| 5735 | return SmiTag(ChangeInt32ToIntPtr(value)); | |||
| 5736 | } | |||
| 5737 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 5738 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 5739 | TNode<PairT<Int32T, BoolT>> pair = Int32AddWithOverflow(value, value); | |||
| 5740 | TNode<BoolT> overflow = Projection<1>(pair); | |||
| 5741 | Label if_overflow(this, Label::kDeferred), if_notoverflow(this), | |||
| 5742 | if_join(this); | |||
| 5743 | Branch(overflow, &if_overflow, &if_notoverflow); | |||
| 5744 | BIND(&if_overflow)Bind(&if_overflow); | |||
| 5745 | { | |||
| 5746 | TNode<Float64T> value64 = ChangeInt32ToFloat64(value); | |||
| 5747 | TNode<HeapNumber> result = AllocateHeapNumberWithValue(value64); | |||
| 5748 | var_result = result; | |||
| 5749 | Goto(&if_join); | |||
| 5750 | } | |||
| 5751 | BIND(&if_notoverflow)Bind(&if_notoverflow); | |||
| 5752 | { | |||
| 5753 | TNode<IntPtrT> almost_tagged_value = | |||
| 5754 | ChangeInt32ToIntPtr(Projection<0>(pair)); | |||
| 5755 | TNode<Smi> result = BitcastWordToTaggedSigned(almost_tagged_value); | |||
| 5756 | var_result = result; | |||
| 5757 | Goto(&if_join); | |||
| 5758 | } | |||
| 5759 | BIND(&if_join)Bind(&if_join); | |||
| 5760 | return var_result.value(); | |||
| 5761 | } | |||
| 5762 | ||||
| 5763 | TNode<Number> CodeStubAssembler::ChangeInt32ToTaggedNoOverflow( | |||
| 5764 | TNode<Int32T> value) { | |||
| 5765 | if (SmiValuesAre32Bits()) { | |||
| 5766 | return SmiTag(ChangeInt32ToIntPtr(value)); | |||
| 5767 | } | |||
| 5768 | DCHECK(SmiValuesAre31Bits())((void) 0); | |||
| 5769 | TNode<Int32T> result_int32 = Int32Add(value, value); | |||
| 5770 | TNode<IntPtrT> almost_tagged_value = ChangeInt32ToIntPtr(result_int32); | |||
| 5771 | TNode<Smi> result = BitcastWordToTaggedSigned(almost_tagged_value); | |||
| 5772 | return result; | |||
| 5773 | } | |||
| 5774 | ||||
| 5775 | TNode<Number> CodeStubAssembler::ChangeUint32ToTagged(TNode<Uint32T> value) { | |||
| 5776 | Label if_overflow(this, Label::kDeferred), if_not_overflow(this), | |||
| 5777 | if_join(this); | |||
| 5778 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 5779 | // If {value} > 2^31 - 1, we need to store it in a HeapNumber. | |||
| 5780 | Branch(Uint32LessThan(Uint32Constant(Smi::kMaxValue), value), &if_overflow, | |||
| 5781 | &if_not_overflow); | |||
| 5782 | ||||
| 5783 | BIND(&if_not_overflow)Bind(&if_not_overflow); | |||
| 5784 | { | |||
| 5785 | // The {value} is definitely in valid Smi range. | |||
| 5786 | var_result = SmiTag(Signed(ChangeUint32ToWord(value))); | |||
| 5787 | } | |||
| 5788 | Goto(&if_join); | |||
| 5789 | ||||
| 5790 | BIND(&if_overflow)Bind(&if_overflow); | |||
| 5791 | { | |||
| 5792 | TNode<Float64T> float64_value = ChangeUint32ToFloat64(value); | |||
| 5793 | var_result = AllocateHeapNumberWithValue(float64_value); | |||
| 5794 | } | |||
| 5795 | Goto(&if_join); | |||
| 5796 | ||||
| 5797 | BIND(&if_join)Bind(&if_join); | |||
| 5798 | return var_result.value(); | |||
| 5799 | } | |||
| 5800 | ||||
| 5801 | TNode<Number> CodeStubAssembler::ChangeUintPtrToTagged(TNode<UintPtrT> value) { | |||
| 5802 | Label if_overflow(this, Label::kDeferred), if_not_overflow(this), | |||
| 5803 | if_join(this); | |||
| 5804 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 5805 | // If {value} > 2^31 - 1, we need to store it in a HeapNumber. | |||
| 5806 | Branch(UintPtrLessThan(UintPtrConstant(Smi::kMaxValue), value), &if_overflow, | |||
| 5807 | &if_not_overflow); | |||
| 5808 | ||||
| 5809 | BIND(&if_not_overflow)Bind(&if_not_overflow); | |||
| 5810 | { | |||
| 5811 | // The {value} is definitely in valid Smi range. | |||
| 5812 | var_result = SmiTag(Signed(value)); | |||
| 5813 | } | |||
| 5814 | Goto(&if_join); | |||
| 5815 | ||||
| 5816 | BIND(&if_overflow)Bind(&if_overflow); | |||
| 5817 | { | |||
| 5818 | TNode<Float64T> float64_value = ChangeUintPtrToFloat64(value); | |||
| 5819 | var_result = AllocateHeapNumberWithValue(float64_value); | |||
| 5820 | } | |||
| 5821 | Goto(&if_join); | |||
| 5822 | ||||
| 5823 | BIND(&if_join)Bind(&if_join); | |||
| 5824 | return var_result.value(); | |||
| 5825 | } | |||
| 5826 | ||||
| 5827 | TNode<Int32T> CodeStubAssembler::ChangeBoolToInt32(TNode<BoolT> b) { | |||
| 5828 | return UncheckedCast<Int32T>(b); | |||
| 5829 | } | |||
| 5830 | ||||
| 5831 | TNode<String> CodeStubAssembler::ToThisString(TNode<Context> context, | |||
| 5832 | TNode<Object> value, | |||
| 5833 | TNode<String> method_name) { | |||
| 5834 | TVARIABLE(Object, var_value, value)TVariable<Object> var_value(value, this); | |||
| 5835 | ||||
| 5836 | // Check if the {value} is a Smi or a HeapObject. | |||
| 5837 | Label if_valueissmi(this, Label::kDeferred), if_valueisnotsmi(this), | |||
| 5838 | if_valueisstring(this); | |||
| 5839 | Branch(TaggedIsSmi(value), &if_valueissmi, &if_valueisnotsmi); | |||
| 5840 | BIND(&if_valueisnotsmi)Bind(&if_valueisnotsmi); | |||
| 5841 | { | |||
| 5842 | // Load the instance type of the {value}. | |||
| 5843 | TNode<Uint16T> value_instance_type = LoadInstanceType(CAST(value)Cast(value)); | |||
| 5844 | ||||
| 5845 | // Check if the {value} is already String. | |||
| 5846 | Label if_valueisnotstring(this, Label::kDeferred); | |||
| 5847 | Branch(IsStringInstanceType(value_instance_type), &if_valueisstring, | |||
| 5848 | &if_valueisnotstring); | |||
| 5849 | BIND(&if_valueisnotstring)Bind(&if_valueisnotstring); | |||
| 5850 | { | |||
| 5851 | // Check if the {value} is null. | |||
| 5852 | Label if_valueisnullorundefined(this, Label::kDeferred); | |||
| 5853 | GotoIf(IsNullOrUndefined(value), &if_valueisnullorundefined); | |||
| 5854 | // Convert the {value} to a String. | |||
| 5855 | var_value = CallBuiltin(Builtin::kToString, context, value); | |||
| 5856 | Goto(&if_valueisstring); | |||
| 5857 | ||||
| 5858 | BIND(&if_valueisnullorundefined)Bind(&if_valueisnullorundefined); | |||
| 5859 | { | |||
| 5860 | // The {value} is either null or undefined. | |||
| 5861 | ThrowTypeError(context, MessageTemplate::kCalledOnNullOrUndefined, | |||
| 5862 | method_name); | |||
| 5863 | } | |||
| 5864 | } | |||
| 5865 | } | |||
| 5866 | BIND(&if_valueissmi)Bind(&if_valueissmi); | |||
| 5867 | { | |||
| 5868 | // The {value} is a Smi, convert it to a String. | |||
| 5869 | var_value = CallBuiltin(Builtin::kNumberToString, context, value); | |||
| 5870 | Goto(&if_valueisstring); | |||
| 5871 | } | |||
| 5872 | BIND(&if_valueisstring)Bind(&if_valueisstring); | |||
| 5873 | return CAST(var_value.value())Cast(var_value.value()); | |||
| 5874 | } | |||
| 5875 | ||||
| 5876 | TNode<Uint32T> CodeStubAssembler::ChangeNumberToUint32(TNode<Number> value) { | |||
| 5877 | TVARIABLE(Uint32T, var_result)TVariable<Uint32T> var_result(this); | |||
| 5878 | Label if_smi(this), if_heapnumber(this, Label::kDeferred), done(this); | |||
| 5879 | Branch(TaggedIsSmi(value), &if_smi, &if_heapnumber); | |||
| 5880 | BIND(&if_smi)Bind(&if_smi); | |||
| 5881 | { | |||
| 5882 | var_result = Unsigned(SmiToInt32(CAST(value)Cast(value))); | |||
| 5883 | Goto(&done); | |||
| 5884 | } | |||
| 5885 | BIND(&if_heapnumber)Bind(&if_heapnumber); | |||
| 5886 | { | |||
| 5887 | var_result = ChangeFloat64ToUint32(LoadHeapNumberValue(CAST(value)Cast(value))); | |||
| 5888 | Goto(&done); | |||
| 5889 | } | |||
| 5890 | BIND(&done)Bind(&done); | |||
| 5891 | return var_result.value(); | |||
| 5892 | } | |||
| 5893 | ||||
| 5894 | TNode<Float64T> CodeStubAssembler::ChangeNumberToFloat64(TNode<Number> value) { | |||
| 5895 | TVARIABLE(Float64T, result)TVariable<Float64T> result(this); | |||
| 5896 | Label smi(this); | |||
| 5897 | Label done(this, &result); | |||
| 5898 | GotoIf(TaggedIsSmi(value), &smi); | |||
| 5899 | result = LoadHeapNumberValue(CAST(value)Cast(value)); | |||
| 5900 | Goto(&done); | |||
| 5901 | ||||
| 5902 | BIND(&smi)Bind(&smi); | |||
| 5903 | { | |||
| 5904 | result = SmiToFloat64(CAST(value)Cast(value)); | |||
| 5905 | Goto(&done); | |||
| 5906 | } | |||
| 5907 | ||||
| 5908 | BIND(&done)Bind(&done); | |||
| 5909 | return result.value(); | |||
| 5910 | } | |||
| 5911 | ||||
| 5912 | TNode<Int32T> CodeStubAssembler::ChangeTaggedNonSmiToInt32( | |||
| 5913 | TNode<Context> context, TNode<HeapObject> input) { | |||
| 5914 | return Select<Int32T>( | |||
| 5915 | IsHeapNumber(input), | |||
| 5916 | [=] { | |||
| 5917 | return Signed(TruncateFloat64ToWord32(LoadHeapNumberValue(input))); | |||
| 5918 | }, | |||
| 5919 | [=] { | |||
| 5920 | return TruncateNumberToWord32( | |||
| 5921 | CAST(CallBuiltin(Builtin::kNonNumberToNumber, context, input))Cast(CallBuiltin(Builtin::kNonNumberToNumber, context, input) )); | |||
| 5922 | }); | |||
| 5923 | } | |||
| 5924 | ||||
| 5925 | TNode<Float64T> CodeStubAssembler::ChangeTaggedToFloat64(TNode<Context> context, | |||
| 5926 | TNode<Object> input) { | |||
| 5927 | TVARIABLE(Float64T, var_result)TVariable<Float64T> var_result(this); | |||
| 5928 | Label end(this), not_smi(this); | |||
| 5929 | ||||
| 5930 | GotoIfNot(TaggedIsSmi(input), ¬_smi); | |||
| 5931 | var_result = SmiToFloat64(CAST(input)Cast(input)); | |||
| 5932 | Goto(&end); | |||
| 5933 | ||||
| 5934 | BIND(¬_smi)Bind(¬_smi); | |||
| 5935 | var_result = Select<Float64T>( | |||
| 5936 | IsHeapNumber(CAST(input)Cast(input)), | |||
| 5937 | [=] { return LoadHeapNumberValue(CAST(input)Cast(input)); }, | |||
| 5938 | [=] { | |||
| 5939 | return ChangeNumberToFloat64( | |||
| 5940 | CAST(CallBuiltin(Builtin::kNonNumberToNumber, context, input))Cast(CallBuiltin(Builtin::kNonNumberToNumber, context, input) )); | |||
| 5941 | }); | |||
| 5942 | Goto(&end); | |||
| 5943 | ||||
| 5944 | BIND(&end)Bind(&end); | |||
| 5945 | return var_result.value(); | |||
| 5946 | } | |||
| 5947 | ||||
| 5948 | TNode<WordT> CodeStubAssembler::TimesSystemPointerSize(TNode<WordT> value) { | |||
| 5949 | return WordShl(value, kSystemPointerSizeLog2); | |||
| 5950 | } | |||
| 5951 | ||||
| 5952 | TNode<WordT> CodeStubAssembler::TimesTaggedSize(TNode<WordT> value) { | |||
| 5953 | return WordShl(value, kTaggedSizeLog2); | |||
| 5954 | } | |||
| 5955 | ||||
| 5956 | TNode<WordT> CodeStubAssembler::TimesDoubleSize(TNode<WordT> value) { | |||
| 5957 | return WordShl(value, kDoubleSizeLog2); | |||
| 5958 | } | |||
| 5959 | ||||
| 5960 | TNode<Object> CodeStubAssembler::ToThisValue(TNode<Context> context, | |||
| 5961 | TNode<Object> input_value, | |||
| 5962 | PrimitiveType primitive_type, | |||
| 5963 | char const* method_name) { | |||
| 5964 | // We might need to loop once due to JSPrimitiveWrapper unboxing. | |||
| 5965 | TVARIABLE(Object, var_value, input_value)TVariable<Object> var_value(input_value, this); | |||
| 5966 | Label loop(this, &var_value), done_loop(this), | |||
| 5967 | done_throw(this, Label::kDeferred); | |||
| 5968 | Goto(&loop); | |||
| 5969 | BIND(&loop)Bind(&loop); | |||
| 5970 | { | |||
| 5971 | // Check if the {value} is a Smi or a HeapObject. | |||
| 5972 | GotoIf( | |||
| 5973 | TaggedIsSmi(var_value.value()), | |||
| 5974 | (primitive_type == PrimitiveType::kNumber) ? &done_loop : &done_throw); | |||
| 5975 | ||||
| 5976 | TNode<HeapObject> value = CAST(var_value.value())Cast(var_value.value()); | |||
| 5977 | ||||
| 5978 | // Load the map of the {value}. | |||
| 5979 | TNode<Map> value_map = LoadMap(value); | |||
| 5980 | ||||
| 5981 | // Load the instance type of the {value}. | |||
| 5982 | TNode<Uint16T> value_instance_type = LoadMapInstanceType(value_map); | |||
| 5983 | ||||
| 5984 | // Check if {value} is a JSPrimitiveWrapper. | |||
| 5985 | Label if_valueiswrapper(this, Label::kDeferred), if_valueisnotwrapper(this); | |||
| 5986 | Branch(InstanceTypeEqual(value_instance_type, JS_PRIMITIVE_WRAPPER_TYPE), | |||
| 5987 | &if_valueiswrapper, &if_valueisnotwrapper); | |||
| 5988 | ||||
| 5989 | BIND(&if_valueiswrapper)Bind(&if_valueiswrapper); | |||
| 5990 | { | |||
| 5991 | // Load the actual value from the {value}. | |||
| 5992 | var_value = LoadObjectField(value, JSPrimitiveWrapper::kValueOffset); | |||
| 5993 | Goto(&loop); | |||
| 5994 | } | |||
| 5995 | ||||
| 5996 | BIND(&if_valueisnotwrapper)Bind(&if_valueisnotwrapper); | |||
| 5997 | { | |||
| 5998 | switch (primitive_type) { | |||
| 5999 | case PrimitiveType::kBoolean: | |||
| 6000 | GotoIf(TaggedEqual(value_map, BooleanMapConstant()), &done_loop); | |||
| 6001 | break; | |||
| 6002 | case PrimitiveType::kNumber: | |||
| 6003 | GotoIf(TaggedEqual(value_map, HeapNumberMapConstant()), &done_loop); | |||
| 6004 | break; | |||
| 6005 | case PrimitiveType::kString: | |||
| 6006 | GotoIf(IsStringInstanceType(value_instance_type), &done_loop); | |||
| 6007 | break; | |||
| 6008 | case PrimitiveType::kSymbol: | |||
| 6009 | GotoIf(TaggedEqual(value_map, SymbolMapConstant()), &done_loop); | |||
| 6010 | break; | |||
| 6011 | } | |||
| 6012 | Goto(&done_throw); | |||
| 6013 | } | |||
| 6014 | } | |||
| 6015 | ||||
| 6016 | BIND(&done_throw)Bind(&done_throw); | |||
| 6017 | { | |||
| 6018 | const char* primitive_name = nullptr; | |||
| 6019 | switch (primitive_type) { | |||
| 6020 | case PrimitiveType::kBoolean: | |||
| 6021 | primitive_name = "Boolean"; | |||
| 6022 | break; | |||
| 6023 | case PrimitiveType::kNumber: | |||
| 6024 | primitive_name = "Number"; | |||
| 6025 | break; | |||
| 6026 | case PrimitiveType::kString: | |||
| 6027 | primitive_name = "String"; | |||
| 6028 | break; | |||
| 6029 | case PrimitiveType::kSymbol: | |||
| 6030 | primitive_name = "Symbol"; | |||
| 6031 | break; | |||
| 6032 | } | |||
| 6033 | CHECK_NOT_NULL(primitive_name)do { if ((__builtin_expect(!!(!((primitive_name) != nullptr)) , 0))) { V8_Fatal("Check failed: %s.", "(primitive_name) != nullptr" ); } } while (false); | |||
| 6034 | ||||
| 6035 | // The {value} is not a compatible receiver for this method. | |||
| 6036 | ThrowTypeError(context, MessageTemplate::kNotGeneric, method_name, | |||
| 6037 | primitive_name); | |||
| 6038 | } | |||
| 6039 | ||||
| 6040 | BIND(&done_loop)Bind(&done_loop); | |||
| 6041 | return var_value.value(); | |||
| 6042 | } | |||
| 6043 | ||||
| 6044 | void CodeStubAssembler::ThrowIfNotInstanceType(TNode<Context> context, | |||
| 6045 | TNode<Object> value, | |||
| 6046 | InstanceType instance_type, | |||
| 6047 | char const* method_name) { | |||
| 6048 | Label out(this), throw_exception(this, Label::kDeferred); | |||
| 6049 | ||||
| 6050 | GotoIf(TaggedIsSmi(value), &throw_exception); | |||
| 6051 | ||||
| 6052 | // Load the instance type of the {value}. | |||
| 6053 | TNode<Map> map = LoadMap(CAST(value)Cast(value)); | |||
| 6054 | const TNode<Uint16T> value_instance_type = LoadMapInstanceType(map); | |||
| 6055 | ||||
| 6056 | Branch(Word32Equal(value_instance_type, Int32Constant(instance_type)), &out, | |||
| 6057 | &throw_exception); | |||
| 6058 | ||||
| 6059 | // The {value} is not a compatible receiver for this method. | |||
| 6060 | BIND(&throw_exception)Bind(&throw_exception); | |||
| 6061 | ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver, | |||
| 6062 | StringConstant(method_name), value); | |||
| 6063 | ||||
| 6064 | BIND(&out)Bind(&out); | |||
| 6065 | } | |||
| 6066 | ||||
| 6067 | void CodeStubAssembler::ThrowIfNotJSReceiver(TNode<Context> context, | |||
| 6068 | TNode<Object> value, | |||
| 6069 | MessageTemplate msg_template, | |||
| 6070 | const char* method_name) { | |||
| 6071 | Label done(this), throw_exception(this, Label::kDeferred); | |||
| 6072 | ||||
| 6073 | GotoIf(TaggedIsSmi(value), &throw_exception); | |||
| 6074 | ||||
| 6075 | // Load the instance type of the {value}. | |||
| 6076 | TNode<Map> value_map = LoadMap(CAST(value)Cast(value)); | |||
| 6077 | const TNode<Uint16T> value_instance_type = LoadMapInstanceType(value_map); | |||
| 6078 | ||||
| 6079 | Branch(IsJSReceiverInstanceType(value_instance_type), &done, | |||
| 6080 | &throw_exception); | |||
| 6081 | ||||
| 6082 | // The {value} is not a compatible receiver for this method. | |||
| 6083 | BIND(&throw_exception)Bind(&throw_exception); | |||
| 6084 | ThrowTypeError(context, msg_template, StringConstant(method_name), value); | |||
| 6085 | ||||
| 6086 | BIND(&done)Bind(&done); | |||
| 6087 | } | |||
| 6088 | ||||
| 6089 | void CodeStubAssembler::ThrowIfNotCallable(TNode<Context> context, | |||
| 6090 | TNode<Object> value, | |||
| 6091 | const char* method_name) { | |||
| 6092 | Label out(this), throw_exception(this, Label::kDeferred); | |||
| 6093 | ||||
| 6094 | GotoIf(TaggedIsSmi(value), &throw_exception); | |||
| 6095 | Branch(IsCallable(CAST(value)Cast(value)), &out, &throw_exception); | |||
| 6096 | ||||
| 6097 | // The {value} is not a compatible receiver for this method. | |||
| 6098 | BIND(&throw_exception)Bind(&throw_exception); | |||
| 6099 | ThrowTypeError(context, MessageTemplate::kCalledNonCallable, method_name); | |||
| 6100 | ||||
| 6101 | BIND(&out)Bind(&out); | |||
| 6102 | } | |||
| 6103 | ||||
| 6104 | void CodeStubAssembler::ThrowRangeError(TNode<Context> context, | |||
| 6105 | MessageTemplate message, | |||
| 6106 | base::Optional<TNode<Object>> arg0, | |||
| 6107 | base::Optional<TNode<Object>> arg1, | |||
| 6108 | base::Optional<TNode<Object>> arg2) { | |||
| 6109 | TNode<Smi> template_index = SmiConstant(static_cast<int>(message)); | |||
| 6110 | if (!arg0) { | |||
| 6111 | CallRuntime(Runtime::kThrowRangeError, context, template_index); | |||
| 6112 | } else if (!arg1) { | |||
| 6113 | CallRuntime(Runtime::kThrowRangeError, context, template_index, *arg0); | |||
| 6114 | } else if (!arg2) { | |||
| 6115 | CallRuntime(Runtime::kThrowRangeError, context, template_index, *arg0, | |||
| 6116 | *arg1); | |||
| 6117 | } else { | |||
| 6118 | CallRuntime(Runtime::kThrowRangeError, context, template_index, *arg0, | |||
| 6119 | *arg1, *arg2); | |||
| 6120 | } | |||
| 6121 | Unreachable(); | |||
| 6122 | } | |||
| 6123 | ||||
| 6124 | void CodeStubAssembler::ThrowTypeError(TNode<Context> context, | |||
| 6125 | MessageTemplate message, | |||
| 6126 | char const* arg0, char const* arg1) { | |||
| 6127 | base::Optional<TNode<Object>> arg0_node; | |||
| 6128 | if (arg0) arg0_node = StringConstant(arg0); | |||
| 6129 | base::Optional<TNode<Object>> arg1_node; | |||
| 6130 | if (arg1) arg1_node = StringConstant(arg1); | |||
| 6131 | ThrowTypeError(context, message, arg0_node, arg1_node); | |||
| 6132 | } | |||
| 6133 | ||||
| 6134 | void CodeStubAssembler::ThrowTypeError(TNode<Context> context, | |||
| 6135 | MessageTemplate message, | |||
| 6136 | base::Optional<TNode<Object>> arg0, | |||
| 6137 | base::Optional<TNode<Object>> arg1, | |||
| 6138 | base::Optional<TNode<Object>> arg2) { | |||
| 6139 | TNode<Smi> template_index = SmiConstant(static_cast<int>(message)); | |||
| 6140 | if (!arg0) { | |||
| 6141 | CallRuntime(Runtime::kThrowTypeError, context, template_index); | |||
| 6142 | } else if (!arg1) { | |||
| 6143 | CallRuntime(Runtime::kThrowTypeError, context, template_index, *arg0); | |||
| 6144 | } else if (!arg2) { | |||
| 6145 | CallRuntime(Runtime::kThrowTypeError, context, template_index, *arg0, | |||
| 6146 | *arg1); | |||
| 6147 | } else { | |||
| 6148 | CallRuntime(Runtime::kThrowTypeError, context, template_index, *arg0, *arg1, | |||
| 6149 | *arg2); | |||
| 6150 | } | |||
| 6151 | Unreachable(); | |||
| 6152 | } | |||
| 6153 | ||||
| 6154 | TNode<HeapObject> CodeStubAssembler::GetPendingMessage() { | |||
| 6155 | TNode<ExternalReference> pending_message = ExternalConstant( | |||
| 6156 | ExternalReference::address_of_pending_message(isolate())); | |||
| 6157 | return UncheckedCast<HeapObject>(LoadFullTagged(pending_message)); | |||
| 6158 | } | |||
| 6159 | void CodeStubAssembler::SetPendingMessage(TNode<HeapObject> message) { | |||
| 6160 | CSA_DCHECK(this, Word32Or(IsTheHole(message),((void)0) | |||
| 6161 | InstanceTypeEqual(LoadInstanceType(message),((void)0) | |||
| 6162 | JS_MESSAGE_OBJECT_TYPE)))((void)0); | |||
| 6163 | TNode<ExternalReference> pending_message = ExternalConstant( | |||
| 6164 | ExternalReference::address_of_pending_message(isolate())); | |||
| 6165 | StoreFullTaggedNoWriteBarrier(pending_message, message); | |||
| 6166 | } | |||
| 6167 | ||||
| 6168 | TNode<BoolT> CodeStubAssembler::InstanceTypeEqual(TNode<Int32T> instance_type, | |||
| 6169 | int type) { | |||
| 6170 | return Word32Equal(instance_type, Int32Constant(type)); | |||
| 6171 | } | |||
| 6172 | ||||
| 6173 | TNode<BoolT> CodeStubAssembler::IsDictionaryMap(TNode<Map> map) { | |||
| 6174 | return IsSetWord32<Map::Bits3::IsDictionaryMapBit>(LoadMapBitField3(map)); | |||
| 6175 | } | |||
| 6176 | ||||
| 6177 | TNode<BoolT> CodeStubAssembler::IsExtensibleMap(TNode<Map> map) { | |||
| 6178 | return IsSetWord32<Map::Bits3::IsExtensibleBit>(LoadMapBitField3(map)); | |||
| 6179 | } | |||
| 6180 | ||||
| 6181 | TNode<BoolT> CodeStubAssembler::IsExtensibleNonPrototypeMap(TNode<Map> map) { | |||
| 6182 | int kMask = | |||
| 6183 | Map::Bits3::IsExtensibleBit::kMask | Map::Bits3::IsPrototypeMapBit::kMask; | |||
| 6184 | int kExpected = Map::Bits3::IsExtensibleBit::kMask; | |||
| 6185 | return Word32Equal(Word32And(LoadMapBitField3(map), Int32Constant(kMask)), | |||
| 6186 | Int32Constant(kExpected)); | |||
| 6187 | } | |||
| 6188 | ||||
| 6189 | TNode<BoolT> CodeStubAssembler::IsCallableMap(TNode<Map> map) { | |||
| 6190 | return IsSetWord32<Map::Bits1::IsCallableBit>(LoadMapBitField(map)); | |||
| 6191 | } | |||
| 6192 | ||||
| 6193 | TNode<BoolT> CodeStubAssembler::IsDeprecatedMap(TNode<Map> map) { | |||
| 6194 | return IsSetWord32<Map::Bits3::IsDeprecatedBit>(LoadMapBitField3(map)); | |||
| 6195 | } | |||
| 6196 | ||||
| 6197 | TNode<BoolT> CodeStubAssembler::IsUndetectableMap(TNode<Map> map) { | |||
| 6198 | return IsSetWord32<Map::Bits1::IsUndetectableBit>(LoadMapBitField(map)); | |||
| 6199 | } | |||
| 6200 | ||||
| 6201 | TNode<BoolT> CodeStubAssembler::IsNoElementsProtectorCellInvalid() { | |||
| 6202 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6203 | TNode<PropertyCell> cell = NoElementsProtectorConstant(); | |||
| 6204 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6205 | return TaggedEqual(cell_value, invalid); | |||
| 6206 | } | |||
| 6207 | ||||
| 6208 | TNode<BoolT> CodeStubAssembler::IsMegaDOMProtectorCellInvalid() { | |||
| 6209 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6210 | TNode<PropertyCell> cell = MegaDOMProtectorConstant(); | |||
| 6211 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6212 | return TaggedEqual(cell_value, invalid); | |||
| 6213 | } | |||
| 6214 | ||||
| 6215 | TNode<BoolT> CodeStubAssembler::IsArrayIteratorProtectorCellInvalid() { | |||
| 6216 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6217 | TNode<PropertyCell> cell = ArrayIteratorProtectorConstant(); | |||
| 6218 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6219 | return TaggedEqual(cell_value, invalid); | |||
| 6220 | } | |||
| 6221 | ||||
| 6222 | TNode<BoolT> CodeStubAssembler::IsPromiseResolveProtectorCellInvalid() { | |||
| 6223 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6224 | TNode<PropertyCell> cell = PromiseResolveProtectorConstant(); | |||
| 6225 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6226 | return TaggedEqual(cell_value, invalid); | |||
| 6227 | } | |||
| 6228 | ||||
| 6229 | TNode<BoolT> CodeStubAssembler::IsPromiseThenProtectorCellInvalid() { | |||
| 6230 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6231 | TNode<PropertyCell> cell = PromiseThenProtectorConstant(); | |||
| 6232 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6233 | return TaggedEqual(cell_value, invalid); | |||
| 6234 | } | |||
| 6235 | ||||
| 6236 | TNode<BoolT> CodeStubAssembler::IsArraySpeciesProtectorCellInvalid() { | |||
| 6237 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6238 | TNode<PropertyCell> cell = ArraySpeciesProtectorConstant(); | |||
| 6239 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6240 | return TaggedEqual(cell_value, invalid); | |||
| 6241 | } | |||
| 6242 | ||||
| 6243 | TNode<BoolT> CodeStubAssembler::IsIsConcatSpreadableProtectorCellInvalid() { | |||
| 6244 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6245 | TNode<PropertyCell> cell = IsConcatSpreadableProtectorConstant(); | |||
| 6246 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6247 | return TaggedEqual(cell_value, invalid); | |||
| 6248 | } | |||
| 6249 | ||||
| 6250 | TNode<BoolT> CodeStubAssembler::IsTypedArraySpeciesProtectorCellInvalid() { | |||
| 6251 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6252 | TNode<PropertyCell> cell = TypedArraySpeciesProtectorConstant(); | |||
| 6253 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6254 | return TaggedEqual(cell_value, invalid); | |||
| 6255 | } | |||
| 6256 | ||||
| 6257 | TNode<BoolT> CodeStubAssembler::IsRegExpSpeciesProtectorCellInvalid() { | |||
| 6258 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6259 | TNode<PropertyCell> cell = RegExpSpeciesProtectorConstant(); | |||
| 6260 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6261 | return TaggedEqual(cell_value, invalid); | |||
| 6262 | } | |||
| 6263 | ||||
| 6264 | TNode<BoolT> CodeStubAssembler::IsPromiseSpeciesProtectorCellInvalid() { | |||
| 6265 | TNode<Smi> invalid = SmiConstant(Protectors::kProtectorInvalid); | |||
| 6266 | TNode<PropertyCell> cell = PromiseSpeciesProtectorConstant(); | |||
| 6267 | TNode<Object> cell_value = LoadObjectField(cell, PropertyCell::kValueOffset); | |||
| 6268 | return TaggedEqual(cell_value, invalid); | |||
| 6269 | } | |||
| 6270 | ||||
| 6271 | TNode<BoolT> CodeStubAssembler::IsPrototypeInitialArrayPrototype( | |||
| 6272 | TNode<Context> context, TNode<Map> map) { | |||
| 6273 | const TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 6274 | const TNode<Object> initial_array_prototype = LoadContextElement( | |||
| 6275 | native_context, Context::INITIAL_ARRAY_PROTOTYPE_INDEX); | |||
| 6276 | TNode<HeapObject> proto = LoadMapPrototype(map); | |||
| 6277 | return TaggedEqual(proto, initial_array_prototype); | |||
| 6278 | } | |||
| 6279 | ||||
| 6280 | TNode<BoolT> CodeStubAssembler::IsPrototypeTypedArrayPrototype( | |||
| 6281 | TNode<Context> context, TNode<Map> map) { | |||
| 6282 | const TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 6283 | const TNode<Object> typed_array_prototype = | |||
| 6284 | LoadContextElement(native_context, Context::TYPED_ARRAY_PROTOTYPE_INDEX); | |||
| 6285 | TNode<HeapObject> proto = LoadMapPrototype(map); | |||
| 6286 | TNode<HeapObject> proto_of_proto = Select<HeapObject>( | |||
| 6287 | IsJSObject(proto), [=] { return LoadMapPrototype(LoadMap(proto)); }, | |||
| 6288 | [=] { return NullConstant(); }); | |||
| 6289 | return TaggedEqual(proto_of_proto, typed_array_prototype); | |||
| 6290 | } | |||
| 6291 | ||||
| 6292 | TNode<BoolT> CodeStubAssembler::IsFastAliasedArgumentsMap( | |||
| 6293 | TNode<Context> context, TNode<Map> map) { | |||
| 6294 | const TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 6295 | const TNode<Object> arguments_map = LoadContextElement( | |||
| 6296 | native_context, Context::FAST_ALIASED_ARGUMENTS_MAP_INDEX); | |||
| 6297 | return TaggedEqual(arguments_map, map); | |||
| 6298 | } | |||
| 6299 | ||||
| 6300 | TNode<BoolT> CodeStubAssembler::IsSlowAliasedArgumentsMap( | |||
| 6301 | TNode<Context> context, TNode<Map> map) { | |||
| 6302 | const TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 6303 | const TNode<Object> arguments_map = LoadContextElement( | |||
| 6304 | native_context, Context::SLOW_ALIASED_ARGUMENTS_MAP_INDEX); | |||
| 6305 | return TaggedEqual(arguments_map, map); | |||
| 6306 | } | |||
| 6307 | ||||
| 6308 | TNode<BoolT> CodeStubAssembler::IsSloppyArgumentsMap(TNode<Context> context, | |||
| 6309 | TNode<Map> map) { | |||
| 6310 | const TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 6311 | const TNode<Object> arguments_map = | |||
| 6312 | LoadContextElement(native_context, Context::SLOPPY_ARGUMENTS_MAP_INDEX); | |||
| 6313 | return TaggedEqual(arguments_map, map); | |||
| 6314 | } | |||
| 6315 | ||||
| 6316 | TNode<BoolT> CodeStubAssembler::IsStrictArgumentsMap(TNode<Context> context, | |||
| 6317 | TNode<Map> map) { | |||
| 6318 | const TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 6319 | const TNode<Object> arguments_map = | |||
| 6320 | LoadContextElement(native_context, Context::STRICT_ARGUMENTS_MAP_INDEX); | |||
| 6321 | return TaggedEqual(arguments_map, map); | |||
| 6322 | } | |||
| 6323 | ||||
| 6324 | TNode<BoolT> CodeStubAssembler::TaggedIsCallable(TNode<Object> object) { | |||
| 6325 | return Select<BoolT>( | |||
| 6326 | TaggedIsSmi(object), [=] { return Int32FalseConstant(); }, | |||
| 6327 | [=] { | |||
| 6328 | return IsCallableMap(LoadMap(UncheckedCast<HeapObject>(object))); | |||
| 6329 | }); | |||
| 6330 | } | |||
| 6331 | ||||
| 6332 | TNode<BoolT> CodeStubAssembler::IsCallable(TNode<HeapObject> object) { | |||
| 6333 | return IsCallableMap(LoadMap(object)); | |||
| 6334 | } | |||
| 6335 | ||||
| 6336 | TNode<BoolT> CodeStubAssembler::IsConstructorMap(TNode<Map> map) { | |||
| 6337 | return IsSetWord32<Map::Bits1::IsConstructorBit>(LoadMapBitField(map)); | |||
| 6338 | } | |||
| 6339 | ||||
| 6340 | TNode<BoolT> CodeStubAssembler::IsConstructor(TNode<HeapObject> object) { | |||
| 6341 | return IsConstructorMap(LoadMap(object)); | |||
| 6342 | } | |||
| 6343 | ||||
| 6344 | TNode<BoolT> CodeStubAssembler::IsFunctionWithPrototypeSlotMap(TNode<Map> map) { | |||
| 6345 | return IsSetWord32<Map::Bits1::HasPrototypeSlotBit>(LoadMapBitField(map)); | |||
| 6346 | } | |||
| 6347 | ||||
| 6348 | TNode<BoolT> CodeStubAssembler::IsSpecialReceiverInstanceType( | |||
| 6349 | TNode<Int32T> instance_type) { | |||
| 6350 | STATIC_ASSERT(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE)static_assert(JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE , "JS_GLOBAL_OBJECT_TYPE <= LAST_SPECIAL_RECEIVER_TYPE"); | |||
| 6351 | return Int32LessThanOrEqual(instance_type, | |||
| 6352 | Int32Constant(LAST_SPECIAL_RECEIVER_TYPE)); | |||
| 6353 | } | |||
| 6354 | ||||
| 6355 | TNode<BoolT> CodeStubAssembler::IsCustomElementsReceiverInstanceType( | |||
| 6356 | TNode<Int32T> instance_type) { | |||
| 6357 | return Int32LessThanOrEqual(instance_type, | |||
| 6358 | Int32Constant(LAST_CUSTOM_ELEMENTS_RECEIVER)); | |||
| 6359 | } | |||
| 6360 | ||||
| 6361 | TNode<BoolT> CodeStubAssembler::IsStringInstanceType( | |||
| 6362 | TNode<Int32T> instance_type) { | |||
| 6363 | STATIC_ASSERT(INTERNALIZED_STRING_TYPE == FIRST_TYPE)static_assert(INTERNALIZED_STRING_TYPE == FIRST_TYPE, "INTERNALIZED_STRING_TYPE == FIRST_TYPE" ); | |||
| 6364 | return Int32LessThan(instance_type, Int32Constant(FIRST_NONSTRING_TYPE)); | |||
| 6365 | } | |||
| 6366 | ||||
| 6367 | TNode<BoolT> CodeStubAssembler::IsTemporalInstantInstanceType( | |||
| 6368 | TNode<Int32T> instance_type) { | |||
| 6369 | return InstanceTypeEqual(instance_type, JS_TEMPORAL_INSTANT_TYPE); | |||
| 6370 | } | |||
| 6371 | ||||
| 6372 | TNode<BoolT> CodeStubAssembler::IsOneByteStringInstanceType( | |||
| 6373 | TNode<Int32T> instance_type) { | |||
| 6374 | CSA_DCHECK(this, IsStringInstanceType(instance_type))((void)0); | |||
| 6375 | return Word32Equal( | |||
| 6376 | Word32And(instance_type, Int32Constant(kStringEncodingMask)), | |||
| 6377 | Int32Constant(kOneByteStringTag)); | |||
| 6378 | } | |||
| 6379 | ||||
| 6380 | TNode<BoolT> CodeStubAssembler::IsSequentialStringInstanceType( | |||
| 6381 | TNode<Int32T> instance_type) { | |||
| 6382 | CSA_DCHECK(this, IsStringInstanceType(instance_type))((void)0); | |||
| 6383 | return Word32Equal( | |||
| 6384 | Word32And(instance_type, Int32Constant(kStringRepresentationMask)), | |||
| 6385 | Int32Constant(kSeqStringTag)); | |||
| 6386 | } | |||
| 6387 | ||||
| 6388 | TNode<BoolT> CodeStubAssembler::IsSeqOneByteStringInstanceType( | |||
| 6389 | TNode<Int32T> instance_type) { | |||
| 6390 | CSA_DCHECK(this, IsStringInstanceType(instance_type))((void)0); | |||
| 6391 | return Word32Equal( | |||
| 6392 | Word32And(instance_type, | |||
| 6393 | Int32Constant(kStringRepresentationAndEncodingMask)), | |||
| 6394 | Int32Constant(kSeqOneByteStringTag)); | |||
| 6395 | } | |||
| 6396 | ||||
| 6397 | TNode<BoolT> CodeStubAssembler::IsConsStringInstanceType( | |||
| 6398 | TNode<Int32T> instance_type) { | |||
| 6399 | CSA_DCHECK(this, IsStringInstanceType(instance_type))((void)0); | |||
| 6400 | return Word32Equal( | |||
| 6401 | Word32And(instance_type, Int32Constant(kStringRepresentationMask)), | |||
| 6402 | Int32Constant(kConsStringTag)); | |||
| 6403 | } | |||
| 6404 | ||||
| 6405 | TNode<BoolT> CodeStubAssembler::IsIndirectStringInstanceType( | |||
| 6406 | TNode<Int32T> instance_type) { | |||
| 6407 | CSA_DCHECK(this, IsStringInstanceType(instance_type))((void)0); | |||
| 6408 | STATIC_ASSERT(kIsIndirectStringMask == 0x1)static_assert(kIsIndirectStringMask == 0x1, "kIsIndirectStringMask == 0x1" ); | |||
| 6409 | STATIC_ASSERT(kIsIndirectStringTag == 0x1)static_assert(kIsIndirectStringTag == 0x1, "kIsIndirectStringTag == 0x1" ); | |||
| 6410 | return UncheckedCast<BoolT>( | |||
| 6411 | Word32And(instance_type, Int32Constant(kIsIndirectStringMask))); | |||
| 6412 | } | |||
| 6413 | ||||
| 6414 | TNode<BoolT> CodeStubAssembler::IsExternalStringInstanceType( | |||
| 6415 | TNode<Int32T> instance_type) { | |||
| 6416 | CSA_DCHECK(this, IsStringInstanceType(instance_type))((void)0); | |||
| 6417 | return Word32Equal( | |||
| 6418 | Word32And(instance_type, Int32Constant(kStringRepresentationMask)), | |||
| 6419 | Int32Constant(kExternalStringTag)); | |||
| 6420 | } | |||
| 6421 | ||||
| 6422 | TNode<BoolT> CodeStubAssembler::IsUncachedExternalStringInstanceType( | |||
| 6423 | TNode<Int32T> instance_type) { | |||
| 6424 | CSA_DCHECK(this, IsStringInstanceType(instance_type))((void)0); | |||
| 6425 | STATIC_ASSERT(kUncachedExternalStringTag != 0)static_assert(kUncachedExternalStringTag != 0, "kUncachedExternalStringTag != 0" ); | |||
| 6426 | return IsSetWord32(instance_type, kUncachedExternalStringMask); | |||
| 6427 | } | |||
| 6428 | ||||
| 6429 | TNode<BoolT> CodeStubAssembler::IsJSReceiverInstanceType( | |||
| 6430 | TNode<Int32T> instance_type) { | |||
| 6431 | STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE)static_assert(LAST_JS_RECEIVER_TYPE == LAST_TYPE, "LAST_JS_RECEIVER_TYPE == LAST_TYPE" ); | |||
| 6432 | return Int32GreaterThanOrEqual(instance_type, | |||
| 6433 | Int32Constant(FIRST_JS_RECEIVER_TYPE)); | |||
| 6434 | } | |||
| 6435 | ||||
| 6436 | TNode<BoolT> CodeStubAssembler::IsJSReceiverMap(TNode<Map> map) { | |||
| 6437 | return IsJSReceiverInstanceType(LoadMapInstanceType(map)); | |||
| 6438 | } | |||
| 6439 | ||||
| 6440 | TNode<BoolT> CodeStubAssembler::IsJSReceiver(TNode<HeapObject> object) { | |||
| 6441 | return IsJSReceiverMap(LoadMap(object)); | |||
| 6442 | } | |||
| 6443 | ||||
| 6444 | TNode<BoolT> CodeStubAssembler::IsNullOrJSReceiver(TNode<HeapObject> object) { | |||
| 6445 | return UncheckedCast<BoolT>(Word32Or(IsJSReceiver(object), IsNull(object))); | |||
| 6446 | } | |||
| 6447 | ||||
| 6448 | TNode<BoolT> CodeStubAssembler::IsNullOrUndefined(TNode<Object> value) { | |||
| 6449 | return UncheckedCast<BoolT>(Word32Or(IsUndefined(value), IsNull(value))); | |||
| 6450 | } | |||
| 6451 | ||||
| 6452 | TNode<BoolT> CodeStubAssembler::IsJSGlobalProxyInstanceType( | |||
| 6453 | TNode<Int32T> instance_type) { | |||
| 6454 | return InstanceTypeEqual(instance_type, JS_GLOBAL_PROXY_TYPE); | |||
| 6455 | } | |||
| 6456 | ||||
| 6457 | TNode<BoolT> CodeStubAssembler::IsJSGlobalProxyMap(TNode<Map> map) { | |||
| 6458 | return IsJSGlobalProxyInstanceType(LoadMapInstanceType(map)); | |||
| 6459 | } | |||
| 6460 | ||||
| 6461 | TNode<BoolT> CodeStubAssembler::IsJSGlobalProxy(TNode<HeapObject> object) { | |||
| 6462 | return IsJSGlobalProxyMap(LoadMap(object)); | |||
| 6463 | } | |||
| 6464 | ||||
| 6465 | TNode<BoolT> CodeStubAssembler::IsJSGeneratorMap(TNode<Map> map) { | |||
| 6466 | return InstanceTypeEqual(LoadMapInstanceType(map), JS_GENERATOR_OBJECT_TYPE); | |||
| 6467 | } | |||
| 6468 | ||||
| 6469 | TNode<BoolT> CodeStubAssembler::IsJSObjectInstanceType( | |||
| 6470 | TNode<Int32T> instance_type) { | |||
| 6471 | STATIC_ASSERT(LAST_JS_OBJECT_TYPE == LAST_TYPE)static_assert(LAST_JS_OBJECT_TYPE == LAST_TYPE, "LAST_JS_OBJECT_TYPE == LAST_TYPE" ); | |||
| 6472 | return Int32GreaterThanOrEqual(instance_type, | |||
| 6473 | Int32Constant(FIRST_JS_OBJECT_TYPE)); | |||
| 6474 | } | |||
| 6475 | ||||
| 6476 | TNode<BoolT> CodeStubAssembler::IsJSApiObjectInstanceType( | |||
| 6477 | TNode<Int32T> instance_type) { | |||
| 6478 | return InstanceTypeEqual(instance_type, JS_API_OBJECT_TYPE); | |||
| 6479 | } | |||
| 6480 | ||||
| 6481 | TNode<BoolT> CodeStubAssembler::IsJSObjectMap(TNode<Map> map) { | |||
| 6482 | return IsJSObjectInstanceType(LoadMapInstanceType(map)); | |||
| 6483 | } | |||
| 6484 | ||||
| 6485 | TNode<BoolT> CodeStubAssembler::IsJSApiObjectMap(TNode<Map> map) { | |||
| 6486 | return IsJSApiObjectInstanceType(LoadMapInstanceType(map)); | |||
| 6487 | } | |||
| 6488 | ||||
| 6489 | TNode<BoolT> CodeStubAssembler::IsJSObject(TNode<HeapObject> object) { | |||
| 6490 | return IsJSObjectMap(LoadMap(object)); | |||
| 6491 | } | |||
| 6492 | ||||
| 6493 | TNode<BoolT> CodeStubAssembler::IsJSApiObject(TNode<HeapObject> object) { | |||
| 6494 | return IsJSApiObjectMap(LoadMap(object)); | |||
| 6495 | } | |||
| 6496 | ||||
| 6497 | TNode<BoolT> CodeStubAssembler::IsJSFinalizationRegistryMap(TNode<Map> map) { | |||
| 6498 | return InstanceTypeEqual(LoadMapInstanceType(map), | |||
| 6499 | JS_FINALIZATION_REGISTRY_TYPE); | |||
| 6500 | } | |||
| 6501 | ||||
| 6502 | TNode<BoolT> CodeStubAssembler::IsJSFinalizationRegistry( | |||
| 6503 | TNode<HeapObject> object) { | |||
| 6504 | return IsJSFinalizationRegistryMap(LoadMap(object)); | |||
| 6505 | } | |||
| 6506 | ||||
| 6507 | TNode<BoolT> CodeStubAssembler::IsJSPromiseMap(TNode<Map> map) { | |||
| 6508 | return InstanceTypeEqual(LoadMapInstanceType(map), JS_PROMISE_TYPE); | |||
| 6509 | } | |||
| 6510 | ||||
| 6511 | TNode<BoolT> CodeStubAssembler::IsJSPromise(TNode<HeapObject> object) { | |||
| 6512 | return IsJSPromiseMap(LoadMap(object)); | |||
| 6513 | } | |||
| 6514 | ||||
| 6515 | TNode<BoolT> CodeStubAssembler::IsJSProxy(TNode<HeapObject> object) { | |||
| 6516 | return HasInstanceType(object, JS_PROXY_TYPE); | |||
| 6517 | } | |||
| 6518 | ||||
| 6519 | TNode<BoolT> CodeStubAssembler::IsJSStringIterator(TNode<HeapObject> object) { | |||
| 6520 | return HasInstanceType(object, JS_STRING_ITERATOR_TYPE); | |||
| 6521 | } | |||
| 6522 | ||||
| 6523 | TNode<BoolT> CodeStubAssembler::IsJSRegExpStringIterator( | |||
| 6524 | TNode<HeapObject> object) { | |||
| 6525 | return HasInstanceType(object, JS_REG_EXP_STRING_ITERATOR_TYPE); | |||
| 6526 | } | |||
| 6527 | ||||
| 6528 | TNode<BoolT> CodeStubAssembler::IsMap(TNode<HeapObject> map) { | |||
| 6529 | return IsMetaMap(LoadMap(map)); | |||
| 6530 | } | |||
| 6531 | ||||
| 6532 | TNode<BoolT> CodeStubAssembler::IsJSPrimitiveWrapperInstanceType( | |||
| 6533 | TNode<Int32T> instance_type) { | |||
| 6534 | return InstanceTypeEqual(instance_type, JS_PRIMITIVE_WRAPPER_TYPE); | |||
| 6535 | } | |||
| 6536 | ||||
| 6537 | TNode<BoolT> CodeStubAssembler::IsJSPrimitiveWrapper(TNode<HeapObject> object) { | |||
| 6538 | return IsJSPrimitiveWrapperMap(LoadMap(object)); | |||
| 6539 | } | |||
| 6540 | ||||
| 6541 | TNode<BoolT> CodeStubAssembler::IsJSPrimitiveWrapperMap(TNode<Map> map) { | |||
| 6542 | return IsJSPrimitiveWrapperInstanceType(LoadMapInstanceType(map)); | |||
| 6543 | } | |||
| 6544 | ||||
| 6545 | TNode<BoolT> CodeStubAssembler::IsJSWrappedFunction(TNode<HeapObject> object) { | |||
| 6546 | return HasInstanceType(object, JS_WRAPPED_FUNCTION_TYPE); | |||
| 6547 | } | |||
| 6548 | ||||
| 6549 | TNode<BoolT> CodeStubAssembler::IsJSArrayInstanceType( | |||
| 6550 | TNode<Int32T> instance_type) { | |||
| 6551 | return InstanceTypeEqual(instance_type, JS_ARRAY_TYPE); | |||
| 6552 | } | |||
| 6553 | ||||
| 6554 | TNode<BoolT> CodeStubAssembler::IsJSArray(TNode<HeapObject> object) { | |||
| 6555 | return IsJSArrayMap(LoadMap(object)); | |||
| 6556 | } | |||
| 6557 | ||||
| 6558 | TNode<BoolT> CodeStubAssembler::IsJSArrayMap(TNode<Map> map) { | |||
| 6559 | return IsJSArrayInstanceType(LoadMapInstanceType(map)); | |||
| 6560 | } | |||
| 6561 | ||||
| 6562 | TNode<BoolT> CodeStubAssembler::IsJSArrayIterator(TNode<HeapObject> object) { | |||
| 6563 | return HasInstanceType(object, JS_ARRAY_ITERATOR_TYPE); | |||
| 6564 | } | |||
| 6565 | ||||
| 6566 | TNode<BoolT> CodeStubAssembler::IsJSSharedStructInstanceType( | |||
| 6567 | TNode<Int32T> instance_type) { | |||
| 6568 | return InstanceTypeEqual(instance_type, JS_SHARED_STRUCT_TYPE); | |||
| 6569 | } | |||
| 6570 | ||||
| 6571 | TNode<BoolT> CodeStubAssembler::IsJSSharedStructMap(TNode<Map> map) { | |||
| 6572 | return IsJSSharedStructInstanceType(LoadMapInstanceType(map)); | |||
| 6573 | } | |||
| 6574 | ||||
| 6575 | TNode<BoolT> CodeStubAssembler::IsJSSharedStruct(TNode<HeapObject> object) { | |||
| 6576 | return IsJSSharedStructMap(LoadMap(object)); | |||
| 6577 | } | |||
| 6578 | ||||
| 6579 | TNode<BoolT> CodeStubAssembler::IsJSSharedStruct(TNode<Object> object) { | |||
| 6580 | return Select<BoolT>( | |||
| 6581 | TaggedIsSmi(object), [=] { return Int32FalseConstant(); }, | |||
| 6582 | [=] { | |||
| 6583 | TNode<HeapObject> heap_object = CAST(object)Cast(object); | |||
| 6584 | return IsJSSharedStruct(heap_object); | |||
| 6585 | }); | |||
| 6586 | } | |||
| 6587 | ||||
| 6588 | TNode<BoolT> CodeStubAssembler::IsJSAsyncGeneratorObject( | |||
| 6589 | TNode<HeapObject> object) { | |||
| 6590 | return HasInstanceType(object, JS_ASYNC_GENERATOR_OBJECT_TYPE); | |||
| 6591 | } | |||
| 6592 | ||||
| 6593 | TNode<BoolT> CodeStubAssembler::IsFixedArray(TNode<HeapObject> object) { | |||
| 6594 | return HasInstanceType(object, FIXED_ARRAY_TYPE); | |||
| 6595 | } | |||
| 6596 | ||||
| 6597 | TNode<BoolT> CodeStubAssembler::IsFixedArraySubclass(TNode<HeapObject> object) { | |||
| 6598 | TNode<Uint16T> instance_type = LoadInstanceType(object); | |||
| 6599 | return UncheckedCast<BoolT>( | |||
| 6600 | Word32And(Int32GreaterThanOrEqual(instance_type, | |||
| 6601 | Int32Constant(FIRST_FIXED_ARRAY_TYPE)), | |||
| 6602 | Int32LessThanOrEqual(instance_type, | |||
| 6603 | Int32Constant(LAST_FIXED_ARRAY_TYPE)))); | |||
| 6604 | } | |||
| 6605 | ||||
| 6606 | TNode<BoolT> CodeStubAssembler::IsNotWeakFixedArraySubclass( | |||
| 6607 | TNode<HeapObject> object) { | |||
| 6608 | TNode<Uint16T> instance_type = LoadInstanceType(object); | |||
| 6609 | return UncheckedCast<BoolT>(Word32Or( | |||
| 6610 | Int32LessThan(instance_type, Int32Constant(FIRST_WEAK_FIXED_ARRAY_TYPE)), | |||
| 6611 | Int32GreaterThan(instance_type, | |||
| 6612 | Int32Constant(LAST_WEAK_FIXED_ARRAY_TYPE)))); | |||
| 6613 | } | |||
| 6614 | ||||
| 6615 | TNode<BoolT> CodeStubAssembler::IsPropertyArray(TNode<HeapObject> object) { | |||
| 6616 | return HasInstanceType(object, PROPERTY_ARRAY_TYPE); | |||
| 6617 | } | |||
| 6618 | ||||
| 6619 | TNode<BoolT> CodeStubAssembler::IsPromiseReactionJobTask( | |||
| 6620 | TNode<HeapObject> object) { | |||
| 6621 | TNode<Uint16T> instance_type = LoadInstanceType(object); | |||
| 6622 | return IsInRange(instance_type, FIRST_PROMISE_REACTION_JOB_TASK_TYPE, | |||
| 6623 | LAST_PROMISE_REACTION_JOB_TASK_TYPE); | |||
| 6624 | } | |||
| 6625 | ||||
| 6626 | // This complicated check is due to elements oddities. If a smi array is empty | |||
| 6627 | // after Array.p.shift, it is replaced by the empty array constant. If it is | |||
| 6628 | // later filled with a double element, we try to grow it but pass in a double | |||
| 6629 | // elements kind. Usually this would cause a size mismatch (since the source | |||
| 6630 | // fixed array has HOLEY_ELEMENTS and destination has | |||
| 6631 | // HOLEY_DOUBLE_ELEMENTS), but we don't have to worry about it when the | |||
| 6632 | // source array is empty. | |||
| 6633 | // TODO(jgruber): It might we worth creating an empty_double_array constant to | |||
| 6634 | // simplify this case. | |||
| 6635 | TNode<BoolT> CodeStubAssembler::IsFixedArrayWithKindOrEmpty( | |||
| 6636 | TNode<FixedArrayBase> object, ElementsKind kind) { | |||
| 6637 | Label out(this); | |||
| 6638 | TVARIABLE(BoolT, var_result, Int32TrueConstant())TVariable<BoolT> var_result(Int32TrueConstant(), this); | |||
| 6639 | ||||
| 6640 | GotoIf(IsFixedArrayWithKind(object, kind), &out); | |||
| 6641 | ||||
| 6642 | const TNode<Smi> length = LoadFixedArrayBaseLength(object); | |||
| 6643 | GotoIf(SmiEqual(length, SmiConstant(0)), &out); | |||
| 6644 | ||||
| 6645 | var_result = Int32FalseConstant(); | |||
| 6646 | Goto(&out); | |||
| 6647 | ||||
| 6648 | BIND(&out)Bind(&out); | |||
| 6649 | return var_result.value(); | |||
| 6650 | } | |||
| 6651 | ||||
| 6652 | TNode<BoolT> CodeStubAssembler::IsFixedArrayWithKind(TNode<HeapObject> object, | |||
| 6653 | ElementsKind kind) { | |||
| 6654 | if (IsDoubleElementsKind(kind)) { | |||
| 6655 | return IsFixedDoubleArray(object); | |||
| 6656 | } else { | |||
| 6657 | DCHECK(IsSmiOrObjectElementsKind(kind) || IsSealedElementsKind(kind) ||((void) 0) | |||
| 6658 | IsNonextensibleElementsKind(kind))((void) 0); | |||
| 6659 | return IsFixedArraySubclass(object); | |||
| 6660 | } | |||
| 6661 | } | |||
| 6662 | ||||
| 6663 | TNode<BoolT> CodeStubAssembler::IsBoolean(TNode<HeapObject> object) { | |||
| 6664 | return IsBooleanMap(LoadMap(object)); | |||
| 6665 | } | |||
| 6666 | ||||
| 6667 | TNode<BoolT> CodeStubAssembler::IsPropertyCell(TNode<HeapObject> object) { | |||
| 6668 | return IsPropertyCellMap(LoadMap(object)); | |||
| 6669 | } | |||
| 6670 | ||||
| 6671 | TNode<BoolT> CodeStubAssembler::IsHeapNumberInstanceType( | |||
| 6672 | TNode<Int32T> instance_type) { | |||
| 6673 | return InstanceTypeEqual(instance_type, HEAP_NUMBER_TYPE); | |||
| 6674 | } | |||
| 6675 | ||||
| 6676 | TNode<BoolT> CodeStubAssembler::IsOddball(TNode<HeapObject> object) { | |||
| 6677 | return IsOddballInstanceType(LoadInstanceType(object)); | |||
| 6678 | } | |||
| 6679 | ||||
| 6680 | TNode<BoolT> CodeStubAssembler::IsOddballInstanceType( | |||
| 6681 | TNode<Int32T> instance_type) { | |||
| 6682 | return InstanceTypeEqual(instance_type, ODDBALL_TYPE); | |||
| 6683 | } | |||
| 6684 | ||||
| 6685 | TNode<BoolT> CodeStubAssembler::IsName(TNode<HeapObject> object) { | |||
| 6686 | return IsNameInstanceType(LoadInstanceType(object)); | |||
| 6687 | } | |||
| 6688 | ||||
| 6689 | TNode<BoolT> CodeStubAssembler::IsNameInstanceType( | |||
| 6690 | TNode<Int32T> instance_type) { | |||
| 6691 | return Int32LessThanOrEqual(instance_type, Int32Constant(LAST_NAME_TYPE)); | |||
| 6692 | } | |||
| 6693 | ||||
| 6694 | TNode<BoolT> CodeStubAssembler::IsString(TNode<HeapObject> object) { | |||
| 6695 | return IsStringInstanceType(LoadInstanceType(object)); | |||
| 6696 | } | |||
| 6697 | ||||
| 6698 | TNode<BoolT> CodeStubAssembler::IsSeqOneByteString(TNode<HeapObject> object) { | |||
| 6699 | return IsSeqOneByteStringInstanceType(LoadInstanceType(object)); | |||
| 6700 | } | |||
| 6701 | ||||
| 6702 | TNode<BoolT> CodeStubAssembler::IsSymbolInstanceType( | |||
| 6703 | TNode<Int32T> instance_type) { | |||
| 6704 | return InstanceTypeEqual(instance_type, SYMBOL_TYPE); | |||
| 6705 | } | |||
| 6706 | ||||
| 6707 | TNode<BoolT> CodeStubAssembler::IsInternalizedStringInstanceType( | |||
| 6708 | TNode<Int32T> instance_type) { | |||
| 6709 | STATIC_ASSERT(kNotInternalizedTag != 0)static_assert(kNotInternalizedTag != 0, "kNotInternalizedTag != 0" ); | |||
| 6710 | return Word32Equal( | |||
| 6711 | Word32And(instance_type, | |||
| 6712 | Int32Constant(kIsNotStringMask | kIsNotInternalizedMask)), | |||
| 6713 | Int32Constant(kStringTag | kInternalizedTag)); | |||
| 6714 | } | |||
| 6715 | ||||
| 6716 | TNode<BoolT> CodeStubAssembler::IsSharedStringInstanceType( | |||
| 6717 | TNode<Int32T> instance_type) { | |||
| 6718 | TNode<BoolT> is_shared = Word32Equal( | |||
| 6719 | Word32And(instance_type, | |||
| 6720 | Int32Constant(kIsNotStringMask | kSharedStringMask)), | |||
| 6721 | Int32Constant(kStringTag | kSharedStringTag)); | |||
| 6722 | // TODO(v8:12007): Internalized strings do not have kSharedStringTag until | |||
| 6723 | // the shared string table ships. | |||
| 6724 | return Word32Or(is_shared, | |||
| 6725 | Word32And(HasSharedStringTableFlag(), | |||
| 6726 | IsInternalizedStringInstanceType(instance_type))); | |||
| 6727 | } | |||
| 6728 | ||||
| 6729 | TNode<BoolT> CodeStubAssembler::IsUniqueName(TNode<HeapObject> object) { | |||
| 6730 | TNode<Uint16T> instance_type = LoadInstanceType(object); | |||
| 6731 | return Select<BoolT>( | |||
| 6732 | IsInternalizedStringInstanceType(instance_type), | |||
| 6733 | [=] { return Int32TrueConstant(); }, | |||
| 6734 | [=] { return IsSymbolInstanceType(instance_type); }); | |||
| 6735 | } | |||
| 6736 | ||||
| 6737 | // Semantics: guaranteed not to be an integer index (i.e. contains non-digit | |||
| 6738 | // characters, or is outside MAX_SAFE_INTEGER/size_t range). Note that for | |||
| 6739 | // non-TypedArray receivers, there are additional strings that must be treated | |||
| 6740 | // as named property keys, namely the range [0xFFFFFFFF, MAX_SAFE_INTEGER]. | |||
| 6741 | TNode<BoolT> CodeStubAssembler::IsUniqueNameNoIndex(TNode<HeapObject> object) { | |||
| 6742 | TNode<Uint16T> instance_type = LoadInstanceType(object); | |||
| 6743 | return Select<BoolT>( | |||
| 6744 | IsInternalizedStringInstanceType(instance_type), | |||
| 6745 | [=] { | |||
| 6746 | return IsNotEqualInWord32<Name::HashFieldTypeBits>( | |||
| 6747 | LoadNameRawHashField(CAST(object)Cast(object)), | |||
| 6748 | Name::HashFieldType::kIntegerIndex); | |||
| 6749 | }, | |||
| 6750 | [=] { return IsSymbolInstanceType(instance_type); }); | |||
| 6751 | } | |||
| 6752 | ||||
| 6753 | // Semantics: {object} is a Symbol, or a String that doesn't have a cached | |||
| 6754 | // index. This returns {true} for strings containing representations of | |||
| 6755 | // integers in the range above 9999999 (per kMaxCachedArrayIndexLength) | |||
| 6756 | // and below MAX_SAFE_INTEGER. For CSA_DCHECKs ensuring correct usage, this is | |||
| 6757 | // better than no checking; and we don't have a good/fast way to accurately | |||
| 6758 | // check such strings for being within "array index" (uint32_t) range. | |||
| 6759 | TNode<BoolT> CodeStubAssembler::IsUniqueNameNoCachedIndex( | |||
| 6760 | TNode<HeapObject> object) { | |||
| 6761 | TNode<Uint16T> instance_type = LoadInstanceType(object); | |||
| 6762 | return Select<BoolT>( | |||
| 6763 | IsInternalizedStringInstanceType(instance_type), | |||
| 6764 | [=] { | |||
| 6765 | return IsSetWord32(LoadNameRawHashField(CAST(object)Cast(object)), | |||
| 6766 | Name::kDoesNotContainCachedArrayIndexMask); | |||
| 6767 | }, | |||
| 6768 | [=] { return IsSymbolInstanceType(instance_type); }); | |||
| 6769 | } | |||
| 6770 | ||||
| 6771 | TNode<BoolT> CodeStubAssembler::IsBigIntInstanceType( | |||
| 6772 | TNode<Int32T> instance_type) { | |||
| 6773 | return InstanceTypeEqual(instance_type, BIGINT_TYPE); | |||
| 6774 | } | |||
| 6775 | ||||
| 6776 | TNode<BoolT> CodeStubAssembler::IsBigInt(TNode<HeapObject> object) { | |||
| 6777 | return IsBigIntInstanceType(LoadInstanceType(object)); | |||
| 6778 | } | |||
| 6779 | ||||
| 6780 | TNode<BoolT> CodeStubAssembler::IsPrimitiveInstanceType( | |||
| 6781 | TNode<Int32T> instance_type) { | |||
| 6782 | return Int32LessThanOrEqual(instance_type, | |||
| 6783 | Int32Constant(LAST_PRIMITIVE_HEAP_OBJECT_TYPE)); | |||
| 6784 | } | |||
| 6785 | ||||
| 6786 | TNode<BoolT> CodeStubAssembler::IsPrivateName(TNode<Symbol> symbol) { | |||
| 6787 | TNode<Uint32T> flags = LoadObjectField<Uint32T>(symbol, Symbol::kFlagsOffset); | |||
| 6788 | return IsSetWord32<Symbol::IsPrivateNameBit>(flags); | |||
| 6789 | } | |||
| 6790 | ||||
| 6791 | TNode<BoolT> CodeStubAssembler::IsHashTable(TNode<HeapObject> object) { | |||
| 6792 | TNode<Uint16T> instance_type = LoadInstanceType(object); | |||
| 6793 | return UncheckedCast<BoolT>( | |||
| 6794 | Word32And(Int32GreaterThanOrEqual(instance_type, | |||
| 6795 | Int32Constant(FIRST_HASH_TABLE_TYPE)), | |||
| 6796 | Int32LessThanOrEqual(instance_type, | |||
| 6797 | Int32Constant(LAST_HASH_TABLE_TYPE)))); | |||
| 6798 | } | |||
| 6799 | ||||
| 6800 | TNode<BoolT> CodeStubAssembler::IsEphemeronHashTable(TNode<HeapObject> object) { | |||
| 6801 | return HasInstanceType(object, EPHEMERON_HASH_TABLE_TYPE); | |||
| 6802 | } | |||
| 6803 | ||||
| 6804 | TNode<BoolT> CodeStubAssembler::IsNameDictionary(TNode<HeapObject> object) { | |||
| 6805 | return HasInstanceType(object, NAME_DICTIONARY_TYPE); | |||
| 6806 | } | |||
| 6807 | TNode<BoolT> CodeStubAssembler::IsOrderedNameDictionary( | |||
| 6808 | TNode<HeapObject> object) { | |||
| 6809 | return HasInstanceType(object, ORDERED_NAME_DICTIONARY_TYPE); | |||
| 6810 | } | |||
| 6811 | ||||
| 6812 | TNode<BoolT> CodeStubAssembler::IsSwissNameDictionary( | |||
| 6813 | TNode<HeapObject> object) { | |||
| 6814 | return HasInstanceType(object, SWISS_NAME_DICTIONARY_TYPE); | |||
| 6815 | } | |||
| 6816 | ||||
| 6817 | TNode<BoolT> CodeStubAssembler::IsGlobalDictionary(TNode<HeapObject> object) { | |||
| 6818 | return HasInstanceType(object, GLOBAL_DICTIONARY_TYPE); | |||
| 6819 | } | |||
| 6820 | ||||
| 6821 | TNode<BoolT> CodeStubAssembler::IsNumberDictionary(TNode<HeapObject> object) { | |||
| 6822 | return HasInstanceType(object, NUMBER_DICTIONARY_TYPE); | |||
| 6823 | } | |||
| 6824 | ||||
| 6825 | TNode<BoolT> CodeStubAssembler::IsJSGeneratorObject(TNode<HeapObject> object) { | |||
| 6826 | return HasInstanceType(object, JS_GENERATOR_OBJECT_TYPE); | |||
| 6827 | } | |||
| 6828 | ||||
| 6829 | TNode<BoolT> CodeStubAssembler::IsFunctionInstanceType( | |||
| 6830 | TNode<Int32T> instance_type) { | |||
| 6831 | return IsInRange(instance_type, | |||
| 6832 | FIRST_JS_FUNCTION_OR_BOUND_FUNCTION_OR_WRAPPED_FUNCTION_TYPE, | |||
| 6833 | LAST_JS_FUNCTION_OR_BOUND_FUNCTION_OR_WRAPPED_FUNCTION_TYPE); | |||
| 6834 | } | |||
| 6835 | TNode<BoolT> CodeStubAssembler::IsJSFunctionInstanceType( | |||
| 6836 | TNode<Int32T> instance_type) { | |||
| 6837 | return IsInRange(instance_type, FIRST_JS_FUNCTION_TYPE, | |||
| 6838 | LAST_JS_FUNCTION_TYPE); | |||
| 6839 | } | |||
| 6840 | ||||
| 6841 | TNode<BoolT> CodeStubAssembler::IsJSFunction(TNode<HeapObject> object) { | |||
| 6842 | return IsJSFunctionMap(LoadMap(object)); | |||
| 6843 | } | |||
| 6844 | ||||
| 6845 | TNode<BoolT> CodeStubAssembler::IsJSBoundFunction(TNode<HeapObject> object) { | |||
| 6846 | return HasInstanceType(object, JS_BOUND_FUNCTION_TYPE); | |||
| 6847 | } | |||
| 6848 | ||||
| 6849 | TNode<BoolT> CodeStubAssembler::IsJSFunctionMap(TNode<Map> map) { | |||
| 6850 | return IsJSFunctionInstanceType(LoadMapInstanceType(map)); | |||
| 6851 | } | |||
| 6852 | ||||
| 6853 | TNode<BoolT> CodeStubAssembler::IsJSTypedArrayInstanceType( | |||
| 6854 | TNode<Int32T> instance_type) { | |||
| 6855 | return InstanceTypeEqual(instance_type, JS_TYPED_ARRAY_TYPE); | |||
| 6856 | } | |||
| 6857 | ||||
| 6858 | TNode<BoolT> CodeStubAssembler::IsJSTypedArrayMap(TNode<Map> map) { | |||
| 6859 | return IsJSTypedArrayInstanceType(LoadMapInstanceType(map)); | |||
| 6860 | } | |||
| 6861 | ||||
| 6862 | TNode<BoolT> CodeStubAssembler::IsJSTypedArray(TNode<HeapObject> object) { | |||
| 6863 | return IsJSTypedArrayMap(LoadMap(object)); | |||
| 6864 | } | |||
| 6865 | ||||
| 6866 | TNode<BoolT> CodeStubAssembler::IsJSArrayBuffer(TNode<HeapObject> object) { | |||
| 6867 | return HasInstanceType(object, JS_ARRAY_BUFFER_TYPE); | |||
| 6868 | } | |||
| 6869 | ||||
| 6870 | TNode<BoolT> CodeStubAssembler::IsJSDataView(TNode<HeapObject> object) { | |||
| 6871 | return HasInstanceType(object, JS_DATA_VIEW_TYPE); | |||
| 6872 | } | |||
| 6873 | ||||
| 6874 | TNode<BoolT> CodeStubAssembler::IsJSRegExp(TNode<HeapObject> object) { | |||
| 6875 | return HasInstanceType(object, JS_REG_EXP_TYPE); | |||
| 6876 | } | |||
| 6877 | ||||
| 6878 | TNode<BoolT> CodeStubAssembler::IsNumeric(TNode<Object> object) { | |||
| 6879 | return Select<BoolT>( | |||
| 6880 | TaggedIsSmi(object), [=] { return Int32TrueConstant(); }, | |||
| 6881 | [=] { | |||
| 6882 | return UncheckedCast<BoolT>( | |||
| 6883 | Word32Or(IsHeapNumber(CAST(object)Cast(object)), IsBigInt(CAST(object)Cast(object)))); | |||
| 6884 | }); | |||
| 6885 | } | |||
| 6886 | ||||
| 6887 | TNode<BoolT> CodeStubAssembler::IsNumberNormalized(TNode<Number> number) { | |||
| 6888 | TVARIABLE(BoolT, var_result, Int32TrueConstant())TVariable<BoolT> var_result(Int32TrueConstant(), this); | |||
| 6889 | Label out(this); | |||
| 6890 | ||||
| 6891 | GotoIf(TaggedIsSmi(number), &out); | |||
| 6892 | ||||
| 6893 | TNode<Float64T> value = LoadHeapNumberValue(CAST(number)Cast(number)); | |||
| 6894 | TNode<Float64T> smi_min = | |||
| 6895 | Float64Constant(static_cast<double>(Smi::kMinValue)); | |||
| 6896 | TNode<Float64T> smi_max = | |||
| 6897 | Float64Constant(static_cast<double>(Smi::kMaxValue)); | |||
| 6898 | ||||
| 6899 | GotoIf(Float64LessThan(value, smi_min), &out); | |||
| 6900 | GotoIf(Float64GreaterThan(value, smi_max), &out); | |||
| 6901 | GotoIfNot(Float64Equal(value, value), &out); // NaN. | |||
| 6902 | ||||
| 6903 | var_result = Int32FalseConstant(); | |||
| 6904 | Goto(&out); | |||
| 6905 | ||||
| 6906 | BIND(&out)Bind(&out); | |||
| 6907 | return var_result.value(); | |||
| 6908 | } | |||
| 6909 | ||||
| 6910 | TNode<BoolT> CodeStubAssembler::IsNumberPositive(TNode<Number> number) { | |||
| 6911 | return Select<BoolT>( | |||
| 6912 | TaggedIsSmi(number), [=] { return TaggedIsPositiveSmi(number); }, | |||
| 6913 | [=] { return IsHeapNumberPositive(CAST(number)Cast(number)); }); | |||
| 6914 | } | |||
| 6915 | ||||
| 6916 | // TODO(cbruni): Use TNode<HeapNumber> instead of custom name. | |||
| 6917 | TNode<BoolT> CodeStubAssembler::IsHeapNumberPositive(TNode<HeapNumber> number) { | |||
| 6918 | TNode<Float64T> value = LoadHeapNumberValue(number); | |||
| 6919 | TNode<Float64T> float_zero = Float64Constant(0.); | |||
| 6920 | return Float64GreaterThanOrEqual(value, float_zero); | |||
| 6921 | } | |||
| 6922 | ||||
| 6923 | TNode<BoolT> CodeStubAssembler::IsNumberNonNegativeSafeInteger( | |||
| 6924 | TNode<Number> number) { | |||
| 6925 | return Select<BoolT>( | |||
| 6926 | // TODO(cbruni): Introduce TaggedIsNonNegateSmi to avoid confusion. | |||
| 6927 | TaggedIsSmi(number), [=] { return TaggedIsPositiveSmi(number); }, | |||
| 6928 | [=] { | |||
| 6929 | TNode<HeapNumber> heap_number = CAST(number)Cast(number); | |||
| 6930 | return Select<BoolT>( | |||
| 6931 | IsInteger(heap_number), | |||
| 6932 | [=] { return IsHeapNumberPositive(heap_number); }, | |||
| 6933 | [=] { return Int32FalseConstant(); }); | |||
| 6934 | }); | |||
| 6935 | } | |||
| 6936 | ||||
| 6937 | TNode<BoolT> CodeStubAssembler::IsSafeInteger(TNode<Object> number) { | |||
| 6938 | return Select<BoolT>( | |||
| 6939 | TaggedIsSmi(number), [=] { return Int32TrueConstant(); }, | |||
| 6940 | [=] { | |||
| 6941 | return Select<BoolT>( | |||
| 6942 | IsHeapNumber(CAST(number)Cast(number)), | |||
| 6943 | [=] { return IsSafeInteger(UncheckedCast<HeapNumber>(number)); }, | |||
| 6944 | [=] { return Int32FalseConstant(); }); | |||
| 6945 | }); | |||
| 6946 | } | |||
| 6947 | ||||
| 6948 | TNode<BoolT> CodeStubAssembler::IsSafeInteger(TNode<HeapNumber> number) { | |||
| 6949 | // Load the actual value of {number}. | |||
| 6950 | TNode<Float64T> number_value = LoadHeapNumberValue(number); | |||
| 6951 | // Truncate the value of {number} to an integer (or an infinity). | |||
| 6952 | TNode<Float64T> integer = Float64Trunc(number_value); | |||
| 6953 | ||||
| 6954 | return Select<BoolT>( | |||
| 6955 | // Check if {number}s value matches the integer (ruling out the | |||
| 6956 | // infinities). | |||
| 6957 | Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)), | |||
| 6958 | [=] { | |||
| 6959 | // Check if the {integer} value is in safe integer range. | |||
| 6960 | return Float64LessThanOrEqual(Float64Abs(integer), | |||
| 6961 | Float64Constant(kMaxSafeInteger)); | |||
| 6962 | }, | |||
| 6963 | [=] { return Int32FalseConstant(); }); | |||
| 6964 | } | |||
| 6965 | ||||
| 6966 | TNode<BoolT> CodeStubAssembler::IsInteger(TNode<Object> number) { | |||
| 6967 | return Select<BoolT>( | |||
| 6968 | TaggedIsSmi(number), [=] { return Int32TrueConstant(); }, | |||
| 6969 | [=] { | |||
| 6970 | return Select<BoolT>( | |||
| 6971 | IsHeapNumber(CAST(number)Cast(number)), | |||
| 6972 | [=] { return IsInteger(UncheckedCast<HeapNumber>(number)); }, | |||
| 6973 | [=] { return Int32FalseConstant(); }); | |||
| 6974 | }); | |||
| 6975 | } | |||
| 6976 | ||||
| 6977 | TNode<BoolT> CodeStubAssembler::IsInteger(TNode<HeapNumber> number) { | |||
| 6978 | TNode<Float64T> number_value = LoadHeapNumberValue(number); | |||
| 6979 | // Truncate the value of {number} to an integer (or an infinity). | |||
| 6980 | TNode<Float64T> integer = Float64Trunc(number_value); | |||
| 6981 | // Check if {number}s value matches the integer (ruling out the infinities). | |||
| 6982 | return Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)); | |||
| 6983 | } | |||
| 6984 | ||||
| 6985 | TNode<BoolT> CodeStubAssembler::IsHeapNumberUint32(TNode<HeapNumber> number) { | |||
| 6986 | // Check that the HeapNumber is a valid uint32 | |||
| 6987 | return Select<BoolT>( | |||
| 6988 | IsHeapNumberPositive(number), | |||
| 6989 | [=] { | |||
| 6990 | TNode<Float64T> value = LoadHeapNumberValue(number); | |||
| 6991 | TNode<Uint32T> int_value = TruncateFloat64ToWord32(value); | |||
| 6992 | return Float64Equal(value, ChangeUint32ToFloat64(int_value)); | |||
| 6993 | }, | |||
| 6994 | [=] { return Int32FalseConstant(); }); | |||
| 6995 | } | |||
| 6996 | ||||
| 6997 | TNode<BoolT> CodeStubAssembler::IsNumberArrayIndex(TNode<Number> number) { | |||
| 6998 | return Select<BoolT>( | |||
| 6999 | TaggedIsSmi(number), [=] { return TaggedIsPositiveSmi(number); }, | |||
| 7000 | [=] { return IsHeapNumberUint32(CAST(number)Cast(number)); }); | |||
| 7001 | } | |||
| 7002 | ||||
| 7003 | TNode<IntPtrT> CodeStubAssembler::LoadBasicMemoryChunkFlags( | |||
| 7004 | TNode<HeapObject> object) { | |||
| 7005 | TNode<IntPtrT> object_word = BitcastTaggedToWord(object); | |||
| 7006 | TNode<IntPtrT> page = PageFromAddress(object_word); | |||
| 7007 | return UncheckedCast<IntPtrT>( | |||
| 7008 | Load(MachineType::Pointer(), page, | |||
| 7009 | IntPtrConstant(BasicMemoryChunk::kFlagsOffset))); | |||
| 7010 | } | |||
| 7011 | ||||
| 7012 | template <typename TIndex> | |||
| 7013 | TNode<BoolT> CodeStubAssembler::FixedArraySizeDoesntFitInNewSpace( | |||
| 7014 | TNode<TIndex> element_count, int base_size) { | |||
| 7015 | static_assert( | |||
| 7016 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 7017 | "Only Smi or IntPtrT element_count is allowed"); | |||
| 7018 | int max_newspace_elements = | |||
| 7019 | (kMaxRegularHeapObjectSize - base_size) / kTaggedSize; | |||
| 7020 | return IntPtrOrSmiGreaterThan( | |||
| 7021 | element_count, IntPtrOrSmiConstant<TIndex>(max_newspace_elements)); | |||
| 7022 | } | |||
| 7023 | ||||
| 7024 | TNode<Uint16T> CodeStubAssembler::StringCharCodeAt(TNode<String> string, | |||
| 7025 | TNode<UintPtrT> index) { | |||
| 7026 | CSA_DCHECK(this, UintPtrLessThan(index, LoadStringLengthAsWord(string)))((void)0); | |||
| 7027 | ||||
| 7028 | TVARIABLE(Uint16T, var_result)TVariable<Uint16T> var_result(this); | |||
| 7029 | ||||
| 7030 | Label return_result(this), if_runtime(this, Label::kDeferred), | |||
| 7031 | if_stringistwobyte(this), if_stringisonebyte(this); | |||
| 7032 | ||||
| 7033 | ToDirectStringAssembler to_direct(state(), string); | |||
| 7034 | to_direct.TryToDirect(&if_runtime); | |||
| 7035 | const TNode<UintPtrT> offset = | |||
| 7036 | UintPtrAdd(index, Unsigned(to_direct.offset())); | |||
| 7037 | const TNode<Int32T> instance_type = to_direct.instance_type(); | |||
| 7038 | const TNode<RawPtrT> string_data = to_direct.PointerToData(&if_runtime); | |||
| 7039 | ||||
| 7040 | // Check if the {string} is a TwoByteSeqString or a OneByteSeqString. | |||
| 7041 | Branch(IsOneByteStringInstanceType(instance_type), &if_stringisonebyte, | |||
| 7042 | &if_stringistwobyte); | |||
| 7043 | ||||
| 7044 | BIND(&if_stringisonebyte)Bind(&if_stringisonebyte); | |||
| 7045 | { | |||
| 7046 | var_result = Load<Uint8T>(string_data, offset); | |||
| 7047 | Goto(&return_result); | |||
| 7048 | } | |||
| 7049 | ||||
| 7050 | BIND(&if_stringistwobyte)Bind(&if_stringistwobyte); | |||
| 7051 | { | |||
| 7052 | var_result = Load<Uint16T>(string_data, WordShl(offset, IntPtrConstant(1))); | |||
| 7053 | Goto(&return_result); | |||
| 7054 | } | |||
| 7055 | ||||
| 7056 | BIND(&if_runtime)Bind(&if_runtime); | |||
| 7057 | { | |||
| 7058 | TNode<Object> result = | |||
| 7059 | CallRuntime(Runtime::kStringCharCodeAt, NoContextConstant(), string, | |||
| 7060 | ChangeUintPtrToTagged(index)); | |||
| 7061 | var_result = UncheckedCast<Uint16T>(SmiToInt32(CAST(result)Cast(result))); | |||
| 7062 | Goto(&return_result); | |||
| 7063 | } | |||
| 7064 | ||||
| 7065 | BIND(&return_result)Bind(&return_result); | |||
| 7066 | return var_result.value(); | |||
| 7067 | } | |||
| 7068 | ||||
| 7069 | TNode<String> CodeStubAssembler::StringFromSingleCharCode(TNode<Int32T> code) { | |||
| 7070 | TVARIABLE(String, var_result)TVariable<String> var_result(this); | |||
| 7071 | ||||
| 7072 | // Check if the {code} is a one-byte char code. | |||
| 7073 | Label if_codeisonebyte(this), if_codeistwobyte(this, Label::kDeferred), | |||
| 7074 | if_done(this); | |||
| 7075 | Branch(Int32LessThanOrEqual(code, Int32Constant(String::kMaxOneByteCharCode)), | |||
| 7076 | &if_codeisonebyte, &if_codeistwobyte); | |||
| 7077 | BIND(&if_codeisonebyte)Bind(&if_codeisonebyte); | |||
| 7078 | { | |||
| 7079 | // Load the isolate wide single character string cache. | |||
| 7080 | TNode<FixedArray> cache = SingleCharacterStringCacheConstant(); | |||
| 7081 | TNode<IntPtrT> code_index = Signed(ChangeUint32ToWord(code)); | |||
| 7082 | ||||
| 7083 | // Check if we have an entry for the {code} in the single character string | |||
| 7084 | // cache already. | |||
| 7085 | Label if_entryisundefined(this, Label::kDeferred), | |||
| 7086 | if_entryisnotundefined(this); | |||
| 7087 | TNode<Object> entry = UnsafeLoadFixedArrayElement(cache, code_index); | |||
| 7088 | Branch(IsUndefined(entry), &if_entryisundefined, &if_entryisnotundefined); | |||
| 7089 | ||||
| 7090 | BIND(&if_entryisundefined)Bind(&if_entryisundefined); | |||
| 7091 | { | |||
| 7092 | // Allocate a new SeqOneByteString for {code} and store it in the {cache}. | |||
| 7093 | TNode<String> result = AllocateSeqOneByteString(1); | |||
| 7094 | StoreNoWriteBarrier( | |||
| 7095 | MachineRepresentation::kWord8, result, | |||
| 7096 | IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag), code); | |||
| 7097 | StoreFixedArrayElement(cache, code_index, result); | |||
| 7098 | var_result = result; | |||
| 7099 | Goto(&if_done); | |||
| 7100 | } | |||
| 7101 | ||||
| 7102 | BIND(&if_entryisnotundefined)Bind(&if_entryisnotundefined); | |||
| 7103 | { | |||
| 7104 | // Return the entry from the {cache}. | |||
| 7105 | var_result = CAST(entry)Cast(entry); | |||
| 7106 | Goto(&if_done); | |||
| 7107 | } | |||
| 7108 | } | |||
| 7109 | ||||
| 7110 | BIND(&if_codeistwobyte)Bind(&if_codeistwobyte); | |||
| 7111 | { | |||
| 7112 | // Allocate a new SeqTwoByteString for {code}. | |||
| 7113 | TNode<String> result = AllocateSeqTwoByteString(1); | |||
| 7114 | StoreNoWriteBarrier( | |||
| 7115 | MachineRepresentation::kWord16, result, | |||
| 7116 | IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag), code); | |||
| 7117 | var_result = result; | |||
| 7118 | Goto(&if_done); | |||
| 7119 | } | |||
| 7120 | ||||
| 7121 | BIND(&if_done)Bind(&if_done); | |||
| 7122 | return var_result.value(); | |||
| 7123 | } | |||
| 7124 | ||||
| 7125 | ToDirectStringAssembler::ToDirectStringAssembler( | |||
| 7126 | compiler::CodeAssemblerState* state, TNode<String> string, Flags flags) | |||
| 7127 | : CodeStubAssembler(state), | |||
| 7128 | var_string_(string, this), | |||
| 7129 | var_instance_type_(LoadInstanceType(string), this), | |||
| 7130 | var_offset_(IntPtrConstant(0), this), | |||
| 7131 | var_is_external_(Int32Constant(0), this), | |||
| 7132 | flags_(flags) {} | |||
| 7133 | ||||
| 7134 | TNode<String> ToDirectStringAssembler::TryToDirect(Label* if_bailout) { | |||
| 7135 | Label dispatch(this, {&var_string_, &var_offset_, &var_instance_type_}); | |||
| 7136 | Label if_iscons(this); | |||
| 7137 | Label if_isexternal(this); | |||
| 7138 | Label if_issliced(this); | |||
| 7139 | Label if_isthin(this); | |||
| 7140 | Label out(this); | |||
| 7141 | ||||
| 7142 | Branch(IsSequentialStringInstanceType(var_instance_type_.value()), &out, | |||
| 7143 | &dispatch); | |||
| 7144 | ||||
| 7145 | // Dispatch based on string representation. | |||
| 7146 | BIND(&dispatch)Bind(&dispatch); | |||
| 7147 | { | |||
| 7148 | int32_t values[] = { | |||
| 7149 | kSeqStringTag, kConsStringTag, kExternalStringTag, | |||
| 7150 | kSlicedStringTag, kThinStringTag, | |||
| 7151 | }; | |||
| 7152 | Label* labels[] = { | |||
| 7153 | &out, &if_iscons, &if_isexternal, &if_issliced, &if_isthin, | |||
| 7154 | }; | |||
| 7155 | STATIC_ASSERT(arraysize(values) == arraysize(labels))static_assert((sizeof(ArraySizeHelper(values))) == (sizeof(ArraySizeHelper (labels))), "arraysize(values) == arraysize(labels)"); | |||
| 7156 | ||||
| 7157 | const TNode<Int32T> representation = Word32And( | |||
| 7158 | var_instance_type_.value(), Int32Constant(kStringRepresentationMask)); | |||
| 7159 | Switch(representation, if_bailout, values, labels, arraysize(values)(sizeof(ArraySizeHelper(values)))); | |||
| 7160 | } | |||
| 7161 | ||||
| 7162 | // Cons string. Check whether it is flat, then fetch first part. | |||
| 7163 | // Flat cons strings have an empty second part. | |||
| 7164 | BIND(&if_iscons)Bind(&if_iscons); | |||
| 7165 | { | |||
| 7166 | const TNode<String> string = var_string_.value(); | |||
| 7167 | GotoIfNot(IsEmptyString( | |||
| 7168 | LoadObjectField<String>(string, ConsString::kSecondOffset)), | |||
| 7169 | if_bailout); | |||
| 7170 | ||||
| 7171 | const TNode<String> lhs = | |||
| 7172 | LoadObjectField<String>(string, ConsString::kFirstOffset); | |||
| 7173 | var_string_ = lhs; | |||
| 7174 | var_instance_type_ = LoadInstanceType(lhs); | |||
| 7175 | ||||
| 7176 | Goto(&dispatch); | |||
| 7177 | } | |||
| 7178 | ||||
| 7179 | // Sliced string. Fetch parent and correct start index by offset. | |||
| 7180 | BIND(&if_issliced)Bind(&if_issliced); | |||
| 7181 | { | |||
| 7182 | if (!FLAG_string_slices || (flags_ & kDontUnpackSlicedStrings)) { | |||
| 7183 | Goto(if_bailout); | |||
| 7184 | } else { | |||
| 7185 | const TNode<String> string = var_string_.value(); | |||
| 7186 | const TNode<IntPtrT> sliced_offset = | |||
| 7187 | LoadAndUntagObjectField(string, SlicedString::kOffsetOffset); | |||
| 7188 | var_offset_ = IntPtrAdd(var_offset_.value(), sliced_offset); | |||
| 7189 | ||||
| 7190 | const TNode<String> parent = | |||
| 7191 | LoadObjectField<String>(string, SlicedString::kParentOffset); | |||
| 7192 | var_string_ = parent; | |||
| 7193 | var_instance_type_ = LoadInstanceType(parent); | |||
| 7194 | ||||
| 7195 | Goto(&dispatch); | |||
| 7196 | } | |||
| 7197 | } | |||
| 7198 | ||||
| 7199 | // Thin string. Fetch the actual string. | |||
| 7200 | BIND(&if_isthin)Bind(&if_isthin); | |||
| 7201 | { | |||
| 7202 | const TNode<String> string = var_string_.value(); | |||
| 7203 | const TNode<String> actual_string = | |||
| 7204 | LoadObjectField<String>(string, ThinString::kActualOffset); | |||
| 7205 | const TNode<Uint16T> actual_instance_type = LoadInstanceType(actual_string); | |||
| 7206 | ||||
| 7207 | var_string_ = actual_string; | |||
| 7208 | var_instance_type_ = actual_instance_type; | |||
| 7209 | ||||
| 7210 | Goto(&dispatch); | |||
| 7211 | } | |||
| 7212 | ||||
| 7213 | // External string. | |||
| 7214 | BIND(&if_isexternal)Bind(&if_isexternal); | |||
| 7215 | var_is_external_ = Int32Constant(1); | |||
| 7216 | Goto(&out); | |||
| 7217 | ||||
| 7218 | BIND(&out)Bind(&out); | |||
| 7219 | return var_string_.value(); | |||
| 7220 | } | |||
| 7221 | ||||
| 7222 | TNode<RawPtrT> ToDirectStringAssembler::TryToSequential( | |||
| 7223 | StringPointerKind ptr_kind, Label* if_bailout) { | |||
| 7224 | CHECK(ptr_kind == PTR_TO_DATA || ptr_kind == PTR_TO_STRING)do { if ((__builtin_expect(!!(!(ptr_kind == PTR_TO_DATA || ptr_kind == PTR_TO_STRING)), 0))) { V8_Fatal("Check failed: %s.", "ptr_kind == PTR_TO_DATA || ptr_kind == PTR_TO_STRING" ); } } while (false); | |||
| 7225 | ||||
| 7226 | TVARIABLE(RawPtrT, var_result)TVariable<RawPtrT> var_result(this); | |||
| 7227 | Label out(this), if_issequential(this), if_isexternal(this, Label::kDeferred); | |||
| 7228 | Branch(is_external(), &if_isexternal, &if_issequential); | |||
| 7229 | ||||
| 7230 | BIND(&if_issequential)Bind(&if_issequential); | |||
| 7231 | { | |||
| 7232 | STATIC_ASSERT(SeqOneByteString::kHeaderSize ==static_assert(SeqOneByteString::kHeaderSize == SeqTwoByteString ::kHeaderSize, "SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize" ) | |||
| 7233 | SeqTwoByteString::kHeaderSize)static_assert(SeqOneByteString::kHeaderSize == SeqTwoByteString ::kHeaderSize, "SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize" ); | |||
| 7234 | TNode<RawPtrT> result = | |||
| 7235 | ReinterpretCast<RawPtrT>(BitcastTaggedToWord(var_string_.value())); | |||
| 7236 | if (ptr_kind == PTR_TO_DATA) { | |||
| 7237 | result = RawPtrAdd(result, IntPtrConstant(SeqOneByteString::kHeaderSize - | |||
| 7238 | kHeapObjectTag)); | |||
| 7239 | } | |||
| 7240 | var_result = result; | |||
| 7241 | Goto(&out); | |||
| 7242 | } | |||
| 7243 | ||||
| 7244 | BIND(&if_isexternal)Bind(&if_isexternal); | |||
| 7245 | { | |||
| 7246 | GotoIf(IsUncachedExternalStringInstanceType(var_instance_type_.value()), | |||
| 7247 | if_bailout); | |||
| 7248 | ||||
| 7249 | TNode<String> string = var_string_.value(); | |||
| 7250 | TNode<RawPtrT> result = LoadExternalStringResourceDataPtr(CAST(string)Cast(string)); | |||
| 7251 | if (ptr_kind == PTR_TO_STRING) { | |||
| 7252 | result = RawPtrSub(result, IntPtrConstant(SeqOneByteString::kHeaderSize - | |||
| 7253 | kHeapObjectTag)); | |||
| 7254 | } | |||
| 7255 | var_result = result; | |||
| 7256 | Goto(&out); | |||
| 7257 | } | |||
| 7258 | ||||
| 7259 | BIND(&out)Bind(&out); | |||
| 7260 | return var_result.value(); | |||
| 7261 | } | |||
| 7262 | ||||
| 7263 | TNode<Number> CodeStubAssembler::StringToNumber(TNode<String> input) { | |||
| 7264 | Label runtime(this, Label::kDeferred); | |||
| 7265 | Label end(this); | |||
| 7266 | ||||
| 7267 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 7268 | ||||
| 7269 | // Check if string has a cached array index. | |||
| 7270 | TNode<Uint32T> raw_hash_field = LoadNameRawHashField(input); | |||
| 7271 | GotoIf(IsSetWord32(raw_hash_field, Name::kDoesNotContainCachedArrayIndexMask), | |||
| 7272 | &runtime); | |||
| 7273 | ||||
| 7274 | var_result = SmiTag(Signed( | |||
| 7275 | DecodeWordFromWord32<String::ArrayIndexValueBits>(raw_hash_field))); | |||
| 7276 | Goto(&end); | |||
| 7277 | ||||
| 7278 | BIND(&runtime)Bind(&runtime); | |||
| 7279 | { | |||
| 7280 | var_result = | |||
| 7281 | CAST(CallRuntime(Runtime::kStringToNumber, NoContextConstant(), input))Cast(CallRuntime(Runtime::kStringToNumber, NoContextConstant( ), input)); | |||
| 7282 | Goto(&end); | |||
| 7283 | } | |||
| 7284 | ||||
| 7285 | BIND(&end)Bind(&end); | |||
| 7286 | return var_result.value(); | |||
| 7287 | } | |||
| 7288 | ||||
| 7289 | TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input, | |||
| 7290 | Label* bailout) { | |||
| 7291 | TVARIABLE(String, result)TVariable<String> result(this); | |||
| 7292 | TVARIABLE(Smi, smi_input)TVariable<Smi> smi_input(this); | |||
| 7293 | Label if_smi(this), if_heap_number(this), done(this, &result); | |||
| 7294 | ||||
| 7295 | // Load the number string cache. | |||
| 7296 | TNode<FixedArray> number_string_cache = NumberStringCacheConstant(); | |||
| 7297 | ||||
| 7298 | // Make the hash mask from the length of the number string cache. It | |||
| 7299 | // contains two elements (number and string) for each cache entry. | |||
| 7300 | TNode<IntPtrT> number_string_cache_length = | |||
| 7301 | LoadAndUntagFixedArrayBaseLength(number_string_cache); | |||
| 7302 | TNode<Int32T> one = Int32Constant(1); | |||
| 7303 | TNode<Word32T> mask = Int32Sub( | |||
| 7304 | Word32Shr(TruncateWordToInt32(number_string_cache_length), one), one); | |||
| 7305 | ||||
| 7306 | GotoIfNot(TaggedIsSmi(input), &if_heap_number); | |||
| 7307 | smi_input = CAST(input)Cast(input); | |||
| 7308 | Goto(&if_smi); | |||
| 7309 | ||||
| 7310 | BIND(&if_heap_number)Bind(&if_heap_number); | |||
| 7311 | { | |||
| 7312 | Comment("NumberToString - HeapNumber"); | |||
| 7313 | TNode<HeapNumber> heap_number_input = CAST(input)Cast(input); | |||
| 7314 | // Try normalizing the HeapNumber. | |||
| 7315 | TryHeapNumberToSmi(heap_number_input, &smi_input, &if_smi); | |||
| 7316 | ||||
| 7317 | // Make a hash from the two 32-bit values of the double. | |||
| 7318 | TNode<Int32T> low = | |||
| 7319 | LoadObjectField<Int32T>(heap_number_input, HeapNumber::kValueOffset); | |||
| 7320 | TNode<Int32T> high = LoadObjectField<Int32T>( | |||
| 7321 | heap_number_input, HeapNumber::kValueOffset + kIntSize); | |||
| 7322 | TNode<Word32T> hash = Word32And(Word32Xor(low, high), mask); | |||
| 7323 | TNode<IntPtrT> entry_index = | |||
| 7324 | Signed(ChangeUint32ToWord(Int32Add(hash, hash))); | |||
| 7325 | ||||
| 7326 | // Cache entry's key must be a heap number | |||
| 7327 | TNode<Object> number_key = | |||
| 7328 | UnsafeLoadFixedArrayElement(number_string_cache, entry_index); | |||
| 7329 | GotoIf(TaggedIsSmi(number_key), bailout); | |||
| 7330 | TNode<HeapObject> number_key_heap_object = CAST(number_key)Cast(number_key); | |||
| 7331 | GotoIfNot(IsHeapNumber(number_key_heap_object), bailout); | |||
| 7332 | ||||
| 7333 | // Cache entry's key must match the heap number value we're looking for. | |||
| 7334 | TNode<Int32T> low_compare = LoadObjectField<Int32T>( | |||
| 7335 | number_key_heap_object, HeapNumber::kValueOffset); | |||
| 7336 | TNode<Int32T> high_compare = LoadObjectField<Int32T>( | |||
| 7337 | number_key_heap_object, HeapNumber::kValueOffset + kIntSize); | |||
| 7338 | GotoIfNot(Word32Equal(low, low_compare), bailout); | |||
| 7339 | GotoIfNot(Word32Equal(high, high_compare), bailout); | |||
| 7340 | ||||
| 7341 | // Heap number match, return value from cache entry. | |||
| 7342 | result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, entry_index,Cast(UnsafeLoadFixedArrayElement(number_string_cache, entry_index , kTaggedSize)) | |||
| 7343 | kTaggedSize))Cast(UnsafeLoadFixedArrayElement(number_string_cache, entry_index , kTaggedSize)); | |||
| 7344 | Goto(&done); | |||
| 7345 | } | |||
| 7346 | ||||
| 7347 | BIND(&if_smi)Bind(&if_smi); | |||
| 7348 | { | |||
| 7349 | Comment("NumberToString - Smi"); | |||
| 7350 | // Load the smi key, make sure it matches the smi we're looking for. | |||
| 7351 | TNode<Word32T> hash = Word32And(SmiToInt32(smi_input.value()), mask); | |||
| 7352 | TNode<IntPtrT> entry_index = | |||
| 7353 | Signed(ChangeUint32ToWord(Int32Add(hash, hash))); | |||
| 7354 | TNode<Object> smi_key = | |||
| 7355 | UnsafeLoadFixedArrayElement(number_string_cache, entry_index); | |||
| 7356 | Label if_smi_cache_missed(this); | |||
| 7357 | GotoIf(TaggedNotEqual(smi_key, smi_input.value()), &if_smi_cache_missed); | |||
| 7358 | ||||
| 7359 | // Smi match, return value from cache entry. | |||
| 7360 | result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, entry_index,Cast(UnsafeLoadFixedArrayElement(number_string_cache, entry_index , kTaggedSize)) | |||
| 7361 | kTaggedSize))Cast(UnsafeLoadFixedArrayElement(number_string_cache, entry_index , kTaggedSize)); | |||
| 7362 | Goto(&done); | |||
| 7363 | ||||
| 7364 | BIND(&if_smi_cache_missed)Bind(&if_smi_cache_missed); | |||
| 7365 | { | |||
| 7366 | Label store_to_cache(this); | |||
| 7367 | ||||
| 7368 | // Bailout when the cache is not full-size. | |||
| 7369 | const int kFullCacheSize = | |||
| 7370 | isolate()->heap()->MaxNumberToStringCacheSize(); | |||
| 7371 | Branch(IntPtrLessThan(number_string_cache_length, | |||
| 7372 | IntPtrConstant(kFullCacheSize)), | |||
| 7373 | bailout, &store_to_cache); | |||
| 7374 | ||||
| 7375 | BIND(&store_to_cache)Bind(&store_to_cache); | |||
| 7376 | { | |||
| 7377 | // Generate string and update string hash field. | |||
| 7378 | result = NumberToStringSmi(SmiToInt32(smi_input.value()), | |||
| 7379 | Int32Constant(10), bailout); | |||
| 7380 | ||||
| 7381 | // Store string into cache. | |||
| 7382 | StoreFixedArrayElement(number_string_cache, entry_index, | |||
| 7383 | smi_input.value()); | |||
| 7384 | StoreFixedArrayElement(number_string_cache, | |||
| 7385 | IntPtrAdd(entry_index, IntPtrConstant(1)), | |||
| 7386 | result.value()); | |||
| 7387 | Goto(&done); | |||
| 7388 | } | |||
| 7389 | } | |||
| 7390 | } | |||
| 7391 | BIND(&done)Bind(&done); | |||
| 7392 | return result.value(); | |||
| 7393 | } | |||
| 7394 | ||||
| 7395 | TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) { | |||
| 7396 | TVARIABLE(String, result)TVariable<String> result(this); | |||
| 7397 | Label runtime(this, Label::kDeferred), done(this, &result); | |||
| 7398 | ||||
| 7399 | GotoIfForceSlowPath(&runtime); | |||
| 7400 | ||||
| 7401 | result = NumberToString(input, &runtime); | |||
| 7402 | Goto(&done); | |||
| 7403 | ||||
| 7404 | BIND(&runtime)Bind(&runtime); | |||
| 7405 | { | |||
| 7406 | // No cache entry, go to the runtime. | |||
| 7407 | result = CAST(Cast(CallRuntime(Runtime::kNumberToStringSlow, NoContextConstant (), input)) | |||
| 7408 | CallRuntime(Runtime::kNumberToStringSlow, NoContextConstant(), input))Cast(CallRuntime(Runtime::kNumberToStringSlow, NoContextConstant (), input)); | |||
| 7409 | Goto(&done); | |||
| 7410 | } | |||
| 7411 | BIND(&done)Bind(&done); | |||
| 7412 | return result.value(); | |||
| 7413 | } | |||
| 7414 | ||||
| 7415 | TNode<Numeric> CodeStubAssembler::NonNumberToNumberOrNumeric( | |||
| 7416 | TNode<Context> context, TNode<HeapObject> input, Object::Conversion mode, | |||
| 7417 | BigIntHandling bigint_handling) { | |||
| 7418 | CSA_DCHECK(this, Word32BinaryNot(IsHeapNumber(input)))((void)0); | |||
| 7419 | ||||
| 7420 | TVARIABLE(HeapObject, var_input, input)TVariable<HeapObject> var_input(input, this); | |||
| 7421 | TVARIABLE(Numeric, var_result)TVariable<Numeric> var_result(this); | |||
| 7422 | TVARIABLE(Uint16T, instance_type, LoadInstanceType(var_input.value()))TVariable<Uint16T> instance_type(LoadInstanceType(var_input .value()), this); | |||
| 7423 | Label end(this), if_inputisreceiver(this, Label::kDeferred), | |||
| 7424 | if_inputisnotreceiver(this); | |||
| 7425 | ||||
| 7426 | // We need to handle JSReceiver first since we might need to do two | |||
| 7427 | // conversions due to ToPritmive. | |||
| 7428 | Branch(IsJSReceiverInstanceType(instance_type.value()), &if_inputisreceiver, | |||
| 7429 | &if_inputisnotreceiver); | |||
| 7430 | ||||
| 7431 | BIND(&if_inputisreceiver)Bind(&if_inputisreceiver); | |||
| 7432 | { | |||
| 7433 | // The {var_input.value()} is a JSReceiver, we need to convert it to a | |||
| 7434 | // Primitive first using the ToPrimitive type conversion, preferably | |||
| 7435 | // yielding a Number. | |||
| 7436 | Callable callable = CodeFactory::NonPrimitiveToPrimitive( | |||
| 7437 | isolate(), ToPrimitiveHint::kNumber); | |||
| 7438 | TNode<Object> result = CallStub(callable, context, var_input.value()); | |||
| 7439 | ||||
| 7440 | // Check if the {result} is already a Number/Numeric. | |||
| 7441 | Label if_done(this), if_notdone(this); | |||
| 7442 | Branch(mode == Object::Conversion::kToNumber ? IsNumber(result) | |||
| 7443 | : IsNumeric(result), | |||
| 7444 | &if_done, &if_notdone); | |||
| 7445 | ||||
| 7446 | BIND(&if_done)Bind(&if_done); | |||
| 7447 | { | |||
| 7448 | // The ToPrimitive conversion already gave us a Number/Numeric, so | |||
| 7449 | // we're done. | |||
| 7450 | var_result = CAST(result)Cast(result); | |||
| 7451 | Goto(&end); | |||
| 7452 | } | |||
| 7453 | ||||
| 7454 | BIND(&if_notdone)Bind(&if_notdone); | |||
| 7455 | { | |||
| 7456 | // We now have a Primitive {result}, but it's not yet a | |||
| 7457 | // Number/Numeric. | |||
| 7458 | var_input = CAST(result)Cast(result); | |||
| 7459 | // We have a new input. Redo the check and reload instance_type. | |||
| 7460 | CSA_DCHECK(this, Word32BinaryNot(IsHeapNumber(var_input.value())))((void)0); | |||
| 7461 | instance_type = LoadInstanceType(var_input.value()); | |||
| 7462 | Goto(&if_inputisnotreceiver); | |||
| 7463 | } | |||
| 7464 | } | |||
| 7465 | ||||
| 7466 | BIND(&if_inputisnotreceiver)Bind(&if_inputisnotreceiver); | |||
| 7467 | { | |||
| 7468 | Label not_plain_primitive(this), if_inputisbigint(this), | |||
| 7469 | if_inputisother(this, Label::kDeferred); | |||
| 7470 | ||||
| 7471 | // String and Oddball cases. | |||
| 7472 | TVARIABLE(Number, var_result_number)TVariable<Number> var_result_number(this); | |||
| 7473 | TryPlainPrimitiveNonNumberToNumber(var_input.value(), &var_result_number, | |||
| 7474 | ¬_plain_primitive); | |||
| 7475 | var_result = var_result_number.value(); | |||
| 7476 | Goto(&end); | |||
| 7477 | ||||
| 7478 | BIND(¬_plain_primitive)Bind(¬_plain_primitive); | |||
| 7479 | { | |||
| 7480 | Branch(IsBigIntInstanceType(instance_type.value()), &if_inputisbigint, | |||
| 7481 | &if_inputisother); | |||
| 7482 | ||||
| 7483 | BIND(&if_inputisbigint)Bind(&if_inputisbigint); | |||
| 7484 | { | |||
| 7485 | if (mode == Object::Conversion::kToNumeric) { | |||
| 7486 | var_result = CAST(var_input.value())Cast(var_input.value()); | |||
| 7487 | Goto(&end); | |||
| 7488 | } else { | |||
| 7489 | DCHECK_EQ(mode, Object::Conversion::kToNumber)((void) 0); | |||
| 7490 | if (bigint_handling == BigIntHandling::kThrow) { | |||
| 7491 | Goto(&if_inputisother); | |||
| 7492 | } else { | |||
| 7493 | DCHECK_EQ(bigint_handling, BigIntHandling::kConvertToNumber)((void) 0); | |||
| 7494 | var_result = CAST(CallRuntime(Runtime::kBigIntToNumber, context,Cast(CallRuntime(Runtime::kBigIntToNumber, context, var_input .value())) | |||
| 7495 | var_input.value()))Cast(CallRuntime(Runtime::kBigIntToNumber, context, var_input .value())); | |||
| 7496 | Goto(&end); | |||
| 7497 | } | |||
| 7498 | } | |||
| 7499 | } | |||
| 7500 | ||||
| 7501 | BIND(&if_inputisother)Bind(&if_inputisother); | |||
| 7502 | { | |||
| 7503 | // The {var_input.value()} is something else (e.g. Symbol), let the | |||
| 7504 | // runtime figure out the correct exception. Note: We cannot tail call | |||
| 7505 | // to the runtime here, as js-to-wasm trampolines also use this code | |||
| 7506 | // currently, and they declare all outgoing parameters as untagged, | |||
| 7507 | // while we would push a tagged object here. | |||
| 7508 | auto function_id = mode == Object::Conversion::kToNumber | |||
| 7509 | ? Runtime::kToNumber | |||
| 7510 | : Runtime::kToNumeric; | |||
| 7511 | var_result = CAST(CallRuntime(function_id, context, var_input.value()))Cast(CallRuntime(function_id, context, var_input.value())); | |||
| 7512 | Goto(&end); | |||
| 7513 | } | |||
| 7514 | } | |||
| 7515 | } | |||
| 7516 | ||||
| 7517 | BIND(&end)Bind(&end); | |||
| 7518 | if (mode == Object::Conversion::kToNumber) { | |||
| 7519 | CSA_DCHECK(this, IsNumber(var_result.value()))((void)0); | |||
| 7520 | } | |||
| 7521 | return var_result.value(); | |||
| 7522 | } | |||
| 7523 | ||||
| 7524 | TNode<Number> CodeStubAssembler::NonNumberToNumber( | |||
| 7525 | TNode<Context> context, TNode<HeapObject> input, | |||
| 7526 | BigIntHandling bigint_handling) { | |||
| 7527 | return CAST(NonNumberToNumberOrNumeric(Cast(NonNumberToNumberOrNumeric( context, input, Object::Conversion ::kToNumber, bigint_handling)) | |||
| 7528 | context, input, Object::Conversion::kToNumber, bigint_handling))Cast(NonNumberToNumberOrNumeric( context, input, Object::Conversion ::kToNumber, bigint_handling)); | |||
| 7529 | } | |||
| 7530 | ||||
| 7531 | void CodeStubAssembler::TryPlainPrimitiveNonNumberToNumber( | |||
| 7532 | TNode<HeapObject> input, TVariable<Number>* var_result, Label* if_bailout) { | |||
| 7533 | CSA_DCHECK(this, Word32BinaryNot(IsHeapNumber(input)))((void)0); | |||
| 7534 | Label done(this); | |||
| 7535 | ||||
| 7536 | // Dispatch on the {input} instance type. | |||
| 7537 | TNode<Uint16T> input_instance_type = LoadInstanceType(input); | |||
| 7538 | Label if_inputisstring(this); | |||
| 7539 | GotoIf(IsStringInstanceType(input_instance_type), &if_inputisstring); | |||
| 7540 | GotoIfNot(InstanceTypeEqual(input_instance_type, ODDBALL_TYPE), if_bailout); | |||
| 7541 | ||||
| 7542 | // The {input} is an Oddball, we just need to load the Number value of it. | |||
| 7543 | *var_result = LoadObjectField<Number>(input, Oddball::kToNumberOffset); | |||
| 7544 | Goto(&done); | |||
| 7545 | ||||
| 7546 | BIND(&if_inputisstring)Bind(&if_inputisstring); | |||
| 7547 | { | |||
| 7548 | // The {input} is a String, use the fast stub to convert it to a Number. | |||
| 7549 | *var_result = StringToNumber(CAST(input)Cast(input)); | |||
| 7550 | Goto(&done); | |||
| 7551 | } | |||
| 7552 | ||||
| 7553 | BIND(&done)Bind(&done); | |||
| 7554 | } | |||
| 7555 | ||||
| 7556 | TNode<Numeric> CodeStubAssembler::NonNumberToNumeric(TNode<Context> context, | |||
| 7557 | TNode<HeapObject> input) { | |||
| 7558 | return NonNumberToNumberOrNumeric(context, input, | |||
| 7559 | Object::Conversion::kToNumeric); | |||
| 7560 | } | |||
| 7561 | ||||
| 7562 | TNode<Number> CodeStubAssembler::ToNumber(TNode<Context> context, | |||
| 7563 | TNode<Object> input, | |||
| 7564 | BigIntHandling bigint_handling) { | |||
| 7565 | return CAST(ToNumberOrNumeric([context] { return context; }, input, nullptr,Cast(ToNumberOrNumeric([context] { return context; }, input, nullptr , Object::Conversion::kToNumber, bigint_handling)) | |||
| 7566 | Object::Conversion::kToNumber,Cast(ToNumberOrNumeric([context] { return context; }, input, nullptr , Object::Conversion::kToNumber, bigint_handling)) | |||
| 7567 | bigint_handling))Cast(ToNumberOrNumeric([context] { return context; }, input, nullptr , Object::Conversion::kToNumber, bigint_handling)); | |||
| 7568 | } | |||
| 7569 | ||||
| 7570 | TNode<Number> CodeStubAssembler::ToNumber_Inline(TNode<Context> context, | |||
| 7571 | TNode<Object> input) { | |||
| 7572 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 7573 | Label end(this), not_smi(this, Label::kDeferred); | |||
| 7574 | ||||
| 7575 | GotoIfNot(TaggedIsSmi(input), ¬_smi); | |||
| 7576 | var_result = CAST(input)Cast(input); | |||
| 7577 | Goto(&end); | |||
| 7578 | ||||
| 7579 | BIND(¬_smi)Bind(¬_smi); | |||
| 7580 | { | |||
| 7581 | var_result = Select<Number>( | |||
| 7582 | IsHeapNumber(CAST(input)Cast(input)), [=] { return CAST(input)Cast(input); }, | |||
| 7583 | [=] { | |||
| 7584 | return CAST(CallBuiltin(Builtin::kNonNumberToNumber, context, input))Cast(CallBuiltin(Builtin::kNonNumberToNumber, context, input) ); | |||
| 7585 | }); | |||
| 7586 | Goto(&end); | |||
| 7587 | } | |||
| 7588 | ||||
| 7589 | BIND(&end)Bind(&end); | |||
| 7590 | return var_result.value(); | |||
| 7591 | } | |||
| 7592 | ||||
| 7593 | TNode<Numeric> CodeStubAssembler::ToNumberOrNumeric( | |||
| 7594 | LazyNode<Context> context, TNode<Object> input, | |||
| 7595 | TVariable<Smi>* var_type_feedback, Object::Conversion mode, | |||
| 7596 | BigIntHandling bigint_handling) { | |||
| 7597 | TVARIABLE(Numeric, var_result)TVariable<Numeric> var_result(this); | |||
| 7598 | Label end(this); | |||
| 7599 | ||||
| 7600 | Label not_smi(this, Label::kDeferred); | |||
| 7601 | GotoIfNot(TaggedIsSmi(input), ¬_smi); | |||
| 7602 | TNode<Smi> input_smi = CAST(input)Cast(input); | |||
| 7603 | var_result = input_smi; | |||
| 7604 | if (var_type_feedback) { | |||
| 7605 | *var_type_feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall); | |||
| 7606 | } | |||
| 7607 | Goto(&end); | |||
| 7608 | ||||
| 7609 | BIND(¬_smi)Bind(¬_smi); | |||
| 7610 | { | |||
| 7611 | Label not_heap_number(this, Label::kDeferred); | |||
| 7612 | TNode<HeapObject> input_ho = CAST(input)Cast(input); | |||
| 7613 | GotoIfNot(IsHeapNumber(input_ho), ¬_heap_number); | |||
| 7614 | ||||
| 7615 | TNode<HeapNumber> input_hn = CAST(input_ho)Cast(input_ho); | |||
| 7616 | var_result = input_hn; | |||
| 7617 | if (var_type_feedback) { | |||
| 7618 | *var_type_feedback = SmiConstant(BinaryOperationFeedback::kNumber); | |||
| 7619 | } | |||
| 7620 | Goto(&end); | |||
| 7621 | ||||
| 7622 | BIND(¬_heap_number)Bind(¬_heap_number); | |||
| 7623 | { | |||
| 7624 | if (mode == Object::Conversion::kToNumeric) { | |||
| 7625 | // Special case for collecting BigInt feedback. | |||
| 7626 | Label not_bigint(this); | |||
| 7627 | GotoIfNot(IsBigInt(input_ho), ¬_bigint); | |||
| 7628 | { | |||
| 7629 | var_result = CAST(input_ho)Cast(input_ho); | |||
| 7630 | *var_type_feedback = SmiConstant(BinaryOperationFeedback::kBigInt); | |||
| 7631 | Goto(&end); | |||
| 7632 | } | |||
| 7633 | BIND(¬_bigint)Bind(¬_bigint); | |||
| 7634 | } | |||
| 7635 | var_result = NonNumberToNumberOrNumeric(context(), input_ho, mode, | |||
| 7636 | bigint_handling); | |||
| 7637 | if (var_type_feedback) { | |||
| 7638 | *var_type_feedback = SmiConstant(BinaryOperationFeedback::kAny); | |||
| 7639 | } | |||
| 7640 | Goto(&end); | |||
| 7641 | } | |||
| 7642 | } | |||
| 7643 | ||||
| 7644 | BIND(&end)Bind(&end); | |||
| 7645 | return var_result.value(); | |||
| 7646 | } | |||
| 7647 | ||||
| 7648 | TNode<Number> CodeStubAssembler::PlainPrimitiveToNumber(TNode<Object> input) { | |||
| 7649 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 7650 | Label end(this), fallback(this); | |||
| 7651 | ||||
| 7652 | Label not_smi(this, Label::kDeferred); | |||
| 7653 | GotoIfNot(TaggedIsSmi(input), ¬_smi); | |||
| 7654 | TNode<Smi> input_smi = CAST(input)Cast(input); | |||
| 7655 | var_result = input_smi; | |||
| 7656 | Goto(&end); | |||
| 7657 | ||||
| 7658 | BIND(¬_smi)Bind(¬_smi); | |||
| 7659 | { | |||
| 7660 | Label not_heap_number(this, Label::kDeferred); | |||
| 7661 | TNode<HeapObject> input_ho = CAST(input)Cast(input); | |||
| 7662 | GotoIfNot(IsHeapNumber(input_ho), ¬_heap_number); | |||
| 7663 | ||||
| 7664 | TNode<HeapNumber> input_hn = CAST(input_ho)Cast(input_ho); | |||
| 7665 | var_result = input_hn; | |||
| 7666 | Goto(&end); | |||
| 7667 | ||||
| 7668 | BIND(¬_heap_number)Bind(¬_heap_number); | |||
| 7669 | { | |||
| 7670 | TryPlainPrimitiveNonNumberToNumber(input_ho, &var_result, &fallback); | |||
| 7671 | Goto(&end); | |||
| 7672 | BIND(&fallback)Bind(&fallback); | |||
| 7673 | Unreachable(); | |||
| 7674 | } | |||
| 7675 | } | |||
| 7676 | ||||
| 7677 | BIND(&end)Bind(&end); | |||
| 7678 | return var_result.value(); | |||
| 7679 | } | |||
| 7680 | ||||
| 7681 | TNode<BigInt> CodeStubAssembler::ToBigInt(TNode<Context> context, | |||
| 7682 | TNode<Object> input) { | |||
| 7683 | TVARIABLE(BigInt, var_result)TVariable<BigInt> var_result(this); | |||
| 7684 | Label if_bigint(this), done(this), if_throw(this); | |||
| 7685 | ||||
| 7686 | GotoIf(TaggedIsSmi(input), &if_throw); | |||
| 7687 | GotoIf(IsBigInt(CAST(input)Cast(input)), &if_bigint); | |||
| 7688 | var_result = CAST(CallRuntime(Runtime::kToBigInt, context, input))Cast(CallRuntime(Runtime::kToBigInt, context, input)); | |||
| 7689 | Goto(&done); | |||
| 7690 | ||||
| 7691 | BIND(&if_bigint)Bind(&if_bigint); | |||
| 7692 | var_result = CAST(input)Cast(input); | |||
| 7693 | Goto(&done); | |||
| 7694 | ||||
| 7695 | BIND(&if_throw)Bind(&if_throw); | |||
| 7696 | ThrowTypeError(context, MessageTemplate::kBigIntFromObject, input); | |||
| 7697 | ||||
| 7698 | BIND(&done)Bind(&done); | |||
| 7699 | return var_result.value(); | |||
| 7700 | } | |||
| 7701 | ||||
| 7702 | void CodeStubAssembler::TaggedToNumeric(TNode<Context> context, | |||
| 7703 | TNode<Object> value, | |||
| 7704 | TVariable<Numeric>* var_numeric) { | |||
| 7705 | TaggedToNumeric(context, value, var_numeric, nullptr); | |||
| 7706 | } | |||
| 7707 | ||||
| 7708 | void CodeStubAssembler::TaggedToNumericWithFeedback( | |||
| 7709 | TNode<Context> context, TNode<Object> value, | |||
| 7710 | TVariable<Numeric>* var_numeric, TVariable<Smi>* var_feedback) { | |||
| 7711 | DCHECK_NOT_NULL(var_feedback)((void) 0); | |||
| 7712 | TaggedToNumeric(context, value, var_numeric, var_feedback); | |||
| 7713 | } | |||
| 7714 | ||||
| 7715 | void CodeStubAssembler::TaggedToNumeric(TNode<Context> context, | |||
| 7716 | TNode<Object> value, | |||
| 7717 | TVariable<Numeric>* var_numeric, | |||
| 7718 | TVariable<Smi>* var_feedback) { | |||
| 7719 | Label done(this), if_smi(this), if_heapnumber(this), if_bigint(this), | |||
| 7720 | if_oddball(this); | |||
| 7721 | GotoIf(TaggedIsSmi(value), &if_smi); | |||
| 7722 | TNode<HeapObject> heap_object_value = CAST(value)Cast(value); | |||
| 7723 | TNode<Map> map = LoadMap(heap_object_value); | |||
| 7724 | GotoIf(IsHeapNumberMap(map), &if_heapnumber); | |||
| 7725 | TNode<Uint16T> instance_type = LoadMapInstanceType(map); | |||
| 7726 | GotoIf(IsBigIntInstanceType(instance_type), &if_bigint); | |||
| 7727 | ||||
| 7728 | // {heap_object_value} is not a Numeric yet. | |||
| 7729 | GotoIf(Word32Equal(instance_type, Int32Constant(ODDBALL_TYPE)), &if_oddball); | |||
| 7730 | *var_numeric = CAST(Cast(CallBuiltin(Builtin::kNonNumberToNumeric, context, heap_object_value )) | |||
| 7731 | CallBuiltin(Builtin::kNonNumberToNumeric, context, heap_object_value))Cast(CallBuiltin(Builtin::kNonNumberToNumeric, context, heap_object_value )); | |||
| 7732 | OverwriteFeedback(var_feedback, BinaryOperationFeedback::kAny); | |||
| 7733 | Goto(&done); | |||
| 7734 | ||||
| 7735 | BIND(&if_smi)Bind(&if_smi); | |||
| 7736 | *var_numeric = CAST(value)Cast(value); | |||
| 7737 | OverwriteFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall); | |||
| 7738 | Goto(&done); | |||
| 7739 | ||||
| 7740 | BIND(&if_heapnumber)Bind(&if_heapnumber); | |||
| 7741 | *var_numeric = CAST(value)Cast(value); | |||
| 7742 | OverwriteFeedback(var_feedback, BinaryOperationFeedback::kNumber); | |||
| 7743 | Goto(&done); | |||
| 7744 | ||||
| 7745 | BIND(&if_bigint)Bind(&if_bigint); | |||
| 7746 | *var_numeric = CAST(value)Cast(value); | |||
| 7747 | OverwriteFeedback(var_feedback, BinaryOperationFeedback::kBigInt); | |||
| 7748 | Goto(&done); | |||
| 7749 | ||||
| 7750 | BIND(&if_oddball)Bind(&if_oddball); | |||
| 7751 | OverwriteFeedback(var_feedback, BinaryOperationFeedback::kNumberOrOddball); | |||
| 7752 | *var_numeric = | |||
| 7753 | CAST(LoadObjectField(heap_object_value, Oddball::kToNumberOffset))Cast(LoadObjectField(heap_object_value, Oddball::kToNumberOffset )); | |||
| 7754 | Goto(&done); | |||
| 7755 | ||||
| 7756 | Bind(&done); | |||
| 7757 | } | |||
| 7758 | ||||
| 7759 | // ES#sec-touint32 | |||
| 7760 | TNode<Number> CodeStubAssembler::ToUint32(TNode<Context> context, | |||
| 7761 | TNode<Object> input) { | |||
| 7762 | const TNode<Float64T> float_zero = Float64Constant(0.0); | |||
| 7763 | const TNode<Float64T> float_two_32 = | |||
| 7764 | Float64Constant(static_cast<double>(1ULL << 32)); | |||
| 7765 | ||||
| 7766 | Label out(this); | |||
| 7767 | ||||
| 7768 | TVARIABLE(Object, var_result, input)TVariable<Object> var_result(input, this); | |||
| 7769 | ||||
| 7770 | // Early exit for positive smis. | |||
| 7771 | { | |||
| 7772 | // TODO(jgruber): This branch and the recheck below can be removed once we | |||
| 7773 | // have a ToNumber with multiple exits. | |||
| 7774 | Label next(this, Label::kDeferred); | |||
| 7775 | Branch(TaggedIsPositiveSmi(input), &out, &next); | |||
| 7776 | BIND(&next)Bind(&next); | |||
| 7777 | } | |||
| 7778 | ||||
| 7779 | const TNode<Number> number = ToNumber(context, input); | |||
| 7780 | var_result = number; | |||
| 7781 | ||||
| 7782 | // Perhaps we have a positive smi now. | |||
| 7783 | { | |||
| 7784 | Label next(this, Label::kDeferred); | |||
| 7785 | Branch(TaggedIsPositiveSmi(number), &out, &next); | |||
| 7786 | BIND(&next)Bind(&next); | |||
| 7787 | } | |||
| 7788 | ||||
| 7789 | Label if_isnegativesmi(this), if_isheapnumber(this); | |||
| 7790 | Branch(TaggedIsSmi(number), &if_isnegativesmi, &if_isheapnumber); | |||
| 7791 | ||||
| 7792 | BIND(&if_isnegativesmi)Bind(&if_isnegativesmi); | |||
| 7793 | { | |||
| 7794 | const TNode<Int32T> uint32_value = SmiToInt32(CAST(number)Cast(number)); | |||
| 7795 | TNode<Float64T> float64_value = ChangeUint32ToFloat64(uint32_value); | |||
| 7796 | var_result = AllocateHeapNumberWithValue(float64_value); | |||
| 7797 | Goto(&out); | |||
| 7798 | } | |||
| 7799 | ||||
| 7800 | BIND(&if_isheapnumber)Bind(&if_isheapnumber); | |||
| 7801 | { | |||
| 7802 | Label return_zero(this); | |||
| 7803 | const TNode<Float64T> value = LoadHeapNumberValue(CAST(number)Cast(number)); | |||
| 7804 | ||||
| 7805 | { | |||
| 7806 | // +-0. | |||
| 7807 | Label next(this); | |||
| 7808 | Branch(Float64Equal(value, float_zero), &return_zero, &next); | |||
| 7809 | BIND(&next)Bind(&next); | |||
| 7810 | } | |||
| 7811 | ||||
| 7812 | { | |||
| 7813 | // NaN. | |||
| 7814 | Label next(this); | |||
| 7815 | Branch(Float64Equal(value, value), &next, &return_zero); | |||
| 7816 | BIND(&next)Bind(&next); | |||
| 7817 | } | |||
| 7818 | ||||
| 7819 | { | |||
| 7820 | // +Infinity. | |||
| 7821 | Label next(this); | |||
| 7822 | const TNode<Float64T> positive_infinity = | |||
| 7823 | Float64Constant(std::numeric_limits<double>::infinity()); | |||
| 7824 | Branch(Float64Equal(value, positive_infinity), &return_zero, &next); | |||
| 7825 | BIND(&next)Bind(&next); | |||
| 7826 | } | |||
| 7827 | ||||
| 7828 | { | |||
| 7829 | // -Infinity. | |||
| 7830 | Label next(this); | |||
| 7831 | const TNode<Float64T> negative_infinity = | |||
| 7832 | Float64Constant(-1.0 * std::numeric_limits<double>::infinity()); | |||
| 7833 | Branch(Float64Equal(value, negative_infinity), &return_zero, &next); | |||
| 7834 | BIND(&next)Bind(&next); | |||
| 7835 | } | |||
| 7836 | ||||
| 7837 | // * Let int be the mathematical value that is the same sign as number and | |||
| 7838 | // whose magnitude is floor(abs(number)). | |||
| 7839 | // * Let int32bit be int modulo 2^32. | |||
| 7840 | // * Return int32bit. | |||
| 7841 | { | |||
| 7842 | TNode<Float64T> x = Float64Trunc(value); | |||
| 7843 | x = Float64Mod(x, float_two_32); | |||
| 7844 | x = Float64Add(x, float_two_32); | |||
| 7845 | x = Float64Mod(x, float_two_32); | |||
| 7846 | ||||
| 7847 | const TNode<Number> result = ChangeFloat64ToTagged(x); | |||
| 7848 | var_result = result; | |||
| 7849 | Goto(&out); | |||
| 7850 | } | |||
| 7851 | ||||
| 7852 | BIND(&return_zero)Bind(&return_zero); | |||
| 7853 | { | |||
| 7854 | var_result = SmiConstant(0); | |||
| 7855 | Goto(&out); | |||
| 7856 | } | |||
| 7857 | } | |||
| 7858 | ||||
| 7859 | BIND(&out)Bind(&out); | |||
| 7860 | return CAST(var_result.value())Cast(var_result.value()); | |||
| 7861 | } | |||
| 7862 | ||||
| 7863 | TNode<String> CodeStubAssembler::ToString_Inline(TNode<Context> context, | |||
| 7864 | TNode<Object> input) { | |||
| 7865 | TVARIABLE(Object, var_result, input)TVariable<Object> var_result(input, this); | |||
| 7866 | Label stub_call(this, Label::kDeferred), out(this); | |||
| 7867 | ||||
| 7868 | GotoIf(TaggedIsSmi(input), &stub_call); | |||
| 7869 | Branch(IsString(CAST(input)Cast(input)), &out, &stub_call); | |||
| 7870 | ||||
| 7871 | BIND(&stub_call)Bind(&stub_call); | |||
| 7872 | var_result = CallBuiltin(Builtin::kToString, context, input); | |||
| 7873 | Goto(&out); | |||
| 7874 | ||||
| 7875 | BIND(&out)Bind(&out); | |||
| 7876 | return CAST(var_result.value())Cast(var_result.value()); | |||
| 7877 | } | |||
| 7878 | ||||
| 7879 | TNode<JSReceiver> CodeStubAssembler::ToObject(TNode<Context> context, | |||
| 7880 | TNode<Object> input) { | |||
| 7881 | return CAST(CallBuiltin(Builtin::kToObject, context, input))Cast(CallBuiltin(Builtin::kToObject, context, input)); | |||
| 7882 | } | |||
| 7883 | ||||
| 7884 | TNode<JSReceiver> CodeStubAssembler::ToObject_Inline(TNode<Context> context, | |||
| 7885 | TNode<Object> input) { | |||
| 7886 | TVARIABLE(JSReceiver, result)TVariable<JSReceiver> result(this); | |||
| 7887 | Label if_isreceiver(this), if_isnotreceiver(this, Label::kDeferred); | |||
| 7888 | Label done(this); | |||
| 7889 | ||||
| 7890 | BranchIfJSReceiver(input, &if_isreceiver, &if_isnotreceiver); | |||
| 7891 | ||||
| 7892 | BIND(&if_isreceiver)Bind(&if_isreceiver); | |||
| 7893 | { | |||
| 7894 | result = CAST(input)Cast(input); | |||
| 7895 | Goto(&done); | |||
| 7896 | } | |||
| 7897 | ||||
| 7898 | BIND(&if_isnotreceiver)Bind(&if_isnotreceiver); | |||
| 7899 | { | |||
| 7900 | result = ToObject(context, input); | |||
| 7901 | Goto(&done); | |||
| 7902 | } | |||
| 7903 | ||||
| 7904 | BIND(&done)Bind(&done); | |||
| 7905 | return result.value(); | |||
| 7906 | } | |||
| 7907 | ||||
| 7908 | TNode<Number> CodeStubAssembler::ToLength_Inline(TNode<Context> context, | |||
| 7909 | TNode<Object> input) { | |||
| 7910 | TNode<Smi> smi_zero = SmiConstant(0); | |||
| 7911 | return Select<Number>( | |||
| 7912 | TaggedIsSmi(input), [=] { return SmiMax(CAST(input)Cast(input), smi_zero); }, | |||
| 7913 | [=] { return CAST(CallBuiltin(Builtin::kToLength, context, input))Cast(CallBuiltin(Builtin::kToLength, context, input)); }); | |||
| 7914 | } | |||
| 7915 | ||||
| 7916 | TNode<Object> CodeStubAssembler::OrdinaryToPrimitive( | |||
| 7917 | TNode<Context> context, TNode<Object> input, OrdinaryToPrimitiveHint hint) { | |||
| 7918 | Callable callable = CodeFactory::OrdinaryToPrimitive(isolate(), hint); | |||
| 7919 | return CallStub(callable, context, input); | |||
| 7920 | } | |||
| 7921 | ||||
| 7922 | TNode<Uint32T> CodeStubAssembler::DecodeWord32(TNode<Word32T> word32, | |||
| 7923 | uint32_t shift, uint32_t mask) { | |||
| 7924 | DCHECK_EQ((mask >> shift) << shift, mask)((void) 0); | |||
| 7925 | if ((std::numeric_limits<uint32_t>::max() >> shift) == | |||
| 7926 | ((std::numeric_limits<uint32_t>::max() & mask) >> shift)) { | |||
| 7927 | return Unsigned(Word32Shr(word32, static_cast<int>(shift))); | |||
| 7928 | } else { | |||
| 7929 | return Unsigned(Word32And(Word32Shr(word32, static_cast<int>(shift)), | |||
| 7930 | Int32Constant(mask >> shift))); | |||
| 7931 | } | |||
| 7932 | } | |||
| 7933 | ||||
| 7934 | TNode<UintPtrT> CodeStubAssembler::DecodeWord(TNode<WordT> word, uint32_t shift, | |||
| 7935 | uintptr_t mask) { | |||
| 7936 | DCHECK_EQ((mask >> shift) << shift, mask)((void) 0); | |||
| 7937 | if ((std::numeric_limits<uintptr_t>::max() >> shift) == | |||
| 7938 | ((std::numeric_limits<uintptr_t>::max() & mask) >> shift)) { | |||
| 7939 | return Unsigned(WordShr(word, static_cast<int>(shift))); | |||
| 7940 | } else { | |||
| 7941 | return Unsigned(WordAnd(WordShr(word, static_cast<int>(shift)), | |||
| 7942 | IntPtrConstant(mask >> shift))); | |||
| 7943 | } | |||
| 7944 | } | |||
| 7945 | ||||
| 7946 | TNode<Word32T> CodeStubAssembler::UpdateWord32(TNode<Word32T> word, | |||
| 7947 | TNode<Uint32T> value, | |||
| 7948 | uint32_t shift, uint32_t mask, | |||
| 7949 | bool starts_as_zero) { | |||
| 7950 | DCHECK_EQ((mask >> shift) << shift, mask)((void) 0); | |||
| 7951 | // Ensure the {value} fits fully in the mask. | |||
| 7952 | CSA_DCHECK(this, Uint32LessThanOrEqual(value, Uint32Constant(mask >> shift)))((void)0); | |||
| 7953 | TNode<Word32T> encoded_value = Word32Shl(value, Int32Constant(shift)); | |||
| 7954 | TNode<Word32T> masked_word; | |||
| 7955 | if (starts_as_zero) { | |||
| 7956 | CSA_DCHECK(this, Word32Equal(Word32And(word, Int32Constant(~mask)), word))((void)0); | |||
| 7957 | masked_word = word; | |||
| 7958 | } else { | |||
| 7959 | masked_word = Word32And(word, Int32Constant(~mask)); | |||
| 7960 | } | |||
| 7961 | return Word32Or(masked_word, encoded_value); | |||
| 7962 | } | |||
| 7963 | ||||
| 7964 | TNode<WordT> CodeStubAssembler::UpdateWord(TNode<WordT> word, | |||
| 7965 | TNode<UintPtrT> value, | |||
| 7966 | uint32_t shift, uintptr_t mask, | |||
| 7967 | bool starts_as_zero) { | |||
| 7968 | DCHECK_EQ((mask >> shift) << shift, mask)((void) 0); | |||
| 7969 | // Ensure the {value} fits fully in the mask. | |||
| 7970 | CSA_DCHECK(this,((void)0) | |||
| 7971 | UintPtrLessThanOrEqual(value, UintPtrConstant(mask >> shift)))((void)0); | |||
| 7972 | TNode<WordT> encoded_value = WordShl(value, static_cast<int>(shift)); | |||
| 7973 | TNode<WordT> masked_word; | |||
| 7974 | if (starts_as_zero) { | |||
| 7975 | CSA_DCHECK(this, WordEqual(WordAnd(word, UintPtrConstant(~mask)), word))((void)0); | |||
| 7976 | masked_word = word; | |||
| 7977 | } else { | |||
| 7978 | masked_word = WordAnd(word, UintPtrConstant(~mask)); | |||
| 7979 | } | |||
| 7980 | return WordOr(masked_word, encoded_value); | |||
| 7981 | } | |||
| 7982 | ||||
| 7983 | void CodeStubAssembler::SetCounter(StatsCounter* counter, int value) { | |||
| 7984 | if (FLAG_native_code_counters && counter->Enabled()) { | |||
| 7985 | TNode<ExternalReference> counter_address = | |||
| 7986 | ExternalConstant(ExternalReference::Create(counter)); | |||
| 7987 | StoreNoWriteBarrier(MachineRepresentation::kWord32, counter_address, | |||
| 7988 | Int32Constant(value)); | |||
| 7989 | } | |||
| 7990 | } | |||
| 7991 | ||||
| 7992 | void CodeStubAssembler::IncrementCounter(StatsCounter* counter, int delta) { | |||
| 7993 | DCHECK_GT(delta, 0)((void) 0); | |||
| 7994 | if (FLAG_native_code_counters && counter->Enabled()) { | |||
| 7995 | TNode<ExternalReference> counter_address = | |||
| 7996 | ExternalConstant(ExternalReference::Create(counter)); | |||
| 7997 | // This operation has to be exactly 32-bit wide in case the external | |||
| 7998 | // reference table redirects the counter to a uint32_t dummy_stats_counter_ | |||
| 7999 | // field. | |||
| 8000 | TNode<Int32T> value = Load<Int32T>(counter_address); | |||
| 8001 | value = Int32Add(value, Int32Constant(delta)); | |||
| 8002 | StoreNoWriteBarrier(MachineRepresentation::kWord32, counter_address, value); | |||
| 8003 | } | |||
| 8004 | } | |||
| 8005 | ||||
| 8006 | void CodeStubAssembler::DecrementCounter(StatsCounter* counter, int delta) { | |||
| 8007 | DCHECK_GT(delta, 0)((void) 0); | |||
| 8008 | if (FLAG_native_code_counters && counter->Enabled()) { | |||
| 8009 | TNode<ExternalReference> counter_address = | |||
| 8010 | ExternalConstant(ExternalReference::Create(counter)); | |||
| 8011 | // This operation has to be exactly 32-bit wide in case the external | |||
| 8012 | // reference table redirects the counter to a uint32_t dummy_stats_counter_ | |||
| 8013 | // field. | |||
| 8014 | TNode<Int32T> value = Load<Int32T>(counter_address); | |||
| 8015 | value = Int32Sub(value, Int32Constant(delta)); | |||
| 8016 | StoreNoWriteBarrier(MachineRepresentation::kWord32, counter_address, value); | |||
| 8017 | } | |||
| 8018 | } | |||
| 8019 | ||||
| 8020 | template <typename TIndex> | |||
| 8021 | void CodeStubAssembler::Increment(TVariable<TIndex>* variable, int value) { | |||
| 8022 | *variable = | |||
| 8023 | IntPtrOrSmiAdd(variable->value(), IntPtrOrSmiConstant<TIndex>(value)); | |||
| 8024 | } | |||
| 8025 | ||||
| 8026 | // Instantiate Increment for Smi and IntPtrT. | |||
| 8027 | // TODO(v8:9708): Consider renaming to [Smi|IntPtrT|RawPtrT]Increment. | |||
| 8028 | template void CodeStubAssembler::Increment<Smi>(TVariable<Smi>* variable, | |||
| 8029 | int value); | |||
| 8030 | template void CodeStubAssembler::Increment<IntPtrT>( | |||
| 8031 | TVariable<IntPtrT>* variable, int value); | |||
| 8032 | template void CodeStubAssembler::Increment<RawPtrT>( | |||
| 8033 | TVariable<RawPtrT>* variable, int value); | |||
| 8034 | ||||
| 8035 | void CodeStubAssembler::Use(Label* label) { | |||
| 8036 | GotoIf(Word32Equal(Int32Constant(0), Int32Constant(1)), label); | |||
| 8037 | } | |||
| 8038 | ||||
| 8039 | void CodeStubAssembler::TryToName(TNode<Object> key, Label* if_keyisindex, | |||
| 8040 | TVariable<IntPtrT>* var_index, | |||
| 8041 | Label* if_keyisunique, | |||
| 8042 | TVariable<Name>* var_unique, | |||
| 8043 | Label* if_bailout, | |||
| 8044 | Label* if_notinternalized) { | |||
| 8045 | Comment("TryToName"); | |||
| 8046 | ||||
| 8047 | TVARIABLE(Int32T, var_instance_type)TVariable<Int32T> var_instance_type(this); | |||
| 8048 | Label if_keyisnotindex(this); | |||
| 8049 | *var_index = TryToIntptr(key, &if_keyisnotindex, &var_instance_type); | |||
| 8050 | Goto(if_keyisindex); | |||
| 8051 | ||||
| 8052 | BIND(&if_keyisnotindex)Bind(&if_keyisnotindex); | |||
| 8053 | { | |||
| 8054 | Label if_symbol(this), if_string(this), | |||
| 8055 | if_keyisother(this, Label::kDeferred); | |||
| 8056 | ||||
| 8057 | // Symbols are unique. | |||
| 8058 | GotoIf(IsSymbolInstanceType(var_instance_type.value()), &if_symbol); | |||
| 8059 | ||||
| 8060 | // Miss if |key| is not a String. | |||
| 8061 | STATIC_ASSERT(FIRST_NAME_TYPE == FIRST_TYPE)static_assert(FIRST_NAME_TYPE == FIRST_TYPE, "FIRST_NAME_TYPE == FIRST_TYPE" ); | |||
| 8062 | Branch(IsStringInstanceType(var_instance_type.value()), &if_string, | |||
| 8063 | &if_keyisother); | |||
| 8064 | ||||
| 8065 | // Symbols are unique. | |||
| 8066 | BIND(&if_symbol)Bind(&if_symbol); | |||
| 8067 | { | |||
| 8068 | *var_unique = CAST(key)Cast(key); | |||
| 8069 | Goto(if_keyisunique); | |||
| 8070 | } | |||
| 8071 | ||||
| 8072 | BIND(&if_string)Bind(&if_string); | |||
| 8073 | { | |||
| 8074 | Label if_thinstring(this), if_has_cached_index(this); | |||
| 8075 | ||||
| 8076 | TNode<Uint32T> raw_hash_field = LoadNameRawHashField(CAST(key)Cast(key)); | |||
| 8077 | GotoIf(IsClearWord32(raw_hash_field, | |||
| 8078 | Name::kDoesNotContainCachedArrayIndexMask), | |||
| 8079 | &if_has_cached_index); | |||
| 8080 | // No cached array index. If the string knows that it contains an index, | |||
| 8081 | // then it must be an uncacheable index. Handle this case in the runtime. | |||
| 8082 | GotoIf(IsEqualInWord32<Name::HashFieldTypeBits>( | |||
| 8083 | raw_hash_field, Name::HashFieldType::kIntegerIndex), | |||
| 8084 | if_bailout); | |||
| 8085 | ||||
| 8086 | GotoIf(InstanceTypeEqual(var_instance_type.value(), THIN_STRING_TYPE), | |||
| 8087 | &if_thinstring); | |||
| 8088 | GotoIf(InstanceTypeEqual(var_instance_type.value(), | |||
| 8089 | THIN_ONE_BYTE_STRING_TYPE), | |||
| 8090 | &if_thinstring); | |||
| 8091 | // Finally, check if |key| is internalized. | |||
| 8092 | STATIC_ASSERT(kNotInternalizedTag != 0)static_assert(kNotInternalizedTag != 0, "kNotInternalizedTag != 0" ); | |||
| 8093 | GotoIf(IsSetWord32(var_instance_type.value(), kIsNotInternalizedMask), | |||
| 8094 | if_notinternalized != nullptr ? if_notinternalized : if_bailout); | |||
| 8095 | ||||
| 8096 | *var_unique = CAST(key)Cast(key); | |||
| 8097 | Goto(if_keyisunique); | |||
| 8098 | ||||
| 8099 | BIND(&if_thinstring)Bind(&if_thinstring); | |||
| 8100 | { | |||
| 8101 | *var_unique = | |||
| 8102 | LoadObjectField<String>(CAST(key)Cast(key), ThinString::kActualOffset); | |||
| 8103 | Goto(if_keyisunique); | |||
| 8104 | } | |||
| 8105 | ||||
| 8106 | BIND(&if_has_cached_index)Bind(&if_has_cached_index); | |||
| 8107 | { | |||
| 8108 | TNode<IntPtrT> index = Signed( | |||
| 8109 | DecodeWordFromWord32<String::ArrayIndexValueBits>(raw_hash_field)); | |||
| 8110 | CSA_DCHECK(this, IntPtrLessThan(index, IntPtrConstant(INT_MAX)))((void)0); | |||
| 8111 | *var_index = index; | |||
| 8112 | Goto(if_keyisindex); | |||
| 8113 | } | |||
| 8114 | } | |||
| 8115 | ||||
| 8116 | BIND(&if_keyisother)Bind(&if_keyisother); | |||
| 8117 | { | |||
| 8118 | GotoIfNot(InstanceTypeEqual(var_instance_type.value(), ODDBALL_TYPE), | |||
| 8119 | if_bailout); | |||
| 8120 | *var_unique = | |||
| 8121 | LoadObjectField<String>(CAST(key)Cast(key), Oddball::kToStringOffset); | |||
| 8122 | Goto(if_keyisunique); | |||
| 8123 | } | |||
| 8124 | } | |||
| 8125 | } | |||
| 8126 | ||||
| 8127 | void CodeStubAssembler::StringWriteToFlatOneByte(TNode<String> source, | |||
| 8128 | TNode<RawPtrT> sink, | |||
| 8129 | TNode<Int32T> start, | |||
| 8130 | TNode<Int32T> length) { | |||
| 8131 | TNode<ExternalReference> function = | |||
| 8132 | ExternalConstant(ExternalReference::string_write_to_flat_one_byte()); | |||
| 8133 | CallCFunction(function, base::nullopt, | |||
| 8134 | std::make_pair(MachineType::AnyTagged(), source), | |||
| 8135 | std::make_pair(MachineType::Pointer(), sink), | |||
| 8136 | std::make_pair(MachineType::Int32(), start), | |||
| 8137 | std::make_pair(MachineType::Int32(), length)); | |||
| 8138 | } | |||
| 8139 | ||||
| 8140 | void CodeStubAssembler::StringWriteToFlatTwoByte(TNode<String> source, | |||
| 8141 | TNode<RawPtrT> sink, | |||
| 8142 | TNode<Int32T> start, | |||
| 8143 | TNode<Int32T> length) { | |||
| 8144 | TNode<ExternalReference> function = | |||
| 8145 | ExternalConstant(ExternalReference::string_write_to_flat_two_byte()); | |||
| 8146 | CallCFunction(function, base::nullopt, | |||
| 8147 | std::make_pair(MachineType::AnyTagged(), source), | |||
| 8148 | std::make_pair(MachineType::Pointer(), sink), | |||
| 8149 | std::make_pair(MachineType::Int32(), start), | |||
| 8150 | std::make_pair(MachineType::Int32(), length)); | |||
| 8151 | } | |||
| 8152 | ||||
| 8153 | TNode<RawPtr<Uint8T>> CodeStubAssembler::ExternalOneByteStringGetChars( | |||
| 8154 | TNode<ExternalOneByteString> string) { | |||
| 8155 | TNode<ExternalReference> function = | |||
| 8156 | ExternalConstant(ExternalReference::external_one_byte_string_get_chars()); | |||
| 8157 | return UncheckedCast<RawPtr<Uint8T>>( | |||
| 8158 | CallCFunction(function, MachineType::Pointer(), | |||
| 8159 | std::make_pair(MachineType::AnyTagged(), string))); | |||
| 8160 | } | |||
| 8161 | ||||
| 8162 | TNode<RawPtr<Uint16T>> CodeStubAssembler::ExternalTwoByteStringGetChars( | |||
| 8163 | TNode<ExternalTwoByteString> string) { | |||
| 8164 | TNode<ExternalReference> function = | |||
| 8165 | ExternalConstant(ExternalReference::external_two_byte_string_get_chars()); | |||
| 8166 | return UncheckedCast<RawPtr<Uint16T>>( | |||
| 8167 | CallCFunction(function, MachineType::Pointer(), | |||
| 8168 | std::make_pair(MachineType::AnyTagged(), string))); | |||
| 8169 | } | |||
| 8170 | ||||
| 8171 | TNode<RawPtr<Uint8T>> CodeStubAssembler::IntlAsciiCollationWeightsL1() { | |||
| 8172 | #ifdef V8_INTL_SUPPORT1 | |||
| 8173 | TNode<RawPtrT> ptr = | |||
| 8174 | ExternalConstant(ExternalReference::intl_ascii_collation_weights_l1()); | |||
| 8175 | return ReinterpretCast<RawPtr<Uint8T>>(ptr); | |||
| 8176 | #else | |||
| 8177 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 8178 | #endif | |||
| 8179 | } | |||
| 8180 | TNode<RawPtr<Uint8T>> CodeStubAssembler::IntlAsciiCollationWeightsL3() { | |||
| 8181 | #ifdef V8_INTL_SUPPORT1 | |||
| 8182 | TNode<RawPtrT> ptr = | |||
| 8183 | ExternalConstant(ExternalReference::intl_ascii_collation_weights_l3()); | |||
| 8184 | return ReinterpretCast<RawPtr<Uint8T>>(ptr); | |||
| 8185 | #else | |||
| 8186 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 8187 | #endif | |||
| 8188 | } | |||
| 8189 | ||||
| 8190 | void CodeStubAssembler::TryInternalizeString( | |||
| 8191 | TNode<String> string, Label* if_index, TVariable<IntPtrT>* var_index, | |||
| 8192 | Label* if_internalized, TVariable<Name>* var_internalized, | |||
| 8193 | Label* if_not_internalized, Label* if_bailout) { | |||
| 8194 | TNode<ExternalReference> function = ExternalConstant( | |||
| 8195 | ExternalReference::try_string_to_index_or_lookup_existing()); | |||
| 8196 | const TNode<ExternalReference> isolate_ptr = | |||
| 8197 | ExternalConstant(ExternalReference::isolate_address(isolate())); | |||
| 8198 | TNode<Object> result = | |||
| 8199 | CAST(CallCFunction(function, MachineType::AnyTagged(),Cast(CallCFunction(function, MachineType::AnyTagged(), std::make_pair (MachineType::Pointer(), isolate_ptr), std::make_pair(MachineType ::AnyTagged(), string))) | |||
| 8200 | std::make_pair(MachineType::Pointer(), isolate_ptr),Cast(CallCFunction(function, MachineType::AnyTagged(), std::make_pair (MachineType::Pointer(), isolate_ptr), std::make_pair(MachineType ::AnyTagged(), string))) | |||
| 8201 | std::make_pair(MachineType::AnyTagged(), string)))Cast(CallCFunction(function, MachineType::AnyTagged(), std::make_pair (MachineType::Pointer(), isolate_ptr), std::make_pair(MachineType ::AnyTagged(), string))); | |||
| 8202 | Label internalized(this); | |||
| 8203 | GotoIf(TaggedIsNotSmi(result), &internalized); | |||
| 8204 | TNode<IntPtrT> word_result = SmiUntag(CAST(result)Cast(result)); | |||
| 8205 | GotoIf(IntPtrEqual(word_result, IntPtrConstant(ResultSentinel::kNotFound)), | |||
| 8206 | if_not_internalized); | |||
| 8207 | GotoIf(IntPtrEqual(word_result, IntPtrConstant(ResultSentinel::kUnsupported)), | |||
| 8208 | if_bailout); | |||
| 8209 | *var_index = word_result; | |||
| 8210 | Goto(if_index); | |||
| 8211 | ||||
| 8212 | BIND(&internalized)Bind(&internalized); | |||
| 8213 | *var_internalized = CAST(result)Cast(result); | |||
| 8214 | Goto(if_internalized); | |||
| 8215 | } | |||
| 8216 | ||||
| 8217 | template <typename Dictionary> | |||
| 8218 | TNode<IntPtrT> CodeStubAssembler::EntryToIndex(TNode<IntPtrT> entry, | |||
| 8219 | int field_index) { | |||
| 8220 | TNode<IntPtrT> entry_index = | |||
| 8221 | IntPtrMul(entry, IntPtrConstant(Dictionary::kEntrySize)); | |||
| 8222 | return IntPtrAdd(entry_index, IntPtrConstant(Dictionary::kElementsStartIndex + | |||
| 8223 | field_index)); | |||
| 8224 | } | |||
| 8225 | ||||
| 8226 | template <typename T> | |||
| 8227 | TNode<T> CodeStubAssembler::LoadDescriptorArrayElement( | |||
| 8228 | TNode<DescriptorArray> object, TNode<IntPtrT> index, | |||
| 8229 | int additional_offset) { | |||
| 8230 | return LoadArrayElement<DescriptorArray, IntPtrT, T>( | |||
| 8231 | object, DescriptorArray::kHeaderSize, index, additional_offset); | |||
| 8232 | } | |||
| 8233 | ||||
| 8234 | TNode<Name> CodeStubAssembler::LoadKeyByKeyIndex( | |||
| 8235 | TNode<DescriptorArray> container, TNode<IntPtrT> key_index) { | |||
| 8236 | return CAST(LoadDescriptorArrayElement<HeapObject>(container, key_index, 0))Cast(LoadDescriptorArrayElement<HeapObject>(container, key_index , 0)); | |||
| 8237 | } | |||
| 8238 | ||||
| 8239 | TNode<Uint32T> CodeStubAssembler::LoadDetailsByKeyIndex( | |||
| 8240 | TNode<DescriptorArray> container, TNode<IntPtrT> key_index) { | |||
| 8241 | const int kKeyToDetailsOffset = | |||
| 8242 | DescriptorArray::kEntryDetailsOffset - DescriptorArray::kEntryKeyOffset; | |||
| 8243 | return Unsigned(LoadAndUntagToWord32ArrayElement( | |||
| 8244 | container, DescriptorArray::kHeaderSize, key_index, kKeyToDetailsOffset)); | |||
| 8245 | } | |||
| 8246 | ||||
| 8247 | TNode<Object> CodeStubAssembler::LoadValueByKeyIndex( | |||
| 8248 | TNode<DescriptorArray> container, TNode<IntPtrT> key_index) { | |||
| 8249 | const int kKeyToValueOffset = | |||
| 8250 | DescriptorArray::kEntryValueOffset - DescriptorArray::kEntryKeyOffset; | |||
| 8251 | return LoadDescriptorArrayElement<Object>(container, key_index, | |||
| 8252 | kKeyToValueOffset); | |||
| 8253 | } | |||
| 8254 | ||||
| 8255 | TNode<MaybeObject> CodeStubAssembler::LoadFieldTypeByKeyIndex( | |||
| 8256 | TNode<DescriptorArray> container, TNode<IntPtrT> key_index) { | |||
| 8257 | const int kKeyToValueOffset = | |||
| 8258 | DescriptorArray::kEntryValueOffset - DescriptorArray::kEntryKeyOffset; | |||
| 8259 | return LoadDescriptorArrayElement<MaybeObject>(container, key_index, | |||
| 8260 | kKeyToValueOffset); | |||
| 8261 | } | |||
| 8262 | ||||
| 8263 | TNode<IntPtrT> CodeStubAssembler::DescriptorEntryToIndex( | |||
| 8264 | TNode<IntPtrT> descriptor_entry) { | |||
| 8265 | return IntPtrMul(descriptor_entry, | |||
| 8266 | IntPtrConstant(DescriptorArray::kEntrySize)); | |||
| 8267 | } | |||
| 8268 | ||||
| 8269 | TNode<Name> CodeStubAssembler::LoadKeyByDescriptorEntry( | |||
| 8270 | TNode<DescriptorArray> container, TNode<IntPtrT> descriptor_entry) { | |||
| 8271 | return CAST(LoadDescriptorArrayElement<HeapObject>(Cast(LoadDescriptorArrayElement<HeapObject>( container, DescriptorEntryToIndex(descriptor_entry), DescriptorArray::ToKeyIndex (0) * kTaggedSize)) | |||
| 8272 | container, DescriptorEntryToIndex(descriptor_entry),Cast(LoadDescriptorArrayElement<HeapObject>( container, DescriptorEntryToIndex(descriptor_entry), DescriptorArray::ToKeyIndex (0) * kTaggedSize)) | |||
| 8273 | DescriptorArray::ToKeyIndex(0) * kTaggedSize))Cast(LoadDescriptorArrayElement<HeapObject>( container, DescriptorEntryToIndex(descriptor_entry), DescriptorArray::ToKeyIndex (0) * kTaggedSize)); | |||
| 8274 | } | |||
| 8275 | ||||
| 8276 | TNode<Name> CodeStubAssembler::LoadKeyByDescriptorEntry( | |||
| 8277 | TNode<DescriptorArray> container, int descriptor_entry) { | |||
| 8278 | return CAST(LoadDescriptorArrayElement<HeapObject>(Cast(LoadDescriptorArrayElement<HeapObject>( container, IntPtrConstant(0), DescriptorArray::ToKeyIndex(descriptor_entry ) * kTaggedSize)) | |||
| 8279 | container, IntPtrConstant(0),Cast(LoadDescriptorArrayElement<HeapObject>( container, IntPtrConstant(0), DescriptorArray::ToKeyIndex(descriptor_entry ) * kTaggedSize)) | |||
| 8280 | DescriptorArray::ToKeyIndex(descriptor_entry) * kTaggedSize))Cast(LoadDescriptorArrayElement<HeapObject>( container, IntPtrConstant(0), DescriptorArray::ToKeyIndex(descriptor_entry ) * kTaggedSize)); | |||
| 8281 | } | |||
| 8282 | ||||
| 8283 | TNode<Uint32T> CodeStubAssembler::LoadDetailsByDescriptorEntry( | |||
| 8284 | TNode<DescriptorArray> container, TNode<IntPtrT> descriptor_entry) { | |||
| 8285 | return Unsigned(LoadAndUntagToWord32ArrayElement( | |||
| 8286 | container, DescriptorArray::kHeaderSize, | |||
| 8287 | DescriptorEntryToIndex(descriptor_entry), | |||
| 8288 | DescriptorArray::ToDetailsIndex(0) * kTaggedSize)); | |||
| 8289 | } | |||
| 8290 | ||||
| 8291 | TNode<Uint32T> CodeStubAssembler::LoadDetailsByDescriptorEntry( | |||
| 8292 | TNode<DescriptorArray> container, int descriptor_entry) { | |||
| 8293 | return Unsigned(LoadAndUntagToWord32ArrayElement( | |||
| 8294 | container, DescriptorArray::kHeaderSize, IntPtrConstant(0), | |||
| 8295 | DescriptorArray::ToDetailsIndex(descriptor_entry) * kTaggedSize)); | |||
| 8296 | } | |||
| 8297 | ||||
| 8298 | TNode<Object> CodeStubAssembler::LoadValueByDescriptorEntry( | |||
| 8299 | TNode<DescriptorArray> container, TNode<IntPtrT> descriptor_entry) { | |||
| 8300 | return LoadDescriptorArrayElement<Object>( | |||
| 8301 | container, DescriptorEntryToIndex(descriptor_entry), | |||
| 8302 | DescriptorArray::ToValueIndex(0) * kTaggedSize); | |||
| 8303 | } | |||
| 8304 | ||||
| 8305 | TNode<Object> CodeStubAssembler::LoadValueByDescriptorEntry( | |||
| 8306 | TNode<DescriptorArray> container, int descriptor_entry) { | |||
| 8307 | return LoadDescriptorArrayElement<Object>( | |||
| 8308 | container, IntPtrConstant(0), | |||
| 8309 | DescriptorArray::ToValueIndex(descriptor_entry) * kTaggedSize); | |||
| 8310 | } | |||
| 8311 | ||||
| 8312 | TNode<MaybeObject> CodeStubAssembler::LoadFieldTypeByDescriptorEntry( | |||
| 8313 | TNode<DescriptorArray> container, TNode<IntPtrT> descriptor_entry) { | |||
| 8314 | return LoadDescriptorArrayElement<MaybeObject>( | |||
| 8315 | container, DescriptorEntryToIndex(descriptor_entry), | |||
| 8316 | DescriptorArray::ToValueIndex(0) * kTaggedSize); | |||
| 8317 | } | |||
| 8318 | ||||
| 8319 | // Loads the value for the entry with the given key_index. | |||
| 8320 | // Returns a tagged value. | |||
| 8321 | template <class ContainerType> | |||
| 8322 | TNode<Object> CodeStubAssembler::LoadValueByKeyIndex( | |||
| 8323 | TNode<ContainerType> container, TNode<IntPtrT> key_index) { | |||
| 8324 | static_assert(!std::is_same<ContainerType, DescriptorArray>::value, | |||
| 8325 | "Use the non-templatized version for DescriptorArray"); | |||
| 8326 | const int kKeyToValueOffset = | |||
| 8327 | (ContainerType::kEntryValueIndex - ContainerType::kEntryKeyIndex) * | |||
| 8328 | kTaggedSize; | |||
| 8329 | return LoadFixedArrayElement(container, key_index, kKeyToValueOffset); | |||
| 8330 | } | |||
| 8331 | ||||
| 8332 | template <> | |||
| 8333 | V8_EXPORT_PRIVATE TNode<Object> CodeStubAssembler::LoadValueByKeyIndex( | |||
| 8334 | TNode<SwissNameDictionary> container, TNode<IntPtrT> key_index) { | |||
| 8335 | TNode<IntPtrT> offset_minus_tag = SwissNameDictionaryOffsetIntoDataTableMT( | |||
| 8336 | container, key_index, SwissNameDictionary::kDataTableValueEntryIndex); | |||
| 8337 | ||||
| 8338 | return Load<Object>(container, offset_minus_tag); | |||
| 8339 | } | |||
| 8340 | ||||
| 8341 | template <class ContainerType> | |||
| 8342 | TNode<Uint32T> CodeStubAssembler::LoadDetailsByKeyIndex( | |||
| 8343 | TNode<ContainerType> container, TNode<IntPtrT> key_index) { | |||
| 8344 | static_assert(!std::is_same<ContainerType, DescriptorArray>::value, | |||
| 8345 | "Use the non-templatized version for DescriptorArray"); | |||
| 8346 | const int kKeyToDetailsOffset = | |||
| 8347 | (ContainerType::kEntryDetailsIndex - ContainerType::kEntryKeyIndex) * | |||
| 8348 | kTaggedSize; | |||
| 8349 | return Unsigned(LoadAndUntagToWord32FixedArrayElement(container, key_index, | |||
| 8350 | kKeyToDetailsOffset)); | |||
| 8351 | } | |||
| 8352 | ||||
| 8353 | template <> | |||
| 8354 | V8_EXPORT_PRIVATE TNode<Uint32T> CodeStubAssembler::LoadDetailsByKeyIndex( | |||
| 8355 | TNode<SwissNameDictionary> container, TNode<IntPtrT> key_index) { | |||
| 8356 | TNode<IntPtrT> capacity = | |||
| 8357 | ChangeInt32ToIntPtr(LoadSwissNameDictionaryCapacity(container)); | |||
| 8358 | return LoadSwissNameDictionaryPropertyDetails(container, capacity, key_index); | |||
| 8359 | } | |||
| 8360 | ||||
| 8361 | // Stores the details for the entry with the given key_index. | |||
| 8362 | // |details| must be a Smi. | |||
| 8363 | template <class ContainerType> | |||
| 8364 | void CodeStubAssembler::StoreDetailsByKeyIndex(TNode<ContainerType> container, | |||
| 8365 | TNode<IntPtrT> key_index, | |||
| 8366 | TNode<Smi> details) { | |||
| 8367 | const int kKeyToDetailsOffset = | |||
| 8368 | (ContainerType::kEntryDetailsIndex - ContainerType::kEntryKeyIndex) * | |||
| 8369 | kTaggedSize; | |||
| 8370 | StoreFixedArrayElement(container, key_index, details, kKeyToDetailsOffset); | |||
| 8371 | } | |||
| 8372 | ||||
| 8373 | template <> | |||
| 8374 | V8_EXPORT_PRIVATE void CodeStubAssembler::StoreDetailsByKeyIndex( | |||
| 8375 | TNode<SwissNameDictionary> container, TNode<IntPtrT> key_index, | |||
| 8376 | TNode<Smi> details) { | |||
| 8377 | TNode<IntPtrT> capacity = | |||
| 8378 | ChangeInt32ToIntPtr(LoadSwissNameDictionaryCapacity(container)); | |||
| 8379 | TNode<Uint8T> details_byte = UncheckedCast<Uint8T>(SmiToInt32(details)); | |||
| 8380 | StoreSwissNameDictionaryPropertyDetails(container, capacity, key_index, | |||
| 8381 | details_byte); | |||
| 8382 | } | |||
| 8383 | ||||
| 8384 | // Stores the value for the entry with the given key_index. | |||
| 8385 | template <class ContainerType> | |||
| 8386 | void CodeStubAssembler::StoreValueByKeyIndex(TNode<ContainerType> container, | |||
| 8387 | TNode<IntPtrT> key_index, | |||
| 8388 | TNode<Object> value, | |||
| 8389 | WriteBarrierMode write_barrier) { | |||
| 8390 | const int kKeyToValueOffset = | |||
| 8391 | (ContainerType::kEntryValueIndex - ContainerType::kEntryKeyIndex) * | |||
| 8392 | kTaggedSize; | |||
| 8393 | StoreFixedArrayElement(container, key_index, value, write_barrier, | |||
| 8394 | kKeyToValueOffset); | |||
| 8395 | } | |||
| 8396 | ||||
| 8397 | template <> | |||
| 8398 | V8_EXPORT_PRIVATE void CodeStubAssembler::StoreValueByKeyIndex( | |||
| 8399 | TNode<SwissNameDictionary> container, TNode<IntPtrT> key_index, | |||
| 8400 | TNode<Object> value, WriteBarrierMode write_barrier) { | |||
| 8401 | TNode<IntPtrT> offset_minus_tag = SwissNameDictionaryOffsetIntoDataTableMT( | |||
| 8402 | container, key_index, SwissNameDictionary::kDataTableValueEntryIndex); | |||
| 8403 | ||||
| 8404 | StoreToObjectWriteBarrier mode; | |||
| 8405 | switch (write_barrier) { | |||
| 8406 | case UNSAFE_SKIP_WRITE_BARRIER: | |||
| 8407 | case SKIP_WRITE_BARRIER: | |||
| 8408 | mode = StoreToObjectWriteBarrier::kNone; | |||
| 8409 | break; | |||
| 8410 | case UPDATE_WRITE_BARRIER: | |||
| 8411 | mode = StoreToObjectWriteBarrier::kFull; | |||
| 8412 | break; | |||
| 8413 | default: | |||
| 8414 | // We shouldn't see anything else. | |||
| 8415 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 8416 | } | |||
| 8417 | StoreToObject(MachineRepresentation::kTagged, container, offset_minus_tag, | |||
| 8418 | value, mode); | |||
| 8419 | } | |||
| 8420 | ||||
| 8421 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 8422 | CodeStubAssembler::EntryToIndex<NameDictionary>(TNode<IntPtrT>, int); | |||
| 8423 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 8424 | CodeStubAssembler::EntryToIndex<GlobalDictionary>(TNode<IntPtrT>, int); | |||
| 8425 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 8426 | CodeStubAssembler::EntryToIndex<NumberDictionary>(TNode<IntPtrT>, int); | |||
| 8427 | ||||
| 8428 | template TNode<Object> CodeStubAssembler::LoadValueByKeyIndex( | |||
| 8429 | TNode<NameDictionary> container, TNode<IntPtrT> key_index); | |||
| 8430 | template TNode<Object> CodeStubAssembler::LoadValueByKeyIndex( | |||
| 8431 | TNode<GlobalDictionary> container, TNode<IntPtrT> key_index); | |||
| 8432 | template TNode<Uint32T> CodeStubAssembler::LoadDetailsByKeyIndex( | |||
| 8433 | TNode<NameDictionary> container, TNode<IntPtrT> key_index); | |||
| 8434 | template void CodeStubAssembler::StoreDetailsByKeyIndex( | |||
| 8435 | TNode<NameDictionary> container, TNode<IntPtrT> key_index, | |||
| 8436 | TNode<Smi> details); | |||
| 8437 | template void CodeStubAssembler::StoreValueByKeyIndex( | |||
| 8438 | TNode<NameDictionary> container, TNode<IntPtrT> key_index, | |||
| 8439 | TNode<Object> value, WriteBarrierMode write_barrier); | |||
| 8440 | ||||
| 8441 | // This must be kept in sync with HashTableBase::ComputeCapacity(). | |||
| 8442 | TNode<IntPtrT> CodeStubAssembler::HashTableComputeCapacity( | |||
| 8443 | TNode<IntPtrT> at_least_space_for) { | |||
| 8444 | TNode<IntPtrT> capacity = IntPtrRoundUpToPowerOfTwo32( | |||
| 8445 | IntPtrAdd(at_least_space_for, WordShr(at_least_space_for, 1))); | |||
| 8446 | return IntPtrMax(capacity, IntPtrConstant(HashTableBase::kMinCapacity)); | |||
| 8447 | } | |||
| 8448 | ||||
| 8449 | TNode<IntPtrT> CodeStubAssembler::IntPtrMax(TNode<IntPtrT> left, | |||
| 8450 | TNode<IntPtrT> right) { | |||
| 8451 | intptr_t left_constant; | |||
| 8452 | intptr_t right_constant; | |||
| 8453 | if (TryToIntPtrConstant(left, &left_constant) && | |||
| 8454 | TryToIntPtrConstant(right, &right_constant)) { | |||
| 8455 | return IntPtrConstant(std::max(left_constant, right_constant)); | |||
| 8456 | } | |||
| 8457 | return SelectConstant<IntPtrT>(IntPtrGreaterThanOrEqual(left, right), left, | |||
| 8458 | right); | |||
| 8459 | } | |||
| 8460 | ||||
| 8461 | TNode<IntPtrT> CodeStubAssembler::IntPtrMin(TNode<IntPtrT> left, | |||
| 8462 | TNode<IntPtrT> right) { | |||
| 8463 | intptr_t left_constant; | |||
| 8464 | intptr_t right_constant; | |||
| 8465 | if (TryToIntPtrConstant(left, &left_constant) && | |||
| 8466 | TryToIntPtrConstant(right, &right_constant)) { | |||
| 8467 | return IntPtrConstant(std::min(left_constant, right_constant)); | |||
| 8468 | } | |||
| 8469 | return SelectConstant<IntPtrT>(IntPtrLessThanOrEqual(left, right), left, | |||
| 8470 | right); | |||
| 8471 | } | |||
| 8472 | ||||
| 8473 | TNode<UintPtrT> CodeStubAssembler::UintPtrMin(TNode<UintPtrT> left, | |||
| 8474 | TNode<UintPtrT> right) { | |||
| 8475 | intptr_t left_constant; | |||
| 8476 | intptr_t right_constant; | |||
| 8477 | if (TryToIntPtrConstant(left, &left_constant) && | |||
| 8478 | TryToIntPtrConstant(right, &right_constant)) { | |||
| 8479 | return UintPtrConstant(std::min(static_cast<uintptr_t>(left_constant), | |||
| 8480 | static_cast<uintptr_t>(right_constant))); | |||
| 8481 | } | |||
| 8482 | return SelectConstant<UintPtrT>(UintPtrLessThanOrEqual(left, right), left, | |||
| 8483 | right); | |||
| 8484 | } | |||
| 8485 | ||||
| 8486 | template <> | |||
| 8487 | TNode<HeapObject> CodeStubAssembler::LoadName<NameDictionary>( | |||
| 8488 | TNode<HeapObject> key) { | |||
| 8489 | CSA_DCHECK(this, Word32Or(IsTheHole(key), IsName(key)))((void)0); | |||
| 8490 | return key; | |||
| 8491 | } | |||
| 8492 | ||||
| 8493 | template <> | |||
| 8494 | TNode<HeapObject> CodeStubAssembler::LoadName<GlobalDictionary>( | |||
| 8495 | TNode<HeapObject> key) { | |||
| 8496 | TNode<PropertyCell> property_cell = CAST(key)Cast(key); | |||
| 8497 | return CAST(LoadObjectField(property_cell, PropertyCell::kNameOffset))Cast(LoadObjectField(property_cell, PropertyCell::kNameOffset )); | |||
| 8498 | } | |||
| 8499 | ||||
| 8500 | template <> | |||
| 8501 | TNode<HeapObject> CodeStubAssembler::LoadName<NameToIndexHashTable>( | |||
| 8502 | TNode<HeapObject> key) { | |||
| 8503 | CSA_DCHECK(this, IsName(key))((void)0); | |||
| 8504 | return key; | |||
| 8505 | } | |||
| 8506 | ||||
| 8507 | // The implementation should be in sync with NameToIndexHashTable::Lookup. | |||
| 8508 | TNode<IntPtrT> CodeStubAssembler::NameToIndexHashTableLookup( | |||
| 8509 | TNode<NameToIndexHashTable> table, TNode<Name> name, Label* not_found) { | |||
| 8510 | TVARIABLE(IntPtrT, var_entry)TVariable<IntPtrT> var_entry(this); | |||
| 8511 | Label index_found(this, {&var_entry}); | |||
| 8512 | NameDictionaryLookup<NameToIndexHashTable>(table, name, &index_found, | |||
| 8513 | &var_entry, not_found, | |||
| 8514 | LookupMode::kFindExisting); | |||
| 8515 | BIND(&index_found)Bind(&index_found); | |||
| 8516 | TNode<Smi> value = | |||
| 8517 | CAST(LoadValueByKeyIndex<NameToIndexHashTable>(table, var_entry.value()))Cast(LoadValueByKeyIndex<NameToIndexHashTable>(table, var_entry .value())); | |||
| 8518 | return SmiToIntPtr(value); | |||
| 8519 | } | |||
| 8520 | ||||
| 8521 | template <typename Dictionary> | |||
| 8522 | void CodeStubAssembler::NameDictionaryLookup( | |||
| 8523 | TNode<Dictionary> dictionary, TNode<Name> unique_name, Label* if_found, | |||
| 8524 | TVariable<IntPtrT>* var_name_index, Label* if_not_found, LookupMode mode) { | |||
| 8525 | static_assert(std::is_same<Dictionary, NameDictionary>::value || | |||
| 8526 | std::is_same<Dictionary, GlobalDictionary>::value || | |||
| 8527 | std::is_same<Dictionary, NameToIndexHashTable>::value, | |||
| 8528 | "Unexpected NameDictionary"); | |||
| 8529 | DCHECK_EQ(MachineType::PointerRepresentation(), var_name_index->rep())((void) 0); | |||
| 8530 | DCHECK_IMPLIES(mode == kFindInsertionIndex, if_found == nullptr)((void) 0); | |||
| 8531 | Comment("NameDictionaryLookup"); | |||
| 8532 | CSA_DCHECK(this, IsUniqueName(unique_name))((void)0); | |||
| 8533 | ||||
| 8534 | TNode<IntPtrT> capacity = SmiUntag(GetCapacity<Dictionary>(dictionary)); | |||
| 8535 | TNode<IntPtrT> mask = IntPtrSub(capacity, IntPtrConstant(1)); | |||
| 8536 | TNode<UintPtrT> hash = ChangeUint32ToWord(LoadNameHash(unique_name)); | |||
| 8537 | ||||
| 8538 | // See Dictionary::FirstProbe(). | |||
| 8539 | TNode<IntPtrT> count = IntPtrConstant(0); | |||
| 8540 | TNode<IntPtrT> initial_entry = Signed(WordAnd(hash, mask)); | |||
| 8541 | TNode<Oddball> undefined = UndefinedConstant(); | |||
| 8542 | ||||
| 8543 | // Appease the variable merging algorithm for "Goto(&loop)" below. | |||
| 8544 | *var_name_index = IntPtrConstant(0); | |||
| 8545 | ||||
| 8546 | TVARIABLE(IntPtrT, var_count, count)TVariable<IntPtrT> var_count(count, this); | |||
| 8547 | TVARIABLE(IntPtrT, var_entry, initial_entry)TVariable<IntPtrT> var_entry(initial_entry, this); | |||
| 8548 | Label loop(this, {&var_count, &var_entry, var_name_index}); | |||
| 8549 | Goto(&loop); | |||
| 8550 | BIND(&loop)Bind(&loop); | |||
| 8551 | { | |||
| 8552 | Label next_probe(this); | |||
| 8553 | TNode<IntPtrT> entry = var_entry.value(); | |||
| 8554 | ||||
| 8555 | TNode<IntPtrT> index = EntryToIndex<Dictionary>(entry); | |||
| 8556 | *var_name_index = index; | |||
| 8557 | ||||
| 8558 | TNode<HeapObject> current = | |||
| 8559 | CAST(UnsafeLoadFixedArrayElement(dictionary, index))Cast(UnsafeLoadFixedArrayElement(dictionary, index)); | |||
| 8560 | GotoIf(TaggedEqual(current, undefined), if_not_found); | |||
| 8561 | if (mode == kFindExisting) { | |||
| 8562 | if (Dictionary::ShapeT::kMatchNeedsHoleCheck) { | |||
| 8563 | GotoIf(TaggedEqual(current, TheHoleConstant()), &next_probe); | |||
| 8564 | } | |||
| 8565 | current = LoadName<Dictionary>(current); | |||
| 8566 | GotoIf(TaggedEqual(current, unique_name), if_found); | |||
| 8567 | } else { | |||
| 8568 | DCHECK_EQ(kFindInsertionIndex, mode)((void) 0); | |||
| 8569 | GotoIf(TaggedEqual(current, TheHoleConstant()), if_not_found); | |||
| 8570 | } | |||
| 8571 | Goto(&next_probe); | |||
| 8572 | ||||
| 8573 | BIND(&next_probe)Bind(&next_probe); | |||
| 8574 | // See Dictionary::NextProbe(). | |||
| 8575 | Increment(&var_count); | |||
| 8576 | entry = Signed(WordAnd(IntPtrAdd(entry, var_count.value()), mask)); | |||
| 8577 | ||||
| 8578 | var_entry = entry; | |||
| 8579 | Goto(&loop); | |||
| 8580 | } | |||
| 8581 | } | |||
| 8582 | ||||
| 8583 | // Instantiate template methods to workaround GCC compilation issue. | |||
| 8584 | template V8_EXPORT_PRIVATE void | |||
| 8585 | CodeStubAssembler::NameDictionaryLookup<NameDictionary>(TNode<NameDictionary>, | |||
| 8586 | TNode<Name>, Label*, | |||
| 8587 | TVariable<IntPtrT>*, | |||
| 8588 | Label*, LookupMode); | |||
| 8589 | template V8_EXPORT_PRIVATE void CodeStubAssembler::NameDictionaryLookup< | |||
| 8590 | GlobalDictionary>(TNode<GlobalDictionary>, TNode<Name>, Label*, | |||
| 8591 | TVariable<IntPtrT>*, Label*, LookupMode); | |||
| 8592 | ||||
| 8593 | TNode<Word32T> CodeStubAssembler::ComputeSeededHash(TNode<IntPtrT> key) { | |||
| 8594 | const TNode<ExternalReference> function_addr = | |||
| 8595 | ExternalConstant(ExternalReference::compute_integer_hash()); | |||
| 8596 | const TNode<ExternalReference> isolate_ptr = | |||
| 8597 | ExternalConstant(ExternalReference::isolate_address(isolate())); | |||
| 8598 | ||||
| 8599 | MachineType type_ptr = MachineType::Pointer(); | |||
| 8600 | MachineType type_uint32 = MachineType::Uint32(); | |||
| 8601 | MachineType type_int32 = MachineType::Int32(); | |||
| 8602 | ||||
| 8603 | return UncheckedCast<Word32T>(CallCFunction( | |||
| 8604 | function_addr, type_uint32, std::make_pair(type_ptr, isolate_ptr), | |||
| 8605 | std::make_pair(type_int32, TruncateIntPtrToInt32(key)))); | |||
| 8606 | } | |||
| 8607 | ||||
| 8608 | template <> | |||
| 8609 | void CodeStubAssembler::NameDictionaryLookup( | |||
| 8610 | TNode<SwissNameDictionary> dictionary, TNode<Name> unique_name, | |||
| 8611 | Label* if_found, TVariable<IntPtrT>* var_name_index, Label* if_not_found, | |||
| 8612 | LookupMode mode) { | |||
| 8613 | SwissNameDictionaryFindEntry(dictionary, unique_name, if_found, | |||
| 8614 | var_name_index, if_not_found); | |||
| 8615 | } | |||
| 8616 | ||||
| 8617 | void CodeStubAssembler::NumberDictionaryLookup( | |||
| 8618 | TNode<NumberDictionary> dictionary, TNode<IntPtrT> intptr_index, | |||
| 8619 | Label* if_found, TVariable<IntPtrT>* var_entry, Label* if_not_found) { | |||
| 8620 | CSA_DCHECK(this, IsNumberDictionary(dictionary))((void)0); | |||
| 8621 | DCHECK_EQ(MachineType::PointerRepresentation(), var_entry->rep())((void) 0); | |||
| 8622 | Comment("NumberDictionaryLookup"); | |||
| 8623 | ||||
| 8624 | TNode<IntPtrT> capacity = SmiUntag(GetCapacity<NumberDictionary>(dictionary)); | |||
| 8625 | TNode<IntPtrT> mask = IntPtrSub(capacity, IntPtrConstant(1)); | |||
| 8626 | ||||
| 8627 | TNode<UintPtrT> hash = ChangeUint32ToWord(ComputeSeededHash(intptr_index)); | |||
| 8628 | TNode<Float64T> key_as_float64 = RoundIntPtrToFloat64(intptr_index); | |||
| 8629 | ||||
| 8630 | // See Dictionary::FirstProbe(). | |||
| 8631 | TNode<IntPtrT> count = IntPtrConstant(0); | |||
| 8632 | TNode<IntPtrT> initial_entry = Signed(WordAnd(hash, mask)); | |||
| 8633 | ||||
| 8634 | TNode<Oddball> undefined = UndefinedConstant(); | |||
| 8635 | TNode<Oddball> the_hole = TheHoleConstant(); | |||
| 8636 | ||||
| 8637 | TVARIABLE(IntPtrT, var_count, count)TVariable<IntPtrT> var_count(count, this); | |||
| 8638 | Label loop(this, {&var_count, var_entry}); | |||
| 8639 | *var_entry = initial_entry; | |||
| 8640 | Goto(&loop); | |||
| 8641 | BIND(&loop)Bind(&loop); | |||
| 8642 | { | |||
| 8643 | TNode<IntPtrT> entry = var_entry->value(); | |||
| 8644 | ||||
| 8645 | TNode<IntPtrT> index = EntryToIndex<NumberDictionary>(entry); | |||
| 8646 | TNode<Object> current = UnsafeLoadFixedArrayElement(dictionary, index); | |||
| 8647 | GotoIf(TaggedEqual(current, undefined), if_not_found); | |||
| 8648 | Label next_probe(this); | |||
| 8649 | { | |||
| 8650 | Label if_currentissmi(this), if_currentisnotsmi(this); | |||
| 8651 | Branch(TaggedIsSmi(current), &if_currentissmi, &if_currentisnotsmi); | |||
| 8652 | BIND(&if_currentissmi)Bind(&if_currentissmi); | |||
| 8653 | { | |||
| 8654 | TNode<IntPtrT> current_value = SmiUntag(CAST(current)Cast(current)); | |||
| 8655 | Branch(WordEqual(current_value, intptr_index), if_found, &next_probe); | |||
| 8656 | } | |||
| 8657 | BIND(&if_currentisnotsmi)Bind(&if_currentisnotsmi); | |||
| 8658 | { | |||
| 8659 | GotoIf(TaggedEqual(current, the_hole), &next_probe); | |||
| 8660 | // Current must be the Number. | |||
| 8661 | TNode<Float64T> current_value = LoadHeapNumberValue(CAST(current)Cast(current)); | |||
| 8662 | Branch(Float64Equal(current_value, key_as_float64), if_found, | |||
| 8663 | &next_probe); | |||
| 8664 | } | |||
| 8665 | } | |||
| 8666 | ||||
| 8667 | BIND(&next_probe)Bind(&next_probe); | |||
| 8668 | // See Dictionary::NextProbe(). | |||
| 8669 | Increment(&var_count); | |||
| 8670 | entry = Signed(WordAnd(IntPtrAdd(entry, var_count.value()), mask)); | |||
| 8671 | ||||
| 8672 | *var_entry = entry; | |||
| 8673 | Goto(&loop); | |||
| 8674 | } | |||
| 8675 | } | |||
| 8676 | ||||
| 8677 | TNode<Object> CodeStubAssembler::BasicLoadNumberDictionaryElement( | |||
| 8678 | TNode<NumberDictionary> dictionary, TNode<IntPtrT> intptr_index, | |||
| 8679 | Label* not_data, Label* if_hole) { | |||
| 8680 | TVARIABLE(IntPtrT, var_entry)TVariable<IntPtrT> var_entry(this); | |||
| 8681 | Label if_found(this); | |||
| 8682 | NumberDictionaryLookup(dictionary, intptr_index, &if_found, &var_entry, | |||
| 8683 | if_hole); | |||
| 8684 | BIND(&if_found)Bind(&if_found); | |||
| 8685 | ||||
| 8686 | // Check that the value is a data property. | |||
| 8687 | TNode<IntPtrT> index = EntryToIndex<NumberDictionary>(var_entry.value()); | |||
| 8688 | TNode<Uint32T> details = LoadDetailsByKeyIndex(dictionary, index); | |||
| 8689 | TNode<Uint32T> kind = DecodeWord32<PropertyDetails::KindField>(details); | |||
| 8690 | // TODO(jkummerow): Support accessors without missing? | |||
| 8691 | GotoIfNot( | |||
| 8692 | Word32Equal(kind, Int32Constant(static_cast<int>(PropertyKind::kData))), | |||
| 8693 | not_data); | |||
| 8694 | // Finally, load the value. | |||
| 8695 | return LoadValueByKeyIndex(dictionary, index); | |||
| 8696 | } | |||
| 8697 | ||||
| 8698 | template <class Dictionary> | |||
| 8699 | void CodeStubAssembler::FindInsertionEntry(TNode<Dictionary> dictionary, | |||
| 8700 | TNode<Name> key, | |||
| 8701 | TVariable<IntPtrT>* var_key_index) { | |||
| 8702 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 8703 | } | |||
| 8704 | ||||
| 8705 | template <> | |||
| 8706 | void CodeStubAssembler::FindInsertionEntry<NameDictionary>( | |||
| 8707 | TNode<NameDictionary> dictionary, TNode<Name> key, | |||
| 8708 | TVariable<IntPtrT>* var_key_index) { | |||
| 8709 | Label done(this); | |||
| 8710 | NameDictionaryLookup<NameDictionary>(dictionary, key, nullptr, var_key_index, | |||
| 8711 | &done, kFindInsertionIndex); | |||
| 8712 | BIND(&done)Bind(&done); | |||
| 8713 | } | |||
| 8714 | ||||
| 8715 | template <class Dictionary> | |||
| 8716 | void CodeStubAssembler::InsertEntry(TNode<Dictionary> dictionary, | |||
| 8717 | TNode<Name> key, TNode<Object> value, | |||
| 8718 | TNode<IntPtrT> index, | |||
| 8719 | TNode<Smi> enum_index) { | |||
| 8720 | UNREACHABLE()V8_Fatal("unreachable code"); // Use specializations instead. | |||
| 8721 | } | |||
| 8722 | ||||
| 8723 | template <> | |||
| 8724 | void CodeStubAssembler::InsertEntry<NameDictionary>( | |||
| 8725 | TNode<NameDictionary> dictionary, TNode<Name> name, TNode<Object> value, | |||
| 8726 | TNode<IntPtrT> index, TNode<Smi> enum_index) { | |||
| 8727 | // This should only be used for adding, not updating existing mappings. | |||
| 8728 | CSA_DCHECK(this,((void)0) | |||
| 8729 | Word32Or(TaggedEqual(LoadFixedArrayElement(dictionary, index),((void)0) | |||
| 8730 | UndefinedConstant()),((void)0) | |||
| 8731 | TaggedEqual(LoadFixedArrayElement(dictionary, index),((void)0) | |||
| 8732 | TheHoleConstant())))((void)0); | |||
| 8733 | ||||
| 8734 | // Store name and value. | |||
| 8735 | StoreFixedArrayElement(dictionary, index, name); | |||
| 8736 | StoreValueByKeyIndex<NameDictionary>(dictionary, index, value); | |||
| 8737 | ||||
| 8738 | // Prepare details of the new property. | |||
| 8739 | PropertyDetails d(PropertyKind::kData, NONE, | |||
| 8740 | PropertyDetails::kConstIfDictConstnessTracking); | |||
| 8741 | ||||
| 8742 | enum_index = | |||
| 8743 | SmiShl(enum_index, PropertyDetails::DictionaryStorageField::kShift); | |||
| 8744 | // We OR over the actual index below, so we expect the initial value to be 0. | |||
| 8745 | DCHECK_EQ(0, d.dictionary_index())((void) 0); | |||
| 8746 | TVARIABLE(Smi, var_details, SmiOr(SmiConstant(d.AsSmi()), enum_index))TVariable<Smi> var_details(SmiOr(SmiConstant(d.AsSmi()) , enum_index), this); | |||
| 8747 | ||||
| 8748 | // Private names must be marked non-enumerable. | |||
| 8749 | Label not_private(this, &var_details); | |||
| 8750 | GotoIfNot(IsPrivateSymbol(name), ¬_private); | |||
| 8751 | TNode<Smi> dont_enum = | |||
| 8752 | SmiShl(SmiConstant(DONT_ENUM), PropertyDetails::AttributesField::kShift); | |||
| 8753 | var_details = SmiOr(var_details.value(), dont_enum); | |||
| 8754 | Goto(¬_private); | |||
| 8755 | BIND(¬_private)Bind(¬_private); | |||
| 8756 | ||||
| 8757 | // Finally, store the details. | |||
| 8758 | StoreDetailsByKeyIndex<NameDictionary>(dictionary, index, | |||
| 8759 | var_details.value()); | |||
| 8760 | } | |||
| 8761 | ||||
| 8762 | template <> | |||
| 8763 | void CodeStubAssembler::InsertEntry<GlobalDictionary>( | |||
| 8764 | TNode<GlobalDictionary> dictionary, TNode<Name> key, TNode<Object> value, | |||
| 8765 | TNode<IntPtrT> index, TNode<Smi> enum_index) { | |||
| 8766 | UNIMPLEMENTED()V8_Fatal("unimplemented code"); | |||
| 8767 | } | |||
| 8768 | ||||
| 8769 | template <class Dictionary> | |||
| 8770 | void CodeStubAssembler::Add(TNode<Dictionary> dictionary, TNode<Name> key, | |||
| 8771 | TNode<Object> value, Label* bailout) { | |||
| 8772 | CSA_DCHECK(this, Word32BinaryNot(IsEmptyPropertyDictionary(dictionary)))((void)0); | |||
| 8773 | TNode<Smi> capacity = GetCapacity<Dictionary>(dictionary); | |||
| 8774 | TNode<Smi> nof = GetNumberOfElements<Dictionary>(dictionary); | |||
| 8775 | TNode<Smi> new_nof = SmiAdd(nof, SmiConstant(1)); | |||
| 8776 | // Require 33% to still be free after adding additional_elements. | |||
| 8777 | // Computing "x + (x >> 1)" on a Smi x does not return a valid Smi! | |||
| 8778 | // But that's OK here because it's only used for a comparison. | |||
| 8779 | TNode<Smi> required_capacity_pseudo_smi = SmiAdd(new_nof, SmiShr(new_nof, 1)); | |||
| 8780 | GotoIf(SmiBelow(capacity, required_capacity_pseudo_smi), bailout); | |||
| 8781 | // Require rehashing if more than 50% of free elements are deleted elements. | |||
| 8782 | TNode<Smi> deleted = GetNumberOfDeletedElements<Dictionary>(dictionary); | |||
| 8783 | CSA_DCHECK(this, SmiAbove(capacity, new_nof))((void)0); | |||
| 8784 | TNode<Smi> half_of_free_elements = SmiShr(SmiSub(capacity, new_nof), 1); | |||
| 8785 | GotoIf(SmiAbove(deleted, half_of_free_elements), bailout); | |||
| 8786 | ||||
| 8787 | TNode<Smi> enum_index = GetNextEnumerationIndex<Dictionary>(dictionary); | |||
| 8788 | TNode<Smi> new_enum_index = SmiAdd(enum_index, SmiConstant(1)); | |||
| 8789 | TNode<Smi> max_enum_index = | |||
| 8790 | SmiConstant(PropertyDetails::DictionaryStorageField::kMax); | |||
| 8791 | GotoIf(SmiAbove(new_enum_index, max_enum_index), bailout); | |||
| 8792 | ||||
| 8793 | // No more bailouts after this point. | |||
| 8794 | // Operations from here on can have side effects. | |||
| 8795 | ||||
| 8796 | SetNextEnumerationIndex<Dictionary>(dictionary, new_enum_index); | |||
| 8797 | SetNumberOfElements<Dictionary>(dictionary, new_nof); | |||
| 8798 | ||||
| 8799 | TVARIABLE(IntPtrT, var_key_index)TVariable<IntPtrT> var_key_index(this); | |||
| 8800 | FindInsertionEntry<Dictionary>(dictionary, key, &var_key_index); | |||
| 8801 | InsertEntry<Dictionary>(dictionary, key, value, var_key_index.value(), | |||
| 8802 | enum_index); | |||
| 8803 | } | |||
| 8804 | ||||
| 8805 | template <> | |||
| 8806 | void CodeStubAssembler::Add(TNode<SwissNameDictionary> dictionary, | |||
| 8807 | TNode<Name> key, TNode<Object> value, | |||
| 8808 | Label* bailout) { | |||
| 8809 | PropertyDetails d(PropertyKind::kData, NONE, | |||
| 8810 | PropertyDetails::kConstIfDictConstnessTracking); | |||
| 8811 | ||||
| 8812 | PropertyDetails d_dont_enum(PropertyKind::kData, DONT_ENUM, | |||
| 8813 | PropertyDetails::kConstIfDictConstnessTracking); | |||
| 8814 | TNode<Uint8T> details_byte_enum = | |||
| 8815 | UncheckedCast<Uint8T>(Uint32Constant(d.ToByte())); | |||
| 8816 | TNode<Uint8T> details_byte_dont_enum = | |||
| 8817 | UncheckedCast<Uint8T>(Uint32Constant(d_dont_enum.ToByte())); | |||
| 8818 | ||||
| 8819 | Label not_private(this); | |||
| 8820 | TVARIABLE(Uint8T, var_details, details_byte_enum)TVariable<Uint8T> var_details(details_byte_enum, this); | |||
| 8821 | ||||
| 8822 | GotoIfNot(IsPrivateSymbol(key), ¬_private); | |||
| 8823 | var_details = details_byte_dont_enum; | |||
| 8824 | Goto(¬_private); | |||
| 8825 | ||||
| 8826 | BIND(¬_private)Bind(¬_private); | |||
| 8827 | SwissNameDictionaryAdd(dictionary, key, value, var_details.value(), bailout); | |||
| 8828 | } | |||
| 8829 | ||||
| 8830 | template void CodeStubAssembler::Add<NameDictionary>(TNode<NameDictionary>, | |||
| 8831 | TNode<Name>, TNode<Object>, | |||
| 8832 | Label*); | |||
| 8833 | ||||
| 8834 | template <class Dictionary> | |||
| 8835 | TNode<Smi> CodeStubAssembler::GetNumberOfElements( | |||
| 8836 | TNode<Dictionary> dictionary) { | |||
| 8837 | return CAST(Cast(LoadFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex )) | |||
| 8838 | LoadFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex))Cast(LoadFixedArrayElement(dictionary, Dictionary::kNumberOfElementsIndex )); | |||
| 8839 | } | |||
| 8840 | ||||
| 8841 | template <> | |||
| 8842 | TNode<Smi> CodeStubAssembler::GetNumberOfElements( | |||
| 8843 | TNode<SwissNameDictionary> dictionary) { | |||
| 8844 | TNode<IntPtrT> capacity = | |||
| 8845 | ChangeInt32ToIntPtr(LoadSwissNameDictionaryCapacity(dictionary)); | |||
| 8846 | return SmiFromIntPtr( | |||
| 8847 | LoadSwissNameDictionaryNumberOfElements(dictionary, capacity)); | |||
| 8848 | } | |||
| 8849 | ||||
| 8850 | template TNode<Smi> CodeStubAssembler::GetNumberOfElements( | |||
| 8851 | TNode<NameDictionary> dictionary); | |||
| 8852 | template TNode<Smi> CodeStubAssembler::GetNumberOfElements( | |||
| 8853 | TNode<NumberDictionary> dictionary); | |||
| 8854 | template TNode<Smi> CodeStubAssembler::GetNumberOfElements( | |||
| 8855 | TNode<GlobalDictionary> dictionary); | |||
| 8856 | ||||
| 8857 | template <typename Array> | |||
| 8858 | void CodeStubAssembler::LookupLinear(TNode<Name> unique_name, | |||
| 8859 | TNode<Array> array, | |||
| 8860 | TNode<Uint32T> number_of_valid_entries, | |||
| 8861 | Label* if_found, | |||
| 8862 | TVariable<IntPtrT>* var_name_index, | |||
| 8863 | Label* if_not_found) { | |||
| 8864 | static_assert(std::is_base_of<FixedArray, Array>::value || | |||
| 8865 | std::is_base_of<WeakFixedArray, Array>::value || | |||
| 8866 | std::is_base_of<DescriptorArray, Array>::value, | |||
| 8867 | "T must be a descendant of FixedArray or a WeakFixedArray"); | |||
| 8868 | Comment("LookupLinear"); | |||
| 8869 | CSA_DCHECK(this, IsUniqueName(unique_name))((void)0); | |||
| 8870 | TNode<IntPtrT> first_inclusive = IntPtrConstant(Array::ToKeyIndex(0)); | |||
| 8871 | TNode<IntPtrT> factor = IntPtrConstant(Array::kEntrySize); | |||
| 8872 | TNode<IntPtrT> last_exclusive = IntPtrAdd( | |||
| 8873 | first_inclusive, | |||
| 8874 | IntPtrMul(ChangeInt32ToIntPtr(number_of_valid_entries), factor)); | |||
| 8875 | ||||
| 8876 | BuildFastLoop<IntPtrT>( | |||
| 8877 | last_exclusive, first_inclusive, | |||
| 8878 | [=](TNode<IntPtrT> name_index) { | |||
| 8879 | TNode<MaybeObject> element = | |||
| 8880 | LoadArrayElement(array, Array::kHeaderSize, name_index); | |||
| 8881 | TNode<Name> candidate_name = CAST(element)Cast(element); | |||
| 8882 | *var_name_index = name_index; | |||
| 8883 | GotoIf(TaggedEqual(candidate_name, unique_name), if_found); | |||
| 8884 | }, | |||
| 8885 | -Array::kEntrySize, IndexAdvanceMode::kPre); | |||
| 8886 | Goto(if_not_found); | |||
| 8887 | } | |||
| 8888 | ||||
| 8889 | template <> | |||
| 8890 | TNode<Uint32T> CodeStubAssembler::NumberOfEntries<DescriptorArray>( | |||
| 8891 | TNode<DescriptorArray> descriptors) { | |||
| 8892 | return Unsigned(LoadNumberOfDescriptors(descriptors)); | |||
| 8893 | } | |||
| 8894 | ||||
| 8895 | template <> | |||
| 8896 | TNode<Uint32T> CodeStubAssembler::NumberOfEntries<TransitionArray>( | |||
| 8897 | TNode<TransitionArray> transitions) { | |||
| 8898 | TNode<IntPtrT> length = LoadAndUntagWeakFixedArrayLength(transitions); | |||
| 8899 | return Select<Uint32T>( | |||
| 8900 | UintPtrLessThan(length, IntPtrConstant(TransitionArray::kFirstIndex)), | |||
| 8901 | [=] { return Unsigned(Int32Constant(0)); }, | |||
| 8902 | [=] { | |||
| 8903 | return Unsigned(LoadAndUntagToWord32ArrayElement( | |||
| 8904 | transitions, WeakFixedArray::kHeaderSize, | |||
| 8905 | IntPtrConstant(TransitionArray::kTransitionLengthIndex))); | |||
| 8906 | }); | |||
| 8907 | } | |||
| 8908 | ||||
| 8909 | template <typename Array> | |||
| 8910 | TNode<IntPtrT> CodeStubAssembler::EntryIndexToIndex( | |||
| 8911 | TNode<Uint32T> entry_index) { | |||
| 8912 | TNode<Int32T> entry_size = Int32Constant(Array::kEntrySize); | |||
| 8913 | TNode<Word32T> index = Int32Mul(entry_index, entry_size); | |||
| 8914 | return ChangeInt32ToIntPtr(index); | |||
| 8915 | } | |||
| 8916 | ||||
| 8917 | template <typename Array> | |||
| 8918 | TNode<IntPtrT> CodeStubAssembler::ToKeyIndex(TNode<Uint32T> entry_index) { | |||
| 8919 | return IntPtrAdd(IntPtrConstant(Array::ToKeyIndex(0)), | |||
| 8920 | EntryIndexToIndex<Array>(entry_index)); | |||
| 8921 | } | |||
| 8922 | ||||
| 8923 | template TNode<IntPtrT> CodeStubAssembler::ToKeyIndex<DescriptorArray>( | |||
| 8924 | TNode<Uint32T>); | |||
| 8925 | template TNode<IntPtrT> CodeStubAssembler::ToKeyIndex<TransitionArray>( | |||
| 8926 | TNode<Uint32T>); | |||
| 8927 | ||||
| 8928 | template <> | |||
| 8929 | TNode<Uint32T> CodeStubAssembler::GetSortedKeyIndex<DescriptorArray>( | |||
| 8930 | TNode<DescriptorArray> descriptors, TNode<Uint32T> descriptor_number) { | |||
| 8931 | TNode<Uint32T> details = | |||
| 8932 | DescriptorArrayGetDetails(descriptors, descriptor_number); | |||
| 8933 | return DecodeWord32<PropertyDetails::DescriptorPointer>(details); | |||
| 8934 | } | |||
| 8935 | ||||
| 8936 | template <> | |||
| 8937 | TNode<Uint32T> CodeStubAssembler::GetSortedKeyIndex<TransitionArray>( | |||
| 8938 | TNode<TransitionArray> transitions, TNode<Uint32T> transition_number) { | |||
| 8939 | return transition_number; | |||
| 8940 | } | |||
| 8941 | ||||
| 8942 | template <typename Array> | |||
| 8943 | TNode<Name> CodeStubAssembler::GetKey(TNode<Array> array, | |||
| 8944 | TNode<Uint32T> entry_index) { | |||
| 8945 | static_assert(std::is_base_of<TransitionArray, Array>::value || | |||
| 8946 | std::is_base_of<DescriptorArray, Array>::value, | |||
| 8947 | "T must be a descendant of DescriptorArray or TransitionArray"); | |||
| 8948 | const int key_offset = Array::ToKeyIndex(0) * kTaggedSize; | |||
| 8949 | TNode<MaybeObject> element = | |||
| 8950 | LoadArrayElement(array, Array::kHeaderSize, | |||
| 8951 | EntryIndexToIndex<Array>(entry_index), key_offset); | |||
| 8952 | return CAST(element)Cast(element); | |||
| 8953 | } | |||
| 8954 | ||||
| 8955 | template TNode<Name> CodeStubAssembler::GetKey<DescriptorArray>( | |||
| 8956 | TNode<DescriptorArray>, TNode<Uint32T>); | |||
| 8957 | template TNode<Name> CodeStubAssembler::GetKey<TransitionArray>( | |||
| 8958 | TNode<TransitionArray>, TNode<Uint32T>); | |||
| 8959 | ||||
| 8960 | TNode<Uint32T> CodeStubAssembler::DescriptorArrayGetDetails( | |||
| 8961 | TNode<DescriptorArray> descriptors, TNode<Uint32T> descriptor_number) { | |||
| 8962 | const int details_offset = DescriptorArray::ToDetailsIndex(0) * kTaggedSize; | |||
| 8963 | return Unsigned(LoadAndUntagToWord32ArrayElement( | |||
| 8964 | descriptors, DescriptorArray::kHeaderSize, | |||
| 8965 | EntryIndexToIndex<DescriptorArray>(descriptor_number), details_offset)); | |||
| 8966 | } | |||
| 8967 | ||||
| 8968 | template <typename Array> | |||
| 8969 | void CodeStubAssembler::LookupBinary(TNode<Name> unique_name, | |||
| 8970 | TNode<Array> array, | |||
| 8971 | TNode<Uint32T> number_of_valid_entries, | |||
| 8972 | Label* if_found, | |||
| 8973 | TVariable<IntPtrT>* var_name_index, | |||
| 8974 | Label* if_not_found) { | |||
| 8975 | Comment("LookupBinary"); | |||
| 8976 | TVARIABLE(Uint32T, var_low, Unsigned(Int32Constant(0)))TVariable<Uint32T> var_low(Unsigned(Int32Constant(0)), this ); | |||
| 8977 | TNode<Uint32T> limit = | |||
| 8978 | Unsigned(Int32Sub(NumberOfEntries<Array>(array), Int32Constant(1))); | |||
| 8979 | TVARIABLE(Uint32T, var_high, limit)TVariable<Uint32T> var_high(limit, this); | |||
| 8980 | TNode<Uint32T> hash = LoadNameHashAssumeComputed(unique_name); | |||
| 8981 | CSA_DCHECK(this, Word32NotEqual(hash, Int32Constant(0)))((void)0); | |||
| 8982 | ||||
| 8983 | // Assume non-empty array. | |||
| 8984 | CSA_DCHECK(this, Uint32LessThanOrEqual(var_low.value(), var_high.value()))((void)0); | |||
| 8985 | ||||
| 8986 | Label binary_loop(this, {&var_high, &var_low}); | |||
| 8987 | Goto(&binary_loop); | |||
| 8988 | BIND(&binary_loop)Bind(&binary_loop); | |||
| 8989 | { | |||
| 8990 | // mid = low + (high - low) / 2 (to avoid overflow in "(low + high) / 2"). | |||
| 8991 | TNode<Uint32T> mid = Unsigned( | |||
| 8992 | Int32Add(var_low.value(), | |||
| 8993 | Word32Shr(Int32Sub(var_high.value(), var_low.value()), 1))); | |||
| 8994 | // mid_name = array->GetSortedKey(mid). | |||
| 8995 | TNode<Uint32T> sorted_key_index = GetSortedKeyIndex<Array>(array, mid); | |||
| 8996 | TNode<Name> mid_name = GetKey<Array>(array, sorted_key_index); | |||
| 8997 | ||||
| 8998 | TNode<Uint32T> mid_hash = LoadNameHashAssumeComputed(mid_name); | |||
| 8999 | ||||
| 9000 | Label mid_greater(this), mid_less(this), merge(this); | |||
| 9001 | Branch(Uint32GreaterThanOrEqual(mid_hash, hash), &mid_greater, &mid_less); | |||
| 9002 | BIND(&mid_greater)Bind(&mid_greater); | |||
| 9003 | { | |||
| 9004 | var_high = mid; | |||
| 9005 | Goto(&merge); | |||
| 9006 | } | |||
| 9007 | BIND(&mid_less)Bind(&mid_less); | |||
| 9008 | { | |||
| 9009 | var_low = Unsigned(Int32Add(mid, Int32Constant(1))); | |||
| 9010 | Goto(&merge); | |||
| 9011 | } | |||
| 9012 | BIND(&merge)Bind(&merge); | |||
| 9013 | GotoIf(Word32NotEqual(var_low.value(), var_high.value()), &binary_loop); | |||
| 9014 | } | |||
| 9015 | ||||
| 9016 | Label scan_loop(this, &var_low); | |||
| 9017 | Goto(&scan_loop); | |||
| 9018 | BIND(&scan_loop)Bind(&scan_loop); | |||
| 9019 | { | |||
| 9020 | GotoIf(Int32GreaterThan(var_low.value(), limit), if_not_found); | |||
| 9021 | ||||
| 9022 | TNode<Uint32T> sort_index = | |||
| 9023 | GetSortedKeyIndex<Array>(array, var_low.value()); | |||
| 9024 | TNode<Name> current_name = GetKey<Array>(array, sort_index); | |||
| 9025 | TNode<Uint32T> current_hash = LoadNameHashAssumeComputed(current_name); | |||
| 9026 | GotoIf(Word32NotEqual(current_hash, hash), if_not_found); | |||
| 9027 | Label next(this); | |||
| 9028 | GotoIf(TaggedNotEqual(current_name, unique_name), &next); | |||
| 9029 | GotoIf(Uint32GreaterThanOrEqual(sort_index, number_of_valid_entries), | |||
| 9030 | if_not_found); | |||
| 9031 | *var_name_index = ToKeyIndex<Array>(sort_index); | |||
| 9032 | Goto(if_found); | |||
| 9033 | ||||
| 9034 | BIND(&next)Bind(&next); | |||
| 9035 | var_low = Unsigned(Int32Add(var_low.value(), Int32Constant(1))); | |||
| 9036 | Goto(&scan_loop); | |||
| 9037 | } | |||
| 9038 | } | |||
| 9039 | ||||
| 9040 | void CodeStubAssembler::ForEachEnumerableOwnProperty( | |||
| 9041 | TNode<Context> context, TNode<Map> map, TNode<JSObject> object, | |||
| 9042 | PropertiesEnumerationMode mode, const ForEachKeyValueFunction& body, | |||
| 9043 | Label* bailout) { | |||
| 9044 | TNode<Uint16T> type = LoadMapInstanceType(map); | |||
| 9045 | TNode<Uint32T> bit_field3 = EnsureOnlyHasSimpleProperties(map, type, bailout); | |||
| 9046 | ||||
| 9047 | TVARIABLE(DescriptorArray, var_descriptors, LoadMapDescriptors(map))TVariable<DescriptorArray> var_descriptors(LoadMapDescriptors (map), this); | |||
| 9048 | TNode<Uint32T> nof_descriptors = | |||
| 9049 | DecodeWord32<Map::Bits3::NumberOfOwnDescriptorsBits>(bit_field3); | |||
| 9050 | ||||
| 9051 | TVARIABLE(BoolT, var_stable, Int32TrueConstant())TVariable<BoolT> var_stable(Int32TrueConstant(), this); | |||
| 9052 | ||||
| 9053 | TVARIABLE(BoolT, var_has_symbol, Int32FalseConstant())TVariable<BoolT> var_has_symbol(Int32FalseConstant(), this ); | |||
| 9054 | // false - iterate only string properties, true - iterate only symbol | |||
| 9055 | // properties | |||
| 9056 | TVARIABLE(BoolT, var_is_symbol_processing_loop, Int32FalseConstant())TVariable<BoolT> var_is_symbol_processing_loop(Int32FalseConstant (), this); | |||
| 9057 | TVARIABLE(IntPtrT, var_start_key_index,TVariable<IntPtrT> var_start_key_index(ToKeyIndex<DescriptorArray >(Unsigned(Int32Constant(0))), this) | |||
| 9058 | ToKeyIndex<DescriptorArray>(Unsigned(Int32Constant(0))))TVariable<IntPtrT> var_start_key_index(ToKeyIndex<DescriptorArray >(Unsigned(Int32Constant(0))), this); | |||
| 9059 | // Note: var_end_key_index is exclusive for the loop | |||
| 9060 | TVARIABLE(IntPtrT, var_end_key_index,TVariable<IntPtrT> var_end_key_index(ToKeyIndex<DescriptorArray >(nof_descriptors), this) | |||
| 9061 | ToKeyIndex<DescriptorArray>(nof_descriptors))TVariable<IntPtrT> var_end_key_index(ToKeyIndex<DescriptorArray >(nof_descriptors), this); | |||
| 9062 | VariableList list({&var_descriptors, &var_stable, &var_has_symbol, | |||
| 9063 | &var_is_symbol_processing_loop, &var_start_key_index, | |||
| 9064 | &var_end_key_index}, | |||
| 9065 | zone()); | |||
| 9066 | Label descriptor_array_loop(this, list); | |||
| 9067 | ||||
| 9068 | Goto(&descriptor_array_loop); | |||
| 9069 | BIND(&descriptor_array_loop)Bind(&descriptor_array_loop); | |||
| 9070 | ||||
| 9071 | BuildFastLoop<IntPtrT>( | |||
| 9072 | list, var_start_key_index.value(), var_end_key_index.value(), | |||
| 9073 | [&](TNode<IntPtrT> descriptor_key_index) { | |||
| 9074 | TNode<Name> next_key = | |||
| 9075 | LoadKeyByKeyIndex(var_descriptors.value(), descriptor_key_index); | |||
| 9076 | ||||
| 9077 | TVARIABLE(Object, var_value, SmiConstant(0))TVariable<Object> var_value(SmiConstant(0), this); | |||
| 9078 | Label callback(this), next_iteration(this); | |||
| 9079 | ||||
| 9080 | if (mode == kEnumerationOrder) { | |||
| 9081 | // |next_key| is either a string or a symbol | |||
| 9082 | // Skip strings or symbols depending on | |||
| 9083 | // |var_is_symbol_processing_loop|. | |||
| 9084 | Label if_string(this), if_symbol(this), if_name_ok(this); | |||
| 9085 | Branch(IsSymbol(next_key), &if_symbol, &if_string); | |||
| 9086 | BIND(&if_symbol)Bind(&if_symbol); | |||
| 9087 | { | |||
| 9088 | // Process symbol property when |var_is_symbol_processing_loop| is | |||
| 9089 | // true. | |||
| 9090 | GotoIf(var_is_symbol_processing_loop.value(), &if_name_ok); | |||
| 9091 | // First iteration need to calculate smaller range for processing | |||
| 9092 | // symbols | |||
| 9093 | Label if_first_symbol(this); | |||
| 9094 | // var_end_key_index is still inclusive at this point. | |||
| 9095 | var_end_key_index = descriptor_key_index; | |||
| 9096 | Branch(var_has_symbol.value(), &next_iteration, &if_first_symbol); | |||
| 9097 | BIND(&if_first_symbol)Bind(&if_first_symbol); | |||
| 9098 | { | |||
| 9099 | var_start_key_index = descriptor_key_index; | |||
| 9100 | var_has_symbol = Int32TrueConstant(); | |||
| 9101 | Goto(&next_iteration); | |||
| 9102 | } | |||
| 9103 | } | |||
| 9104 | BIND(&if_string)Bind(&if_string); | |||
| 9105 | { | |||
| 9106 | CSA_DCHECK(this, IsString(next_key))((void)0); | |||
| 9107 | // Process string property when |var_is_symbol_processing_loop| is | |||
| 9108 | // false. | |||
| 9109 | Branch(var_is_symbol_processing_loop.value(), &next_iteration, | |||
| 9110 | &if_name_ok); | |||
| 9111 | } | |||
| 9112 | BIND(&if_name_ok)Bind(&if_name_ok); | |||
| 9113 | } | |||
| 9114 | { | |||
| 9115 | TVARIABLE(Map, var_map)TVariable<Map> var_map(this); | |||
| 9116 | TVARIABLE(HeapObject, var_meta_storage)TVariable<HeapObject> var_meta_storage(this); | |||
| 9117 | TVARIABLE(IntPtrT, var_entry)TVariable<IntPtrT> var_entry(this); | |||
| 9118 | TVARIABLE(Uint32T, var_details)TVariable<Uint32T> var_details(this); | |||
| 9119 | Label if_found(this); | |||
| 9120 | ||||
| 9121 | Label if_found_fast(this), if_found_dict(this); | |||
| 9122 | ||||
| 9123 | Label if_stable(this), if_not_stable(this); | |||
| 9124 | Branch(var_stable.value(), &if_stable, &if_not_stable); | |||
| 9125 | BIND(&if_stable)Bind(&if_stable); | |||
| 9126 | { | |||
| 9127 | // Directly decode from the descriptor array if |object| did not | |||
| 9128 | // change shape. | |||
| 9129 | var_map = map; | |||
| 9130 | var_meta_storage = var_descriptors.value(); | |||
| 9131 | var_entry = Signed(descriptor_key_index); | |||
| 9132 | Goto(&if_found_fast); | |||
| 9133 | } | |||
| 9134 | BIND(&if_not_stable)Bind(&if_not_stable); | |||
| 9135 | { | |||
| 9136 | // If the map did change, do a slower lookup. We are still | |||
| 9137 | // guaranteed that the object has a simple shape, and that the key | |||
| 9138 | // is a name. | |||
| 9139 | var_map = LoadMap(object); | |||
| 9140 | TryLookupPropertyInSimpleObject(object, var_map.value(), next_key, | |||
| 9141 | &if_found_fast, &if_found_dict, | |||
| 9142 | &var_meta_storage, &var_entry, | |||
| 9143 | &next_iteration, bailout); | |||
| 9144 | } | |||
| 9145 | ||||
| 9146 | BIND(&if_found_fast)Bind(&if_found_fast); | |||
| 9147 | { | |||
| 9148 | TNode<DescriptorArray> descriptors = CAST(var_meta_storage.value())Cast(var_meta_storage.value()); | |||
| 9149 | TNode<IntPtrT> name_index = var_entry.value(); | |||
| 9150 | ||||
| 9151 | // Skip non-enumerable properties. | |||
| 9152 | var_details = LoadDetailsByKeyIndex(descriptors, name_index); | |||
| 9153 | GotoIf(IsSetWord32(var_details.value(), | |||
| 9154 | PropertyDetails::kAttributesDontEnumMask), | |||
| 9155 | &next_iteration); | |||
| 9156 | ||||
| 9157 | LoadPropertyFromFastObject(object, var_map.value(), descriptors, | |||
| 9158 | name_index, var_details.value(), | |||
| 9159 | &var_value); | |||
| 9160 | Goto(&if_found); | |||
| 9161 | } | |||
| 9162 | BIND(&if_found_dict)Bind(&if_found_dict); | |||
| 9163 | { | |||
| 9164 | TNode<PropertyDictionary> dictionary = | |||
| 9165 | CAST(var_meta_storage.value())Cast(var_meta_storage.value()); | |||
| 9166 | TNode<IntPtrT> entry = var_entry.value(); | |||
| 9167 | ||||
| 9168 | TNode<Uint32T> details = LoadDetailsByKeyIndex(dictionary, entry); | |||
| 9169 | // Skip non-enumerable properties. | |||
| 9170 | GotoIf( | |||
| 9171 | IsSetWord32(details, PropertyDetails::kAttributesDontEnumMask), | |||
| 9172 | &next_iteration); | |||
| 9173 | ||||
| 9174 | var_details = details; | |||
| 9175 | var_value = | |||
| 9176 | LoadValueByKeyIndex<PropertyDictionary>(dictionary, entry); | |||
| 9177 | Goto(&if_found); | |||
| 9178 | } | |||
| 9179 | ||||
| 9180 | // Here we have details and value which could be an accessor. | |||
| 9181 | BIND(&if_found)Bind(&if_found); | |||
| 9182 | { | |||
| 9183 | Label slow_load(this, Label::kDeferred); | |||
| 9184 | ||||
| 9185 | var_value = CallGetterIfAccessor( | |||
| 9186 | var_value.value(), object, var_details.value(), context, object, | |||
| 9187 | next_key, &slow_load, kCallJSGetterUseCachedName); | |||
| 9188 | Goto(&callback); | |||
| 9189 | ||||
| 9190 | BIND(&slow_load)Bind(&slow_load); | |||
| 9191 | var_value = | |||
| 9192 | CallRuntime(Runtime::kGetProperty, context, object, next_key); | |||
| 9193 | Goto(&callback); | |||
| 9194 | ||||
| 9195 | BIND(&callback)Bind(&callback); | |||
| 9196 | body(next_key, var_value.value()); | |||
| 9197 | ||||
| 9198 | // Check if |object| is still stable, i.e. the descriptors in the | |||
| 9199 | // preloaded |descriptors| are still the same modulo in-place | |||
| 9200 | // representation changes. | |||
| 9201 | GotoIfNot(var_stable.value(), &next_iteration); | |||
| 9202 | var_stable = TaggedEqual(LoadMap(object), map); | |||
| 9203 | // Reload the descriptors just in case the actual array changed, and | |||
| 9204 | // any of the field representations changed in-place. | |||
| 9205 | var_descriptors = LoadMapDescriptors(map); | |||
| 9206 | ||||
| 9207 | Goto(&next_iteration); | |||
| 9208 | } | |||
| 9209 | } | |||
| 9210 | BIND(&next_iteration)Bind(&next_iteration); | |||
| 9211 | }, | |||
| 9212 | DescriptorArray::kEntrySize, IndexAdvanceMode::kPost); | |||
| 9213 | ||||
| 9214 | if (mode == kEnumerationOrder) { | |||
| 9215 | Label done(this); | |||
| 9216 | GotoIf(var_is_symbol_processing_loop.value(), &done); | |||
| 9217 | GotoIfNot(var_has_symbol.value(), &done); | |||
| 9218 | // All string properties are processed, now process symbol properties. | |||
| 9219 | var_is_symbol_processing_loop = Int32TrueConstant(); | |||
| 9220 | // Add DescriptorArray::kEntrySize to make the var_end_key_index exclusive | |||
| 9221 | // as BuildFastLoop() expects. | |||
| 9222 | Increment(&var_end_key_index, DescriptorArray::kEntrySize); | |||
| 9223 | Goto(&descriptor_array_loop); | |||
| 9224 | ||||
| 9225 | BIND(&done)Bind(&done); | |||
| 9226 | } | |||
| 9227 | } | |||
| 9228 | ||||
| 9229 | TNode<Object> CodeStubAssembler::GetConstructor(TNode<Map> map) { | |||
| 9230 | TVARIABLE(HeapObject, var_maybe_constructor)TVariable<HeapObject> var_maybe_constructor(this); | |||
| 9231 | var_maybe_constructor = map; | |||
| 9232 | Label loop(this, &var_maybe_constructor), done(this); | |||
| 9233 | GotoIfNot(IsMap(var_maybe_constructor.value()), &done); | |||
| 9234 | Goto(&loop); | |||
| 9235 | ||||
| 9236 | BIND(&loop)Bind(&loop); | |||
| 9237 | { | |||
| 9238 | var_maybe_constructor = CAST(Cast(LoadObjectField(var_maybe_constructor.value(), Map::kConstructorOrBackPointerOrNativeContextOffset )) | |||
| 9239 | LoadObjectField(var_maybe_constructor.value(),Cast(LoadObjectField(var_maybe_constructor.value(), Map::kConstructorOrBackPointerOrNativeContextOffset )) | |||
| 9240 | Map::kConstructorOrBackPointerOrNativeContextOffset))Cast(LoadObjectField(var_maybe_constructor.value(), Map::kConstructorOrBackPointerOrNativeContextOffset )); | |||
| 9241 | GotoIf(IsMap(var_maybe_constructor.value()), &loop); | |||
| 9242 | Goto(&done); | |||
| 9243 | } | |||
| 9244 | ||||
| 9245 | BIND(&done)Bind(&done); | |||
| 9246 | return var_maybe_constructor.value(); | |||
| 9247 | } | |||
| 9248 | ||||
| 9249 | TNode<NativeContext> CodeStubAssembler::GetCreationContext( | |||
| 9250 | TNode<JSReceiver> receiver, Label* if_bailout) { | |||
| 9251 | TNode<Map> receiver_map = LoadMap(receiver); | |||
| 9252 | TNode<Object> constructor = GetConstructor(receiver_map); | |||
| 9253 | ||||
| 9254 | TVARIABLE(JSFunction, var_function)TVariable<JSFunction> var_function(this); | |||
| 9255 | ||||
| 9256 | Label done(this), if_jsfunction(this), if_jsgenerator(this); | |||
| 9257 | GotoIf(TaggedIsSmi(constructor), if_bailout); | |||
| 9258 | ||||
| 9259 | TNode<Map> function_map = LoadMap(CAST(constructor)Cast(constructor)); | |||
| 9260 | GotoIf(IsJSFunctionMap(function_map), &if_jsfunction); | |||
| 9261 | GotoIf(IsJSGeneratorMap(function_map), &if_jsgenerator); | |||
| 9262 | // Remote objects don't have a creation context. | |||
| 9263 | GotoIf(IsFunctionTemplateInfoMap(function_map), if_bailout); | |||
| 9264 | ||||
| 9265 | CSA_DCHECK(this, IsJSFunctionMap(receiver_map))((void)0); | |||
| 9266 | var_function = CAST(receiver)Cast(receiver); | |||
| 9267 | Goto(&done); | |||
| 9268 | ||||
| 9269 | BIND(&if_jsfunction)Bind(&if_jsfunction); | |||
| 9270 | { | |||
| 9271 | var_function = CAST(constructor)Cast(constructor); | |||
| 9272 | Goto(&done); | |||
| 9273 | } | |||
| 9274 | ||||
| 9275 | BIND(&if_jsgenerator)Bind(&if_jsgenerator); | |||
| 9276 | { | |||
| 9277 | var_function = LoadJSGeneratorObjectFunction(CAST(receiver)Cast(receiver)); | |||
| 9278 | Goto(&done); | |||
| 9279 | } | |||
| 9280 | ||||
| 9281 | BIND(&done)Bind(&done); | |||
| 9282 | TNode<Context> context = LoadJSFunctionContext(var_function.value()); | |||
| 9283 | ||||
| 9284 | GotoIfNot(IsContext(context), if_bailout); | |||
| 9285 | ||||
| 9286 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 9287 | return native_context; | |||
| 9288 | } | |||
| 9289 | ||||
| 9290 | TNode<NativeContext> CodeStubAssembler::GetFunctionRealm( | |||
| 9291 | TNode<Context> context, TNode<JSReceiver> receiver, Label* if_bailout) { | |||
| 9292 | TVARIABLE(JSReceiver, current)TVariable<JSReceiver> current(this); | |||
| 9293 | Label loop(this, VariableList({¤t}, zone())), is_proxy(this), | |||
| 9294 | is_function(this), is_bound_function(this), is_wrapped_function(this), | |||
| 9295 | proxy_revoked(this, Label::kDeferred); | |||
| 9296 | CSA_DCHECK(this, IsCallable(receiver))((void)0); | |||
| 9297 | current = receiver; | |||
| 9298 | Goto(&loop); | |||
| 9299 | ||||
| 9300 | BIND(&loop)Bind(&loop); | |||
| 9301 | { | |||
| 9302 | TNode<JSReceiver> current_value = current.value(); | |||
| 9303 | GotoIf(IsJSProxy(current_value), &is_proxy); | |||
| 9304 | GotoIf(IsJSFunction(current_value), &is_function); | |||
| 9305 | GotoIf(IsJSBoundFunction(current_value), &is_bound_function); | |||
| 9306 | GotoIf(IsJSWrappedFunction(current_value), &is_wrapped_function); | |||
| 9307 | Goto(if_bailout); | |||
| 9308 | } | |||
| 9309 | ||||
| 9310 | BIND(&is_proxy)Bind(&is_proxy); | |||
| 9311 | { | |||
| 9312 | TNode<JSProxy> proxy = CAST(current.value())Cast(current.value()); | |||
| 9313 | TNode<HeapObject> handler = | |||
| 9314 | CAST(LoadObjectField(proxy, JSProxy::kHandlerOffset))Cast(LoadObjectField(proxy, JSProxy::kHandlerOffset)); | |||
| 9315 | // Proxy is revoked. | |||
| 9316 | GotoIfNot(IsJSReceiver(handler), &proxy_revoked); | |||
| 9317 | TNode<JSReceiver> target = | |||
| 9318 | CAST(LoadObjectField(proxy, JSProxy::kTargetOffset))Cast(LoadObjectField(proxy, JSProxy::kTargetOffset)); | |||
| 9319 | current = target; | |||
| 9320 | Goto(&loop); | |||
| 9321 | } | |||
| 9322 | ||||
| 9323 | BIND(&proxy_revoked)Bind(&proxy_revoked); | |||
| 9324 | { ThrowTypeError(context, MessageTemplate::kProxyRevoked, "apply"); } | |||
| 9325 | ||||
| 9326 | BIND(&is_bound_function)Bind(&is_bound_function); | |||
| 9327 | { | |||
| 9328 | TNode<JSBoundFunction> bound_function = CAST(current.value())Cast(current.value()); | |||
| 9329 | TNode<JSReceiver> target = CAST(LoadObjectField(Cast(LoadObjectField( bound_function, JSBoundFunction::kBoundTargetFunctionOffset )) | |||
| 9330 | bound_function, JSBoundFunction::kBoundTargetFunctionOffset))Cast(LoadObjectField( bound_function, JSBoundFunction::kBoundTargetFunctionOffset )); | |||
| 9331 | current = target; | |||
| 9332 | Goto(&loop); | |||
| 9333 | } | |||
| 9334 | ||||
| 9335 | BIND(&is_wrapped_function)Bind(&is_wrapped_function); | |||
| 9336 | { | |||
| 9337 | TNode<JSWrappedFunction> wrapped_function = CAST(current.value())Cast(current.value()); | |||
| 9338 | TNode<JSReceiver> target = CAST(LoadObjectField(Cast(LoadObjectField( wrapped_function, JSWrappedFunction::kWrappedTargetFunctionOffset )) | |||
| 9339 | wrapped_function, JSWrappedFunction::kWrappedTargetFunctionOffset))Cast(LoadObjectField( wrapped_function, JSWrappedFunction::kWrappedTargetFunctionOffset )); | |||
| 9340 | current = target; | |||
| 9341 | Goto(&loop); | |||
| 9342 | } | |||
| 9343 | ||||
| 9344 | BIND(&is_function)Bind(&is_function); | |||
| 9345 | { | |||
| 9346 | TNode<JSFunction> function = CAST(current.value())Cast(current.value()); | |||
| 9347 | TNode<Context> context = | |||
| 9348 | CAST(LoadObjectField(function, JSFunction::kContextOffset))Cast(LoadObjectField(function, JSFunction::kContextOffset)); | |||
| 9349 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 9350 | return native_context; | |||
| 9351 | } | |||
| 9352 | } | |||
| 9353 | ||||
| 9354 | void CodeStubAssembler::DescriptorLookup(TNode<Name> unique_name, | |||
| 9355 | TNode<DescriptorArray> descriptors, | |||
| 9356 | TNode<Uint32T> bitfield3, | |||
| 9357 | Label* if_found, | |||
| 9358 | TVariable<IntPtrT>* var_name_index, | |||
| 9359 | Label* if_not_found) { | |||
| 9360 | Comment("DescriptorArrayLookup"); | |||
| 9361 | TNode<Uint32T> nof = | |||
| 9362 | DecodeWord32<Map::Bits3::NumberOfOwnDescriptorsBits>(bitfield3); | |||
| 9363 | Lookup<DescriptorArray>(unique_name, descriptors, nof, if_found, | |||
| 9364 | var_name_index, if_not_found); | |||
| 9365 | } | |||
| 9366 | ||||
| 9367 | void CodeStubAssembler::TransitionLookup(TNode<Name> unique_name, | |||
| 9368 | TNode<TransitionArray> transitions, | |||
| 9369 | Label* if_found, | |||
| 9370 | TVariable<IntPtrT>* var_name_index, | |||
| 9371 | Label* if_not_found) { | |||
| 9372 | Comment("TransitionArrayLookup"); | |||
| 9373 | TNode<Uint32T> number_of_valid_transitions = | |||
| 9374 | NumberOfEntries<TransitionArray>(transitions); | |||
| 9375 | Lookup<TransitionArray>(unique_name, transitions, number_of_valid_transitions, | |||
| 9376 | if_found, var_name_index, if_not_found); | |||
| 9377 | } | |||
| 9378 | ||||
| 9379 | template <typename Array> | |||
| 9380 | void CodeStubAssembler::Lookup(TNode<Name> unique_name, TNode<Array> array, | |||
| 9381 | TNode<Uint32T> number_of_valid_entries, | |||
| 9382 | Label* if_found, | |||
| 9383 | TVariable<IntPtrT>* var_name_index, | |||
| 9384 | Label* if_not_found) { | |||
| 9385 | Comment("ArrayLookup"); | |||
| 9386 | if (!number_of_valid_entries) { | |||
| 9387 | number_of_valid_entries = NumberOfEntries(array); | |||
| 9388 | } | |||
| 9389 | GotoIf(Word32Equal(number_of_valid_entries, Int32Constant(0)), if_not_found); | |||
| 9390 | Label linear_search(this), binary_search(this); | |||
| 9391 | const int kMaxElementsForLinearSearch = 32; | |||
| 9392 | Branch(Uint32LessThanOrEqual(number_of_valid_entries, | |||
| 9393 | Int32Constant(kMaxElementsForLinearSearch)), | |||
| 9394 | &linear_search, &binary_search); | |||
| 9395 | BIND(&linear_search)Bind(&linear_search); | |||
| 9396 | { | |||
| 9397 | LookupLinear<Array>(unique_name, array, number_of_valid_entries, if_found, | |||
| 9398 | var_name_index, if_not_found); | |||
| 9399 | } | |||
| 9400 | BIND(&binary_search)Bind(&binary_search); | |||
| 9401 | { | |||
| 9402 | LookupBinary<Array>(unique_name, array, number_of_valid_entries, if_found, | |||
| 9403 | var_name_index, if_not_found); | |||
| 9404 | } | |||
| 9405 | } | |||
| 9406 | ||||
| 9407 | void CodeStubAssembler::TryLookupPropertyInSimpleObject( | |||
| 9408 | TNode<JSObject> object, TNode<Map> map, TNode<Name> unique_name, | |||
| 9409 | Label* if_found_fast, Label* if_found_dict, | |||
| 9410 | TVariable<HeapObject>* var_meta_storage, TVariable<IntPtrT>* var_name_index, | |||
| 9411 | Label* if_not_found, Label* bailout) { | |||
| 9412 | CSA_DCHECK(this, IsSimpleObjectMap(map))((void)0); | |||
| 9413 | CSA_DCHECK(this, IsUniqueNameNoCachedIndex(unique_name))((void)0); | |||
| 9414 | ||||
| 9415 | TNode<Uint32T> bit_field3 = LoadMapBitField3(map); | |||
| 9416 | Label if_isfastmap(this), if_isslowmap(this); | |||
| 9417 | Branch(IsSetWord32<Map::Bits3::IsDictionaryMapBit>(bit_field3), &if_isslowmap, | |||
| 9418 | &if_isfastmap); | |||
| 9419 | BIND(&if_isfastmap)Bind(&if_isfastmap); | |||
| 9420 | { | |||
| 9421 | TNode<DescriptorArray> descriptors = LoadMapDescriptors(map); | |||
| 9422 | *var_meta_storage = descriptors; | |||
| 9423 | ||||
| 9424 | DescriptorLookup(unique_name, descriptors, bit_field3, if_found_fast, | |||
| 9425 | var_name_index, if_not_found); | |||
| 9426 | } | |||
| 9427 | BIND(&if_isslowmap)Bind(&if_isslowmap); | |||
| 9428 | { | |||
| 9429 | TNode<PropertyDictionary> dictionary = CAST(LoadSlowProperties(object))Cast(LoadSlowProperties(object)); | |||
| 9430 | *var_meta_storage = dictionary; | |||
| 9431 | ||||
| 9432 | NameDictionaryLookup<PropertyDictionary>( | |||
| 9433 | dictionary, unique_name, if_found_dict, var_name_index, if_not_found); | |||
| 9434 | } | |||
| 9435 | } | |||
| 9436 | ||||
| 9437 | void CodeStubAssembler::TryLookupProperty( | |||
| 9438 | TNode<HeapObject> object, TNode<Map> map, TNode<Int32T> instance_type, | |||
| 9439 | TNode<Name> unique_name, Label* if_found_fast, Label* if_found_dict, | |||
| 9440 | Label* if_found_global, TVariable<HeapObject>* var_meta_storage, | |||
| 9441 | TVariable<IntPtrT>* var_name_index, Label* if_not_found, | |||
| 9442 | Label* if_bailout) { | |||
| 9443 | Label if_objectisspecial(this); | |||
| 9444 | GotoIf(IsSpecialReceiverInstanceType(instance_type), &if_objectisspecial); | |||
| 9445 | ||||
| 9446 | TryLookupPropertyInSimpleObject(CAST(object)Cast(object), map, unique_name, if_found_fast, | |||
| 9447 | if_found_dict, var_meta_storage, | |||
| 9448 | var_name_index, if_not_found, if_bailout); | |||
| 9449 | ||||
| 9450 | BIND(&if_objectisspecial)Bind(&if_objectisspecial); | |||
| 9451 | { | |||
| 9452 | // Handle global object here and bailout for other special objects. | |||
| 9453 | GotoIfNot(InstanceTypeEqual(instance_type, JS_GLOBAL_OBJECT_TYPE), | |||
| 9454 | if_bailout); | |||
| 9455 | ||||
| 9456 | // Handle interceptors and access checks in runtime. | |||
| 9457 | TNode<Int32T> bit_field = LoadMapBitField(map); | |||
| 9458 | int mask = Map::Bits1::HasNamedInterceptorBit::kMask | | |||
| 9459 | Map::Bits1::IsAccessCheckNeededBit::kMask; | |||
| 9460 | GotoIf(IsSetWord32(bit_field, mask), if_bailout); | |||
| 9461 | ||||
| 9462 | TNode<GlobalDictionary> dictionary = CAST(LoadSlowProperties(CAST(object)))Cast(LoadSlowProperties(Cast(object))); | |||
| 9463 | *var_meta_storage = dictionary; | |||
| 9464 | ||||
| 9465 | NameDictionaryLookup<GlobalDictionary>( | |||
| 9466 | dictionary, unique_name, if_found_global, var_name_index, if_not_found); | |||
| 9467 | } | |||
| 9468 | } | |||
| 9469 | ||||
| 9470 | void CodeStubAssembler::TryHasOwnProperty(TNode<HeapObject> object, | |||
| 9471 | TNode<Map> map, | |||
| 9472 | TNode<Int32T> instance_type, | |||
| 9473 | TNode<Name> unique_name, | |||
| 9474 | Label* if_found, Label* if_not_found, | |||
| 9475 | Label* if_bailout) { | |||
| 9476 | Comment("TryHasOwnProperty"); | |||
| 9477 | CSA_DCHECK(this, IsUniqueNameNoCachedIndex(unique_name))((void)0); | |||
| 9478 | TVARIABLE(HeapObject, var_meta_storage)TVariable<HeapObject> var_meta_storage(this); | |||
| 9479 | TVARIABLE(IntPtrT, var_name_index)TVariable<IntPtrT> var_name_index(this); | |||
| 9480 | ||||
| 9481 | Label if_found_global(this); | |||
| 9482 | TryLookupProperty(object, map, instance_type, unique_name, if_found, if_found, | |||
| 9483 | &if_found_global, &var_meta_storage, &var_name_index, | |||
| 9484 | if_not_found, if_bailout); | |||
| 9485 | ||||
| 9486 | BIND(&if_found_global)Bind(&if_found_global); | |||
| 9487 | { | |||
| 9488 | TVARIABLE(Object, var_value)TVariable<Object> var_value(this); | |||
| 9489 | TVARIABLE(Uint32T, var_details)TVariable<Uint32T> var_details(this); | |||
| 9490 | // Check if the property cell is not deleted. | |||
| 9491 | LoadPropertyFromGlobalDictionary(CAST(var_meta_storage.value())Cast(var_meta_storage.value()), | |||
| 9492 | var_name_index.value(), &var_details, | |||
| 9493 | &var_value, if_not_found); | |||
| 9494 | Goto(if_found); | |||
| 9495 | } | |||
| 9496 | } | |||
| 9497 | ||||
| 9498 | TNode<Object> CodeStubAssembler::GetMethod(TNode<Context> context, | |||
| 9499 | TNode<Object> object, | |||
| 9500 | Handle<Name> name, | |||
| 9501 | Label* if_null_or_undefined) { | |||
| 9502 | TNode<Object> method = GetProperty(context, object, name); | |||
| 9503 | ||||
| 9504 | GotoIf(IsUndefined(method), if_null_or_undefined); | |||
| 9505 | GotoIf(IsNull(method), if_null_or_undefined); | |||
| 9506 | ||||
| 9507 | return method; | |||
| 9508 | } | |||
| 9509 | ||||
| 9510 | TNode<Object> CodeStubAssembler::GetIteratorMethod( | |||
| 9511 | TNode<Context> context, TNode<HeapObject> heap_obj, | |||
| 9512 | Label* if_iteratorundefined) { | |||
| 9513 | return GetMethod(context, heap_obj, isolate()->factory()->iterator_symbol(), | |||
| 9514 | if_iteratorundefined); | |||
| 9515 | } | |||
| 9516 | ||||
| 9517 | TNode<Object> CodeStubAssembler::CreateAsyncFromSyncIterator( | |||
| 9518 | TNode<Context> context, TNode<Object> sync_iterator) { | |||
| 9519 | Label not_receiver(this, Label::kDeferred); | |||
| 9520 | Label done(this); | |||
| 9521 | TVARIABLE(Object, return_value)TVariable<Object> return_value(this); | |||
| 9522 | ||||
| 9523 | GotoIf(TaggedIsSmi(sync_iterator), ¬_receiver); | |||
| 9524 | GotoIfNot(IsJSReceiver(CAST(sync_iterator)Cast(sync_iterator)), ¬_receiver); | |||
| 9525 | ||||
| 9526 | const TNode<Object> next = | |||
| 9527 | GetProperty(context, sync_iterator, factory()->next_string()); | |||
| 9528 | ||||
| 9529 | const TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 9530 | const TNode<Map> map = CAST(LoadContextElement(Cast(LoadContextElement( native_context, Context::ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX )) | |||
| 9531 | native_context, Context::ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX))Cast(LoadContextElement( native_context, Context::ASYNC_FROM_SYNC_ITERATOR_MAP_INDEX )); | |||
| 9532 | const TNode<JSObject> iterator = AllocateJSObjectFromMap(map); | |||
| 9533 | ||||
| 9534 | StoreObjectFieldNoWriteBarrier( | |||
| 9535 | iterator, JSAsyncFromSyncIterator::kSyncIteratorOffset, sync_iterator); | |||
| 9536 | StoreObjectFieldNoWriteBarrier(iterator, JSAsyncFromSyncIterator::kNextOffset, | |||
| 9537 | next); | |||
| 9538 | ||||
| 9539 | return_value = iterator; | |||
| 9540 | Goto(&done); | |||
| 9541 | ||||
| 9542 | BIND(¬_receiver)Bind(¬_receiver); | |||
| 9543 | { | |||
| 9544 | return_value = CallRuntime(Runtime::kThrowSymbolIteratorInvalid, context); | |||
| 9545 | ||||
| 9546 | // Unreachable due to the Throw in runtime call. | |||
| 9547 | Goto(&done); | |||
| 9548 | } | |||
| 9549 | ||||
| 9550 | BIND(&done)Bind(&done); | |||
| 9551 | return return_value.value(); | |||
| 9552 | } | |||
| 9553 | ||||
| 9554 | void CodeStubAssembler::LoadPropertyFromFastObject( | |||
| 9555 | TNode<HeapObject> object, TNode<Map> map, | |||
| 9556 | TNode<DescriptorArray> descriptors, TNode<IntPtrT> name_index, | |||
| 9557 | TVariable<Uint32T>* var_details, TVariable<Object>* var_value) { | |||
| 9558 | TNode<Uint32T> details = LoadDetailsByKeyIndex(descriptors, name_index); | |||
| 9559 | *var_details = details; | |||
| 9560 | ||||
| 9561 | LoadPropertyFromFastObject(object, map, descriptors, name_index, details, | |||
| 9562 | var_value); | |||
| 9563 | } | |||
| 9564 | ||||
| 9565 | void CodeStubAssembler::LoadPropertyFromFastObject( | |||
| 9566 | TNode<HeapObject> object, TNode<Map> map, | |||
| 9567 | TNode<DescriptorArray> descriptors, TNode<IntPtrT> name_index, | |||
| 9568 | TNode<Uint32T> details, TVariable<Object>* var_value) { | |||
| 9569 | Comment("[ LoadPropertyFromFastObject"); | |||
| 9570 | ||||
| 9571 | TNode<Uint32T> location = | |||
| 9572 | DecodeWord32<PropertyDetails::LocationField>(details); | |||
| 9573 | ||||
| 9574 | Label if_in_field(this), if_in_descriptor(this), done(this); | |||
| 9575 | Branch(Word32Equal(location, Int32Constant(static_cast<int32_t>( | |||
| 9576 | PropertyLocation::kField))), | |||
| 9577 | &if_in_field, &if_in_descriptor); | |||
| 9578 | BIND(&if_in_field)Bind(&if_in_field); | |||
| 9579 | { | |||
| 9580 | TNode<IntPtrT> field_index = | |||
| 9581 | Signed(DecodeWordFromWord32<PropertyDetails::FieldIndexField>(details)); | |||
| 9582 | TNode<Uint32T> representation = | |||
| 9583 | DecodeWord32<PropertyDetails::RepresentationField>(details); | |||
| 9584 | ||||
| 9585 | // TODO(ishell): support WasmValues. | |||
| 9586 | CSA_DCHECK(this, Word32NotEqual(representation,((void)0) | |||
| 9587 | Int32Constant(Representation::kWasmValue)))((void)0); | |||
| 9588 | field_index = | |||
| 9589 | IntPtrAdd(field_index, LoadMapInobjectPropertiesStartInWords(map)); | |||
| 9590 | TNode<IntPtrT> instance_size_in_words = LoadMapInstanceSizeInWords(map); | |||
| 9591 | ||||
| 9592 | Label if_inobject(this), if_backing_store(this); | |||
| 9593 | TVARIABLE(Float64T, var_double_value)TVariable<Float64T> var_double_value(this); | |||
| 9594 | Label rebox_double(this, &var_double_value); | |||
| 9595 | Branch(UintPtrLessThan(field_index, instance_size_in_words), &if_inobject, | |||
| 9596 | &if_backing_store); | |||
| 9597 | BIND(&if_inobject)Bind(&if_inobject); | |||
| 9598 | { | |||
| 9599 | Comment("if_inobject"); | |||
| 9600 | TNode<IntPtrT> field_offset = TimesTaggedSize(field_index); | |||
| 9601 | ||||
| 9602 | Label if_double(this), if_tagged(this); | |||
| 9603 | Branch(Word32NotEqual(representation, | |||
| 9604 | Int32Constant(Representation::kDouble)), | |||
| 9605 | &if_tagged, &if_double); | |||
| 9606 | BIND(&if_tagged)Bind(&if_tagged); | |||
| 9607 | { | |||
| 9608 | *var_value = LoadObjectField(object, field_offset); | |||
| 9609 | Goto(&done); | |||
| 9610 | } | |||
| 9611 | BIND(&if_double)Bind(&if_double); | |||
| 9612 | { | |||
| 9613 | TNode<HeapNumber> heap_number = | |||
| 9614 | CAST(LoadObjectField(object, field_offset))Cast(LoadObjectField(object, field_offset)); | |||
| 9615 | var_double_value = LoadHeapNumberValue(heap_number); | |||
| 9616 | Goto(&rebox_double); | |||
| 9617 | } | |||
| 9618 | } | |||
| 9619 | BIND(&if_backing_store)Bind(&if_backing_store); | |||
| 9620 | { | |||
| 9621 | Comment("if_backing_store"); | |||
| 9622 | TNode<HeapObject> properties = LoadFastProperties(CAST(object)Cast(object)); | |||
| 9623 | field_index = Signed(IntPtrSub(field_index, instance_size_in_words)); | |||
| 9624 | TNode<Object> value = | |||
| 9625 | LoadPropertyArrayElement(CAST(properties)Cast(properties), field_index); | |||
| 9626 | ||||
| 9627 | Label if_double(this), if_tagged(this); | |||
| 9628 | Branch(Word32NotEqual(representation, | |||
| 9629 | Int32Constant(Representation::kDouble)), | |||
| 9630 | &if_tagged, &if_double); | |||
| 9631 | BIND(&if_tagged)Bind(&if_tagged); | |||
| 9632 | { | |||
| 9633 | *var_value = value; | |||
| 9634 | Goto(&done); | |||
| 9635 | } | |||
| 9636 | BIND(&if_double)Bind(&if_double); | |||
| 9637 | { | |||
| 9638 | var_double_value = LoadHeapNumberValue(CAST(value)Cast(value)); | |||
| 9639 | Goto(&rebox_double); | |||
| 9640 | } | |||
| 9641 | } | |||
| 9642 | BIND(&rebox_double)Bind(&rebox_double); | |||
| 9643 | { | |||
| 9644 | Comment("rebox_double"); | |||
| 9645 | TNode<HeapNumber> heap_number = | |||
| 9646 | AllocateHeapNumberWithValue(var_double_value.value()); | |||
| 9647 | *var_value = heap_number; | |||
| 9648 | Goto(&done); | |||
| 9649 | } | |||
| 9650 | } | |||
| 9651 | BIND(&if_in_descriptor)Bind(&if_in_descriptor); | |||
| 9652 | { | |||
| 9653 | *var_value = LoadValueByKeyIndex(descriptors, name_index); | |||
| 9654 | Goto(&done); | |||
| 9655 | } | |||
| 9656 | BIND(&done)Bind(&done); | |||
| 9657 | ||||
| 9658 | Comment("] LoadPropertyFromFastObject"); | |||
| 9659 | } | |||
| 9660 | ||||
| 9661 | template <typename Dictionary> | |||
| 9662 | void CodeStubAssembler::LoadPropertyFromDictionary( | |||
| 9663 | TNode<Dictionary> dictionary, TNode<IntPtrT> name_index, | |||
| 9664 | TVariable<Uint32T>* var_details, TVariable<Object>* var_value) { | |||
| 9665 | Comment("LoadPropertyFromNameDictionary"); | |||
| 9666 | *var_details = LoadDetailsByKeyIndex(dictionary, name_index); | |||
| 9667 | *var_value = LoadValueByKeyIndex(dictionary, name_index); | |||
| 9668 | ||||
| 9669 | Comment("] LoadPropertyFromNameDictionary"); | |||
| 9670 | } | |||
| 9671 | ||||
| 9672 | void CodeStubAssembler::LoadPropertyFromGlobalDictionary( | |||
| 9673 | TNode<GlobalDictionary> dictionary, TNode<IntPtrT> name_index, | |||
| 9674 | TVariable<Uint32T>* var_details, TVariable<Object>* var_value, | |||
| 9675 | Label* if_deleted) { | |||
| 9676 | Comment("[ LoadPropertyFromGlobalDictionary"); | |||
| 9677 | TNode<PropertyCell> property_cell = | |||
| 9678 | CAST(LoadFixedArrayElement(dictionary, name_index))Cast(LoadFixedArrayElement(dictionary, name_index)); | |||
| 9679 | ||||
| 9680 | TNode<Object> value = | |||
| 9681 | LoadObjectField(property_cell, PropertyCell::kValueOffset); | |||
| 9682 | GotoIf(TaggedEqual(value, TheHoleConstant()), if_deleted); | |||
| 9683 | ||||
| 9684 | *var_value = value; | |||
| 9685 | ||||
| 9686 | TNode<Uint32T> details = Unsigned(LoadAndUntagToWord32ObjectField( | |||
| 9687 | property_cell, PropertyCell::kPropertyDetailsRawOffset)); | |||
| 9688 | *var_details = details; | |||
| 9689 | ||||
| 9690 | Comment("] LoadPropertyFromGlobalDictionary"); | |||
| 9691 | } | |||
| 9692 | ||||
| 9693 | template void CodeStubAssembler::LoadPropertyFromDictionary( | |||
| 9694 | TNode<NameDictionary> dictionary, TNode<IntPtrT> name_index, | |||
| 9695 | TVariable<Uint32T>* var_details, TVariable<Object>* var_value); | |||
| 9696 | ||||
| 9697 | template void CodeStubAssembler::LoadPropertyFromDictionary( | |||
| 9698 | TNode<SwissNameDictionary> dictionary, TNode<IntPtrT> name_index, | |||
| 9699 | TVariable<Uint32T>* var_details, TVariable<Object>* var_value); | |||
| 9700 | ||||
| 9701 | // |value| is the property backing store's contents, which is either a value or | |||
| 9702 | // an accessor pair, as specified by |details|. |holder| is a JSObject or a | |||
| 9703 | // PropertyCell (TODO: use UnionT). Returns either the original value, or the | |||
| 9704 | // result of the getter call. | |||
| 9705 | TNode<Object> CodeStubAssembler::CallGetterIfAccessor( | |||
| 9706 | TNode<Object> value, TNode<HeapObject> holder, TNode<Uint32T> details, | |||
| 9707 | TNode<Context> context, TNode<Object> receiver, TNode<Object> name, | |||
| 9708 | Label* if_bailout, GetOwnPropertyMode mode) { | |||
| 9709 | TVARIABLE(Object, var_value, value)TVariable<Object> var_value(value, this); | |||
| 9710 | Label done(this), if_accessor_info(this, Label::kDeferred); | |||
| 9711 | ||||
| 9712 | TNode<Uint32T> kind = DecodeWord32<PropertyDetails::KindField>(details); | |||
| 9713 | GotoIf( | |||
| 9714 | Word32Equal(kind, Int32Constant(static_cast<int>(PropertyKind::kData))), | |||
| 9715 | &done); | |||
| 9716 | ||||
| 9717 | // Accessor case. | |||
| 9718 | GotoIfNot(IsAccessorPair(CAST(value)Cast(value)), &if_accessor_info); | |||
| 9719 | ||||
| 9720 | // AccessorPair case. | |||
| 9721 | { | |||
| 9722 | if (mode == kCallJSGetterUseCachedName || | |||
| 9723 | mode == kCallJSGetterDontUseCachedName) { | |||
| 9724 | Label if_callable(this), if_function_template_info(this); | |||
| 9725 | TNode<AccessorPair> accessor_pair = CAST(value)Cast(value); | |||
| 9726 | TNode<HeapObject> getter = | |||
| 9727 | CAST(LoadObjectField(accessor_pair, AccessorPair::kGetterOffset))Cast(LoadObjectField(accessor_pair, AccessorPair::kGetterOffset )); | |||
| 9728 | TNode<Map> getter_map = LoadMap(getter); | |||
| 9729 | ||||
| 9730 | GotoIf(IsCallableMap(getter_map), &if_callable); | |||
| 9731 | GotoIf(IsFunctionTemplateInfoMap(getter_map), &if_function_template_info); | |||
| 9732 | ||||
| 9733 | // Return undefined if the {getter} is not callable. | |||
| 9734 | var_value = UndefinedConstant(); | |||
| 9735 | Goto(&done); | |||
| 9736 | ||||
| 9737 | BIND(&if_callable)Bind(&if_callable); | |||
| 9738 | { | |||
| 9739 | // Call the accessor. No need to check side-effect mode here, since it | |||
| 9740 | // will be checked later in DebugOnFunctionCall. | |||
| 9741 | var_value = Call(context, getter, receiver); | |||
| 9742 | Goto(&done); | |||
| 9743 | } | |||
| 9744 | ||||
| 9745 | BIND(&if_function_template_info)Bind(&if_function_template_info); | |||
| 9746 | { | |||
| 9747 | Label runtime(this, Label::kDeferred); | |||
| 9748 | Label use_cached_property(this); | |||
| 9749 | GotoIf(IsSideEffectFreeDebuggingActive(), &runtime); | |||
| 9750 | TNode<HeapObject> cached_property_name = LoadObjectField<HeapObject>( | |||
| 9751 | getter, FunctionTemplateInfo::kCachedPropertyNameOffset); | |||
| 9752 | ||||
| 9753 | Label* has_cached_property = mode == kCallJSGetterUseCachedName | |||
| 9754 | ? &use_cached_property | |||
| 9755 | : if_bailout; | |||
| 9756 | GotoIfNot(IsTheHole(cached_property_name), has_cached_property); | |||
| 9757 | ||||
| 9758 | TNode<NativeContext> creation_context = | |||
| 9759 | GetCreationContext(CAST(holder)Cast(holder), if_bailout); | |||
| 9760 | var_value = CallBuiltin( | |||
| 9761 | Builtin::kCallFunctionTemplate_CheckAccessAndCompatibleReceiver, | |||
| 9762 | creation_context, getter, IntPtrConstant(i::JSParameterCount(0)), | |||
| 9763 | receiver); | |||
| 9764 | Goto(&done); | |||
| 9765 | ||||
| 9766 | if (mode == kCallJSGetterUseCachedName) { | |||
| 9767 | Bind(&use_cached_property); | |||
| 9768 | ||||
| 9769 | var_value = GetProperty(context, holder, cached_property_name); | |||
| 9770 | ||||
| 9771 | Goto(&done); | |||
| 9772 | } | |||
| 9773 | ||||
| 9774 | BIND(&runtime)Bind(&runtime); | |||
| 9775 | { | |||
| 9776 | var_value = CallRuntime(Runtime::kGetProperty, context, holder, name, | |||
| 9777 | receiver); | |||
| 9778 | Goto(&done); | |||
| 9779 | } | |||
| 9780 | } | |||
| 9781 | } else { | |||
| 9782 | DCHECK_EQ(mode, kReturnAccessorPair)((void) 0); | |||
| 9783 | Goto(&done); | |||
| 9784 | } | |||
| 9785 | } | |||
| 9786 | ||||
| 9787 | // AccessorInfo case. | |||
| 9788 | BIND(&if_accessor_info)Bind(&if_accessor_info); | |||
| 9789 | { | |||
| 9790 | TNode<AccessorInfo> accessor_info = CAST(value)Cast(value); | |||
| 9791 | Label if_array(this), if_function(this), if_wrapper(this); | |||
| 9792 | ||||
| 9793 | // Dispatch based on {holder} instance type. | |||
| 9794 | TNode<Map> holder_map = LoadMap(holder); | |||
| 9795 | TNode<Uint16T> holder_instance_type = LoadMapInstanceType(holder_map); | |||
| 9796 | GotoIf(IsJSArrayInstanceType(holder_instance_type), &if_array); | |||
| 9797 | GotoIf(IsJSFunctionInstanceType(holder_instance_type), &if_function); | |||
| 9798 | Branch(IsJSPrimitiveWrapperInstanceType(holder_instance_type), &if_wrapper, | |||
| 9799 | if_bailout); | |||
| 9800 | ||||
| 9801 | // JSArray AccessorInfo case. | |||
| 9802 | BIND(&if_array)Bind(&if_array); | |||
| 9803 | { | |||
| 9804 | // We only deal with the "length" accessor on JSArray. | |||
| 9805 | GotoIfNot(IsLengthString( | |||
| 9806 | LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), | |||
| 9807 | if_bailout); | |||
| 9808 | TNode<JSArray> array = CAST(holder)Cast(holder); | |||
| 9809 | var_value = LoadJSArrayLength(array); | |||
| 9810 | Goto(&done); | |||
| 9811 | } | |||
| 9812 | ||||
| 9813 | // JSFunction AccessorInfo case. | |||
| 9814 | BIND(&if_function)Bind(&if_function); | |||
| 9815 | { | |||
| 9816 | // We only deal with the "prototype" accessor on JSFunction here. | |||
| 9817 | GotoIfNot(IsPrototypeString( | |||
| 9818 | LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), | |||
| 9819 | if_bailout); | |||
| 9820 | ||||
| 9821 | TNode<JSFunction> function = CAST(holder)Cast(holder); | |||
| 9822 | GotoIfPrototypeRequiresRuntimeLookup(function, holder_map, if_bailout); | |||
| 9823 | var_value = LoadJSFunctionPrototype(function, if_bailout); | |||
| 9824 | Goto(&done); | |||
| 9825 | } | |||
| 9826 | ||||
| 9827 | // JSPrimitiveWrapper AccessorInfo case. | |||
| 9828 | BIND(&if_wrapper)Bind(&if_wrapper); | |||
| 9829 | { | |||
| 9830 | // We only deal with the "length" accessor on JSPrimitiveWrapper string | |||
| 9831 | // wrappers. | |||
| 9832 | GotoIfNot(IsLengthString( | |||
| 9833 | LoadObjectField(accessor_info, AccessorInfo::kNameOffset)), | |||
| 9834 | if_bailout); | |||
| 9835 | TNode<Object> holder_value = LoadJSPrimitiveWrapperValue(CAST(holder)Cast(holder)); | |||
| 9836 | GotoIfNot(TaggedIsNotSmi(holder_value), if_bailout); | |||
| 9837 | GotoIfNot(IsString(CAST(holder_value)Cast(holder_value)), if_bailout); | |||
| 9838 | var_value = LoadStringLengthAsSmi(CAST(holder_value)Cast(holder_value)); | |||
| 9839 | Goto(&done); | |||
| 9840 | } | |||
| 9841 | } | |||
| 9842 | ||||
| 9843 | BIND(&done)Bind(&done); | |||
| 9844 | return var_value.value(); | |||
| 9845 | } | |||
| 9846 | ||||
| 9847 | void CodeStubAssembler::TryGetOwnProperty( | |||
| 9848 | TNode<Context> context, TNode<Object> receiver, TNode<JSReceiver> object, | |||
| 9849 | TNode<Map> map, TNode<Int32T> instance_type, TNode<Name> unique_name, | |||
| 9850 | Label* if_found_value, TVariable<Object>* var_value, Label* if_not_found, | |||
| 9851 | Label* if_bailout) { | |||
| 9852 | TryGetOwnProperty(context, receiver, object, map, instance_type, unique_name, | |||
| 9853 | if_found_value, var_value, nullptr, nullptr, if_not_found, | |||
| 9854 | if_bailout, kCallJSGetterUseCachedName); | |||
| 9855 | } | |||
| 9856 | ||||
| 9857 | void CodeStubAssembler::TryGetOwnProperty( | |||
| 9858 | TNode<Context> context, TNode<Object> receiver, TNode<JSReceiver> object, | |||
| 9859 | TNode<Map> map, TNode<Int32T> instance_type, TNode<Name> unique_name, | |||
| 9860 | Label* if_found_value, TVariable<Object>* var_value, | |||
| 9861 | TVariable<Uint32T>* var_details, TVariable<Object>* var_raw_value, | |||
| 9862 | Label* if_not_found, Label* if_bailout, GetOwnPropertyMode mode) { | |||
| 9863 | DCHECK_EQ(MachineRepresentation::kTagged, var_value->rep())((void) 0); | |||
| 9864 | Comment("TryGetOwnProperty"); | |||
| 9865 | CSA_DCHECK(this, IsUniqueNameNoCachedIndex(unique_name))((void)0); | |||
| 9866 | TVARIABLE(HeapObject, var_meta_storage)TVariable<HeapObject> var_meta_storage(this); | |||
| 9867 | TVARIABLE(IntPtrT, var_entry)TVariable<IntPtrT> var_entry(this); | |||
| 9868 | ||||
| 9869 | Label if_found_fast(this), if_found_dict(this), if_found_global(this); | |||
| 9870 | ||||
| 9871 | TVARIABLE(Uint32T, local_var_details)TVariable<Uint32T> local_var_details(this); | |||
| 9872 | if (!var_details) { | |||
| 9873 | var_details = &local_var_details; | |||
| 9874 | } | |||
| 9875 | Label if_found(this); | |||
| 9876 | ||||
| 9877 | TryLookupProperty(object, map, instance_type, unique_name, &if_found_fast, | |||
| 9878 | &if_found_dict, &if_found_global, &var_meta_storage, | |||
| 9879 | &var_entry, if_not_found, if_bailout); | |||
| 9880 | BIND(&if_found_fast)Bind(&if_found_fast); | |||
| 9881 | { | |||
| 9882 | TNode<DescriptorArray> descriptors = CAST(var_meta_storage.value())Cast(var_meta_storage.value()); | |||
| 9883 | TNode<IntPtrT> name_index = var_entry.value(); | |||
| 9884 | ||||
| 9885 | LoadPropertyFromFastObject(object, map, descriptors, name_index, | |||
| 9886 | var_details, var_value); | |||
| 9887 | Goto(&if_found); | |||
| 9888 | } | |||
| 9889 | BIND(&if_found_dict)Bind(&if_found_dict); | |||
| 9890 | { | |||
| 9891 | TNode<PropertyDictionary> dictionary = CAST(var_meta_storage.value())Cast(var_meta_storage.value()); | |||
| 9892 | TNode<IntPtrT> entry = var_entry.value(); | |||
| 9893 | LoadPropertyFromDictionary(dictionary, entry, var_details, var_value); | |||
| 9894 | ||||
| 9895 | Goto(&if_found); | |||
| 9896 | } | |||
| 9897 | BIND(&if_found_global)Bind(&if_found_global); | |||
| 9898 | { | |||
| 9899 | TNode<GlobalDictionary> dictionary = CAST(var_meta_storage.value())Cast(var_meta_storage.value()); | |||
| 9900 | TNode<IntPtrT> entry = var_entry.value(); | |||
| 9901 | ||||
| 9902 | LoadPropertyFromGlobalDictionary(dictionary, entry, var_details, var_value, | |||
| 9903 | if_not_found); | |||
| 9904 | Goto(&if_found); | |||
| 9905 | } | |||
| 9906 | // Here we have details and value which could be an accessor. | |||
| 9907 | BIND(&if_found)Bind(&if_found); | |||
| 9908 | { | |||
| 9909 | // TODO(ishell): Execute C++ accessor in case of accessor info | |||
| 9910 | if (var_raw_value) { | |||
| 9911 | *var_raw_value = *var_value; | |||
| 9912 | } | |||
| 9913 | TNode<Object> value = | |||
| 9914 | CallGetterIfAccessor(var_value->value(), object, var_details->value(), | |||
| 9915 | context, receiver, unique_name, if_bailout, mode); | |||
| 9916 | *var_value = value; | |||
| 9917 | Goto(if_found_value); | |||
| 9918 | } | |||
| 9919 | } | |||
| 9920 | ||||
| 9921 | void CodeStubAssembler::TryLookupElement( | |||
| 9922 | TNode<HeapObject> object, TNode<Map> map, TNode<Int32T> instance_type, | |||
| 9923 | TNode<IntPtrT> intptr_index, Label* if_found, Label* if_absent, | |||
| 9924 | Label* if_not_found, Label* if_bailout) { | |||
| 9925 | // Handle special objects in runtime. | |||
| 9926 | GotoIf(IsSpecialReceiverInstanceType(instance_type), if_bailout); | |||
| 9927 | ||||
| 9928 | TNode<Int32T> elements_kind = LoadMapElementsKind(map); | |||
| 9929 | ||||
| 9930 | // TODO(verwaest): Support other elements kinds as well. | |||
| 9931 | Label if_isobjectorsmi(this), if_isdouble(this), if_isdictionary(this), | |||
| 9932 | if_isfaststringwrapper(this), if_isslowstringwrapper(this), if_oob(this), | |||
| 9933 | if_typedarray(this), if_rab_gsab_typedarray(this); | |||
| 9934 | // clang-format off | |||
| 9935 | int32_t values[] = { | |||
| 9936 | // Handled by {if_isobjectorsmi}. | |||
| 9937 | PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_ELEMENTS, HOLEY_ELEMENTS, | |||
| 9938 | PACKED_NONEXTENSIBLE_ELEMENTS, PACKED_SEALED_ELEMENTS, | |||
| 9939 | HOLEY_NONEXTENSIBLE_ELEMENTS, HOLEY_SEALED_ELEMENTS, | |||
| 9940 | PACKED_FROZEN_ELEMENTS, HOLEY_FROZEN_ELEMENTS, | |||
| 9941 | // Handled by {if_isdouble}. | |||
| 9942 | PACKED_DOUBLE_ELEMENTS, HOLEY_DOUBLE_ELEMENTS, | |||
| 9943 | // Handled by {if_isdictionary}. | |||
| 9944 | DICTIONARY_ELEMENTS, | |||
| 9945 | // Handled by {if_isfaststringwrapper}. | |||
| 9946 | FAST_STRING_WRAPPER_ELEMENTS, | |||
| 9947 | // Handled by {if_isslowstringwrapper}. | |||
| 9948 | SLOW_STRING_WRAPPER_ELEMENTS, | |||
| 9949 | // Handled by {if_not_found}. | |||
| 9950 | NO_ELEMENTS, | |||
| 9951 | // Handled by {if_typed_array}. | |||
| 9952 | UINT8_ELEMENTS, | |||
| 9953 | INT8_ELEMENTS, | |||
| 9954 | UINT16_ELEMENTS, | |||
| 9955 | INT16_ELEMENTS, | |||
| 9956 | UINT32_ELEMENTS, | |||
| 9957 | INT32_ELEMENTS, | |||
| 9958 | FLOAT32_ELEMENTS, | |||
| 9959 | FLOAT64_ELEMENTS, | |||
| 9960 | UINT8_CLAMPED_ELEMENTS, | |||
| 9961 | BIGUINT64_ELEMENTS, | |||
| 9962 | BIGINT64_ELEMENTS, | |||
| 9963 | RAB_GSAB_UINT8_ELEMENTS, | |||
| 9964 | RAB_GSAB_INT8_ELEMENTS, | |||
| 9965 | RAB_GSAB_UINT16_ELEMENTS, | |||
| 9966 | RAB_GSAB_INT16_ELEMENTS, | |||
| 9967 | RAB_GSAB_UINT32_ELEMENTS, | |||
| 9968 | RAB_GSAB_INT32_ELEMENTS, | |||
| 9969 | RAB_GSAB_FLOAT32_ELEMENTS, | |||
| 9970 | RAB_GSAB_FLOAT64_ELEMENTS, | |||
| 9971 | RAB_GSAB_UINT8_CLAMPED_ELEMENTS, | |||
| 9972 | RAB_GSAB_BIGUINT64_ELEMENTS, | |||
| 9973 | RAB_GSAB_BIGINT64_ELEMENTS, | |||
| 9974 | }; | |||
| 9975 | Label* labels[] = { | |||
| 9976 | &if_isobjectorsmi, &if_isobjectorsmi, &if_isobjectorsmi, | |||
| 9977 | &if_isobjectorsmi, &if_isobjectorsmi, &if_isobjectorsmi, | |||
| 9978 | &if_isobjectorsmi, &if_isobjectorsmi, &if_isobjectorsmi, | |||
| 9979 | &if_isobjectorsmi, | |||
| 9980 | &if_isdouble, &if_isdouble, | |||
| 9981 | &if_isdictionary, | |||
| 9982 | &if_isfaststringwrapper, | |||
| 9983 | &if_isslowstringwrapper, | |||
| 9984 | if_not_found, | |||
| 9985 | &if_typedarray, | |||
| 9986 | &if_typedarray, | |||
| 9987 | &if_typedarray, | |||
| 9988 | &if_typedarray, | |||
| 9989 | &if_typedarray, | |||
| 9990 | &if_typedarray, | |||
| 9991 | &if_typedarray, | |||
| 9992 | &if_typedarray, | |||
| 9993 | &if_typedarray, | |||
| 9994 | &if_typedarray, | |||
| 9995 | &if_typedarray, | |||
| 9996 | &if_rab_gsab_typedarray, | |||
| 9997 | &if_rab_gsab_typedarray, | |||
| 9998 | &if_rab_gsab_typedarray, | |||
| 9999 | &if_rab_gsab_typedarray, | |||
| 10000 | &if_rab_gsab_typedarray, | |||
| 10001 | &if_rab_gsab_typedarray, | |||
| 10002 | &if_rab_gsab_typedarray, | |||
| 10003 | &if_rab_gsab_typedarray, | |||
| 10004 | &if_rab_gsab_typedarray, | |||
| 10005 | &if_rab_gsab_typedarray, | |||
| 10006 | &if_rab_gsab_typedarray, | |||
| 10007 | }; | |||
| 10008 | // clang-format on | |||
| 10009 | STATIC_ASSERT(arraysize(values) == arraysize(labels))static_assert((sizeof(ArraySizeHelper(values))) == (sizeof(ArraySizeHelper (labels))), "arraysize(values) == arraysize(labels)"); | |||
| 10010 | Switch(elements_kind, if_bailout, values, labels, arraysize(values)(sizeof(ArraySizeHelper(values)))); | |||
| 10011 | ||||
| 10012 | BIND(&if_isobjectorsmi)Bind(&if_isobjectorsmi); | |||
| 10013 | { | |||
| 10014 | TNode<FixedArray> elements = CAST(LoadElements(CAST(object)))Cast(LoadElements(Cast(object))); | |||
| 10015 | TNode<IntPtrT> length = LoadAndUntagFixedArrayBaseLength(elements); | |||
| 10016 | ||||
| 10017 | GotoIfNot(UintPtrLessThan(intptr_index, length), &if_oob); | |||
| 10018 | ||||
| 10019 | TNode<Object> element = UnsafeLoadFixedArrayElement(elements, intptr_index); | |||
| 10020 | TNode<Oddball> the_hole = TheHoleConstant(); | |||
| 10021 | Branch(TaggedEqual(element, the_hole), if_not_found, if_found); | |||
| 10022 | } | |||
| 10023 | BIND(&if_isdouble)Bind(&if_isdouble); | |||
| 10024 | { | |||
| 10025 | TNode<FixedArrayBase> elements = LoadElements(CAST(object)Cast(object)); | |||
| 10026 | TNode<IntPtrT> length = LoadAndUntagFixedArrayBaseLength(elements); | |||
| 10027 | ||||
| 10028 | GotoIfNot(UintPtrLessThan(intptr_index, length), &if_oob); | |||
| 10029 | ||||
| 10030 | // Check if the element is a double hole, but don't load it. | |||
| 10031 | LoadFixedDoubleArrayElement(CAST(elements)Cast(elements), intptr_index, if_not_found, | |||
| 10032 | MachineType::None()); | |||
| 10033 | Goto(if_found); | |||
| 10034 | } | |||
| 10035 | BIND(&if_isdictionary)Bind(&if_isdictionary); | |||
| 10036 | { | |||
| 10037 | // Negative and too-large keys must be converted to property names. | |||
| 10038 | if (Is64()) { | |||
| 10039 | GotoIf(UintPtrLessThan(IntPtrConstant(JSObject::kMaxElementIndex), | |||
| 10040 | intptr_index), | |||
| 10041 | if_bailout); | |||
| 10042 | } else { | |||
| 10043 | GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), if_bailout); | |||
| 10044 | } | |||
| 10045 | ||||
| 10046 | TVARIABLE(IntPtrT, var_entry)TVariable<IntPtrT> var_entry(this); | |||
| 10047 | TNode<NumberDictionary> elements = CAST(LoadElements(CAST(object)))Cast(LoadElements(Cast(object))); | |||
| 10048 | NumberDictionaryLookup(elements, intptr_index, if_found, &var_entry, | |||
| 10049 | if_not_found); | |||
| 10050 | } | |||
| 10051 | BIND(&if_isfaststringwrapper)Bind(&if_isfaststringwrapper); | |||
| 10052 | { | |||
| 10053 | TNode<String> string = CAST(LoadJSPrimitiveWrapperValue(CAST(object)))Cast(LoadJSPrimitiveWrapperValue(Cast(object))); | |||
| 10054 | TNode<IntPtrT> length = LoadStringLengthAsWord(string); | |||
| 10055 | GotoIf(UintPtrLessThan(intptr_index, length), if_found); | |||
| 10056 | Goto(&if_isobjectorsmi); | |||
| 10057 | } | |||
| 10058 | BIND(&if_isslowstringwrapper)Bind(&if_isslowstringwrapper); | |||
| 10059 | { | |||
| 10060 | TNode<String> string = CAST(LoadJSPrimitiveWrapperValue(CAST(object)))Cast(LoadJSPrimitiveWrapperValue(Cast(object))); | |||
| 10061 | TNode<IntPtrT> length = LoadStringLengthAsWord(string); | |||
| 10062 | GotoIf(UintPtrLessThan(intptr_index, length), if_found); | |||
| 10063 | Goto(&if_isdictionary); | |||
| 10064 | } | |||
| 10065 | BIND(&if_typedarray)Bind(&if_typedarray); | |||
| 10066 | { | |||
| 10067 | TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(CAST(object)Cast(object)); | |||
| 10068 | GotoIf(IsDetachedBuffer(buffer), if_absent); | |||
| 10069 | ||||
| 10070 | TNode<UintPtrT> length = LoadJSTypedArrayLength(CAST(object)Cast(object)); | |||
| 10071 | Branch(UintPtrLessThan(intptr_index, length), if_found, if_absent); | |||
| 10072 | } | |||
| 10073 | BIND(&if_rab_gsab_typedarray)Bind(&if_rab_gsab_typedarray); | |||
| 10074 | { | |||
| 10075 | TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(CAST(object)Cast(object)); | |||
| 10076 | TNode<UintPtrT> length = | |||
| 10077 | LoadVariableLengthJSTypedArrayLength(CAST(object)Cast(object), buffer, if_absent); | |||
| 10078 | Branch(UintPtrLessThan(intptr_index, length), if_found, if_absent); | |||
| 10079 | } | |||
| 10080 | BIND(&if_oob)Bind(&if_oob); | |||
| 10081 | { | |||
| 10082 | // Positive OOB indices mean "not found", negative indices and indices | |||
| 10083 | // out of array index range must be converted to property names. | |||
| 10084 | if (Is64()) { | |||
| 10085 | GotoIf(UintPtrLessThan(IntPtrConstant(JSObject::kMaxElementIndex), | |||
| 10086 | intptr_index), | |||
| 10087 | if_bailout); | |||
| 10088 | } else { | |||
| 10089 | GotoIf(IntPtrLessThan(intptr_index, IntPtrConstant(0)), if_bailout); | |||
| 10090 | } | |||
| 10091 | Goto(if_not_found); | |||
| 10092 | } | |||
| 10093 | } | |||
| 10094 | ||||
| 10095 | void CodeStubAssembler::BranchIfMaybeSpecialIndex(TNode<String> name_string, | |||
| 10096 | Label* if_maybe_special_index, | |||
| 10097 | Label* if_not_special_index) { | |||
| 10098 | // TODO(cwhan.tunz): Implement fast cases more. | |||
| 10099 | ||||
| 10100 | // If a name is empty or too long, it's not a special index | |||
| 10101 | // Max length of canonical double: -X.XXXXXXXXXXXXXXXXX-eXXX | |||
| 10102 | const int kBufferSize = 24; | |||
| 10103 | TNode<Smi> string_length = LoadStringLengthAsSmi(name_string); | |||
| 10104 | GotoIf(SmiEqual(string_length, SmiConstant(0)), if_not_special_index); | |||
| 10105 | GotoIf(SmiGreaterThan(string_length, SmiConstant(kBufferSize)), | |||
| 10106 | if_not_special_index); | |||
| 10107 | ||||
| 10108 | // If the first character of name is not a digit or '-', or we can't match it | |||
| 10109 | // to Infinity or NaN, then this is not a special index. | |||
| 10110 | TNode<Int32T> first_char = StringCharCodeAt(name_string, UintPtrConstant(0)); | |||
| 10111 | // If the name starts with '-', it can be a negative index. | |||
| 10112 | GotoIf(Word32Equal(first_char, Int32Constant('-')), if_maybe_special_index); | |||
| 10113 | // If the name starts with 'I', it can be "Infinity". | |||
| 10114 | GotoIf(Word32Equal(first_char, Int32Constant('I')), if_maybe_special_index); | |||
| 10115 | // If the name starts with 'N', it can be "NaN". | |||
| 10116 | GotoIf(Word32Equal(first_char, Int32Constant('N')), if_maybe_special_index); | |||
| 10117 | // Finally, if the first character is not a digit either, then we are sure | |||
| 10118 | // that the name is not a special index. | |||
| 10119 | GotoIf(Uint32LessThan(first_char, Int32Constant('0')), if_not_special_index); | |||
| 10120 | GotoIf(Uint32LessThan(Int32Constant('9'), first_char), if_not_special_index); | |||
| 10121 | Goto(if_maybe_special_index); | |||
| 10122 | } | |||
| 10123 | ||||
| 10124 | void CodeStubAssembler::TryPrototypeChainLookup( | |||
| 10125 | TNode<Object> receiver, TNode<Object> object_arg, TNode<Object> key, | |||
| 10126 | const LookupPropertyInHolder& lookup_property_in_holder, | |||
| 10127 | const LookupElementInHolder& lookup_element_in_holder, Label* if_end, | |||
| 10128 | Label* if_bailout, Label* if_proxy, bool handle_private_names) { | |||
| 10129 | // Ensure receiver is JSReceiver, otherwise bailout. | |||
| 10130 | GotoIf(TaggedIsSmi(receiver), if_bailout); | |||
| 10131 | TNode<HeapObject> object = CAST(object_arg)Cast(object_arg); | |||
| 10132 | ||||
| 10133 | TNode<Map> map = LoadMap(object); | |||
| 10134 | TNode<Uint16T> instance_type = LoadMapInstanceType(map); | |||
| 10135 | { | |||
| 10136 | Label if_objectisreceiver(this); | |||
| 10137 | Branch(IsJSReceiverInstanceType(instance_type), &if_objectisreceiver, | |||
| 10138 | if_bailout); | |||
| 10139 | BIND(&if_objectisreceiver)Bind(&if_objectisreceiver); | |||
| 10140 | ||||
| 10141 | GotoIf(InstanceTypeEqual(instance_type, JS_PROXY_TYPE), if_proxy); | |||
| 10142 | } | |||
| 10143 | ||||
| 10144 | TVARIABLE(IntPtrT, var_index)TVariable<IntPtrT> var_index(this); | |||
| 10145 | TVARIABLE(Name, var_unique)TVariable<Name> var_unique(this); | |||
| 10146 | ||||
| 10147 | Label if_keyisindex(this), if_iskeyunique(this); | |||
| 10148 | TryToName(key, &if_keyisindex, &var_index, &if_iskeyunique, &var_unique, | |||
| 10149 | if_bailout); | |||
| 10150 | ||||
| 10151 | BIND(&if_iskeyunique)Bind(&if_iskeyunique); | |||
| 10152 | { | |||
| 10153 | TVARIABLE(HeapObject, var_holder, object)TVariable<HeapObject> var_holder(object, this); | |||
| 10154 | TVARIABLE(Map, var_holder_map, map)TVariable<Map> var_holder_map(map, this); | |||
| 10155 | TVARIABLE(Int32T, var_holder_instance_type, instance_type)TVariable<Int32T> var_holder_instance_type(instance_type , this); | |||
| 10156 | ||||
| 10157 | Label loop(this, {&var_holder, &var_holder_map, &var_holder_instance_type}); | |||
| 10158 | Goto(&loop); | |||
| 10159 | BIND(&loop)Bind(&loop); | |||
| 10160 | { | |||
| 10161 | TNode<Map> holder_map = var_holder_map.value(); | |||
| 10162 | TNode<Int32T> holder_instance_type = var_holder_instance_type.value(); | |||
| 10163 | ||||
| 10164 | Label next_proto(this), check_integer_indexed_exotic(this); | |||
| 10165 | lookup_property_in_holder(CAST(receiver)Cast(receiver), var_holder.value(), holder_map, | |||
| 10166 | holder_instance_type, var_unique.value(), | |||
| 10167 | &check_integer_indexed_exotic, if_bailout); | |||
| 10168 | ||||
| 10169 | BIND(&check_integer_indexed_exotic)Bind(&check_integer_indexed_exotic); | |||
| 10170 | { | |||
| 10171 | // Bailout if it can be an integer indexed exotic case. | |||
| 10172 | GotoIfNot(InstanceTypeEqual(holder_instance_type, JS_TYPED_ARRAY_TYPE), | |||
| 10173 | &next_proto); | |||
| 10174 | GotoIfNot(IsString(var_unique.value()), &next_proto); | |||
| 10175 | BranchIfMaybeSpecialIndex(CAST(var_unique.value())Cast(var_unique.value()), if_bailout, | |||
| 10176 | &next_proto); | |||
| 10177 | } | |||
| 10178 | ||||
| 10179 | BIND(&next_proto)Bind(&next_proto); | |||
| 10180 | ||||
| 10181 | if (handle_private_names) { | |||
| 10182 | // Private name lookup doesn't walk the prototype chain. | |||
| 10183 | GotoIf(IsPrivateSymbol(CAST(key)Cast(key)), if_end); | |||
| 10184 | } | |||
| 10185 | ||||
| 10186 | TNode<HeapObject> proto = LoadMapPrototype(holder_map); | |||
| 10187 | ||||
| 10188 | GotoIf(IsNull(proto), if_end); | |||
| 10189 | ||||
| 10190 | TNode<Map> proto_map = LoadMap(proto); | |||
| 10191 | TNode<Uint16T> proto_instance_type = LoadMapInstanceType(proto_map); | |||
| 10192 | ||||
| 10193 | var_holder = proto; | |||
| 10194 | var_holder_map = proto_map; | |||
| 10195 | var_holder_instance_type = proto_instance_type; | |||
| 10196 | Goto(&loop); | |||
| 10197 | } | |||
| 10198 | } | |||
| 10199 | BIND(&if_keyisindex)Bind(&if_keyisindex); | |||
| 10200 | { | |||
| 10201 | TVARIABLE(HeapObject, var_holder, object)TVariable<HeapObject> var_holder(object, this); | |||
| 10202 | TVARIABLE(Map, var_holder_map, map)TVariable<Map> var_holder_map(map, this); | |||
| 10203 | TVARIABLE(Int32T, var_holder_instance_type, instance_type)TVariable<Int32T> var_holder_instance_type(instance_type , this); | |||
| 10204 | ||||
| 10205 | Label loop(this, {&var_holder, &var_holder_map, &var_holder_instance_type}); | |||
| 10206 | Goto(&loop); | |||
| 10207 | BIND(&loop)Bind(&loop); | |||
| 10208 | { | |||
| 10209 | Label next_proto(this); | |||
| 10210 | lookup_element_in_holder(CAST(receiver)Cast(receiver), var_holder.value(), | |||
| 10211 | var_holder_map.value(), | |||
| 10212 | var_holder_instance_type.value(), | |||
| 10213 | var_index.value(), &next_proto, if_bailout); | |||
| 10214 | BIND(&next_proto)Bind(&next_proto); | |||
| 10215 | ||||
| 10216 | TNode<HeapObject> proto = LoadMapPrototype(var_holder_map.value()); | |||
| 10217 | ||||
| 10218 | GotoIf(IsNull(proto), if_end); | |||
| 10219 | ||||
| 10220 | TNode<Map> proto_map = LoadMap(proto); | |||
| 10221 | TNode<Uint16T> proto_instance_type = LoadMapInstanceType(proto_map); | |||
| 10222 | ||||
| 10223 | var_holder = proto; | |||
| 10224 | var_holder_map = proto_map; | |||
| 10225 | var_holder_instance_type = proto_instance_type; | |||
| 10226 | Goto(&loop); | |||
| 10227 | } | |||
| 10228 | } | |||
| 10229 | } | |||
| 10230 | ||||
| 10231 | TNode<Oddball> CodeStubAssembler::HasInPrototypeChain(TNode<Context> context, | |||
| 10232 | TNode<HeapObject> object, | |||
| 10233 | TNode<Object> prototype) { | |||
| 10234 | TVARIABLE(Oddball, var_result)TVariable<Oddball> var_result(this); | |||
| 10235 | Label return_false(this), return_true(this), | |||
| 10236 | return_runtime(this, Label::kDeferred), return_result(this); | |||
| 10237 | ||||
| 10238 | // Loop through the prototype chain looking for the {prototype}. | |||
| 10239 | TVARIABLE(Map, var_object_map, LoadMap(object))TVariable<Map> var_object_map(LoadMap(object), this); | |||
| 10240 | Label loop(this, &var_object_map); | |||
| 10241 | Goto(&loop); | |||
| 10242 | BIND(&loop)Bind(&loop); | |||
| 10243 | { | |||
| 10244 | // Check if we can determine the prototype directly from the {object_map}. | |||
| 10245 | Label if_objectisdirect(this), if_objectisspecial(this, Label::kDeferred); | |||
| 10246 | TNode<Map> object_map = var_object_map.value(); | |||
| 10247 | TNode<Uint16T> object_instance_type = LoadMapInstanceType(object_map); | |||
| 10248 | Branch(IsSpecialReceiverInstanceType(object_instance_type), | |||
| 10249 | &if_objectisspecial, &if_objectisdirect); | |||
| 10250 | BIND(&if_objectisspecial)Bind(&if_objectisspecial); | |||
| 10251 | { | |||
| 10252 | // The {object_map} is a special receiver map or a primitive map, check | |||
| 10253 | // if we need to use the if_objectisspecial path in the runtime. | |||
| 10254 | GotoIf(InstanceTypeEqual(object_instance_type, JS_PROXY_TYPE), | |||
| 10255 | &return_runtime); | |||
| 10256 | TNode<Int32T> object_bitfield = LoadMapBitField(object_map); | |||
| 10257 | int mask = Map::Bits1::HasNamedInterceptorBit::kMask | | |||
| 10258 | Map::Bits1::IsAccessCheckNeededBit::kMask; | |||
| 10259 | Branch(IsSetWord32(object_bitfield, mask), &return_runtime, | |||
| 10260 | &if_objectisdirect); | |||
| 10261 | } | |||
| 10262 | BIND(&if_objectisdirect)Bind(&if_objectisdirect); | |||
| 10263 | ||||
| 10264 | // Check the current {object} prototype. | |||
| 10265 | TNode<HeapObject> object_prototype = LoadMapPrototype(object_map); | |||
| 10266 | GotoIf(IsNull(object_prototype), &return_false); | |||
| 10267 | GotoIf(TaggedEqual(object_prototype, prototype), &return_true); | |||
| 10268 | ||||
| 10269 | // Continue with the prototype. | |||
| 10270 | CSA_DCHECK(this, TaggedIsNotSmi(object_prototype))((void)0); | |||
| 10271 | var_object_map = LoadMap(object_prototype); | |||
| 10272 | Goto(&loop); | |||
| 10273 | } | |||
| 10274 | ||||
| 10275 | BIND(&return_true)Bind(&return_true); | |||
| 10276 | var_result = TrueConstant(); | |||
| 10277 | Goto(&return_result); | |||
| 10278 | ||||
| 10279 | BIND(&return_false)Bind(&return_false); | |||
| 10280 | var_result = FalseConstant(); | |||
| 10281 | Goto(&return_result); | |||
| 10282 | ||||
| 10283 | BIND(&return_runtime)Bind(&return_runtime); | |||
| 10284 | { | |||
| 10285 | // Fallback to the runtime implementation. | |||
| 10286 | var_result = CAST(Cast(CallRuntime(Runtime::kHasInPrototypeChain, context, object , prototype)) | |||
| 10287 | CallRuntime(Runtime::kHasInPrototypeChain, context, object, prototype))Cast(CallRuntime(Runtime::kHasInPrototypeChain, context, object , prototype)); | |||
| 10288 | } | |||
| 10289 | Goto(&return_result); | |||
| 10290 | ||||
| 10291 | BIND(&return_result)Bind(&return_result); | |||
| 10292 | return var_result.value(); | |||
| 10293 | } | |||
| 10294 | ||||
| 10295 | TNode<Oddball> CodeStubAssembler::OrdinaryHasInstance( | |||
| 10296 | TNode<Context> context, TNode<Object> callable_maybe_smi, | |||
| 10297 | TNode<Object> object_maybe_smi) { | |||
| 10298 | TVARIABLE(Oddball, var_result)TVariable<Oddball> var_result(this); | |||
| 10299 | Label return_runtime(this, Label::kDeferred), return_result(this); | |||
| 10300 | ||||
| 10301 | GotoIfForceSlowPath(&return_runtime); | |||
| 10302 | ||||
| 10303 | // Goto runtime if {object} is a Smi. | |||
| 10304 | GotoIf(TaggedIsSmi(object_maybe_smi), &return_runtime); | |||
| 10305 | ||||
| 10306 | // Goto runtime if {callable} is a Smi. | |||
| 10307 | GotoIf(TaggedIsSmi(callable_maybe_smi), &return_runtime); | |||
| 10308 | ||||
| 10309 | { | |||
| 10310 | // Load map of {callable}. | |||
| 10311 | TNode<HeapObject> object = CAST(object_maybe_smi)Cast(object_maybe_smi); | |||
| 10312 | TNode<HeapObject> callable = CAST(callable_maybe_smi)Cast(callable_maybe_smi); | |||
| 10313 | TNode<Map> callable_map = LoadMap(callable); | |||
| 10314 | ||||
| 10315 | // Goto runtime if {callable} is not a JSFunction. | |||
| 10316 | TNode<Uint16T> callable_instance_type = LoadMapInstanceType(callable_map); | |||
| 10317 | GotoIfNot(IsJSFunctionInstanceType(callable_instance_type), | |||
| 10318 | &return_runtime); | |||
| 10319 | ||||
| 10320 | GotoIfPrototypeRequiresRuntimeLookup(CAST(callable)Cast(callable), callable_map, | |||
| 10321 | &return_runtime); | |||
| 10322 | ||||
| 10323 | // Get the "prototype" (or initial map) of the {callable}. | |||
| 10324 | TNode<HeapObject> callable_prototype = LoadObjectField<HeapObject>( | |||
| 10325 | callable, JSFunction::kPrototypeOrInitialMapOffset); | |||
| 10326 | { | |||
| 10327 | Label no_initial_map(this), walk_prototype_chain(this); | |||
| 10328 | TVARIABLE(HeapObject, var_callable_prototype, callable_prototype)TVariable<HeapObject> var_callable_prototype(callable_prototype , this); | |||
| 10329 | ||||
| 10330 | // Resolve the "prototype" if the {callable} has an initial map. | |||
| 10331 | GotoIfNot(IsMap(callable_prototype), &no_initial_map); | |||
| 10332 | var_callable_prototype = LoadObjectField<HeapObject>( | |||
| 10333 | callable_prototype, Map::kPrototypeOffset); | |||
| 10334 | Goto(&walk_prototype_chain); | |||
| 10335 | ||||
| 10336 | BIND(&no_initial_map)Bind(&no_initial_map); | |||
| 10337 | // {callable_prototype} is the hole if the "prototype" property hasn't | |||
| 10338 | // been requested so far. | |||
| 10339 | Branch(TaggedEqual(callable_prototype, TheHoleConstant()), | |||
| 10340 | &return_runtime, &walk_prototype_chain); | |||
| 10341 | ||||
| 10342 | BIND(&walk_prototype_chain)Bind(&walk_prototype_chain); | |||
| 10343 | callable_prototype = var_callable_prototype.value(); | |||
| 10344 | } | |||
| 10345 | ||||
| 10346 | // Loop through the prototype chain looking for the {callable} prototype. | |||
| 10347 | var_result = HasInPrototypeChain(context, object, callable_prototype); | |||
| 10348 | Goto(&return_result); | |||
| 10349 | } | |||
| 10350 | ||||
| 10351 | BIND(&return_runtime)Bind(&return_runtime); | |||
| 10352 | { | |||
| 10353 | // Fallback to the runtime implementation. | |||
| 10354 | var_result = CAST(CallRuntime(Runtime::kOrdinaryHasInstance, context,Cast(CallRuntime(Runtime::kOrdinaryHasInstance, context, callable_maybe_smi , object_maybe_smi)) | |||
| 10355 | callable_maybe_smi, object_maybe_smi))Cast(CallRuntime(Runtime::kOrdinaryHasInstance, context, callable_maybe_smi , object_maybe_smi)); | |||
| 10356 | } | |||
| 10357 | Goto(&return_result); | |||
| 10358 | ||||
| 10359 | BIND(&return_result)Bind(&return_result); | |||
| 10360 | return var_result.value(); | |||
| 10361 | } | |||
| 10362 | ||||
| 10363 | template <typename TIndex> | |||
| 10364 | TNode<IntPtrT> CodeStubAssembler::ElementOffsetFromIndex( | |||
| 10365 | TNode<TIndex> index_node, ElementsKind kind, int base_size) { | |||
| 10366 | // TODO(v8:9708): Remove IntPtrT variant in favor of UintPtrT. | |||
| 10367 | static_assert(std::is_same<TIndex, Smi>::value || | |||
| 10368 | std::is_same<TIndex, TaggedIndex>::value || | |||
| 10369 | std::is_same<TIndex, IntPtrT>::value || | |||
| 10370 | std::is_same<TIndex, UintPtrT>::value, | |||
| 10371 | "Only Smi, UintPtrT or IntPtrT index nodes are allowed"); | |||
| 10372 | int element_size_shift = ElementsKindToShiftSize(kind); | |||
| 10373 | int element_size = 1 << element_size_shift; | |||
| 10374 | intptr_t index = 0; | |||
| 10375 | TNode<IntPtrT> intptr_index_node; | |||
| 10376 | bool constant_index = false; | |||
| 10377 | if (std::is_same<TIndex, Smi>::value) { | |||
| 10378 | TNode<Smi> smi_index_node = ReinterpretCast<Smi>(index_node); | |||
| 10379 | int const kSmiShiftBits = kSmiShiftSize + kSmiTagSize; | |||
| 10380 | element_size_shift -= kSmiShiftBits; | |||
| 10381 | Smi smi_index; | |||
| 10382 | constant_index = TryToSmiConstant(smi_index_node, &smi_index); | |||
| 10383 | if (constant_index) { | |||
| 10384 | index = smi_index.value(); | |||
| 10385 | } else { | |||
| 10386 | if (COMPRESS_POINTERS_BOOLfalse) { | |||
| 10387 | smi_index_node = NormalizeSmiIndex(smi_index_node); | |||
| 10388 | } | |||
| 10389 | } | |||
| 10390 | intptr_index_node = BitcastTaggedToWordForTagAndSmiBits(smi_index_node); | |||
| 10391 | } else if (std::is_same<TIndex, TaggedIndex>::value) { | |||
| 10392 | TNode<TaggedIndex> tagged_index_node = | |||
| 10393 | ReinterpretCast<TaggedIndex>(index_node); | |||
| 10394 | element_size_shift -= kSmiTagSize; | |||
| 10395 | intptr_index_node = BitcastTaggedToWordForTagAndSmiBits(tagged_index_node); | |||
| 10396 | constant_index = TryToIntPtrConstant(intptr_index_node, &index); | |||
| 10397 | } else { | |||
| 10398 | intptr_index_node = ReinterpretCast<IntPtrT>(index_node); | |||
| 10399 | constant_index = TryToIntPtrConstant(intptr_index_node, &index); | |||
| 10400 | } | |||
| 10401 | if (constant_index) { | |||
| 10402 | return IntPtrConstant(base_size + element_size * index); | |||
| 10403 | } | |||
| 10404 | ||||
| 10405 | TNode<IntPtrT> shifted_index = | |||
| 10406 | (element_size_shift == 0) | |||
| 10407 | ? intptr_index_node | |||
| 10408 | : ((element_size_shift > 0) | |||
| 10409 | ? WordShl(intptr_index_node, | |||
| 10410 | IntPtrConstant(element_size_shift)) | |||
| 10411 | : WordSar(intptr_index_node, | |||
| 10412 | IntPtrConstant(-element_size_shift))); | |||
| 10413 | return IntPtrAdd(IntPtrConstant(base_size), Signed(shifted_index)); | |||
| 10414 | } | |||
| 10415 | ||||
| 10416 | // Instantiate ElementOffsetFromIndex for Smi and IntPtrT. | |||
| 10417 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 10418 | CodeStubAssembler::ElementOffsetFromIndex<Smi>(TNode<Smi> index_node, | |||
| 10419 | ElementsKind kind, | |||
| 10420 | int base_size); | |||
| 10421 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 10422 | CodeStubAssembler::ElementOffsetFromIndex<TaggedIndex>( | |||
| 10423 | TNode<TaggedIndex> index_node, ElementsKind kind, int base_size); | |||
| 10424 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 10425 | CodeStubAssembler::ElementOffsetFromIndex<IntPtrT>(TNode<IntPtrT> index_node, | |||
| 10426 | ElementsKind kind, | |||
| 10427 | int base_size); | |||
| 10428 | ||||
| 10429 | TNode<BoolT> CodeStubAssembler::IsOffsetInBounds(TNode<IntPtrT> offset, | |||
| 10430 | TNode<IntPtrT> length, | |||
| 10431 | int header_size, | |||
| 10432 | ElementsKind kind) { | |||
| 10433 | // Make sure we point to the last field. | |||
| 10434 | int element_size = 1 << ElementsKindToShiftSize(kind); | |||
| 10435 | int correction = header_size - kHeapObjectTag - element_size; | |||
| 10436 | TNode<IntPtrT> last_offset = ElementOffsetFromIndex(length, kind, correction); | |||
| 10437 | return IntPtrLessThanOrEqual(offset, last_offset); | |||
| 10438 | } | |||
| 10439 | ||||
| 10440 | TNode<HeapObject> CodeStubAssembler::LoadFeedbackCellValue( | |||
| 10441 | TNode<JSFunction> closure) { | |||
| 10442 | TNode<FeedbackCell> feedback_cell = | |||
| 10443 | LoadObjectField<FeedbackCell>(closure, JSFunction::kFeedbackCellOffset); | |||
| 10444 | return LoadObjectField<HeapObject>(feedback_cell, FeedbackCell::kValueOffset); | |||
| 10445 | } | |||
| 10446 | ||||
| 10447 | TNode<HeapObject> CodeStubAssembler::LoadFeedbackVector( | |||
| 10448 | TNode<JSFunction> closure) { | |||
| 10449 | TVARIABLE(HeapObject, maybe_vector, LoadFeedbackCellValue(closure))TVariable<HeapObject> maybe_vector(LoadFeedbackCellValue (closure), this); | |||
| 10450 | Label done(this); | |||
| 10451 | ||||
| 10452 | // If the closure doesn't have a feedback vector allocated yet, return | |||
| 10453 | // undefined. FeedbackCell can contain Undefined / FixedArray (for lazy | |||
| 10454 | // allocations) / FeedbackVector. | |||
| 10455 | GotoIf(IsFeedbackVector(maybe_vector.value()), &done); | |||
| 10456 | ||||
| 10457 | // In all other cases return Undefined. | |||
| 10458 | maybe_vector = UndefinedConstant(); | |||
| 10459 | Goto(&done); | |||
| 10460 | ||||
| 10461 | BIND(&done)Bind(&done); | |||
| 10462 | return maybe_vector.value(); | |||
| 10463 | } | |||
| 10464 | ||||
| 10465 | TNode<ClosureFeedbackCellArray> CodeStubAssembler::LoadClosureFeedbackArray( | |||
| 10466 | TNode<JSFunction> closure) { | |||
| 10467 | TVARIABLE(HeapObject, feedback_cell_array, LoadFeedbackCellValue(closure))TVariable<HeapObject> feedback_cell_array(LoadFeedbackCellValue (closure), this); | |||
| 10468 | Label end(this); | |||
| 10469 | ||||
| 10470 | // When feedback vectors are not yet allocated feedback cell contains a | |||
| 10471 | // an array of feedback cells used by create closures. | |||
| 10472 | GotoIf(HasInstanceType(feedback_cell_array.value(), | |||
| 10473 | CLOSURE_FEEDBACK_CELL_ARRAY_TYPE), | |||
| 10474 | &end); | |||
| 10475 | ||||
| 10476 | // Load FeedbackCellArray from feedback vector. | |||
| 10477 | TNode<FeedbackVector> vector = CAST(feedback_cell_array.value())Cast(feedback_cell_array.value()); | |||
| 10478 | feedback_cell_array = CAST(Cast(LoadObjectField(vector, FeedbackVector::kClosureFeedbackCellArrayOffset )) | |||
| 10479 | LoadObjectField(vector, FeedbackVector::kClosureFeedbackCellArrayOffset))Cast(LoadObjectField(vector, FeedbackVector::kClosureFeedbackCellArrayOffset )); | |||
| 10480 | Goto(&end); | |||
| 10481 | ||||
| 10482 | BIND(&end)Bind(&end); | |||
| 10483 | return CAST(feedback_cell_array.value())Cast(feedback_cell_array.value()); | |||
| 10484 | } | |||
| 10485 | ||||
| 10486 | TNode<FeedbackVector> CodeStubAssembler::LoadFeedbackVectorForStub() { | |||
| 10487 | TNode<JSFunction> function = | |||
| 10488 | CAST(LoadFromParentFrame(StandardFrameConstants::kFunctionOffset))Cast(LoadFromParentFrame(StandardFrameConstants::kFunctionOffset )); | |||
| 10489 | return CAST(LoadFeedbackVector(function))Cast(LoadFeedbackVector(function)); | |||
| 10490 | } | |||
| 10491 | ||||
| 10492 | TNode<FeedbackVector> CodeStubAssembler::LoadFeedbackVectorFromBaseline() { | |||
| 10493 | return CAST(Cast(LoadFromParentFrame(InterpreterFrameConstants::kBytecodeOffsetFromFp )) | |||
| 10494 | LoadFromParentFrame(InterpreterFrameConstants::kBytecodeOffsetFromFp))Cast(LoadFromParentFrame(InterpreterFrameConstants::kBytecodeOffsetFromFp )); | |||
| 10495 | } | |||
| 10496 | ||||
| 10497 | TNode<Context> CodeStubAssembler::LoadContextFromBaseline() { | |||
| 10498 | return CAST(LoadFromParentFrame(InterpreterFrameConstants::kContextOffset))Cast(LoadFromParentFrame(InterpreterFrameConstants::kContextOffset )); | |||
| 10499 | } | |||
| 10500 | ||||
| 10501 | TNode<FeedbackVector> | |||
| 10502 | CodeStubAssembler::LoadFeedbackVectorForStubWithTrampoline() { | |||
| 10503 | TNode<RawPtrT> frame_pointer = LoadParentFramePointer(); | |||
| 10504 | TNode<RawPtrT> parent_frame_pointer = Load<RawPtrT>(frame_pointer); | |||
| 10505 | TNode<JSFunction> function = CAST(Cast(LoadFullTagged(parent_frame_pointer, IntPtrConstant(StandardFrameConstants ::kFunctionOffset))) | |||
| 10506 | LoadFullTagged(parent_frame_pointer,Cast(LoadFullTagged(parent_frame_pointer, IntPtrConstant(StandardFrameConstants ::kFunctionOffset))) | |||
| 10507 | IntPtrConstant(StandardFrameConstants::kFunctionOffset)))Cast(LoadFullTagged(parent_frame_pointer, IntPtrConstant(StandardFrameConstants ::kFunctionOffset))); | |||
| 10508 | return CAST(LoadFeedbackVector(function))Cast(LoadFeedbackVector(function)); | |||
| 10509 | } | |||
| 10510 | ||||
| 10511 | void CodeStubAssembler::UpdateFeedback(TNode<Smi> feedback, | |||
| 10512 | TNode<HeapObject> maybe_feedback_vector, | |||
| 10513 | TNode<UintPtrT> slot_id, | |||
| 10514 | UpdateFeedbackMode mode) { | |||
| 10515 | switch (mode) { | |||
| 10516 | case UpdateFeedbackMode::kOptionalFeedback: | |||
| 10517 | MaybeUpdateFeedback(feedback, maybe_feedback_vector, slot_id); | |||
| 10518 | break; | |||
| 10519 | case UpdateFeedbackMode::kGuaranteedFeedback: | |||
| 10520 | CSA_DCHECK(this, IsFeedbackVector(maybe_feedback_vector))((void)0); | |||
| 10521 | UpdateFeedback(feedback, CAST(maybe_feedback_vector)Cast(maybe_feedback_vector), slot_id); | |||
| 10522 | break; | |||
| 10523 | } | |||
| 10524 | } | |||
| 10525 | ||||
| 10526 | void CodeStubAssembler::MaybeUpdateFeedback(TNode<Smi> feedback, | |||
| 10527 | TNode<HeapObject> maybe_vector, | |||
| 10528 | TNode<UintPtrT> slot_id) { | |||
| 10529 | Label end(this); | |||
| 10530 | GotoIf(IsUndefined(maybe_vector), &end); | |||
| 10531 | { | |||
| 10532 | UpdateFeedback(feedback, CAST(maybe_vector)Cast(maybe_vector), slot_id); | |||
| 10533 | Goto(&end); | |||
| 10534 | } | |||
| 10535 | BIND(&end)Bind(&end); | |||
| 10536 | } | |||
| 10537 | ||||
| 10538 | void CodeStubAssembler::UpdateFeedback(TNode<Smi> feedback, | |||
| 10539 | TNode<FeedbackVector> feedback_vector, | |||
| 10540 | TNode<UintPtrT> slot_id) { | |||
| 10541 | Label end(this); | |||
| 10542 | ||||
| 10543 | // This method is used for binary op and compare feedback. These | |||
| 10544 | // vector nodes are initialized with a smi 0, so we can simply OR | |||
| 10545 | // our new feedback in place. | |||
| 10546 | TNode<MaybeObject> feedback_element = | |||
| 10547 | LoadFeedbackVectorSlot(feedback_vector, slot_id); | |||
| 10548 | TNode<Smi> previous_feedback = CAST(feedback_element)Cast(feedback_element); | |||
| 10549 | TNode<Smi> combined_feedback = SmiOr(previous_feedback, feedback); | |||
| 10550 | ||||
| 10551 | GotoIf(SmiEqual(previous_feedback, combined_feedback), &end); | |||
| 10552 | { | |||
| 10553 | StoreFeedbackVectorSlot(feedback_vector, slot_id, combined_feedback, | |||
| 10554 | SKIP_WRITE_BARRIER); | |||
| 10555 | ReportFeedbackUpdate(feedback_vector, slot_id, "UpdateFeedback"); | |||
| 10556 | Goto(&end); | |||
| 10557 | } | |||
| 10558 | ||||
| 10559 | BIND(&end)Bind(&end); | |||
| 10560 | } | |||
| 10561 | ||||
| 10562 | void CodeStubAssembler::ReportFeedbackUpdate( | |||
| 10563 | TNode<FeedbackVector> feedback_vector, TNode<UintPtrT> slot_id, | |||
| 10564 | const char* reason) { | |||
| 10565 | // Reset profiler ticks. | |||
| 10566 | StoreObjectFieldNoWriteBarrier( | |||
| 10567 | feedback_vector, FeedbackVector::kProfilerTicksOffset, Int32Constant(0)); | |||
| 10568 | ||||
| 10569 | #ifdef V8_TRACE_FEEDBACK_UPDATES | |||
| 10570 | // Trace the update. | |||
| 10571 | CallRuntime(Runtime::kTraceUpdateFeedback, NoContextConstant(), | |||
| 10572 | LoadFromParentFrame(StandardFrameConstants::kFunctionOffset), | |||
| 10573 | SmiTag(Signed(slot_id)), StringConstant(reason)); | |||
| 10574 | #endif // V8_TRACE_FEEDBACK_UPDATES | |||
| 10575 | } | |||
| 10576 | ||||
| 10577 | void CodeStubAssembler::OverwriteFeedback(TVariable<Smi>* existing_feedback, | |||
| 10578 | int new_feedback) { | |||
| 10579 | if (existing_feedback == nullptr) return; | |||
| 10580 | *existing_feedback = SmiConstant(new_feedback); | |||
| 10581 | } | |||
| 10582 | ||||
| 10583 | void CodeStubAssembler::CombineFeedback(TVariable<Smi>* existing_feedback, | |||
| 10584 | int feedback) { | |||
| 10585 | if (existing_feedback == nullptr) return; | |||
| 10586 | *existing_feedback = SmiOr(existing_feedback->value(), SmiConstant(feedback)); | |||
| 10587 | } | |||
| 10588 | ||||
| 10589 | void CodeStubAssembler::CombineFeedback(TVariable<Smi>* existing_feedback, | |||
| 10590 | TNode<Smi> feedback) { | |||
| 10591 | if (existing_feedback == nullptr) return; | |||
| 10592 | *existing_feedback = SmiOr(existing_feedback->value(), feedback); | |||
| 10593 | } | |||
| 10594 | ||||
| 10595 | void CodeStubAssembler::CheckForAssociatedProtector(TNode<Name> name, | |||
| 10596 | Label* if_protector) { | |||
| 10597 | // This list must be kept in sync with LookupIterator::UpdateProtector! | |||
| 10598 | // TODO(jkummerow): Would it be faster to have a bit in Symbol::flags()? | |||
| 10599 | GotoIf(TaggedEqual(name, ConstructorStringConstant()), if_protector); | |||
| 10600 | GotoIf(TaggedEqual(name, IteratorSymbolConstant()), if_protector); | |||
| 10601 | GotoIf(TaggedEqual(name, NextStringConstant()), if_protector); | |||
| 10602 | GotoIf(TaggedEqual(name, SpeciesSymbolConstant()), if_protector); | |||
| 10603 | GotoIf(TaggedEqual(name, IsConcatSpreadableSymbolConstant()), if_protector); | |||
| 10604 | GotoIf(TaggedEqual(name, ResolveStringConstant()), if_protector); | |||
| 10605 | GotoIf(TaggedEqual(name, ThenStringConstant()), if_protector); | |||
| 10606 | // Fall through if no case matched. | |||
| 10607 | } | |||
| 10608 | ||||
| 10609 | TNode<Map> CodeStubAssembler::LoadReceiverMap(TNode<Object> receiver) { | |||
| 10610 | return Select<Map>( | |||
| 10611 | TaggedIsSmi(receiver), [=] { return HeapNumberMapConstant(); }, | |||
| 10612 | [=] { return LoadMap(UncheckedCast<HeapObject>(receiver)); }); | |||
| 10613 | } | |||
| 10614 | ||||
| 10615 | TNode<IntPtrT> CodeStubAssembler::TryToIntptr( | |||
| 10616 | TNode<Object> key, Label* if_not_intptr, | |||
| 10617 | TVariable<Int32T>* var_instance_type) { | |||
| 10618 | TVARIABLE(IntPtrT, var_intptr_key)TVariable<IntPtrT> var_intptr_key(this); | |||
| 10619 | Label done(this, &var_intptr_key), key_is_smi(this), key_is_heapnumber(this); | |||
| 10620 | GotoIf(TaggedIsSmi(key), &key_is_smi); | |||
| 10621 | ||||
| 10622 | TNode<Int32T> instance_type = LoadInstanceType(CAST(key)Cast(key)); | |||
| 10623 | if (var_instance_type != nullptr) { | |||
| 10624 | *var_instance_type = instance_type; | |||
| 10625 | } | |||
| 10626 | ||||
| 10627 | Branch(IsHeapNumberInstanceType(instance_type), &key_is_heapnumber, | |||
| 10628 | if_not_intptr); | |||
| 10629 | ||||
| 10630 | BIND(&key_is_smi)Bind(&key_is_smi); | |||
| 10631 | { | |||
| 10632 | var_intptr_key = SmiUntag(CAST(key)Cast(key)); | |||
| 10633 | Goto(&done); | |||
| 10634 | } | |||
| 10635 | ||||
| 10636 | BIND(&key_is_heapnumber)Bind(&key_is_heapnumber); | |||
| 10637 | { | |||
| 10638 | TNode<Float64T> value = LoadHeapNumberValue(CAST(key)Cast(key)); | |||
| 10639 | TNode<IntPtrT> int_value = ChangeFloat64ToIntPtr(value); | |||
| 10640 | GotoIfNot(Float64Equal(value, RoundIntPtrToFloat64(int_value)), | |||
| 10641 | if_not_intptr); | |||
| 10642 | #if V8_TARGET_ARCH_64_BIT1 | |||
| 10643 | // We can't rely on Is64() alone because 32-bit compilers rightly complain | |||
| 10644 | // about kMaxSafeIntegerUint64 not fitting into an intptr_t. | |||
| 10645 | DCHECK(Is64())((void) 0); | |||
| 10646 | // TODO(jkummerow): Investigate whether we can drop support for | |||
| 10647 | // negative indices. | |||
| 10648 | GotoIfNot(IsInRange(int_value, static_cast<intptr_t>(-kMaxSafeInteger), | |||
| 10649 | static_cast<intptr_t>(kMaxSafeIntegerUint64)), | |||
| 10650 | if_not_intptr); | |||
| 10651 | #else | |||
| 10652 | DCHECK(!Is64())((void) 0); | |||
| 10653 | #endif | |||
| 10654 | var_intptr_key = int_value; | |||
| 10655 | Goto(&done); | |||
| 10656 | } | |||
| 10657 | ||||
| 10658 | BIND(&done)Bind(&done); | |||
| 10659 | return var_intptr_key.value(); | |||
| 10660 | } | |||
| 10661 | ||||
| 10662 | TNode<Context> CodeStubAssembler::LoadScriptContext( | |||
| 10663 | TNode<Context> context, TNode<IntPtrT> context_index) { | |||
| 10664 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 10665 | TNode<ScriptContextTable> script_context_table = CAST(Cast(LoadContextElement(native_context, Context::SCRIPT_CONTEXT_TABLE_INDEX )) | |||
| 10666 | LoadContextElement(native_context, Context::SCRIPT_CONTEXT_TABLE_INDEX))Cast(LoadContextElement(native_context, Context::SCRIPT_CONTEXT_TABLE_INDEX )); | |||
| 10667 | ||||
| 10668 | TNode<Context> script_context = CAST(LoadFixedArrayElement(Cast(LoadFixedArrayElement( script_context_table, context_index , ScriptContextTable::kFirstContextSlotIndex * kTaggedSize)) | |||
| 10669 | script_context_table, context_index,Cast(LoadFixedArrayElement( script_context_table, context_index , ScriptContextTable::kFirstContextSlotIndex * kTaggedSize)) | |||
| 10670 | ScriptContextTable::kFirstContextSlotIndex * kTaggedSize))Cast(LoadFixedArrayElement( script_context_table, context_index , ScriptContextTable::kFirstContextSlotIndex * kTaggedSize)); | |||
| 10671 | return script_context; | |||
| 10672 | } | |||
| 10673 | ||||
| 10674 | namespace { | |||
| 10675 | ||||
| 10676 | // Converts typed array elements kind to a machine representations. | |||
| 10677 | MachineRepresentation ElementsKindToMachineRepresentation(ElementsKind kind) { | |||
| 10678 | switch (kind) { | |||
| 10679 | case UINT8_CLAMPED_ELEMENTS: | |||
| 10680 | case UINT8_ELEMENTS: | |||
| 10681 | case INT8_ELEMENTS: | |||
| 10682 | return MachineRepresentation::kWord8; | |||
| 10683 | case UINT16_ELEMENTS: | |||
| 10684 | case INT16_ELEMENTS: | |||
| 10685 | return MachineRepresentation::kWord16; | |||
| 10686 | case UINT32_ELEMENTS: | |||
| 10687 | case INT32_ELEMENTS: | |||
| 10688 | return MachineRepresentation::kWord32; | |||
| 10689 | case FLOAT32_ELEMENTS: | |||
| 10690 | return MachineRepresentation::kFloat32; | |||
| 10691 | case FLOAT64_ELEMENTS: | |||
| 10692 | return MachineRepresentation::kFloat64; | |||
| 10693 | default: | |||
| 10694 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 10695 | } | |||
| 10696 | } | |||
| 10697 | ||||
| 10698 | } // namespace | |||
| 10699 | ||||
| 10700 | // TODO(solanes): Since we can't use `if constexpr` until we enable C++17 we | |||
| 10701 | // have to specialize the BigInt and Word32T cases. Since we can't partly | |||
| 10702 | // specialize, we have to specialize all used combinations. | |||
| 10703 | template <typename TIndex> | |||
| 10704 | void CodeStubAssembler::StoreElementTypedArrayBigInt(TNode<RawPtrT> elements, | |||
| 10705 | ElementsKind kind, | |||
| 10706 | TNode<TIndex> index, | |||
| 10707 | TNode<BigInt> value) { | |||
| 10708 | static_assert(std::is_same<TIndex, UintPtrT>::value || | |||
| 10709 | std::is_same<TIndex, IntPtrT>::value, | |||
| 10710 | "Only UintPtrT or IntPtrT indices is allowed"); | |||
| 10711 | DCHECK(kind == BIGINT64_ELEMENTS || kind == BIGUINT64_ELEMENTS)((void) 0); | |||
| 10712 | TNode<IntPtrT> offset = ElementOffsetFromIndex(index, kind, 0); | |||
| 10713 | TVARIABLE(UintPtrT, var_low)TVariable<UintPtrT> var_low(this); | |||
| 10714 | // Only used on 32-bit platforms. | |||
| 10715 | TVARIABLE(UintPtrT, var_high)TVariable<UintPtrT> var_high(this); | |||
| 10716 | BigIntToRawBytes(value, &var_low, &var_high); | |||
| 10717 | ||||
| 10718 | MachineRepresentation rep = WordT::kMachineRepresentation; | |||
| 10719 | #if defined(V8_TARGET_BIG_ENDIAN) | |||
| 10720 | if (!Is64()) { | |||
| 10721 | StoreNoWriteBarrier(rep, elements, offset, var_high.value()); | |||
| 10722 | StoreNoWriteBarrier(rep, elements, | |||
| 10723 | IntPtrAdd(offset, IntPtrConstant(kSystemPointerSize)), | |||
| 10724 | var_low.value()); | |||
| 10725 | } else { | |||
| 10726 | StoreNoWriteBarrier(rep, elements, offset, var_low.value()); | |||
| 10727 | } | |||
| 10728 | #else | |||
| 10729 | StoreNoWriteBarrier(rep, elements, offset, var_low.value()); | |||
| 10730 | if (!Is64()) { | |||
| 10731 | StoreNoWriteBarrier(rep, elements, | |||
| 10732 | IntPtrAdd(offset, IntPtrConstant(kSystemPointerSize)), | |||
| 10733 | var_high.value()); | |||
| 10734 | } | |||
| 10735 | #endif | |||
| 10736 | } | |||
| 10737 | ||||
| 10738 | template <> | |||
| 10739 | void CodeStubAssembler::StoreElementTypedArray(TNode<RawPtrT> elements, | |||
| 10740 | ElementsKind kind, | |||
| 10741 | TNode<UintPtrT> index, | |||
| 10742 | TNode<BigInt> value) { | |||
| 10743 | StoreElementTypedArrayBigInt(elements, kind, index, value); | |||
| 10744 | } | |||
| 10745 | ||||
| 10746 | template <> | |||
| 10747 | void CodeStubAssembler::StoreElementTypedArray(TNode<RawPtrT> elements, | |||
| 10748 | ElementsKind kind, | |||
| 10749 | TNode<IntPtrT> index, | |||
| 10750 | TNode<BigInt> value) { | |||
| 10751 | StoreElementTypedArrayBigInt(elements, kind, index, value); | |||
| 10752 | } | |||
| 10753 | ||||
| 10754 | template <typename TIndex> | |||
| 10755 | void CodeStubAssembler::StoreElementTypedArrayWord32(TNode<RawPtrT> elements, | |||
| 10756 | ElementsKind kind, | |||
| 10757 | TNode<TIndex> index, | |||
| 10758 | TNode<Word32T> value) { | |||
| 10759 | static_assert(std::is_same<TIndex, UintPtrT>::value || | |||
| 10760 | std::is_same<TIndex, IntPtrT>::value, | |||
| 10761 | "Only UintPtrT or IntPtrT indices is allowed"); | |||
| 10762 | DCHECK(IsTypedArrayElementsKind(kind))((void) 0); | |||
| 10763 | if (kind == UINT8_CLAMPED_ELEMENTS) { | |||
| 10764 | CSA_DCHECK(this, Word32Equal(value, Word32And(Int32Constant(0xFF), value)))((void)0); | |||
| 10765 | } | |||
| 10766 | TNode<IntPtrT> offset = ElementOffsetFromIndex(index, kind, 0); | |||
| 10767 | // TODO(cbruni): Add OOB check once typed. | |||
| 10768 | MachineRepresentation rep = ElementsKindToMachineRepresentation(kind); | |||
| 10769 | StoreNoWriteBarrier(rep, elements, offset, value); | |||
| 10770 | } | |||
| 10771 | ||||
| 10772 | template <> | |||
| 10773 | void CodeStubAssembler::StoreElementTypedArray(TNode<RawPtrT> elements, | |||
| 10774 | ElementsKind kind, | |||
| 10775 | TNode<UintPtrT> index, | |||
| 10776 | TNode<Word32T> value) { | |||
| 10777 | StoreElementTypedArrayWord32(elements, kind, index, value); | |||
| 10778 | } | |||
| 10779 | ||||
| 10780 | template <> | |||
| 10781 | void CodeStubAssembler::StoreElementTypedArray(TNode<RawPtrT> elements, | |||
| 10782 | ElementsKind kind, | |||
| 10783 | TNode<IntPtrT> index, | |||
| 10784 | TNode<Word32T> value) { | |||
| 10785 | StoreElementTypedArrayWord32(elements, kind, index, value); | |||
| 10786 | } | |||
| 10787 | ||||
| 10788 | template <typename TArray, typename TIndex, typename TValue> | |||
| 10789 | void CodeStubAssembler::StoreElementTypedArray(TNode<TArray> elements, | |||
| 10790 | ElementsKind kind, | |||
| 10791 | TNode<TIndex> index, | |||
| 10792 | TNode<TValue> value) { | |||
| 10793 | // TODO(v8:9708): Do we want to keep both IntPtrT and UintPtrT variants? | |||
| 10794 | static_assert(std::is_same<TIndex, Smi>::value || | |||
| 10795 | std::is_same<TIndex, UintPtrT>::value || | |||
| 10796 | std::is_same<TIndex, IntPtrT>::value, | |||
| 10797 | "Only Smi, UintPtrT or IntPtrT indices is allowed"); | |||
| 10798 | static_assert(std::is_same<TArray, RawPtrT>::value || | |||
| 10799 | std::is_same<TArray, FixedArrayBase>::value, | |||
| 10800 | "Only RawPtrT or FixedArrayBase elements are allowed"); | |||
| 10801 | static_assert(std::is_same<TValue, Int32T>::value || | |||
| 10802 | std::is_same<TValue, Float32T>::value || | |||
| 10803 | std::is_same<TValue, Float64T>::value || | |||
| 10804 | std::is_same<TValue, Object>::value, | |||
| 10805 | "Only Int32T, Float32T, Float64T or object value " | |||
| 10806 | "types are allowed"); | |||
| 10807 | DCHECK(IsTypedArrayElementsKind(kind))((void) 0); | |||
| 10808 | TNode<IntPtrT> offset = ElementOffsetFromIndex(index, kind, 0); | |||
| 10809 | // TODO(cbruni): Add OOB check once typed. | |||
| 10810 | MachineRepresentation rep = ElementsKindToMachineRepresentation(kind); | |||
| 10811 | StoreNoWriteBarrier(rep, elements, offset, value); | |||
| 10812 | } | |||
| 10813 | ||||
| 10814 | template <typename TIndex> | |||
| 10815 | void CodeStubAssembler::StoreElement(TNode<FixedArrayBase> elements, | |||
| 10816 | ElementsKind kind, TNode<TIndex> index, | |||
| 10817 | TNode<Object> value) { | |||
| 10818 | static_assert( | |||
| 10819 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 10820 | "Only Smi or IntPtrT indices are allowed"); | |||
| 10821 | DCHECK(!IsDoubleElementsKind(kind))((void) 0); | |||
| 10822 | if (IsTypedArrayElementsKind(kind)) { | |||
| 10823 | StoreElementTypedArray(elements, kind, index, value); | |||
| 10824 | } else if (IsSmiElementsKind(kind)) { | |||
| 10825 | TNode<Smi> smi_value = CAST(value)Cast(value); | |||
| 10826 | StoreFixedArrayElement(CAST(elements)Cast(elements), index, smi_value); | |||
| 10827 | } else { | |||
| 10828 | StoreFixedArrayElement(CAST(elements)Cast(elements), index, value); | |||
| 10829 | } | |||
| 10830 | } | |||
| 10831 | ||||
| 10832 | template <typename TIndex> | |||
| 10833 | void CodeStubAssembler::StoreElement(TNode<FixedArrayBase> elements, | |||
| 10834 | ElementsKind kind, TNode<TIndex> index, | |||
| 10835 | TNode<Float64T> value) { | |||
| 10836 | static_assert( | |||
| 10837 | std::is_same<TIndex, Smi>::value || std::is_same<TIndex, IntPtrT>::value, | |||
| 10838 | "Only Smi or IntPtrT indices are allowed"); | |||
| 10839 | DCHECK(IsDoubleElementsKind(kind))((void) 0); | |||
| 10840 | StoreFixedDoubleArrayElement(CAST(elements)Cast(elements), index, value); | |||
| 10841 | } | |||
| 10842 | ||||
| 10843 | template <typename TIndex, typename TValue> | |||
| 10844 | void CodeStubAssembler::StoreElement(TNode<RawPtrT> elements, ElementsKind kind, | |||
| 10845 | TNode<TIndex> index, TNode<TValue> value) { | |||
| 10846 | static_assert(std::is_same<TIndex, Smi>::value || | |||
| 10847 | std::is_same<TIndex, IntPtrT>::value || | |||
| 10848 | std::is_same<TIndex, UintPtrT>::value, | |||
| 10849 | "Only Smi, IntPtrT or UintPtrT indices are allowed"); | |||
| 10850 | static_assert( | |||
| 10851 | std::is_same<TValue, Int32T>::value || | |||
| 10852 | std::is_same<TValue, Word32T>::value || | |||
| 10853 | std::is_same<TValue, Float32T>::value || | |||
| 10854 | std::is_same<TValue, Float64T>::value || | |||
| 10855 | std::is_same<TValue, BigInt>::value, | |||
| 10856 | "Only Int32T, Word32T, Float32T, Float64T or BigInt value types " | |||
| 10857 | "are allowed"); | |||
| 10858 | ||||
| 10859 | DCHECK(IsTypedArrayElementsKind(kind))((void) 0); | |||
| 10860 | StoreElementTypedArray(elements, kind, index, value); | |||
| 10861 | } | |||
| 10862 | template V8_EXPORT_PRIVATE void CodeStubAssembler::StoreElement(TNode<RawPtrT>, | |||
| 10863 | ElementsKind, | |||
| 10864 | TNode<UintPtrT>, | |||
| 10865 | TNode<Int32T>); | |||
| 10866 | template V8_EXPORT_PRIVATE void CodeStubAssembler::StoreElement(TNode<RawPtrT>, | |||
| 10867 | ElementsKind, | |||
| 10868 | TNode<UintPtrT>, | |||
| 10869 | TNode<Word32T>); | |||
| 10870 | template V8_EXPORT_PRIVATE void CodeStubAssembler::StoreElement( | |||
| 10871 | TNode<RawPtrT>, ElementsKind, TNode<UintPtrT>, TNode<Float32T>); | |||
| 10872 | template V8_EXPORT_PRIVATE void CodeStubAssembler::StoreElement( | |||
| 10873 | TNode<RawPtrT>, ElementsKind, TNode<UintPtrT>, TNode<Float64T>); | |||
| 10874 | template V8_EXPORT_PRIVATE void CodeStubAssembler::StoreElement(TNode<RawPtrT>, | |||
| 10875 | ElementsKind, | |||
| 10876 | TNode<UintPtrT>, | |||
| 10877 | TNode<BigInt>); | |||
| 10878 | ||||
| 10879 | TNode<Uint8T> CodeStubAssembler::Int32ToUint8Clamped( | |||
| 10880 | TNode<Int32T> int32_value) { | |||
| 10881 | Label done(this); | |||
| 10882 | TNode<Int32T> int32_zero = Int32Constant(0); | |||
| 10883 | TNode<Int32T> int32_255 = Int32Constant(255); | |||
| 10884 | TVARIABLE(Word32T, var_value, int32_value)TVariable<Word32T> var_value(int32_value, this); | |||
| 10885 | GotoIf(Uint32LessThanOrEqual(int32_value, int32_255), &done); | |||
| 10886 | var_value = int32_zero; | |||
| 10887 | GotoIf(Int32LessThan(int32_value, int32_zero), &done); | |||
| 10888 | var_value = int32_255; | |||
| 10889 | Goto(&done); | |||
| 10890 | BIND(&done)Bind(&done); | |||
| 10891 | return UncheckedCast<Uint8T>(var_value.value()); | |||
| 10892 | } | |||
| 10893 | ||||
| 10894 | TNode<Uint8T> CodeStubAssembler::Float64ToUint8Clamped( | |||
| 10895 | TNode<Float64T> float64_value) { | |||
| 10896 | Label done(this); | |||
| 10897 | TVARIABLE(Word32T, var_value, Int32Constant(0))TVariable<Word32T> var_value(Int32Constant(0), this); | |||
| 10898 | GotoIf(Float64LessThanOrEqual(float64_value, Float64Constant(0.0)), &done); | |||
| 10899 | var_value = Int32Constant(255); | |||
| 10900 | GotoIf(Float64LessThanOrEqual(Float64Constant(255.0), float64_value), &done); | |||
| 10901 | { | |||
| 10902 | TNode<Float64T> rounded_value = Float64RoundToEven(float64_value); | |||
| 10903 | var_value = TruncateFloat64ToWord32(rounded_value); | |||
| 10904 | Goto(&done); | |||
| 10905 | } | |||
| 10906 | BIND(&done)Bind(&done); | |||
| 10907 | return UncheckedCast<Uint8T>(var_value.value()); | |||
| 10908 | } | |||
| 10909 | ||||
| 10910 | template <> | |||
| 10911 | TNode<Word32T> CodeStubAssembler::PrepareValueForWriteToTypedArray<Word32T>( | |||
| 10912 | TNode<Object> input, ElementsKind elements_kind, TNode<Context> context) { | |||
| 10913 | DCHECK(IsTypedArrayElementsKind(elements_kind))((void) 0); | |||
| 10914 | ||||
| 10915 | switch (elements_kind) { | |||
| 10916 | case UINT8_ELEMENTS: | |||
| 10917 | case INT8_ELEMENTS: | |||
| 10918 | case UINT16_ELEMENTS: | |||
| 10919 | case INT16_ELEMENTS: | |||
| 10920 | case UINT32_ELEMENTS: | |||
| 10921 | case INT32_ELEMENTS: | |||
| 10922 | case UINT8_CLAMPED_ELEMENTS: | |||
| 10923 | break; | |||
| 10924 | default: | |||
| 10925 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 10926 | } | |||
| 10927 | ||||
| 10928 | TVARIABLE(Word32T, var_result)TVariable<Word32T> var_result(this); | |||
| 10929 | TVARIABLE(Object, var_input, input)TVariable<Object> var_input(input, this); | |||
| 10930 | Label done(this, &var_result), if_smi(this), if_heapnumber_or_oddball(this), | |||
| 10931 | convert(this), loop(this, &var_input); | |||
| 10932 | Goto(&loop); | |||
| 10933 | BIND(&loop)Bind(&loop); | |||
| 10934 | GotoIf(TaggedIsSmi(var_input.value()), &if_smi); | |||
| 10935 | // We can handle both HeapNumber and Oddball here, since Oddball has the | |||
| 10936 | // same layout as the HeapNumber for the HeapNumber::value field. This | |||
| 10937 | // way we can also properly optimize stores of oddballs to typed arrays. | |||
| 10938 | TNode<HeapObject> heap_object = CAST(var_input.value())Cast(var_input.value()); | |||
| 10939 | GotoIf(IsHeapNumber(heap_object), &if_heapnumber_or_oddball); | |||
| 10940 | STATIC_ASSERT_FIELD_OFFSETS_EQUAL(HeapNumber::kValueOffset,static_assert(static_cast<int>(HeapNumber::kValueOffset ) == Oddball::kToNumberRawOffset, "static_cast<int>(HeapNumber::kValueOffset) == Oddball::kToNumberRawOffset" ) | |||
| 10941 | Oddball::kToNumberRawOffset)static_assert(static_cast<int>(HeapNumber::kValueOffset ) == Oddball::kToNumberRawOffset, "static_cast<int>(HeapNumber::kValueOffset) == Oddball::kToNumberRawOffset" ); | |||
| 10942 | Branch(HasInstanceType(heap_object, ODDBALL_TYPE), &if_heapnumber_or_oddball, | |||
| 10943 | &convert); | |||
| 10944 | ||||
| 10945 | BIND(&if_heapnumber_or_oddball)Bind(&if_heapnumber_or_oddball); | |||
| 10946 | { | |||
| 10947 | TNode<Float64T> value = | |||
| 10948 | LoadObjectField<Float64T>(heap_object, HeapNumber::kValueOffset); | |||
| 10949 | if (elements_kind == UINT8_CLAMPED_ELEMENTS) { | |||
| 10950 | var_result = Float64ToUint8Clamped(value); | |||
| 10951 | } else { | |||
| 10952 | var_result = TruncateFloat64ToWord32(value); | |||
| 10953 | } | |||
| 10954 | Goto(&done); | |||
| 10955 | } | |||
| 10956 | ||||
| 10957 | BIND(&if_smi)Bind(&if_smi); | |||
| 10958 | { | |||
| 10959 | TNode<Int32T> value = SmiToInt32(CAST(var_input.value())Cast(var_input.value())); | |||
| 10960 | if (elements_kind == UINT8_CLAMPED_ELEMENTS) { | |||
| 10961 | var_result = Int32ToUint8Clamped(value); | |||
| 10962 | } else { | |||
| 10963 | var_result = value; | |||
| 10964 | } | |||
| 10965 | Goto(&done); | |||
| 10966 | } | |||
| 10967 | ||||
| 10968 | BIND(&convert)Bind(&convert); | |||
| 10969 | { | |||
| 10970 | var_input = CallBuiltin(Builtin::kNonNumberToNumber, context, input); | |||
| 10971 | Goto(&loop); | |||
| 10972 | } | |||
| 10973 | ||||
| 10974 | BIND(&done)Bind(&done); | |||
| 10975 | return var_result.value(); | |||
| 10976 | } | |||
| 10977 | ||||
| 10978 | template <> | |||
| 10979 | TNode<Float32T> CodeStubAssembler::PrepareValueForWriteToTypedArray<Float32T>( | |||
| 10980 | TNode<Object> input, ElementsKind elements_kind, TNode<Context> context) { | |||
| 10981 | DCHECK(IsTypedArrayElementsKind(elements_kind))((void) 0); | |||
| 10982 | CHECK_EQ(elements_kind, FLOAT32_ELEMENTS)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(elements_kind)>::type, typename ::v8::base::pass_value_or_ref<decltype(FLOAT32_ELEMENTS)> ::type>((elements_kind), (FLOAT32_ELEMENTS)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "elements_kind" " " "==" " " "FLOAT32_ELEMENTS"); } } while (false); } while (false); | |||
| 10983 | ||||
| 10984 | TVARIABLE(Float32T, var_result)TVariable<Float32T> var_result(this); | |||
| 10985 | TVARIABLE(Object, var_input, input)TVariable<Object> var_input(input, this); | |||
| 10986 | Label done(this, &var_result), if_smi(this), if_heapnumber_or_oddball(this), | |||
| 10987 | convert(this), loop(this, &var_input); | |||
| 10988 | Goto(&loop); | |||
| 10989 | BIND(&loop)Bind(&loop); | |||
| 10990 | GotoIf(TaggedIsSmi(var_input.value()), &if_smi); | |||
| 10991 | // We can handle both HeapNumber and Oddball here, since Oddball has the | |||
| 10992 | // same layout as the HeapNumber for the HeapNumber::value field. This | |||
| 10993 | // way we can also properly optimize stores of oddballs to typed arrays. | |||
| 10994 | TNode<HeapObject> heap_object = CAST(var_input.value())Cast(var_input.value()); | |||
| 10995 | GotoIf(IsHeapNumber(heap_object), &if_heapnumber_or_oddball); | |||
| 10996 | STATIC_ASSERT_FIELD_OFFSETS_EQUAL(HeapNumber::kValueOffset,static_assert(static_cast<int>(HeapNumber::kValueOffset ) == Oddball::kToNumberRawOffset, "static_cast<int>(HeapNumber::kValueOffset) == Oddball::kToNumberRawOffset" ) | |||
| 10997 | Oddball::kToNumberRawOffset)static_assert(static_cast<int>(HeapNumber::kValueOffset ) == Oddball::kToNumberRawOffset, "static_cast<int>(HeapNumber::kValueOffset) == Oddball::kToNumberRawOffset" ); | |||
| 10998 | Branch(HasInstanceType(heap_object, ODDBALL_TYPE), &if_heapnumber_or_oddball, | |||
| 10999 | &convert); | |||
| 11000 | ||||
| 11001 | BIND(&if_heapnumber_or_oddball)Bind(&if_heapnumber_or_oddball); | |||
| 11002 | { | |||
| 11003 | TNode<Float64T> value = | |||
| 11004 | LoadObjectField<Float64T>(heap_object, HeapNumber::kValueOffset); | |||
| 11005 | var_result = TruncateFloat64ToFloat32(value); | |||
| 11006 | Goto(&done); | |||
| 11007 | } | |||
| 11008 | ||||
| 11009 | BIND(&if_smi)Bind(&if_smi); | |||
| 11010 | { | |||
| 11011 | TNode<Int32T> value = SmiToInt32(CAST(var_input.value())Cast(var_input.value())); | |||
| 11012 | var_result = RoundInt32ToFloat32(value); | |||
| 11013 | Goto(&done); | |||
| 11014 | } | |||
| 11015 | ||||
| 11016 | BIND(&convert)Bind(&convert); | |||
| 11017 | { | |||
| 11018 | var_input = CallBuiltin(Builtin::kNonNumberToNumber, context, input); | |||
| 11019 | Goto(&loop); | |||
| 11020 | } | |||
| 11021 | ||||
| 11022 | BIND(&done)Bind(&done); | |||
| 11023 | return var_result.value(); | |||
| 11024 | } | |||
| 11025 | ||||
| 11026 | template <> | |||
| 11027 | TNode<Float64T> CodeStubAssembler::PrepareValueForWriteToTypedArray<Float64T>( | |||
| 11028 | TNode<Object> input, ElementsKind elements_kind, TNode<Context> context) { | |||
| 11029 | DCHECK(IsTypedArrayElementsKind(elements_kind))((void) 0); | |||
| 11030 | CHECK_EQ(elements_kind, FLOAT64_ELEMENTS)do { bool _cmp = ::v8::base::CmpEQImpl< typename ::v8::base ::pass_value_or_ref<decltype(elements_kind)>::type, typename ::v8::base::pass_value_or_ref<decltype(FLOAT64_ELEMENTS)> ::type>((elements_kind), (FLOAT64_ELEMENTS)); do { if ((__builtin_expect (!!(!(_cmp)), 0))) { V8_Fatal("Check failed: %s.", "elements_kind" " " "==" " " "FLOAT64_ELEMENTS"); } } while (false); } while (false); | |||
| 11031 | ||||
| 11032 | TVARIABLE(Float64T, var_result)TVariable<Float64T> var_result(this); | |||
| 11033 | TVARIABLE(Object, var_input, input)TVariable<Object> var_input(input, this); | |||
| 11034 | Label done(this, &var_result), if_smi(this), if_heapnumber_or_oddball(this), | |||
| 11035 | convert(this), loop(this, &var_input); | |||
| 11036 | Goto(&loop); | |||
| 11037 | BIND(&loop)Bind(&loop); | |||
| 11038 | GotoIf(TaggedIsSmi(var_input.value()), &if_smi); | |||
| 11039 | // We can handle both HeapNumber and Oddball here, since Oddball has the | |||
| 11040 | // same layout as the HeapNumber for the HeapNumber::value field. This | |||
| 11041 | // way we can also properly optimize stores of oddballs to typed arrays. | |||
| 11042 | TNode<HeapObject> heap_object = CAST(var_input.value())Cast(var_input.value()); | |||
| 11043 | GotoIf(IsHeapNumber(heap_object), &if_heapnumber_or_oddball); | |||
| 11044 | STATIC_ASSERT_FIELD_OFFSETS_EQUAL(HeapNumber::kValueOffset,static_assert(static_cast<int>(HeapNumber::kValueOffset ) == Oddball::kToNumberRawOffset, "static_cast<int>(HeapNumber::kValueOffset) == Oddball::kToNumberRawOffset" ) | |||
| 11045 | Oddball::kToNumberRawOffset)static_assert(static_cast<int>(HeapNumber::kValueOffset ) == Oddball::kToNumberRawOffset, "static_cast<int>(HeapNumber::kValueOffset) == Oddball::kToNumberRawOffset" ); | |||
| 11046 | Branch(HasInstanceType(heap_object, ODDBALL_TYPE), &if_heapnumber_or_oddball, | |||
| 11047 | &convert); | |||
| 11048 | ||||
| 11049 | BIND(&if_heapnumber_or_oddball)Bind(&if_heapnumber_or_oddball); | |||
| 11050 | { | |||
| 11051 | var_result = | |||
| 11052 | LoadObjectField<Float64T>(heap_object, HeapNumber::kValueOffset); | |||
| 11053 | Goto(&done); | |||
| 11054 | } | |||
| 11055 | ||||
| 11056 | BIND(&if_smi)Bind(&if_smi); | |||
| 11057 | { | |||
| 11058 | TNode<Int32T> value = SmiToInt32(CAST(var_input.value())Cast(var_input.value())); | |||
| 11059 | var_result = ChangeInt32ToFloat64(value); | |||
| 11060 | Goto(&done); | |||
| 11061 | } | |||
| 11062 | ||||
| 11063 | BIND(&convert)Bind(&convert); | |||
| 11064 | { | |||
| 11065 | var_input = CallBuiltin(Builtin::kNonNumberToNumber, context, input); | |||
| 11066 | Goto(&loop); | |||
| 11067 | } | |||
| 11068 | ||||
| 11069 | BIND(&done)Bind(&done); | |||
| 11070 | return var_result.value(); | |||
| 11071 | } | |||
| 11072 | ||||
| 11073 | template <> | |||
| 11074 | TNode<BigInt> CodeStubAssembler::PrepareValueForWriteToTypedArray<BigInt>( | |||
| 11075 | TNode<Object> input, ElementsKind elements_kind, TNode<Context> context) { | |||
| 11076 | DCHECK(elements_kind == BIGINT64_ELEMENTS ||((void) 0) | |||
| 11077 | elements_kind == BIGUINT64_ELEMENTS)((void) 0); | |||
| 11078 | return ToBigInt(context, input); | |||
| 11079 | } | |||
| 11080 | ||||
| 11081 | void CodeStubAssembler::BigIntToRawBytes(TNode<BigInt> bigint, | |||
| 11082 | TVariable<UintPtrT>* var_low, | |||
| 11083 | TVariable<UintPtrT>* var_high) { | |||
| 11084 | Label done(this); | |||
| 11085 | *var_low = Unsigned(IntPtrConstant(0)); | |||
| 11086 | *var_high = Unsigned(IntPtrConstant(0)); | |||
| 11087 | TNode<Word32T> bitfield = LoadBigIntBitfield(bigint); | |||
| 11088 | TNode<Uint32T> length = DecodeWord32<BigIntBase::LengthBits>(bitfield); | |||
| 11089 | TNode<Uint32T> sign = DecodeWord32<BigIntBase::SignBits>(bitfield); | |||
| 11090 | GotoIf(Word32Equal(length, Int32Constant(0)), &done); | |||
| 11091 | *var_low = LoadBigIntDigit(bigint, 0); | |||
| 11092 | if (!Is64()) { | |||
| 11093 | Label load_done(this); | |||
| 11094 | GotoIf(Word32Equal(length, Int32Constant(1)), &load_done); | |||
| 11095 | *var_high = LoadBigIntDigit(bigint, 1); | |||
| 11096 | Goto(&load_done); | |||
| 11097 | BIND(&load_done)Bind(&load_done); | |||
| 11098 | } | |||
| 11099 | GotoIf(Word32Equal(sign, Int32Constant(0)), &done); | |||
| 11100 | // Negative value. Simulate two's complement. | |||
| 11101 | if (!Is64()) { | |||
| 11102 | *var_high = Unsigned(IntPtrSub(IntPtrConstant(0), var_high->value())); | |||
| 11103 | Label no_carry(this); | |||
| 11104 | GotoIf(IntPtrEqual(var_low->value(), IntPtrConstant(0)), &no_carry); | |||
| 11105 | *var_high = Unsigned(IntPtrSub(var_high->value(), IntPtrConstant(1))); | |||
| 11106 | Goto(&no_carry); | |||
| 11107 | BIND(&no_carry)Bind(&no_carry); | |||
| 11108 | } | |||
| 11109 | *var_low = Unsigned(IntPtrSub(IntPtrConstant(0), var_low->value())); | |||
| 11110 | Goto(&done); | |||
| 11111 | BIND(&done)Bind(&done); | |||
| 11112 | } | |||
| 11113 | ||||
| 11114 | template <> | |||
| 11115 | void CodeStubAssembler::EmitElementStoreTypedArrayUpdateValue( | |||
| 11116 | TNode<Object> value, ElementsKind elements_kind, | |||
| 11117 | TNode<Word32T> converted_value, TVariable<Object>* maybe_converted_value) { | |||
| 11118 | switch (elements_kind) { | |||
| 11119 | case UINT8_ELEMENTS: | |||
| 11120 | case INT8_ELEMENTS: | |||
| 11121 | case UINT16_ELEMENTS: | |||
| 11122 | case INT16_ELEMENTS: | |||
| 11123 | case UINT8_CLAMPED_ELEMENTS: | |||
| 11124 | *maybe_converted_value = | |||
| 11125 | SmiFromInt32(UncheckedCast<Int32T>(converted_value)); | |||
| 11126 | break; | |||
| 11127 | case UINT32_ELEMENTS: | |||
| 11128 | *maybe_converted_value = | |||
| 11129 | ChangeUint32ToTagged(UncheckedCast<Uint32T>(converted_value)); | |||
| 11130 | break; | |||
| 11131 | case INT32_ELEMENTS: | |||
| 11132 | *maybe_converted_value = | |||
| 11133 | ChangeInt32ToTagged(UncheckedCast<Int32T>(converted_value)); | |||
| 11134 | break; | |||
| 11135 | default: | |||
| 11136 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 11137 | } | |||
| 11138 | } | |||
| 11139 | ||||
| 11140 | template <> | |||
| 11141 | void CodeStubAssembler::EmitElementStoreTypedArrayUpdateValue( | |||
| 11142 | TNode<Object> value, ElementsKind elements_kind, | |||
| 11143 | TNode<Float32T> converted_value, TVariable<Object>* maybe_converted_value) { | |||
| 11144 | Label dont_allocate_heap_number(this), end(this); | |||
| 11145 | GotoIf(TaggedIsSmi(value), &dont_allocate_heap_number); | |||
| 11146 | GotoIf(IsHeapNumber(CAST(value)Cast(value)), &dont_allocate_heap_number); | |||
| 11147 | { | |||
| 11148 | *maybe_converted_value = | |||
| 11149 | AllocateHeapNumberWithValue(ChangeFloat32ToFloat64(converted_value)); | |||
| 11150 | Goto(&end); | |||
| 11151 | } | |||
| 11152 | BIND(&dont_allocate_heap_number)Bind(&dont_allocate_heap_number); | |||
| 11153 | { | |||
| 11154 | *maybe_converted_value = value; | |||
| 11155 | Goto(&end); | |||
| 11156 | } | |||
| 11157 | BIND(&end)Bind(&end); | |||
| 11158 | } | |||
| 11159 | ||||
| 11160 | template <> | |||
| 11161 | void CodeStubAssembler::EmitElementStoreTypedArrayUpdateValue( | |||
| 11162 | TNode<Object> value, ElementsKind elements_kind, | |||
| 11163 | TNode<Float64T> converted_value, TVariable<Object>* maybe_converted_value) { | |||
| 11164 | Label dont_allocate_heap_number(this), end(this); | |||
| 11165 | GotoIf(TaggedIsSmi(value), &dont_allocate_heap_number); | |||
| 11166 | GotoIf(IsHeapNumber(CAST(value)Cast(value)), &dont_allocate_heap_number); | |||
| 11167 | { | |||
| 11168 | *maybe_converted_value = AllocateHeapNumberWithValue(converted_value); | |||
| 11169 | Goto(&end); | |||
| 11170 | } | |||
| 11171 | BIND(&dont_allocate_heap_number)Bind(&dont_allocate_heap_number); | |||
| 11172 | { | |||
| 11173 | *maybe_converted_value = value; | |||
| 11174 | Goto(&end); | |||
| 11175 | } | |||
| 11176 | BIND(&end)Bind(&end); | |||
| 11177 | } | |||
| 11178 | ||||
| 11179 | template <> | |||
| 11180 | void CodeStubAssembler::EmitElementStoreTypedArrayUpdateValue( | |||
| 11181 | TNode<Object> value, ElementsKind elements_kind, | |||
| 11182 | TNode<BigInt> converted_value, TVariable<Object>* maybe_converted_value) { | |||
| 11183 | *maybe_converted_value = converted_value; | |||
| 11184 | } | |||
| 11185 | ||||
| 11186 | template <typename TValue> | |||
| 11187 | void CodeStubAssembler::EmitElementStoreTypedArray( | |||
| 11188 | TNode<JSTypedArray> typed_array, TNode<IntPtrT> key, TNode<Object> value, | |||
| 11189 | ElementsKind elements_kind, KeyedAccessStoreMode store_mode, Label* bailout, | |||
| 11190 | TNode<Context> context, TVariable<Object>* maybe_converted_value) { | |||
| 11191 | Label done(this), update_value_and_bailout(this, Label::kDeferred); | |||
| 11192 | ||||
| 11193 | bool is_rab_gsab = false; | |||
| 11194 | if (IsRabGsabTypedArrayElementsKind(elements_kind)) { | |||
| 11195 | is_rab_gsab = true; | |||
| 11196 | // For the rest of the function, use the corresponding non-RAB/GSAB | |||
| 11197 | // ElementsKind. | |||
| 11198 | elements_kind = GetCorrespondingNonRabGsabElementsKind(elements_kind); | |||
| 11199 | } | |||
| 11200 | ||||
| 11201 | TNode<TValue> converted_value = | |||
| 11202 | PrepareValueForWriteToTypedArray<TValue>(value, elements_kind, context); | |||
| 11203 | ||||
| 11204 | // There must be no allocations between the buffer load and | |||
| 11205 | // and the actual store to backing store, because GC may decide that | |||
| 11206 | // the buffer is not alive or move the elements. | |||
| 11207 | // TODO(ishell): introduce DisallowGarbageCollectionCode scope here. | |||
| 11208 | ||||
| 11209 | // Check if buffer has been detached. (For RAB / GSAB this is part of loading | |||
| 11210 | // the length, so no additional check is needed.) | |||
| 11211 | TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(typed_array); | |||
| 11212 | if (!is_rab_gsab) { | |||
| 11213 | GotoIf(IsDetachedBuffer(buffer), &update_value_and_bailout); | |||
| 11214 | } | |||
| 11215 | ||||
| 11216 | // Bounds check. | |||
| 11217 | TNode<UintPtrT> length; | |||
| 11218 | if (is_rab_gsab) { | |||
| 11219 | length = LoadVariableLengthJSTypedArrayLength( | |||
| 11220 | typed_array, buffer, | |||
| 11221 | store_mode == STORE_IGNORE_OUT_OF_BOUNDS ? &done | |||
| 11222 | : &update_value_and_bailout); | |||
| 11223 | } else { | |||
| 11224 | length = LoadJSTypedArrayLength(typed_array); | |||
| 11225 | } | |||
| 11226 | ||||
| 11227 | if (store_mode == STORE_IGNORE_OUT_OF_BOUNDS) { | |||
| 11228 | // Skip the store if we write beyond the length or | |||
| 11229 | // to a property with a negative integer index. | |||
| 11230 | GotoIfNot(UintPtrLessThan(key, length), &done); | |||
| 11231 | } else { | |||
| 11232 | DCHECK_EQ(store_mode, STANDARD_STORE)((void) 0); | |||
| 11233 | GotoIfNot(UintPtrLessThan(key, length), &update_value_and_bailout); | |||
| 11234 | } | |||
| 11235 | ||||
| 11236 | TNode<RawPtrT> data_ptr = LoadJSTypedArrayDataPtr(typed_array); | |||
| 11237 | StoreElement(data_ptr, elements_kind, key, converted_value); | |||
| 11238 | Goto(&done); | |||
| 11239 | ||||
| 11240 | if (!is_rab_gsab || store_mode != STORE_IGNORE_OUT_OF_BOUNDS) { | |||
| 11241 | BIND(&update_value_and_bailout)Bind(&update_value_and_bailout); | |||
| 11242 | // We already prepared the incoming value for storing into a typed array. | |||
| 11243 | // This might involve calling ToNumber in some cases. We shouldn't call | |||
| 11244 | // ToNumber again in the runtime so pass the converted value to the runtime. | |||
| 11245 | // The prepared value is an untagged value. Convert it to a tagged value | |||
| 11246 | // to pass it to runtime. It is not possible to do the detached buffer check | |||
| 11247 | // before we prepare the value, since ToNumber can detach the ArrayBuffer. | |||
| 11248 | // The spec specifies the order of these operations. | |||
| 11249 | if (maybe_converted_value != nullptr) { | |||
| 11250 | EmitElementStoreTypedArrayUpdateValue( | |||
| 11251 | value, elements_kind, converted_value, maybe_converted_value); | |||
| 11252 | } | |||
| 11253 | Goto(bailout); | |||
| 11254 | } | |||
| 11255 | ||||
| 11256 | BIND(&done)Bind(&done); | |||
| 11257 | } | |||
| 11258 | ||||
| 11259 | void CodeStubAssembler::EmitElementStore( | |||
| 11260 | TNode<JSObject> object, TNode<Object> key, TNode<Object> value, | |||
| 11261 | ElementsKind elements_kind, KeyedAccessStoreMode store_mode, Label* bailout, | |||
| 11262 | TNode<Context> context, TVariable<Object>* maybe_converted_value) { | |||
| 11263 | CSA_DCHECK(this, Word32BinaryNot(IsJSProxy(object)))((void)0); | |||
| 11264 | ||||
| 11265 | TNode<FixedArrayBase> elements = LoadElements(object); | |||
| 11266 | if (!(IsSmiOrObjectElementsKind(elements_kind) || | |||
| 11267 | IsSealedElementsKind(elements_kind) || | |||
| 11268 | IsNonextensibleElementsKind(elements_kind))) { | |||
| 11269 | CSA_DCHECK(this, Word32BinaryNot(IsFixedCOWArrayMap(LoadMap(elements))))((void)0); | |||
| 11270 | } else if (!IsCOWHandlingStoreMode(store_mode)) { | |||
| 11271 | GotoIf(IsFixedCOWArrayMap(LoadMap(elements)), bailout); | |||
| 11272 | } | |||
| 11273 | ||||
| 11274 | // TODO(ishell): introduce TryToIntPtrOrSmi() and use BInt. | |||
| 11275 | TNode<IntPtrT> intptr_key = TryToIntptr(key, bailout); | |||
| 11276 | ||||
| 11277 | // TODO(rmcilroy): TNodify the converted value once this funciton and | |||
| 11278 | // StoreElement are templated based on the type elements_kind type. | |||
| 11279 | if (IsTypedArrayOrRabGsabTypedArrayElementsKind(elements_kind)) { | |||
| 11280 | TNode<JSTypedArray> typed_array = CAST(object)Cast(object); | |||
| 11281 | switch (elements_kind) { | |||
| 11282 | case UINT8_ELEMENTS: | |||
| 11283 | case INT8_ELEMENTS: | |||
| 11284 | case UINT16_ELEMENTS: | |||
| 11285 | case INT16_ELEMENTS: | |||
| 11286 | case UINT32_ELEMENTS: | |||
| 11287 | case INT32_ELEMENTS: | |||
| 11288 | case UINT8_CLAMPED_ELEMENTS: | |||
| 11289 | case RAB_GSAB_UINT8_ELEMENTS: | |||
| 11290 | case RAB_GSAB_INT8_ELEMENTS: | |||
| 11291 | case RAB_GSAB_UINT16_ELEMENTS: | |||
| 11292 | case RAB_GSAB_INT16_ELEMENTS: | |||
| 11293 | case RAB_GSAB_UINT32_ELEMENTS: | |||
| 11294 | case RAB_GSAB_INT32_ELEMENTS: | |||
| 11295 | case RAB_GSAB_UINT8_CLAMPED_ELEMENTS: | |||
| 11296 | EmitElementStoreTypedArray<Word32T>(typed_array, intptr_key, value, | |||
| 11297 | elements_kind, store_mode, bailout, | |||
| 11298 | context, maybe_converted_value); | |||
| 11299 | break; | |||
| 11300 | case FLOAT32_ELEMENTS: | |||
| 11301 | case RAB_GSAB_FLOAT32_ELEMENTS: | |||
| 11302 | EmitElementStoreTypedArray<Float32T>(typed_array, intptr_key, value, | |||
| 11303 | elements_kind, store_mode, bailout, | |||
| 11304 | context, maybe_converted_value); | |||
| 11305 | break; | |||
| 11306 | case FLOAT64_ELEMENTS: | |||
| 11307 | case RAB_GSAB_FLOAT64_ELEMENTS: | |||
| 11308 | EmitElementStoreTypedArray<Float64T>(typed_array, intptr_key, value, | |||
| 11309 | elements_kind, store_mode, bailout, | |||
| 11310 | context, maybe_converted_value); | |||
| 11311 | break; | |||
| 11312 | case BIGINT64_ELEMENTS: | |||
| 11313 | case BIGUINT64_ELEMENTS: | |||
| 11314 | case RAB_GSAB_BIGINT64_ELEMENTS: | |||
| 11315 | case RAB_GSAB_BIGUINT64_ELEMENTS: | |||
| 11316 | EmitElementStoreTypedArray<BigInt>(typed_array, intptr_key, value, | |||
| 11317 | elements_kind, store_mode, bailout, | |||
| 11318 | context, maybe_converted_value); | |||
| 11319 | break; | |||
| 11320 | default: | |||
| 11321 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 11322 | } | |||
| 11323 | return; | |||
| 11324 | } | |||
| 11325 | DCHECK(IsFastElementsKind(elements_kind) ||((void) 0) | |||
| 11326 | IsSealedElementsKind(elements_kind) ||((void) 0) | |||
| 11327 | IsNonextensibleElementsKind(elements_kind))((void) 0); | |||
| 11328 | ||||
| 11329 | // In case value is stored into a fast smi array, assure that the value is | |||
| 11330 | // a smi before manipulating the backing store. Otherwise the backing store | |||
| 11331 | // may be left in an invalid state. | |||
| 11332 | base::Optional<TNode<Float64T>> float_value; | |||
| 11333 | if (IsSmiElementsKind(elements_kind)) { | |||
| 11334 | GotoIfNot(TaggedIsSmi(value), bailout); | |||
| 11335 | } else if (IsDoubleElementsKind(elements_kind)) { | |||
| 11336 | float_value = TryTaggedToFloat64(value, bailout); | |||
| 11337 | } | |||
| 11338 | ||||
| 11339 | TNode<Smi> smi_length = Select<Smi>( | |||
| 11340 | IsJSArray(object), | |||
| 11341 | [=]() { | |||
| 11342 | // This is casting Number -> Smi which may not actually be safe. | |||
| 11343 | return CAST(LoadJSArrayLength(CAST(object)))Cast(LoadJSArrayLength(Cast(object))); | |||
| 11344 | }, | |||
| 11345 | [=]() { return LoadFixedArrayBaseLength(elements); }); | |||
| 11346 | ||||
| 11347 | TNode<UintPtrT> length = Unsigned(SmiUntag(smi_length)); | |||
| 11348 | if (IsGrowStoreMode(store_mode) && | |||
| 11349 | !(IsSealedElementsKind(elements_kind) || | |||
| 11350 | IsNonextensibleElementsKind(elements_kind))) { | |||
| 11351 | elements = CheckForCapacityGrow(object, elements, elements_kind, length, | |||
| 11352 | intptr_key, bailout); | |||
| 11353 | } else { | |||
| 11354 | GotoIfNot(UintPtrLessThan(Unsigned(intptr_key), length), bailout); | |||
| 11355 | } | |||
| 11356 | ||||
| 11357 | // Cannot store to a hole in holey sealed elements so bailout. | |||
| 11358 | if (elements_kind == HOLEY_SEALED_ELEMENTS || | |||
| 11359 | elements_kind == HOLEY_NONEXTENSIBLE_ELEMENTS) { | |||
| 11360 | TNode<Object> target_value = | |||
| 11361 | LoadFixedArrayElement(CAST(elements)Cast(elements), intptr_key); | |||
| 11362 | GotoIf(IsTheHole(target_value), bailout); | |||
| 11363 | } | |||
| 11364 | ||||
| 11365 | // If we didn't grow {elements}, it might still be COW, in which case we | |||
| 11366 | // copy it now. | |||
| 11367 | if (!(IsSmiOrObjectElementsKind(elements_kind) || | |||
| 11368 | IsSealedElementsKind(elements_kind) || | |||
| 11369 | IsNonextensibleElementsKind(elements_kind))) { | |||
| 11370 | CSA_DCHECK(this, Word32BinaryNot(IsFixedCOWArrayMap(LoadMap(elements))))((void)0); | |||
| 11371 | } else if (IsCOWHandlingStoreMode(store_mode)) { | |||
| 11372 | elements = CopyElementsOnWrite(object, elements, elements_kind, | |||
| 11373 | Signed(length), bailout); | |||
| 11374 | } | |||
| 11375 | ||||
| 11376 | CSA_DCHECK(this, Word32BinaryNot(IsFixedCOWArrayMap(LoadMap(elements))))((void)0); | |||
| 11377 | if (float_value) { | |||
| 11378 | StoreElement(elements, elements_kind, intptr_key, float_value.value()); | |||
| 11379 | } else { | |||
| 11380 | StoreElement(elements, elements_kind, intptr_key, value); | |||
| 11381 | } | |||
| 11382 | } | |||
| 11383 | ||||
| 11384 | TNode<FixedArrayBase> CodeStubAssembler::CheckForCapacityGrow( | |||
| 11385 | TNode<JSObject> object, TNode<FixedArrayBase> elements, ElementsKind kind, | |||
| 11386 | TNode<UintPtrT> length, TNode<IntPtrT> key, Label* bailout) { | |||
| 11387 | DCHECK(IsFastElementsKind(kind))((void) 0); | |||
| 11388 | TVARIABLE(FixedArrayBase, checked_elements)TVariable<FixedArrayBase> checked_elements(this); | |||
| 11389 | Label grow_case(this), no_grow_case(this), done(this), | |||
| 11390 | grow_bailout(this, Label::kDeferred); | |||
| 11391 | ||||
| 11392 | TNode<BoolT> condition; | |||
| 11393 | if (IsHoleyElementsKind(kind)) { | |||
| 11394 | condition = UintPtrGreaterThanOrEqual(key, length); | |||
| 11395 | } else { | |||
| 11396 | // We don't support growing here unless the value is being appended. | |||
| 11397 | condition = WordEqual(key, length); | |||
| 11398 | } | |||
| 11399 | Branch(condition, &grow_case, &no_grow_case); | |||
| 11400 | ||||
| 11401 | BIND(&grow_case)Bind(&grow_case); | |||
| 11402 | { | |||
| 11403 | TNode<IntPtrT> current_capacity = | |||
| 11404 | SmiUntag(LoadFixedArrayBaseLength(elements)); | |||
| 11405 | checked_elements = elements; | |||
| 11406 | Label fits_capacity(this); | |||
| 11407 | // If key is negative, we will notice in Runtime::kGrowArrayElements. | |||
| 11408 | GotoIf(UintPtrLessThan(key, current_capacity), &fits_capacity); | |||
| 11409 | ||||
| 11410 | { | |||
| 11411 | TNode<FixedArrayBase> new_elements = TryGrowElementsCapacity( | |||
| 11412 | object, elements, kind, key, current_capacity, &grow_bailout); | |||
| 11413 | checked_elements = new_elements; | |||
| 11414 | Goto(&fits_capacity); | |||
| 11415 | } | |||
| 11416 | ||||
| 11417 | BIND(&grow_bailout)Bind(&grow_bailout); | |||
| 11418 | { | |||
| 11419 | GotoIf(IntPtrLessThan(key, IntPtrConstant(0)), bailout); | |||
| 11420 | TNode<Number> tagged_key = ChangeUintPtrToTagged(Unsigned(key)); | |||
| 11421 | TNode<Object> maybe_elements = CallRuntime( | |||
| 11422 | Runtime::kGrowArrayElements, NoContextConstant(), object, tagged_key); | |||
| 11423 | GotoIf(TaggedIsSmi(maybe_elements), bailout); | |||
| 11424 | TNode<FixedArrayBase> new_elements = CAST(maybe_elements)Cast(maybe_elements); | |||
| 11425 | CSA_DCHECK(this, IsFixedArrayWithKind(new_elements, kind))((void)0); | |||
| 11426 | checked_elements = new_elements; | |||
| 11427 | Goto(&fits_capacity); | |||
| 11428 | } | |||
| 11429 | ||||
| 11430 | BIND(&fits_capacity)Bind(&fits_capacity); | |||
| 11431 | GotoIfNot(IsJSArray(object), &done); | |||
| 11432 | ||||
| 11433 | TNode<IntPtrT> new_length = IntPtrAdd(key, IntPtrConstant(1)); | |||
| 11434 | StoreObjectFieldNoWriteBarrier(object, JSArray::kLengthOffset, | |||
| 11435 | SmiTag(new_length)); | |||
| 11436 | Goto(&done); | |||
| 11437 | } | |||
| 11438 | ||||
| 11439 | BIND(&no_grow_case)Bind(&no_grow_case); | |||
| 11440 | { | |||
| 11441 | GotoIfNot(UintPtrLessThan(key, length), bailout); | |||
| 11442 | checked_elements = elements; | |||
| 11443 | Goto(&done); | |||
| 11444 | } | |||
| 11445 | ||||
| 11446 | BIND(&done)Bind(&done); | |||
| 11447 | return checked_elements.value(); | |||
| 11448 | } | |||
| 11449 | ||||
| 11450 | TNode<FixedArrayBase> CodeStubAssembler::CopyElementsOnWrite( | |||
| 11451 | TNode<HeapObject> object, TNode<FixedArrayBase> elements, ElementsKind kind, | |||
| 11452 | TNode<IntPtrT> length, Label* bailout) { | |||
| 11453 | TVARIABLE(FixedArrayBase, new_elements_var, elements)TVariable<FixedArrayBase> new_elements_var(elements, this ); | |||
| 11454 | Label done(this); | |||
| 11455 | ||||
| 11456 | GotoIfNot(IsFixedCOWArrayMap(LoadMap(elements)), &done); | |||
| 11457 | { | |||
| 11458 | TNode<IntPtrT> capacity = SmiUntag(LoadFixedArrayBaseLength(elements)); | |||
| 11459 | TNode<FixedArrayBase> new_elements = GrowElementsCapacity( | |||
| 11460 | object, elements, kind, kind, length, capacity, bailout); | |||
| 11461 | new_elements_var = new_elements; | |||
| 11462 | Goto(&done); | |||
| 11463 | } | |||
| 11464 | ||||
| 11465 | BIND(&done)Bind(&done); | |||
| 11466 | return new_elements_var.value(); | |||
| 11467 | } | |||
| 11468 | ||||
| 11469 | void CodeStubAssembler::TransitionElementsKind(TNode<JSObject> object, | |||
| 11470 | TNode<Map> map, | |||
| 11471 | ElementsKind from_kind, | |||
| 11472 | ElementsKind to_kind, | |||
| 11473 | Label* bailout) { | |||
| 11474 | DCHECK(!IsHoleyElementsKind(from_kind) || IsHoleyElementsKind(to_kind))((void) 0); | |||
| 11475 | if (AllocationSite::ShouldTrack(from_kind, to_kind)) { | |||
| 11476 | TrapAllocationMemento(object, bailout); | |||
| 11477 | } | |||
| 11478 | ||||
| 11479 | if (!IsSimpleMapChangeTransition(from_kind, to_kind)) { | |||
| 11480 | Comment("Non-simple map transition"); | |||
| 11481 | TNode<FixedArrayBase> elements = LoadElements(object); | |||
| 11482 | ||||
| 11483 | Label done(this); | |||
| 11484 | GotoIf(TaggedEqual(elements, EmptyFixedArrayConstant()), &done); | |||
| 11485 | ||||
| 11486 | // TODO(ishell): Use BInt for elements_length and array_length. | |||
| 11487 | TNode<IntPtrT> elements_length = | |||
| 11488 | SmiUntag(LoadFixedArrayBaseLength(elements)); | |||
| 11489 | TNode<IntPtrT> array_length = Select<IntPtrT>( | |||
| 11490 | IsJSArray(object), | |||
| 11491 | [=]() { | |||
| 11492 | CSA_DCHECK(this, IsFastElementsKind(LoadElementsKind(object)))((void)0); | |||
| 11493 | return SmiUntag(LoadFastJSArrayLength(CAST(object)Cast(object))); | |||
| 11494 | }, | |||
| 11495 | [=]() { return elements_length; }); | |||
| 11496 | ||||
| 11497 | CSA_DCHECK(this, WordNotEqual(elements_length, IntPtrConstant(0)))((void)0); | |||
| 11498 | ||||
| 11499 | GrowElementsCapacity(object, elements, from_kind, to_kind, array_length, | |||
| 11500 | elements_length, bailout); | |||
| 11501 | Goto(&done); | |||
| 11502 | BIND(&done)Bind(&done); | |||
| 11503 | } | |||
| 11504 | ||||
| 11505 | StoreMap(object, map); | |||
| 11506 | } | |||
| 11507 | ||||
| 11508 | void CodeStubAssembler::TrapAllocationMemento(TNode<JSObject> object, | |||
| 11509 | Label* memento_found) { | |||
| 11510 | DCHECK(V8_ALLOCATION_SITE_TRACKING_BOOL)((void) 0); | |||
| 11511 | Comment("[ TrapAllocationMemento"); | |||
| 11512 | Label no_memento_found(this); | |||
| 11513 | Label top_check(this), map_check(this); | |||
| 11514 | ||||
| 11515 | TNode<ExternalReference> new_space_top_address = ExternalConstant( | |||
| 11516 | ExternalReference::new_space_allocation_top_address(isolate())); | |||
| 11517 | const int kMementoMapOffset = JSArray::kHeaderSize; | |||
| 11518 | const int kMementoLastWordOffset = | |||
| 11519 | kMementoMapOffset + AllocationMemento::kSize - kTaggedSize; | |||
| 11520 | ||||
| 11521 | // Bail out if the object is not in new space. | |||
| 11522 | TNode<IntPtrT> object_word = BitcastTaggedToWord(object); | |||
| 11523 | // TODO(v8:11641): Skip TrapAllocationMemento when allocation-site | |||
| 11524 | // tracking is disabled. | |||
| 11525 | TNode<IntPtrT> object_page = PageFromAddress(object_word); | |||
| 11526 | { | |||
| 11527 | TNode<IntPtrT> page_flags = | |||
| 11528 | Load<IntPtrT>(object_page, IntPtrConstant(Page::kFlagsOffset)); | |||
| 11529 | GotoIf(WordEqual( | |||
| 11530 | WordAnd(page_flags, | |||
| 11531 | IntPtrConstant(MemoryChunk::kIsInYoungGenerationMask)), | |||
| 11532 | IntPtrConstant(0)), | |||
| 11533 | &no_memento_found); | |||
| 11534 | // TODO(v8:11799): Support allocation memento for a large object by | |||
| 11535 | // allocating additional word for the memento after the large object. | |||
| 11536 | GotoIf(WordNotEqual(WordAnd(page_flags, | |||
| 11537 | IntPtrConstant(MemoryChunk::kIsLargePageMask)), | |||
| 11538 | IntPtrConstant(0)), | |||
| 11539 | &no_memento_found); | |||
| 11540 | } | |||
| 11541 | ||||
| 11542 | TNode<IntPtrT> memento_last_word = IntPtrAdd( | |||
| 11543 | object_word, IntPtrConstant(kMementoLastWordOffset - kHeapObjectTag)); | |||
| 11544 | TNode<IntPtrT> memento_last_word_page = PageFromAddress(memento_last_word); | |||
| 11545 | ||||
| 11546 | TNode<IntPtrT> new_space_top = Load<IntPtrT>(new_space_top_address); | |||
| 11547 | TNode<IntPtrT> new_space_top_page = PageFromAddress(new_space_top); | |||
| 11548 | ||||
| 11549 | // If the object is in new space, we need to check whether respective | |||
| 11550 | // potential memento object is on the same page as the current top. | |||
| 11551 | GotoIf(WordEqual(memento_last_word_page, new_space_top_page), &top_check); | |||
| 11552 | ||||
| 11553 | // The object is on a different page than allocation top. Bail out if the | |||
| 11554 | // object sits on the page boundary as no memento can follow and we cannot | |||
| 11555 | // touch the memory following it. | |||
| 11556 | Branch(WordEqual(object_page, memento_last_word_page), &map_check, | |||
| 11557 | &no_memento_found); | |||
| 11558 | ||||
| 11559 | // If top is on the same page as the current object, we need to check whether | |||
| 11560 | // we are below top. | |||
| 11561 | BIND(&top_check)Bind(&top_check); | |||
| 11562 | { | |||
| 11563 | Branch(UintPtrGreaterThanOrEqual(memento_last_word, new_space_top), | |||
| 11564 | &no_memento_found, &map_check); | |||
| 11565 | } | |||
| 11566 | ||||
| 11567 | // Memento map check. | |||
| 11568 | BIND(&map_check)Bind(&map_check); | |||
| 11569 | { | |||
| 11570 | TNode<AnyTaggedT> maybe_mapword = | |||
| 11571 | LoadObjectField(object, kMementoMapOffset); | |||
| 11572 | TNode<AnyTaggedT> memento_mapword = | |||
| 11573 | LoadRootMapWord(RootIndex::kAllocationMementoMap); | |||
| 11574 | Branch(TaggedEqual(maybe_mapword, memento_mapword), memento_found, | |||
| 11575 | &no_memento_found); | |||
| 11576 | } | |||
| 11577 | BIND(&no_memento_found)Bind(&no_memento_found); | |||
| 11578 | Comment("] TrapAllocationMemento"); | |||
| 11579 | } | |||
| 11580 | ||||
| 11581 | TNode<IntPtrT> CodeStubAssembler::PageFromAddress(TNode<IntPtrT> address) { | |||
| 11582 | DCHECK(!V8_ENABLE_THIRD_PARTY_HEAP_BOOL)((void) 0); | |||
| 11583 | return WordAnd(address, IntPtrConstant(~kPageAlignmentMask)); | |||
| 11584 | } | |||
| 11585 | ||||
| 11586 | TNode<AllocationSite> CodeStubAssembler::CreateAllocationSiteInFeedbackVector( | |||
| 11587 | TNode<FeedbackVector> feedback_vector, TNode<UintPtrT> slot) { | |||
| 11588 | TNode<IntPtrT> size = IntPtrConstant(AllocationSite::kSizeWithWeakNext); | |||
| 11589 | TNode<HeapObject> site = Allocate(size, AllocationFlag::kPretenured); | |||
| 11590 | StoreMapNoWriteBarrier(site, RootIndex::kAllocationSiteWithWeakNextMap); | |||
| 11591 | // Should match AllocationSite::Initialize. | |||
| 11592 | TNode<WordT> field = UpdateWord<AllocationSite::ElementsKindBits>( | |||
| 11593 | IntPtrConstant(0), UintPtrConstant(GetInitialFastElementsKind())); | |||
| 11594 | StoreObjectFieldNoWriteBarrier( | |||
| 11595 | site, AllocationSite::kTransitionInfoOrBoilerplateOffset, | |||
| 11596 | SmiTag(Signed(field))); | |||
| 11597 | ||||
| 11598 | // Unlike literals, constructed arrays don't have nested sites | |||
| 11599 | TNode<Smi> zero = SmiConstant(0); | |||
| 11600 | StoreObjectFieldNoWriteBarrier(site, AllocationSite::kNestedSiteOffset, zero); | |||
| 11601 | ||||
| 11602 | // Pretenuring calculation field. | |||
| 11603 | StoreObjectFieldNoWriteBarrier(site, AllocationSite::kPretenureDataOffset, | |||
| 11604 | Int32Constant(0)); | |||
| 11605 | ||||
| 11606 | // Pretenuring memento creation count field. | |||
| 11607 | StoreObjectFieldNoWriteBarrier( | |||
| 11608 | site, AllocationSite::kPretenureCreateCountOffset, Int32Constant(0)); | |||
| 11609 | ||||
| 11610 | // Store an empty fixed array for the code dependency. | |||
| 11611 | StoreObjectFieldRoot(site, AllocationSite::kDependentCodeOffset, | |||
| 11612 | DependentCode::kEmptyDependentCode); | |||
| 11613 | ||||
| 11614 | // Link the object to the allocation site list | |||
| 11615 | TNode<ExternalReference> site_list = ExternalConstant( | |||
| 11616 | ExternalReference::allocation_sites_list_address(isolate())); | |||
| 11617 | TNode<Object> next_site = | |||
| 11618 | LoadBufferObject(ReinterpretCast<RawPtrT>(site_list), 0); | |||
| 11619 | ||||
| 11620 | // TODO(mvstanton): This is a store to a weak pointer, which we may want to | |||
| 11621 | // mark as such in order to skip the write barrier, once we have a unified | |||
| 11622 | // system for weakness. For now we decided to keep it like this because having | |||
| 11623 | // an initial write barrier backed store makes this pointer strong until the | |||
| 11624 | // next GC, and allocation sites are designed to survive several GCs anyway. | |||
| 11625 | StoreObjectField(site, AllocationSite::kWeakNextOffset, next_site); | |||
| 11626 | StoreFullTaggedNoWriteBarrier(site_list, site); | |||
| 11627 | ||||
| 11628 | StoreFeedbackVectorSlot(feedback_vector, slot, site); | |||
| 11629 | return CAST(site)Cast(site); | |||
| 11630 | } | |||
| 11631 | ||||
| 11632 | TNode<MaybeObject> CodeStubAssembler::StoreWeakReferenceInFeedbackVector( | |||
| 11633 | TNode<FeedbackVector> feedback_vector, TNode<UintPtrT> slot, | |||
| 11634 | TNode<HeapObject> value, int additional_offset) { | |||
| 11635 | TNode<MaybeObject> weak_value = MakeWeak(value); | |||
| 11636 | StoreFeedbackVectorSlot(feedback_vector, slot, weak_value, | |||
| 11637 | UPDATE_WRITE_BARRIER, additional_offset); | |||
| 11638 | return weak_value; | |||
| 11639 | } | |||
| 11640 | ||||
| 11641 | TNode<BoolT> CodeStubAssembler::HasBoilerplate( | |||
| 11642 | TNode<Object> maybe_literal_site) { | |||
| 11643 | return TaggedIsNotSmi(maybe_literal_site); | |||
| 11644 | } | |||
| 11645 | ||||
| 11646 | TNode<Smi> CodeStubAssembler::LoadTransitionInfo( | |||
| 11647 | TNode<AllocationSite> allocation_site) { | |||
| 11648 | TNode<Smi> transition_info = CAST(LoadObjectField(Cast(LoadObjectField( allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset )) | |||
| 11649 | allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset))Cast(LoadObjectField( allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset )); | |||
| 11650 | return transition_info; | |||
| 11651 | } | |||
| 11652 | ||||
| 11653 | TNode<JSObject> CodeStubAssembler::LoadBoilerplate( | |||
| 11654 | TNode<AllocationSite> allocation_site) { | |||
| 11655 | TNode<JSObject> boilerplate = CAST(LoadObjectField(Cast(LoadObjectField( allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset )) | |||
| 11656 | allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset))Cast(LoadObjectField( allocation_site, AllocationSite::kTransitionInfoOrBoilerplateOffset )); | |||
| 11657 | return boilerplate; | |||
| 11658 | } | |||
| 11659 | ||||
| 11660 | TNode<Int32T> CodeStubAssembler::LoadElementsKind( | |||
| 11661 | TNode<AllocationSite> allocation_site) { | |||
| 11662 | TNode<Smi> transition_info = LoadTransitionInfo(allocation_site); | |||
| 11663 | TNode<Int32T> elements_kind = | |||
| 11664 | Signed(DecodeWord32<AllocationSite::ElementsKindBits>( | |||
| 11665 | SmiToInt32(transition_info))); | |||
| 11666 | CSA_DCHECK(this, IsFastElementsKind(elements_kind))((void)0); | |||
| 11667 | return elements_kind; | |||
| 11668 | } | |||
| 11669 | ||||
| 11670 | template <typename TIndex> | |||
| 11671 | TNode<TIndex> CodeStubAssembler::BuildFastLoop(const VariableList& vars, | |||
| 11672 | TNode<TIndex> start_index, | |||
| 11673 | TNode<TIndex> end_index, | |||
| 11674 | const FastLoopBody<TIndex>& body, | |||
| 11675 | int increment, | |||
| 11676 | IndexAdvanceMode advance_mode) { | |||
| 11677 | TVARIABLE(TIndex, var, start_index)TVariable<TIndex> var(start_index, this); | |||
| 11678 | VariableList vars_copy(vars.begin(), vars.end(), zone()); | |||
| 11679 | vars_copy.push_back(&var); | |||
| 11680 | Label loop(this, vars_copy); | |||
| 11681 | Label after_loop(this); | |||
| 11682 | // Introduce an explicit second check of the termination condition before the | |||
| 11683 | // loop that helps turbofan generate better code. If there's only a single | |||
| 11684 | // check, then the CodeStubAssembler forces it to be at the beginning of the | |||
| 11685 | // loop requiring a backwards branch at the end of the loop (it's not possible | |||
| 11686 | // to force the loop header check at the end of the loop and branch forward to | |||
| 11687 | // it from the pre-header). The extra branch is slower in the case that the | |||
| 11688 | // loop actually iterates. | |||
| 11689 | TNode<BoolT> first_check = IntPtrOrSmiEqual(var.value(), end_index); | |||
| 11690 | int32_t first_check_val; | |||
| 11691 | if (TryToInt32Constant(first_check, &first_check_val)) { | |||
| 11692 | if (first_check_val) return var.value(); | |||
| 11693 | Goto(&loop); | |||
| 11694 | } else { | |||
| 11695 | Branch(first_check, &after_loop, &loop); | |||
| 11696 | } | |||
| 11697 | ||||
| 11698 | BIND(&loop)Bind(&loop); | |||
| 11699 | { | |||
| 11700 | if (advance_mode == IndexAdvanceMode::kPre) { | |||
| 11701 | Increment(&var, increment); | |||
| 11702 | } | |||
| 11703 | body(var.value()); | |||
| 11704 | if (advance_mode == IndexAdvanceMode::kPost) { | |||
| 11705 | Increment(&var, increment); | |||
| 11706 | } | |||
| 11707 | Branch(IntPtrOrSmiNotEqual(var.value(), end_index), &loop, &after_loop); | |||
| 11708 | } | |||
| 11709 | BIND(&after_loop)Bind(&after_loop); | |||
| 11710 | return var.value(); | |||
| 11711 | } | |||
| 11712 | ||||
| 11713 | // Instantiate BuildFastLoop for IntPtrT and UintPtrT. | |||
| 11714 | template V8_EXPORT_PRIVATE TNode<IntPtrT> | |||
| 11715 | CodeStubAssembler::BuildFastLoop<IntPtrT>(const VariableList& vars, | |||
| 11716 | TNode<IntPtrT> start_index, | |||
| 11717 | TNode<IntPtrT> end_index, | |||
| 11718 | const FastLoopBody<IntPtrT>& body, | |||
| 11719 | int increment, | |||
| 11720 | IndexAdvanceMode advance_mode); | |||
| 11721 | template V8_EXPORT_PRIVATE TNode<UintPtrT> | |||
| 11722 | CodeStubAssembler::BuildFastLoop<UintPtrT>(const VariableList& vars, | |||
| 11723 | TNode<UintPtrT> start_index, | |||
| 11724 | TNode<UintPtrT> end_index, | |||
| 11725 | const FastLoopBody<UintPtrT>& body, | |||
| 11726 | int increment, | |||
| 11727 | IndexAdvanceMode advance_mode); | |||
| 11728 | ||||
| 11729 | template <typename TIndex> | |||
| 11730 | void CodeStubAssembler::BuildFastArrayForEach( | |||
| 11731 | TNode<UnionT<UnionT<FixedArray, PropertyArray>, HeapObject>> array, | |||
| 11732 | ElementsKind kind, TNode<TIndex> first_element_inclusive, | |||
| 11733 | TNode<TIndex> last_element_exclusive, const FastArrayForEachBody& body, | |||
| 11734 | ForEachDirection direction) { | |||
| 11735 | STATIC_ASSERT(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize)static_assert(FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize , "FixedArray::kHeaderSize == FixedDoubleArray::kHeaderSize"); | |||
| 11736 | CSA_SLOW_DCHECK(this, Word32Or(IsFixedArrayWithKind(array, kind),((void)0) | |||
| 11737 | IsPropertyArray(array)))((void)0); | |||
| 11738 | ||||
| 11739 | intptr_t first_val; | |||
| 11740 | bool constant_first = | |||
| 11741 | TryToIntPtrConstant(first_element_inclusive, &first_val); | |||
| 11742 | intptr_t last_val; | |||
| 11743 | bool constent_last = TryToIntPtrConstant(last_element_exclusive, &last_val); | |||
| 11744 | if (constant_first && constent_last) { | |||
| 11745 | intptr_t delta = last_val - first_val; | |||
| 11746 | DCHECK_GE(delta, 0)((void) 0); | |||
| 11747 | if (delta <= kElementLoopUnrollThreshold) { | |||
| 11748 | if (direction == ForEachDirection::kForward) { | |||
| 11749 | for (intptr_t i = first_val; i < last_val; ++i) { | |||
| 11750 | TNode<IntPtrT> index = IntPtrConstant(i); | |||
| 11751 | TNode<IntPtrT> offset = ElementOffsetFromIndex( | |||
| 11752 | index, kind, FixedArray::kHeaderSize - kHeapObjectTag); | |||
| 11753 | body(array, offset); | |||
| 11754 | } | |||
| 11755 | } else { | |||
| 11756 | for (intptr_t i = last_val - 1; i >= first_val; --i) { | |||
| 11757 | TNode<IntPtrT> index = IntPtrConstant(i); | |||
| 11758 | TNode<IntPtrT> offset = ElementOffsetFromIndex( | |||
| 11759 | index, kind, FixedArray::kHeaderSize - kHeapObjectTag); | |||
| 11760 | body(array, offset); | |||
| 11761 | } | |||
| 11762 | } | |||
| 11763 | return; | |||
| 11764 | } | |||
| 11765 | } | |||
| 11766 | ||||
| 11767 | TNode<IntPtrT> start = ElementOffsetFromIndex( | |||
| 11768 | first_element_inclusive, kind, FixedArray::kHeaderSize - kHeapObjectTag); | |||
| 11769 | TNode<IntPtrT> limit = ElementOffsetFromIndex( | |||
| 11770 | last_element_exclusive, kind, FixedArray::kHeaderSize - kHeapObjectTag); | |||
| 11771 | if (direction == ForEachDirection::kReverse) std::swap(start, limit); | |||
| 11772 | ||||
| 11773 | int increment = IsDoubleElementsKind(kind) ? kDoubleSize : kTaggedSize; | |||
| 11774 | BuildFastLoop<IntPtrT>( | |||
| 11775 | start, limit, [&](TNode<IntPtrT> offset) { body(array, offset); }, | |||
| 11776 | direction == ForEachDirection::kReverse ? -increment : increment, | |||
| 11777 | direction == ForEachDirection::kReverse ? IndexAdvanceMode::kPre | |||
| 11778 | : IndexAdvanceMode::kPost); | |||
| 11779 | } | |||
| 11780 | ||||
| 11781 | template <typename TIndex> | |||
| 11782 | void CodeStubAssembler::GotoIfFixedArraySizeDoesntFitInNewSpace( | |||
| 11783 | TNode<TIndex> element_count, Label* doesnt_fit, int base_size) { | |||
| 11784 | GotoIf(FixedArraySizeDoesntFitInNewSpace(element_count, base_size), | |||
| 11785 | doesnt_fit); | |||
| 11786 | } | |||
| 11787 | ||||
| 11788 | void CodeStubAssembler::InitializeFieldsWithRoot(TNode<HeapObject> object, | |||
| 11789 | TNode<IntPtrT> start_offset, | |||
| 11790 | TNode<IntPtrT> end_offset, | |||
| 11791 | RootIndex root_index) { | |||
| 11792 | CSA_SLOW_DCHECK(this, TaggedIsNotSmi(object))((void)0); | |||
| 11793 | start_offset = IntPtrAdd(start_offset, IntPtrConstant(-kHeapObjectTag)); | |||
| 11794 | end_offset = IntPtrAdd(end_offset, IntPtrConstant(-kHeapObjectTag)); | |||
| 11795 | TNode<AnyTaggedT> root_value; | |||
| 11796 | if (root_index == RootIndex::kOnePointerFillerMap) { | |||
| 11797 | root_value = LoadRootMapWord(root_index); | |||
| 11798 | } else { | |||
| 11799 | root_value = LoadRoot(root_index); | |||
| 11800 | } | |||
| 11801 | BuildFastLoop<IntPtrT>( | |||
| 11802 | end_offset, start_offset, | |||
| 11803 | [=](TNode<IntPtrT> current) { | |||
| 11804 | StoreNoWriteBarrier(MachineRepresentation::kTagged, object, current, | |||
| 11805 | root_value); | |||
| 11806 | }, | |||
| 11807 | -kTaggedSize, CodeStubAssembler::IndexAdvanceMode::kPre); | |||
| 11808 | } | |||
| 11809 | ||||
| 11810 | void CodeStubAssembler::BranchIfNumberRelationalComparison(Operation op, | |||
| 11811 | TNode<Number> left, | |||
| 11812 | TNode<Number> right, | |||
| 11813 | Label* if_true, | |||
| 11814 | Label* if_false) { | |||
| 11815 | Label do_float_comparison(this); | |||
| 11816 | TVARIABLE(Float64T, var_left_float)TVariable<Float64T> var_left_float(this); | |||
| 11817 | TVARIABLE(Float64T, var_right_float)TVariable<Float64T> var_right_float(this); | |||
| 11818 | ||||
| 11819 | Branch( | |||
| 11820 | TaggedIsSmi(left), | |||
| 11821 | [&] { | |||
| 11822 | TNode<Smi> smi_left = CAST(left)Cast(left); | |||
| 11823 | ||||
| 11824 | Branch( | |||
| 11825 | TaggedIsSmi(right), | |||
| 11826 | [&] { | |||
| 11827 | TNode<Smi> smi_right = CAST(right)Cast(right); | |||
| 11828 | ||||
| 11829 | // Both {left} and {right} are Smi, so just perform a fast | |||
| 11830 | // Smi comparison. | |||
| 11831 | switch (op) { | |||
| 11832 | case Operation::kEqual: | |||
| 11833 | BranchIfSmiEqual(smi_left, smi_right, if_true, if_false); | |||
| 11834 | break; | |||
| 11835 | case Operation::kLessThan: | |||
| 11836 | BranchIfSmiLessThan(smi_left, smi_right, if_true, if_false); | |||
| 11837 | break; | |||
| 11838 | case Operation::kLessThanOrEqual: | |||
| 11839 | BranchIfSmiLessThanOrEqual(smi_left, smi_right, if_true, | |||
| 11840 | if_false); | |||
| 11841 | break; | |||
| 11842 | case Operation::kGreaterThan: | |||
| 11843 | BranchIfSmiLessThan(smi_right, smi_left, if_true, if_false); | |||
| 11844 | break; | |||
| 11845 | case Operation::kGreaterThanOrEqual: | |||
| 11846 | BranchIfSmiLessThanOrEqual(smi_right, smi_left, if_true, | |||
| 11847 | if_false); | |||
| 11848 | break; | |||
| 11849 | default: | |||
| 11850 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 11851 | } | |||
| 11852 | }, | |||
| 11853 | [&] { | |||
| 11854 | var_left_float = SmiToFloat64(smi_left); | |||
| 11855 | var_right_float = LoadHeapNumberValue(CAST(right)Cast(right)); | |||
| 11856 | Goto(&do_float_comparison); | |||
| 11857 | }); | |||
| 11858 | }, | |||
| 11859 | [&] { | |||
| 11860 | var_left_float = LoadHeapNumberValue(CAST(left)Cast(left)); | |||
| 11861 | ||||
| 11862 | Branch( | |||
| 11863 | TaggedIsSmi(right), | |||
| 11864 | [&] { | |||
| 11865 | var_right_float = SmiToFloat64(CAST(right)Cast(right)); | |||
| 11866 | Goto(&do_float_comparison); | |||
| 11867 | }, | |||
| 11868 | [&] { | |||
| 11869 | var_right_float = LoadHeapNumberValue(CAST(right)Cast(right)); | |||
| 11870 | Goto(&do_float_comparison); | |||
| 11871 | }); | |||
| 11872 | }); | |||
| 11873 | ||||
| 11874 | BIND(&do_float_comparison)Bind(&do_float_comparison); | |||
| 11875 | { | |||
| 11876 | switch (op) { | |||
| 11877 | case Operation::kEqual: | |||
| 11878 | Branch(Float64Equal(var_left_float.value(), var_right_float.value()), | |||
| 11879 | if_true, if_false); | |||
| 11880 | break; | |||
| 11881 | case Operation::kLessThan: | |||
| 11882 | Branch(Float64LessThan(var_left_float.value(), var_right_float.value()), | |||
| 11883 | if_true, if_false); | |||
| 11884 | break; | |||
| 11885 | case Operation::kLessThanOrEqual: | |||
| 11886 | Branch(Float64LessThanOrEqual(var_left_float.value(), | |||
| 11887 | var_right_float.value()), | |||
| 11888 | if_true, if_false); | |||
| 11889 | break; | |||
| 11890 | case Operation::kGreaterThan: | |||
| 11891 | Branch( | |||
| 11892 | Float64GreaterThan(var_left_float.value(), var_right_float.value()), | |||
| 11893 | if_true, if_false); | |||
| 11894 | break; | |||
| 11895 | case Operation::kGreaterThanOrEqual: | |||
| 11896 | Branch(Float64GreaterThanOrEqual(var_left_float.value(), | |||
| 11897 | var_right_float.value()), | |||
| 11898 | if_true, if_false); | |||
| 11899 | break; | |||
| 11900 | default: | |||
| 11901 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 11902 | } | |||
| 11903 | } | |||
| 11904 | } | |||
| 11905 | ||||
| 11906 | void CodeStubAssembler::GotoIfNumberGreaterThanOrEqual(TNode<Number> left, | |||
| 11907 | TNode<Number> right, | |||
| 11908 | Label* if_true) { | |||
| 11909 | Label if_false(this); | |||
| 11910 | BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, left, | |||
| 11911 | right, if_true, &if_false); | |||
| 11912 | BIND(&if_false)Bind(&if_false); | |||
| 11913 | } | |||
| 11914 | ||||
| 11915 | namespace { | |||
| 11916 | Operation Reverse(Operation op) { | |||
| 11917 | switch (op) { | |||
| 11918 | case Operation::kLessThan: | |||
| 11919 | return Operation::kGreaterThan; | |||
| 11920 | case Operation::kLessThanOrEqual: | |||
| 11921 | return Operation::kGreaterThanOrEqual; | |||
| 11922 | case Operation::kGreaterThan: | |||
| 11923 | return Operation::kLessThan; | |||
| 11924 | case Operation::kGreaterThanOrEqual: | |||
| 11925 | return Operation::kLessThanOrEqual; | |||
| 11926 | default: | |||
| 11927 | break; | |||
| 11928 | } | |||
| 11929 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 11930 | } | |||
| 11931 | } // anonymous namespace | |||
| 11932 | ||||
| 11933 | TNode<Context> CodeStubAssembler::GotoIfHasContextExtensionUpToDepth( | |||
| 11934 | TNode<Context> context, TNode<Uint32T> depth, Label* target) { | |||
| 11935 | TVARIABLE(Context, cur_context, context)TVariable<Context> cur_context(context, this); | |||
| 11936 | TVARIABLE(Uint32T, cur_depth, depth)TVariable<Uint32T> cur_depth(depth, this); | |||
| 11937 | ||||
| 11938 | Label context_search(this, {&cur_depth, &cur_context}); | |||
| 11939 | Label exit_loop(this); | |||
| 11940 | Label no_extension(this); | |||
| 11941 | ||||
| 11942 | // Loop until the depth is 0. | |||
| 11943 | CSA_DCHECK(this, Word32NotEqual(cur_depth.value(), Int32Constant(0)))((void)0); | |||
| 11944 | Goto(&context_search); | |||
| 11945 | BIND(&context_search)Bind(&context_search); | |||
| 11946 | { | |||
| 11947 | // Check if context has an extension slot. | |||
| 11948 | TNode<BoolT> has_extension = | |||
| 11949 | LoadScopeInfoHasExtensionField(LoadScopeInfo(cur_context.value())); | |||
| 11950 | GotoIfNot(has_extension, &no_extension); | |||
| 11951 | ||||
| 11952 | // Jump to the target if the extension slot is not an undefined value. | |||
| 11953 | TNode<Object> extension_slot = | |||
| 11954 | LoadContextElement(cur_context.value(), Context::EXTENSION_INDEX); | |||
| 11955 | Branch(TaggedNotEqual(extension_slot, UndefinedConstant()), target, | |||
| 11956 | &no_extension); | |||
| 11957 | ||||
| 11958 | BIND(&no_extension)Bind(&no_extension); | |||
| 11959 | { | |||
| 11960 | cur_depth = Unsigned(Int32Sub(cur_depth.value(), Int32Constant(1))); | |||
| 11961 | cur_context = CAST(Cast(LoadContextElement(cur_context.value(), Context::PREVIOUS_INDEX )) | |||
| 11962 | LoadContextElement(cur_context.value(), Context::PREVIOUS_INDEX))Cast(LoadContextElement(cur_context.value(), Context::PREVIOUS_INDEX )); | |||
| 11963 | ||||
| 11964 | Branch(Word32NotEqual(cur_depth.value(), Int32Constant(0)), | |||
| 11965 | &context_search, &exit_loop); | |||
| 11966 | } | |||
| 11967 | } | |||
| 11968 | BIND(&exit_loop)Bind(&exit_loop); | |||
| 11969 | return cur_context.value(); | |||
| 11970 | } | |||
| 11971 | ||||
| 11972 | TNode<Oddball> CodeStubAssembler::RelationalComparison( | |||
| 11973 | Operation op, TNode<Object> left, TNode<Object> right, | |||
| 11974 | const LazyNode<Context>& context, TVariable<Smi>* var_type_feedback) { | |||
| 11975 | Label return_true(this), return_false(this), do_float_comparison(this), | |||
| 11976 | end(this); | |||
| 11977 | TVARIABLE(Oddball, var_result)TVariable<Oddball> var_result(this); // Actually only "true" or "false". | |||
| 11978 | TVARIABLE(Float64T, var_left_float)TVariable<Float64T> var_left_float(this); | |||
| 11979 | TVARIABLE(Float64T, var_right_float)TVariable<Float64T> var_right_float(this); | |||
| 11980 | ||||
| 11981 | // We might need to loop several times due to ToPrimitive and/or ToNumeric | |||
| 11982 | // conversions. | |||
| 11983 | TVARIABLE(Object, var_left, left)TVariable<Object> var_left(left, this); | |||
| 11984 | TVARIABLE(Object, var_right, right)TVariable<Object> var_right(right, this); | |||
| 11985 | VariableList loop_variable_list({&var_left, &var_right}, zone()); | |||
| 11986 | if (var_type_feedback != nullptr) { | |||
| 11987 | // Initialize the type feedback to None. The current feedback is combined | |||
| 11988 | // with the previous feedback. | |||
| 11989 | *var_type_feedback = SmiConstant(CompareOperationFeedback::kNone); | |||
| 11990 | loop_variable_list.push_back(var_type_feedback); | |||
| 11991 | } | |||
| 11992 | Label loop(this, loop_variable_list); | |||
| 11993 | Goto(&loop); | |||
| 11994 | BIND(&loop)Bind(&loop); | |||
| 11995 | { | |||
| 11996 | left = var_left.value(); | |||
| 11997 | right = var_right.value(); | |||
| 11998 | ||||
| 11999 | Label if_left_smi(this), if_left_not_smi(this); | |||
| 12000 | Branch(TaggedIsSmi(left), &if_left_smi, &if_left_not_smi); | |||
| 12001 | ||||
| 12002 | BIND(&if_left_smi)Bind(&if_left_smi); | |||
| 12003 | { | |||
| 12004 | TNode<Smi> smi_left = CAST(left)Cast(left); | |||
| 12005 | Label if_right_smi(this), if_right_heapnumber(this), | |||
| 12006 | if_right_bigint(this, Label::kDeferred), | |||
| 12007 | if_right_not_numeric(this, Label::kDeferred); | |||
| 12008 | GotoIf(TaggedIsSmi(right), &if_right_smi); | |||
| 12009 | TNode<Map> right_map = LoadMap(CAST(right)Cast(right)); | |||
| 12010 | GotoIf(IsHeapNumberMap(right_map), &if_right_heapnumber); | |||
| 12011 | TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); | |||
| 12012 | Branch(IsBigIntInstanceType(right_instance_type), &if_right_bigint, | |||
| 12013 | &if_right_not_numeric); | |||
| 12014 | ||||
| 12015 | BIND(&if_right_smi)Bind(&if_right_smi); | |||
| 12016 | { | |||
| 12017 | TNode<Smi> smi_right = CAST(right)Cast(right); | |||
| 12018 | CombineFeedback(var_type_feedback, | |||
| 12019 | CompareOperationFeedback::kSignedSmall); | |||
| 12020 | switch (op) { | |||
| 12021 | case Operation::kLessThan: | |||
| 12022 | BranchIfSmiLessThan(smi_left, smi_right, &return_true, | |||
| 12023 | &return_false); | |||
| 12024 | break; | |||
| 12025 | case Operation::kLessThanOrEqual: | |||
| 12026 | BranchIfSmiLessThanOrEqual(smi_left, smi_right, &return_true, | |||
| 12027 | &return_false); | |||
| 12028 | break; | |||
| 12029 | case Operation::kGreaterThan: | |||
| 12030 | BranchIfSmiLessThan(smi_right, smi_left, &return_true, | |||
| 12031 | &return_false); | |||
| 12032 | break; | |||
| 12033 | case Operation::kGreaterThanOrEqual: | |||
| 12034 | BranchIfSmiLessThanOrEqual(smi_right, smi_left, &return_true, | |||
| 12035 | &return_false); | |||
| 12036 | break; | |||
| 12037 | default: | |||
| 12038 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 12039 | } | |||
| 12040 | } | |||
| 12041 | ||||
| 12042 | BIND(&if_right_heapnumber)Bind(&if_right_heapnumber); | |||
| 12043 | { | |||
| 12044 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 12045 | var_left_float = SmiToFloat64(smi_left); | |||
| 12046 | var_right_float = LoadHeapNumberValue(CAST(right)Cast(right)); | |||
| 12047 | Goto(&do_float_comparison); | |||
| 12048 | } | |||
| 12049 | ||||
| 12050 | BIND(&if_right_bigint)Bind(&if_right_bigint); | |||
| 12051 | { | |||
| 12052 | OverwriteFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 12053 | var_result = CAST(CallRuntime(Runtime::kBigIntCompareToNumber,Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(Reverse(op)), right, left)) | |||
| 12054 | NoContextConstant(),Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(Reverse(op)), right, left)) | |||
| 12055 | SmiConstant(Reverse(op)), right, left))Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(Reverse(op)), right, left)); | |||
| 12056 | Goto(&end); | |||
| 12057 | } | |||
| 12058 | ||||
| 12059 | BIND(&if_right_not_numeric)Bind(&if_right_not_numeric); | |||
| 12060 | { | |||
| 12061 | OverwriteFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 12062 | // Convert {right} to a Numeric; we don't need to perform the | |||
| 12063 | // dedicated ToPrimitive(right, hint Number) operation, as the | |||
| 12064 | // ToNumeric(right) will by itself already invoke ToPrimitive with | |||
| 12065 | // a Number hint. | |||
| 12066 | var_right = CallBuiltin(Builtin::kNonNumberToNumeric, context(), right); | |||
| 12067 | Goto(&loop); | |||
| 12068 | } | |||
| 12069 | } | |||
| 12070 | ||||
| 12071 | BIND(&if_left_not_smi)Bind(&if_left_not_smi); | |||
| 12072 | { | |||
| 12073 | TNode<Map> left_map = LoadMap(CAST(left)Cast(left)); | |||
| 12074 | ||||
| 12075 | Label if_right_smi(this), if_right_not_smi(this); | |||
| 12076 | Branch(TaggedIsSmi(right), &if_right_smi, &if_right_not_smi); | |||
| 12077 | ||||
| 12078 | BIND(&if_right_smi)Bind(&if_right_smi); | |||
| 12079 | { | |||
| 12080 | Label if_left_heapnumber(this), if_left_bigint(this, Label::kDeferred), | |||
| 12081 | if_left_not_numeric(this, Label::kDeferred); | |||
| 12082 | GotoIf(IsHeapNumberMap(left_map), &if_left_heapnumber); | |||
| 12083 | TNode<Uint16T> left_instance_type = LoadMapInstanceType(left_map); | |||
| 12084 | Branch(IsBigIntInstanceType(left_instance_type), &if_left_bigint, | |||
| 12085 | &if_left_not_numeric); | |||
| 12086 | ||||
| 12087 | BIND(&if_left_heapnumber)Bind(&if_left_heapnumber); | |||
| 12088 | { | |||
| 12089 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 12090 | var_left_float = LoadHeapNumberValue(CAST(left)Cast(left)); | |||
| 12091 | var_right_float = SmiToFloat64(CAST(right)Cast(right)); | |||
| 12092 | Goto(&do_float_comparison); | |||
| 12093 | } | |||
| 12094 | ||||
| 12095 | BIND(&if_left_bigint)Bind(&if_left_bigint); | |||
| 12096 | { | |||
| 12097 | OverwriteFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 12098 | var_result = CAST(CallRuntime(Runtime::kBigIntCompareToNumber,Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12099 | NoContextConstant(), SmiConstant(op),Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12100 | left, right))Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(op), left, right)); | |||
| 12101 | Goto(&end); | |||
| 12102 | } | |||
| 12103 | ||||
| 12104 | BIND(&if_left_not_numeric)Bind(&if_left_not_numeric); | |||
| 12105 | { | |||
| 12106 | OverwriteFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 12107 | // Convert {left} to a Numeric; we don't need to perform the | |||
| 12108 | // dedicated ToPrimitive(left, hint Number) operation, as the | |||
| 12109 | // ToNumeric(left) will by itself already invoke ToPrimitive with | |||
| 12110 | // a Number hint. | |||
| 12111 | var_left = CallBuiltin(Builtin::kNonNumberToNumeric, context(), left); | |||
| 12112 | Goto(&loop); | |||
| 12113 | } | |||
| 12114 | } | |||
| 12115 | ||||
| 12116 | BIND(&if_right_not_smi)Bind(&if_right_not_smi); | |||
| 12117 | { | |||
| 12118 | TNode<Map> right_map = LoadMap(CAST(right)Cast(right)); | |||
| 12119 | ||||
| 12120 | Label if_left_heapnumber(this), if_left_bigint(this, Label::kDeferred), | |||
| 12121 | if_left_string(this, Label::kDeferred), | |||
| 12122 | if_left_other(this, Label::kDeferred); | |||
| 12123 | GotoIf(IsHeapNumberMap(left_map), &if_left_heapnumber); | |||
| 12124 | TNode<Uint16T> left_instance_type = LoadMapInstanceType(left_map); | |||
| 12125 | GotoIf(IsBigIntInstanceType(left_instance_type), &if_left_bigint); | |||
| 12126 | Branch(IsStringInstanceType(left_instance_type), &if_left_string, | |||
| 12127 | &if_left_other); | |||
| 12128 | ||||
| 12129 | BIND(&if_left_heapnumber)Bind(&if_left_heapnumber); | |||
| 12130 | { | |||
| 12131 | Label if_right_heapnumber(this), | |||
| 12132 | if_right_bigint(this, Label::kDeferred), | |||
| 12133 | if_right_not_numeric(this, Label::kDeferred); | |||
| 12134 | GotoIf(TaggedEqual(right_map, left_map), &if_right_heapnumber); | |||
| 12135 | TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); | |||
| 12136 | Branch(IsBigIntInstanceType(right_instance_type), &if_right_bigint, | |||
| 12137 | &if_right_not_numeric); | |||
| 12138 | ||||
| 12139 | BIND(&if_right_heapnumber)Bind(&if_right_heapnumber); | |||
| 12140 | { | |||
| 12141 | CombineFeedback(var_type_feedback, | |||
| 12142 | CompareOperationFeedback::kNumber); | |||
| 12143 | var_left_float = LoadHeapNumberValue(CAST(left)Cast(left)); | |||
| 12144 | var_right_float = LoadHeapNumberValue(CAST(right)Cast(right)); | |||
| 12145 | Goto(&do_float_comparison); | |||
| 12146 | } | |||
| 12147 | ||||
| 12148 | BIND(&if_right_bigint)Bind(&if_right_bigint); | |||
| 12149 | { | |||
| 12150 | OverwriteFeedback(var_type_feedback, | |||
| 12151 | CompareOperationFeedback::kAny); | |||
| 12152 | var_result = CAST(CallRuntime(Cast(CallRuntime( Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(Reverse(op)), right, left)) | |||
| 12153 | Runtime::kBigIntCompareToNumber, NoContextConstant(),Cast(CallRuntime( Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(Reverse(op)), right, left)) | |||
| 12154 | SmiConstant(Reverse(op)), right, left))Cast(CallRuntime( Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(Reverse(op)), right, left)); | |||
| 12155 | Goto(&end); | |||
| 12156 | } | |||
| 12157 | ||||
| 12158 | BIND(&if_right_not_numeric)Bind(&if_right_not_numeric); | |||
| 12159 | { | |||
| 12160 | OverwriteFeedback(var_type_feedback, | |||
| 12161 | CompareOperationFeedback::kAny); | |||
| 12162 | // Convert {right} to a Numeric; we don't need to perform | |||
| 12163 | // dedicated ToPrimitive(right, hint Number) operation, as the | |||
| 12164 | // ToNumeric(right) will by itself already invoke ToPrimitive with | |||
| 12165 | // a Number hint. | |||
| 12166 | var_right = | |||
| 12167 | CallBuiltin(Builtin::kNonNumberToNumeric, context(), right); | |||
| 12168 | Goto(&loop); | |||
| 12169 | } | |||
| 12170 | } | |||
| 12171 | ||||
| 12172 | BIND(&if_left_bigint)Bind(&if_left_bigint); | |||
| 12173 | { | |||
| 12174 | Label if_right_heapnumber(this), if_right_bigint(this), | |||
| 12175 | if_right_string(this), if_right_other(this); | |||
| 12176 | GotoIf(IsHeapNumberMap(right_map), &if_right_heapnumber); | |||
| 12177 | TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); | |||
| 12178 | GotoIf(IsBigIntInstanceType(right_instance_type), &if_right_bigint); | |||
| 12179 | Branch(IsStringInstanceType(right_instance_type), &if_right_string, | |||
| 12180 | &if_right_other); | |||
| 12181 | ||||
| 12182 | BIND(&if_right_heapnumber)Bind(&if_right_heapnumber); | |||
| 12183 | { | |||
| 12184 | OverwriteFeedback(var_type_feedback, | |||
| 12185 | CompareOperationFeedback::kAny); | |||
| 12186 | var_result = CAST(CallRuntime(Runtime::kBigIntCompareToNumber,Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12187 | NoContextConstant(), SmiConstant(op),Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12188 | left, right))Cast(CallRuntime(Runtime::kBigIntCompareToNumber, NoContextConstant (), SmiConstant(op), left, right)); | |||
| 12189 | Goto(&end); | |||
| 12190 | } | |||
| 12191 | ||||
| 12192 | BIND(&if_right_bigint)Bind(&if_right_bigint); | |||
| 12193 | { | |||
| 12194 | CombineFeedback(var_type_feedback, | |||
| 12195 | CompareOperationFeedback::kBigInt); | |||
| 12196 | var_result = CAST(CallRuntime(Runtime::kBigIntCompareToBigInt,Cast(CallRuntime(Runtime::kBigIntCompareToBigInt, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12197 | NoContextConstant(), SmiConstant(op),Cast(CallRuntime(Runtime::kBigIntCompareToBigInt, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12198 | left, right))Cast(CallRuntime(Runtime::kBigIntCompareToBigInt, NoContextConstant (), SmiConstant(op), left, right)); | |||
| 12199 | Goto(&end); | |||
| 12200 | } | |||
| 12201 | ||||
| 12202 | BIND(&if_right_string)Bind(&if_right_string); | |||
| 12203 | { | |||
| 12204 | OverwriteFeedback(var_type_feedback, | |||
| 12205 | CompareOperationFeedback::kAny); | |||
| 12206 | var_result = CAST(CallRuntime(Runtime::kBigIntCompareToString,Cast(CallRuntime(Runtime::kBigIntCompareToString, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12207 | NoContextConstant(), SmiConstant(op),Cast(CallRuntime(Runtime::kBigIntCompareToString, NoContextConstant (), SmiConstant(op), left, right)) | |||
| 12208 | left, right))Cast(CallRuntime(Runtime::kBigIntCompareToString, NoContextConstant (), SmiConstant(op), left, right)); | |||
| 12209 | Goto(&end); | |||
| 12210 | } | |||
| 12211 | ||||
| 12212 | // {right} is not a Number, BigInt, or String. | |||
| 12213 | BIND(&if_right_other)Bind(&if_right_other); | |||
| 12214 | { | |||
| 12215 | OverwriteFeedback(var_type_feedback, | |||
| 12216 | CompareOperationFeedback::kAny); | |||
| 12217 | // Convert {right} to a Numeric; we don't need to perform | |||
| 12218 | // dedicated ToPrimitive(right, hint Number) operation, as the | |||
| 12219 | // ToNumeric(right) will by itself already invoke ToPrimitive with | |||
| 12220 | // a Number hint. | |||
| 12221 | var_right = | |||
| 12222 | CallBuiltin(Builtin::kNonNumberToNumeric, context(), right); | |||
| 12223 | Goto(&loop); | |||
| 12224 | } | |||
| 12225 | } | |||
| 12226 | ||||
| 12227 | BIND(&if_left_string)Bind(&if_left_string); | |||
| 12228 | { | |||
| 12229 | TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); | |||
| 12230 | ||||
| 12231 | Label if_right_not_string(this, Label::kDeferred); | |||
| 12232 | GotoIfNot(IsStringInstanceType(right_instance_type), | |||
| 12233 | &if_right_not_string); | |||
| 12234 | ||||
| 12235 | // Both {left} and {right} are strings. | |||
| 12236 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kString); | |||
| 12237 | Builtin builtin; | |||
| 12238 | switch (op) { | |||
| 12239 | case Operation::kLessThan: | |||
| 12240 | builtin = Builtin::kStringLessThan; | |||
| 12241 | break; | |||
| 12242 | case Operation::kLessThanOrEqual: | |||
| 12243 | builtin = Builtin::kStringLessThanOrEqual; | |||
| 12244 | break; | |||
| 12245 | case Operation::kGreaterThan: | |||
| 12246 | builtin = Builtin::kStringGreaterThan; | |||
| 12247 | break; | |||
| 12248 | case Operation::kGreaterThanOrEqual: | |||
| 12249 | builtin = Builtin::kStringGreaterThanOrEqual; | |||
| 12250 | break; | |||
| 12251 | default: | |||
| 12252 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 12253 | } | |||
| 12254 | var_result = CAST(CallBuiltin(builtin, context(), left, right))Cast(CallBuiltin(builtin, context(), left, right)); | |||
| 12255 | Goto(&end); | |||
| 12256 | ||||
| 12257 | BIND(&if_right_not_string)Bind(&if_right_not_string); | |||
| 12258 | { | |||
| 12259 | OverwriteFeedback(var_type_feedback, | |||
| 12260 | CompareOperationFeedback::kAny); | |||
| 12261 | // {left} is a String, while {right} isn't. Check if {right} is | |||
| 12262 | // a BigInt, otherwise call ToPrimitive(right, hint Number) if | |||
| 12263 | // {right} is a receiver, or ToNumeric(left) and then | |||
| 12264 | // ToNumeric(right) in the other cases. | |||
| 12265 | STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE)static_assert(LAST_JS_RECEIVER_TYPE == LAST_TYPE, "LAST_JS_RECEIVER_TYPE == LAST_TYPE" ); | |||
| 12266 | Label if_right_bigint(this), | |||
| 12267 | if_right_receiver(this, Label::kDeferred); | |||
| 12268 | GotoIf(IsBigIntInstanceType(right_instance_type), &if_right_bigint); | |||
| 12269 | GotoIf(IsJSReceiverInstanceType(right_instance_type), | |||
| 12270 | &if_right_receiver); | |||
| 12271 | ||||
| 12272 | var_left = | |||
| 12273 | CallBuiltin(Builtin::kNonNumberToNumeric, context(), left); | |||
| 12274 | var_right = CallBuiltin(Builtin::kToNumeric, context(), right); | |||
| 12275 | Goto(&loop); | |||
| 12276 | ||||
| 12277 | BIND(&if_right_bigint)Bind(&if_right_bigint); | |||
| 12278 | { | |||
| 12279 | var_result = CAST(CallRuntime(Cast(CallRuntime( Runtime::kBigIntCompareToString, NoContextConstant (), SmiConstant(Reverse(op)), right, left)) | |||
| 12280 | Runtime::kBigIntCompareToString, NoContextConstant(),Cast(CallRuntime( Runtime::kBigIntCompareToString, NoContextConstant (), SmiConstant(Reverse(op)), right, left)) | |||
| 12281 | SmiConstant(Reverse(op)), right, left))Cast(CallRuntime( Runtime::kBigIntCompareToString, NoContextConstant (), SmiConstant(Reverse(op)), right, left)); | |||
| 12282 | Goto(&end); | |||
| 12283 | } | |||
| 12284 | ||||
| 12285 | BIND(&if_right_receiver)Bind(&if_right_receiver); | |||
| 12286 | { | |||
| 12287 | Callable callable = CodeFactory::NonPrimitiveToPrimitive( | |||
| 12288 | isolate(), ToPrimitiveHint::kNumber); | |||
| 12289 | var_right = CallStub(callable, context(), right); | |||
| 12290 | Goto(&loop); | |||
| 12291 | } | |||
| 12292 | } | |||
| 12293 | } | |||
| 12294 | ||||
| 12295 | BIND(&if_left_other)Bind(&if_left_other); | |||
| 12296 | { | |||
| 12297 | // {left} is neither a Numeric nor a String, and {right} is not a Smi. | |||
| 12298 | if (var_type_feedback != nullptr) { | |||
| 12299 | // Collect NumberOrOddball feedback if {left} is an Oddball | |||
| 12300 | // and {right} is either a HeapNumber or Oddball. Otherwise collect | |||
| 12301 | // Any feedback. | |||
| 12302 | Label collect_any_feedback(this), collect_oddball_feedback(this), | |||
| 12303 | collect_feedback_done(this); | |||
| 12304 | GotoIfNot(InstanceTypeEqual(left_instance_type, ODDBALL_TYPE), | |||
| 12305 | &collect_any_feedback); | |||
| 12306 | ||||
| 12307 | GotoIf(IsHeapNumberMap(right_map), &collect_oddball_feedback); | |||
| 12308 | TNode<Uint16T> right_instance_type = LoadMapInstanceType(right_map); | |||
| 12309 | Branch(InstanceTypeEqual(right_instance_type, ODDBALL_TYPE), | |||
| 12310 | &collect_oddball_feedback, &collect_any_feedback); | |||
| 12311 | ||||
| 12312 | BIND(&collect_oddball_feedback)Bind(&collect_oddball_feedback); | |||
| 12313 | { | |||
| 12314 | CombineFeedback(var_type_feedback, | |||
| 12315 | CompareOperationFeedback::kNumberOrOddball); | |||
| 12316 | Goto(&collect_feedback_done); | |||
| 12317 | } | |||
| 12318 | ||||
| 12319 | BIND(&collect_any_feedback)Bind(&collect_any_feedback); | |||
| 12320 | { | |||
| 12321 | OverwriteFeedback(var_type_feedback, | |||
| 12322 | CompareOperationFeedback::kAny); | |||
| 12323 | Goto(&collect_feedback_done); | |||
| 12324 | } | |||
| 12325 | ||||
| 12326 | BIND(&collect_feedback_done)Bind(&collect_feedback_done); | |||
| 12327 | } | |||
| 12328 | ||||
| 12329 | // If {left} is a receiver, call ToPrimitive(left, hint Number). | |||
| 12330 | // Otherwise call ToNumeric(right) and then ToNumeric(left), the | |||
| 12331 | // order here is important as it's observable by user code. | |||
| 12332 | STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE)static_assert(LAST_JS_RECEIVER_TYPE == LAST_TYPE, "LAST_JS_RECEIVER_TYPE == LAST_TYPE" ); | |||
| 12333 | Label if_left_receiver(this, Label::kDeferred); | |||
| 12334 | GotoIf(IsJSReceiverInstanceType(left_instance_type), | |||
| 12335 | &if_left_receiver); | |||
| 12336 | ||||
| 12337 | var_right = CallBuiltin(Builtin::kToNumeric, context(), right); | |||
| 12338 | var_left = CallBuiltin(Builtin::kNonNumberToNumeric, context(), left); | |||
| 12339 | Goto(&loop); | |||
| 12340 | ||||
| 12341 | BIND(&if_left_receiver)Bind(&if_left_receiver); | |||
| 12342 | { | |||
| 12343 | Callable callable = CodeFactory::NonPrimitiveToPrimitive( | |||
| 12344 | isolate(), ToPrimitiveHint::kNumber); | |||
| 12345 | var_left = CallStub(callable, context(), left); | |||
| 12346 | Goto(&loop); | |||
| 12347 | } | |||
| 12348 | } | |||
| 12349 | } | |||
| 12350 | } | |||
| 12351 | } | |||
| 12352 | ||||
| 12353 | BIND(&do_float_comparison)Bind(&do_float_comparison); | |||
| 12354 | { | |||
| 12355 | switch (op) { | |||
| 12356 | case Operation::kLessThan: | |||
| 12357 | Branch(Float64LessThan(var_left_float.value(), var_right_float.value()), | |||
| 12358 | &return_true, &return_false); | |||
| 12359 | break; | |||
| 12360 | case Operation::kLessThanOrEqual: | |||
| 12361 | Branch(Float64LessThanOrEqual(var_left_float.value(), | |||
| 12362 | var_right_float.value()), | |||
| 12363 | &return_true, &return_false); | |||
| 12364 | break; | |||
| 12365 | case Operation::kGreaterThan: | |||
| 12366 | Branch( | |||
| 12367 | Float64GreaterThan(var_left_float.value(), var_right_float.value()), | |||
| 12368 | &return_true, &return_false); | |||
| 12369 | break; | |||
| 12370 | case Operation::kGreaterThanOrEqual: | |||
| 12371 | Branch(Float64GreaterThanOrEqual(var_left_float.value(), | |||
| 12372 | var_right_float.value()), | |||
| 12373 | &return_true, &return_false); | |||
| 12374 | break; | |||
| 12375 | default: | |||
| 12376 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 12377 | } | |||
| 12378 | } | |||
| 12379 | ||||
| 12380 | BIND(&return_true)Bind(&return_true); | |||
| 12381 | { | |||
| 12382 | var_result = TrueConstant(); | |||
| 12383 | Goto(&end); | |||
| 12384 | } | |||
| 12385 | ||||
| 12386 | BIND(&return_false)Bind(&return_false); | |||
| 12387 | { | |||
| 12388 | var_result = FalseConstant(); | |||
| 12389 | Goto(&end); | |||
| 12390 | } | |||
| 12391 | ||||
| 12392 | BIND(&end)Bind(&end); | |||
| 12393 | return var_result.value(); | |||
| 12394 | } | |||
| 12395 | ||||
| 12396 | TNode<Smi> CodeStubAssembler::CollectFeedbackForString( | |||
| 12397 | TNode<Int32T> instance_type) { | |||
| 12398 | TNode<Smi> feedback = SelectSmiConstant( | |||
| 12399 | Word32Equal( | |||
| 12400 | Word32And(instance_type, Int32Constant(kIsNotInternalizedMask)), | |||
| 12401 | Int32Constant(kInternalizedTag)), | |||
| 12402 | CompareOperationFeedback::kInternalizedString, | |||
| 12403 | CompareOperationFeedback::kString); | |||
| 12404 | return feedback; | |||
| 12405 | } | |||
| 12406 | ||||
| 12407 | void CodeStubAssembler::GenerateEqual_Same(TNode<Object> value, Label* if_equal, | |||
| 12408 | Label* if_notequal, | |||
| 12409 | TVariable<Smi>* var_type_feedback) { | |||
| 12410 | // In case of abstract or strict equality checks, we need additional checks | |||
| 12411 | // for NaN values because they are not considered equal, even if both the | |||
| 12412 | // left and the right hand side reference exactly the same value. | |||
| 12413 | ||||
| 12414 | Label if_smi(this), if_heapnumber(this); | |||
| 12415 | GotoIf(TaggedIsSmi(value), &if_smi); | |||
| 12416 | ||||
| 12417 | TNode<HeapObject> value_heapobject = CAST(value)Cast(value); | |||
| 12418 | TNode<Map> value_map = LoadMap(value_heapobject); | |||
| 12419 | GotoIf(IsHeapNumberMap(value_map), &if_heapnumber); | |||
| 12420 | ||||
| 12421 | // For non-HeapNumbers, all we do is collect type feedback. | |||
| 12422 | if (var_type_feedback != nullptr) { | |||
| 12423 | TNode<Uint16T> instance_type = LoadMapInstanceType(value_map); | |||
| 12424 | ||||
| 12425 | Label if_string(this), if_receiver(this), if_oddball(this), if_symbol(this), | |||
| 12426 | if_bigint(this); | |||
| 12427 | GotoIf(IsStringInstanceType(instance_type), &if_string); | |||
| 12428 | GotoIf(IsJSReceiverInstanceType(instance_type), &if_receiver); | |||
| 12429 | GotoIf(IsOddballInstanceType(instance_type), &if_oddball); | |||
| 12430 | Branch(IsBigIntInstanceType(instance_type), &if_bigint, &if_symbol); | |||
| 12431 | ||||
| 12432 | BIND(&if_string)Bind(&if_string); | |||
| 12433 | { | |||
| 12434 | CSA_DCHECK(this, IsString(value_heapobject))((void)0); | |||
| 12435 | CombineFeedback(var_type_feedback, | |||
| 12436 | CollectFeedbackForString(instance_type)); | |||
| 12437 | Goto(if_equal); | |||
| 12438 | } | |||
| 12439 | ||||
| 12440 | BIND(&if_symbol)Bind(&if_symbol); | |||
| 12441 | { | |||
| 12442 | CSA_DCHECK(this, IsSymbol(value_heapobject))((void)0); | |||
| 12443 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kSymbol); | |||
| 12444 | Goto(if_equal); | |||
| 12445 | } | |||
| 12446 | ||||
| 12447 | BIND(&if_receiver)Bind(&if_receiver); | |||
| 12448 | { | |||
| 12449 | CSA_DCHECK(this, IsJSReceiver(value_heapobject))((void)0); | |||
| 12450 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kReceiver); | |||
| 12451 | Goto(if_equal); | |||
| 12452 | } | |||
| 12453 | ||||
| 12454 | BIND(&if_bigint)Bind(&if_bigint); | |||
| 12455 | { | |||
| 12456 | CSA_DCHECK(this, IsBigInt(value_heapobject))((void)0); | |||
| 12457 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kBigInt); | |||
| 12458 | Goto(if_equal); | |||
| 12459 | } | |||
| 12460 | ||||
| 12461 | BIND(&if_oddball)Bind(&if_oddball); | |||
| 12462 | { | |||
| 12463 | CSA_DCHECK(this, IsOddball(value_heapobject))((void)0); | |||
| 12464 | Label if_boolean(this), if_not_boolean(this); | |||
| 12465 | Branch(IsBooleanMap(value_map), &if_boolean, &if_not_boolean); | |||
| 12466 | ||||
| 12467 | BIND(&if_boolean)Bind(&if_boolean); | |||
| 12468 | { | |||
| 12469 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kBoolean); | |||
| 12470 | Goto(if_equal); | |||
| 12471 | } | |||
| 12472 | ||||
| 12473 | BIND(&if_not_boolean)Bind(&if_not_boolean); | |||
| 12474 | { | |||
| 12475 | CSA_DCHECK(this, IsNullOrUndefined(value_heapobject))((void)0); | |||
| 12476 | CombineFeedback(var_type_feedback, | |||
| 12477 | CompareOperationFeedback::kReceiverOrNullOrUndefined); | |||
| 12478 | Goto(if_equal); | |||
| 12479 | } | |||
| 12480 | } | |||
| 12481 | } else { | |||
| 12482 | Goto(if_equal); | |||
| 12483 | } | |||
| 12484 | ||||
| 12485 | BIND(&if_heapnumber)Bind(&if_heapnumber); | |||
| 12486 | { | |||
| 12487 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 12488 | TNode<Float64T> number_value = LoadHeapNumberValue(value_heapobject); | |||
| 12489 | BranchIfFloat64IsNaN(number_value, if_notequal, if_equal); | |||
| 12490 | } | |||
| 12491 | ||||
| 12492 | BIND(&if_smi)Bind(&if_smi); | |||
| 12493 | { | |||
| 12494 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kSignedSmall); | |||
| 12495 | Goto(if_equal); | |||
| 12496 | } | |||
| 12497 | } | |||
| 12498 | ||||
| 12499 | // ES6 section 7.2.12 Abstract Equality Comparison | |||
| 12500 | TNode<Oddball> CodeStubAssembler::Equal(TNode<Object> left, TNode<Object> right, | |||
| 12501 | const LazyNode<Context>& context, | |||
| 12502 | TVariable<Smi>* var_type_feedback) { | |||
| 12503 | // This is a slightly optimized version of Object::Equals. Whenever you | |||
| 12504 | // change something functionality wise in here, remember to update the | |||
| 12505 | // Object::Equals method as well. | |||
| 12506 | ||||
| 12507 | Label if_equal(this), if_notequal(this), do_float_comparison(this), | |||
| 12508 | do_right_stringtonumber(this, Label::kDeferred), end(this); | |||
| 12509 | TVARIABLE(Oddball, result)TVariable<Oddball> result(this); | |||
| 12510 | TVARIABLE(Float64T, var_left_float)TVariable<Float64T> var_left_float(this); | |||
| 12511 | TVARIABLE(Float64T, var_right_float)TVariable<Float64T> var_right_float(this); | |||
| 12512 | ||||
| 12513 | // We can avoid code duplication by exploiting the fact that abstract equality | |||
| 12514 | // is symmetric. | |||
| 12515 | Label use_symmetry(this); | |||
| 12516 | ||||
| 12517 | // We might need to loop several times due to ToPrimitive and/or ToNumber | |||
| 12518 | // conversions. | |||
| 12519 | TVARIABLE(Object, var_left, left)TVariable<Object> var_left(left, this); | |||
| 12520 | TVARIABLE(Object, var_right, right)TVariable<Object> var_right(right, this); | |||
| 12521 | VariableList loop_variable_list({&var_left, &var_right}, zone()); | |||
| 12522 | if (var_type_feedback != nullptr) { | |||
| 12523 | // Initialize the type feedback to None. The current feedback will be | |||
| 12524 | // combined with the previous feedback. | |||
| 12525 | OverwriteFeedback(var_type_feedback, CompareOperationFeedback::kNone); | |||
| 12526 | loop_variable_list.push_back(var_type_feedback); | |||
| 12527 | } | |||
| 12528 | Label loop(this, loop_variable_list); | |||
| 12529 | Goto(&loop); | |||
| 12530 | BIND(&loop)Bind(&loop); | |||
| 12531 | { | |||
| 12532 | left = var_left.value(); | |||
| 12533 | right = var_right.value(); | |||
| 12534 | ||||
| 12535 | Label if_notsame(this); | |||
| 12536 | GotoIf(TaggedNotEqual(left, right), &if_notsame); | |||
| 12537 | { | |||
| 12538 | // {left} and {right} reference the exact same value, yet we need special | |||
| 12539 | // treatment for HeapNumber, as NaN is not equal to NaN. | |||
| 12540 | GenerateEqual_Same(left, &if_equal, &if_notequal, var_type_feedback); | |||
| 12541 | } | |||
| 12542 | ||||
| 12543 | BIND(&if_notsame)Bind(&if_notsame); | |||
| 12544 | Label if_left_smi(this), if_left_not_smi(this); | |||
| 12545 | Branch(TaggedIsSmi(left), &if_left_smi, &if_left_not_smi); | |||
| 12546 | ||||
| 12547 | BIND(&if_left_smi)Bind(&if_left_smi); | |||
| 12548 | { | |||
| 12549 | Label if_right_smi(this), if_right_not_smi(this); | |||
| 12550 | CombineFeedback(var_type_feedback, | |||
| 12551 | CompareOperationFeedback::kSignedSmall); | |||
| 12552 | Branch(TaggedIsSmi(right), &if_right_smi, &if_right_not_smi); | |||
| 12553 | ||||
| 12554 | BIND(&if_right_smi)Bind(&if_right_smi); | |||
| 12555 | { | |||
| 12556 | // We have already checked for {left} and {right} being the same value, | |||
| 12557 | // so when we get here they must be different Smis. | |||
| 12558 | Goto(&if_notequal); | |||
| 12559 | } | |||
| 12560 | ||||
| 12561 | BIND(&if_right_not_smi)Bind(&if_right_not_smi); | |||
| 12562 | { | |||
| 12563 | TNode<Map> right_map = LoadMap(CAST(right)Cast(right)); | |||
| 12564 | Label if_right_heapnumber(this), if_right_oddball(this), | |||
| 12565 | if_right_bigint(this, Label::kDeferred), | |||
| 12566 | if_right_receiver(this, Label::kDeferred); | |||
| 12567 | GotoIf(IsHeapNumberMap(right_map), &if_right_heapnumber); | |||
| 12568 | ||||
| 12569 | // {left} is Smi and {right} is not HeapNumber or Smi. | |||
| 12570 | TNode<Uint16T> right_type = LoadMapInstanceType(right_map); | |||
| 12571 | GotoIf(IsStringInstanceType(right_type), &do_right_stringtonumber); | |||
| 12572 | GotoIf(IsOddballInstanceType(right_type), &if_right_oddball); | |||
| 12573 | GotoIf(IsBigIntInstanceType(right_type), &if_right_bigint); | |||
| 12574 | GotoIf(IsJSReceiverInstanceType(right_type), &if_right_receiver); | |||
| 12575 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 12576 | Goto(&if_notequal); | |||
| 12577 | ||||
| 12578 | BIND(&if_right_heapnumber)Bind(&if_right_heapnumber); | |||
| 12579 | { | |||
| 12580 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 12581 | var_left_float = SmiToFloat64(CAST(left)Cast(left)); | |||
| 12582 | var_right_float = LoadHeapNumberValue(CAST(right)Cast(right)); | |||
| 12583 | Goto(&do_float_comparison); | |||
| 12584 | } | |||
| 12585 | ||||
| 12586 | BIND(&if_right_oddball)Bind(&if_right_oddball); | |||
| 12587 | { | |||
| 12588 | Label if_right_boolean(this); | |||
| 12589 | GotoIf(IsBooleanMap(right_map), &if_right_boolean); | |||
| 12590 | CombineFeedback(var_type_feedback, | |||
| 12591 | CompareOperationFeedback::kOddball); | |||
| 12592 | Goto(&if_notequal); | |||
| 12593 | ||||
| 12594 | BIND(&if_right_boolean)Bind(&if_right_boolean); | |||
| 12595 | { | |||
| 12596 | CombineFeedback(var_type_feedback, | |||
| 12597 | CompareOperationFeedback::kBoolean); | |||
| 12598 | var_right = LoadObjectField(CAST(right)Cast(right), Oddball::kToNumberOffset); | |||
| 12599 | Goto(&loop); | |||
| 12600 | } | |||
| 12601 | } | |||
| 12602 | ||||
| 12603 | BIND(&if_right_bigint)Bind(&if_right_bigint); | |||
| 12604 | { | |||
| 12605 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kBigInt); | |||
| 12606 | result = CAST(CallRuntime(Runtime::kBigIntEqualToNumber,Cast(CallRuntime(Runtime::kBigIntEqualToNumber, NoContextConstant (), right, left)) | |||
| 12607 | NoContextConstant(), right, left))Cast(CallRuntime(Runtime::kBigIntEqualToNumber, NoContextConstant (), right, left)); | |||
| 12608 | Goto(&end); | |||
| 12609 | } | |||
| 12610 | ||||
| 12611 | BIND(&if_right_receiver)Bind(&if_right_receiver); | |||
| 12612 | { | |||
| 12613 | CombineFeedback(var_type_feedback, | |||
| 12614 | CompareOperationFeedback::kReceiver); | |||
| 12615 | Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate()); | |||
| 12616 | var_right = CallStub(callable, context(), right); | |||
| 12617 | Goto(&loop); | |||
| 12618 | } | |||
| 12619 | } | |||
| 12620 | } | |||
| 12621 | ||||
| 12622 | BIND(&if_left_not_smi)Bind(&if_left_not_smi); | |||
| 12623 | { | |||
| 12624 | GotoIf(TaggedIsSmi(right), &use_symmetry); | |||
| 12625 | ||||
| 12626 | Label if_left_symbol(this), if_left_number(this), | |||
| 12627 | if_left_string(this, Label::kDeferred), | |||
| 12628 | if_left_bigint(this, Label::kDeferred), if_left_oddball(this), | |||
| 12629 | if_left_receiver(this); | |||
| 12630 | ||||
| 12631 | TNode<Map> left_map = LoadMap(CAST(left)Cast(left)); | |||
| 12632 | TNode<Map> right_map = LoadMap(CAST(right)Cast(right)); | |||
| 12633 | TNode<Uint16T> left_type = LoadMapInstanceType(left_map); | |||
| 12634 | TNode<Uint16T> right_type = LoadMapInstanceType(right_map); | |||
| 12635 | ||||
| 12636 | GotoIf(IsStringInstanceType(left_type), &if_left_string); | |||
| 12637 | GotoIf(IsSymbolInstanceType(left_type), &if_left_symbol); | |||
| 12638 | GotoIf(IsHeapNumberInstanceType(left_type), &if_left_number); | |||
| 12639 | GotoIf(IsOddballInstanceType(left_type), &if_left_oddball); | |||
| 12640 | Branch(IsBigIntInstanceType(left_type), &if_left_bigint, | |||
| 12641 | &if_left_receiver); | |||
| 12642 | ||||
| 12643 | BIND(&if_left_string)Bind(&if_left_string); | |||
| 12644 | { | |||
| 12645 | GotoIfNot(IsStringInstanceType(right_type), &use_symmetry); | |||
| 12646 | result = | |||
| 12647 | CAST(CallBuiltin(Builtin::kStringEqual, context(), left, right))Cast(CallBuiltin(Builtin::kStringEqual, context(), left, right )); | |||
| 12648 | CombineFeedback(var_type_feedback, | |||
| 12649 | SmiOr(CollectFeedbackForString(left_type), | |||
| 12650 | CollectFeedbackForString(right_type))); | |||
| 12651 | Goto(&end); | |||
| 12652 | } | |||
| 12653 | ||||
| 12654 | BIND(&if_left_number)Bind(&if_left_number); | |||
| 12655 | { | |||
| 12656 | Label if_right_not_number(this); | |||
| 12657 | ||||
| 12658 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 12659 | GotoIf(Word32NotEqual(left_type, right_type), &if_right_not_number); | |||
| 12660 | ||||
| 12661 | var_left_float = LoadHeapNumberValue(CAST(left)Cast(left)); | |||
| 12662 | var_right_float = LoadHeapNumberValue(CAST(right)Cast(right)); | |||
| 12663 | Goto(&do_float_comparison); | |||
| 12664 | ||||
| 12665 | BIND(&if_right_not_number)Bind(&if_right_not_number); | |||
| 12666 | { | |||
| 12667 | Label if_right_oddball(this); | |||
| 12668 | ||||
| 12669 | GotoIf(IsStringInstanceType(right_type), &do_right_stringtonumber); | |||
| 12670 | GotoIf(IsOddballInstanceType(right_type), &if_right_oddball); | |||
| 12671 | GotoIf(IsBigIntInstanceType(right_type), &use_symmetry); | |||
| 12672 | GotoIf(IsJSReceiverInstanceType(right_type), &use_symmetry); | |||
| 12673 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 12674 | Goto(&if_notequal); | |||
| 12675 | ||||
| 12676 | BIND(&if_right_oddball)Bind(&if_right_oddball); | |||
| 12677 | { | |||
| 12678 | Label if_right_boolean(this); | |||
| 12679 | GotoIf(IsBooleanMap(right_map), &if_right_boolean); | |||
| 12680 | CombineFeedback(var_type_feedback, | |||
| 12681 | CompareOperationFeedback::kOddball); | |||
| 12682 | Goto(&if_notequal); | |||
| 12683 | ||||
| 12684 | BIND(&if_right_boolean)Bind(&if_right_boolean); | |||
| 12685 | { | |||
| 12686 | CombineFeedback(var_type_feedback, | |||
| 12687 | CompareOperationFeedback::kBoolean); | |||
| 12688 | var_right = | |||
| 12689 | LoadObjectField(CAST(right)Cast(right), Oddball::kToNumberOffset); | |||
| 12690 | Goto(&loop); | |||
| 12691 | } | |||
| 12692 | } | |||
| 12693 | } | |||
| 12694 | } | |||
| 12695 | ||||
| 12696 | BIND(&if_left_bigint)Bind(&if_left_bigint); | |||
| 12697 | { | |||
| 12698 | Label if_right_heapnumber(this), if_right_bigint(this), | |||
| 12699 | if_right_string(this), if_right_boolean(this); | |||
| 12700 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kBigInt); | |||
| 12701 | ||||
| 12702 | GotoIf(IsHeapNumberMap(right_map), &if_right_heapnumber); | |||
| 12703 | GotoIf(IsBigIntInstanceType(right_type), &if_right_bigint); | |||
| 12704 | GotoIf(IsStringInstanceType(right_type), &if_right_string); | |||
| 12705 | GotoIf(IsBooleanMap(right_map), &if_right_boolean); | |||
| 12706 | Branch(IsJSReceiverInstanceType(right_type), &use_symmetry, | |||
| 12707 | &if_notequal); | |||
| 12708 | ||||
| 12709 | BIND(&if_right_heapnumber)Bind(&if_right_heapnumber); | |||
| 12710 | { | |||
| 12711 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 12712 | result = CAST(CallRuntime(Runtime::kBigIntEqualToNumber,Cast(CallRuntime(Runtime::kBigIntEqualToNumber, NoContextConstant (), left, right)) | |||
| 12713 | NoContextConstant(), left, right))Cast(CallRuntime(Runtime::kBigIntEqualToNumber, NoContextConstant (), left, right)); | |||
| 12714 | Goto(&end); | |||
| 12715 | } | |||
| 12716 | ||||
| 12717 | BIND(&if_right_bigint)Bind(&if_right_bigint); | |||
| 12718 | { | |||
| 12719 | // We already have BigInt feedback. | |||
| 12720 | result = CAST(CallRuntime(Runtime::kBigIntEqualToBigInt,Cast(CallRuntime(Runtime::kBigIntEqualToBigInt, NoContextConstant (), left, right)) | |||
| 12721 | NoContextConstant(), left, right))Cast(CallRuntime(Runtime::kBigIntEqualToBigInt, NoContextConstant (), left, right)); | |||
| 12722 | Goto(&end); | |||
| 12723 | } | |||
| 12724 | ||||
| 12725 | BIND(&if_right_string)Bind(&if_right_string); | |||
| 12726 | { | |||
| 12727 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kString); | |||
| 12728 | result = CAST(CallRuntime(Runtime::kBigIntEqualToString,Cast(CallRuntime(Runtime::kBigIntEqualToString, NoContextConstant (), left, right)) | |||
| 12729 | NoContextConstant(), left, right))Cast(CallRuntime(Runtime::kBigIntEqualToString, NoContextConstant (), left, right)); | |||
| 12730 | Goto(&end); | |||
| 12731 | } | |||
| 12732 | ||||
| 12733 | BIND(&if_right_boolean)Bind(&if_right_boolean); | |||
| 12734 | { | |||
| 12735 | CombineFeedback(var_type_feedback, | |||
| 12736 | CompareOperationFeedback::kBoolean); | |||
| 12737 | var_right = LoadObjectField(CAST(right)Cast(right), Oddball::kToNumberOffset); | |||
| 12738 | Goto(&loop); | |||
| 12739 | } | |||
| 12740 | } | |||
| 12741 | ||||
| 12742 | BIND(&if_left_oddball)Bind(&if_left_oddball); | |||
| 12743 | { | |||
| 12744 | Label if_left_boolean(this), if_left_not_boolean(this); | |||
| 12745 | GotoIf(IsBooleanMap(left_map), &if_left_boolean); | |||
| 12746 | if (var_type_feedback != nullptr) { | |||
| 12747 | CombineFeedback(var_type_feedback, | |||
| 12748 | CompareOperationFeedback::kNullOrUndefined); | |||
| 12749 | GotoIf(IsUndetectableMap(left_map), &if_left_not_boolean); | |||
| 12750 | } | |||
| 12751 | Goto(&if_left_not_boolean); | |||
| 12752 | ||||
| 12753 | BIND(&if_left_not_boolean)Bind(&if_left_not_boolean); | |||
| 12754 | { | |||
| 12755 | // {left} is either Null or Undefined. Check if {right} is | |||
| 12756 | // undetectable (which includes Null and Undefined). | |||
| 12757 | Label if_right_undetectable(this), if_right_number(this), | |||
| 12758 | if_right_oddball(this), | |||
| 12759 | if_right_not_number_or_oddball_or_undetectable(this); | |||
| 12760 | GotoIf(IsUndetectableMap(right_map), &if_right_undetectable); | |||
| 12761 | GotoIf(IsHeapNumberInstanceType(right_type), &if_right_number); | |||
| 12762 | GotoIf(IsOddballInstanceType(right_type), &if_right_oddball); | |||
| 12763 | Goto(&if_right_not_number_or_oddball_or_undetectable); | |||
| 12764 | ||||
| 12765 | BIND(&if_right_undetectable)Bind(&if_right_undetectable); | |||
| 12766 | { | |||
| 12767 | // If {right} is undetectable, it must be either also | |||
| 12768 | // Null or Undefined, or a Receiver (aka document.all). | |||
| 12769 | CombineFeedback( | |||
| 12770 | var_type_feedback, | |||
| 12771 | CompareOperationFeedback::kReceiverOrNullOrUndefined); | |||
| 12772 | Goto(&if_equal); | |||
| 12773 | } | |||
| 12774 | ||||
| 12775 | BIND(&if_right_number)Bind(&if_right_number); | |||
| 12776 | { | |||
| 12777 | CombineFeedback(var_type_feedback, | |||
| 12778 | CompareOperationFeedback::kNumber); | |||
| 12779 | Goto(&if_notequal); | |||
| 12780 | } | |||
| 12781 | ||||
| 12782 | BIND(&if_right_oddball)Bind(&if_right_oddball); | |||
| 12783 | { | |||
| 12784 | CombineFeedback(var_type_feedback, | |||
| 12785 | CompareOperationFeedback::kOddball); | |||
| 12786 | Goto(&if_notequal); | |||
| 12787 | } | |||
| 12788 | ||||
| 12789 | BIND(&if_right_not_number_or_oddball_or_undetectable)Bind(&if_right_not_number_or_oddball_or_undetectable); | |||
| 12790 | { | |||
| 12791 | if (var_type_feedback != nullptr) { | |||
| 12792 | // Track whether {right} is Null, Undefined or Receiver. | |||
| 12793 | CombineFeedback( | |||
| 12794 | var_type_feedback, | |||
| 12795 | CompareOperationFeedback::kReceiverOrNullOrUndefined); | |||
| 12796 | GotoIf(IsJSReceiverInstanceType(right_type), &if_notequal); | |||
| 12797 | CombineFeedback(var_type_feedback, | |||
| 12798 | CompareOperationFeedback::kAny); | |||
| 12799 | } | |||
| 12800 | Goto(&if_notequal); | |||
| 12801 | } | |||
| 12802 | } | |||
| 12803 | ||||
| 12804 | BIND(&if_left_boolean)Bind(&if_left_boolean); | |||
| 12805 | { | |||
| 12806 | CombineFeedback(var_type_feedback, | |||
| 12807 | CompareOperationFeedback::kBoolean); | |||
| 12808 | ||||
| 12809 | // If {right} is a Boolean too, it must be a different Boolean. | |||
| 12810 | GotoIf(TaggedEqual(right_map, left_map), &if_notequal); | |||
| 12811 | ||||
| 12812 | // Otherwise, convert {left} to number and try again. | |||
| 12813 | var_left = LoadObjectField(CAST(left)Cast(left), Oddball::kToNumberOffset); | |||
| 12814 | Goto(&loop); | |||
| 12815 | } | |||
| 12816 | } | |||
| 12817 | ||||
| 12818 | BIND(&if_left_symbol)Bind(&if_left_symbol); | |||
| 12819 | { | |||
| 12820 | Label if_right_receiver(this); | |||
| 12821 | GotoIf(IsJSReceiverInstanceType(right_type), &if_right_receiver); | |||
| 12822 | // {right} is not a JSReceiver and also not the same Symbol as {left}, | |||
| 12823 | // so the result is "not equal". | |||
| 12824 | if (var_type_feedback != nullptr) { | |||
| 12825 | Label if_right_symbol(this); | |||
| 12826 | GotoIf(IsSymbolInstanceType(right_type), &if_right_symbol); | |||
| 12827 | *var_type_feedback = SmiConstant(CompareOperationFeedback::kAny); | |||
| 12828 | Goto(&if_notequal); | |||
| 12829 | ||||
| 12830 | BIND(&if_right_symbol)Bind(&if_right_symbol); | |||
| 12831 | { | |||
| 12832 | CombineFeedback(var_type_feedback, | |||
| 12833 | CompareOperationFeedback::kSymbol); | |||
| 12834 | Goto(&if_notequal); | |||
| 12835 | } | |||
| 12836 | } else { | |||
| 12837 | Goto(&if_notequal); | |||
| 12838 | } | |||
| 12839 | ||||
| 12840 | BIND(&if_right_receiver)Bind(&if_right_receiver); | |||
| 12841 | { | |||
| 12842 | // {left} is a Primitive and {right} is a JSReceiver, so swapping | |||
| 12843 | // the order is not observable. | |||
| 12844 | if (var_type_feedback != nullptr) { | |||
| 12845 | *var_type_feedback = SmiConstant(CompareOperationFeedback::kAny); | |||
| 12846 | } | |||
| 12847 | Goto(&use_symmetry); | |||
| 12848 | } | |||
| 12849 | } | |||
| 12850 | ||||
| 12851 | BIND(&if_left_receiver)Bind(&if_left_receiver); | |||
| 12852 | { | |||
| 12853 | CSA_DCHECK(this, IsJSReceiverInstanceType(left_type))((void)0); | |||
| 12854 | Label if_right_receiver(this), if_right_not_receiver(this); | |||
| 12855 | Branch(IsJSReceiverInstanceType(right_type), &if_right_receiver, | |||
| 12856 | &if_right_not_receiver); | |||
| 12857 | ||||
| 12858 | BIND(&if_right_receiver)Bind(&if_right_receiver); | |||
| 12859 | { | |||
| 12860 | // {left} and {right} are different JSReceiver references. | |||
| 12861 | CombineFeedback(var_type_feedback, | |||
| 12862 | CompareOperationFeedback::kReceiver); | |||
| 12863 | Goto(&if_notequal); | |||
| 12864 | } | |||
| 12865 | ||||
| 12866 | BIND(&if_right_not_receiver)Bind(&if_right_not_receiver); | |||
| 12867 | { | |||
| 12868 | // Check if {right} is undetectable, which means it must be Null | |||
| 12869 | // or Undefined, since we already ruled out Receiver for {right}. | |||
| 12870 | Label if_right_undetectable(this), | |||
| 12871 | if_right_not_undetectable(this, Label::kDeferred); | |||
| 12872 | Branch(IsUndetectableMap(right_map), &if_right_undetectable, | |||
| 12873 | &if_right_not_undetectable); | |||
| 12874 | ||||
| 12875 | BIND(&if_right_undetectable)Bind(&if_right_undetectable); | |||
| 12876 | { | |||
| 12877 | // When we get here, {right} must be either Null or Undefined. | |||
| 12878 | CSA_DCHECK(this, IsNullOrUndefined(right))((void)0); | |||
| 12879 | if (var_type_feedback != nullptr) { | |||
| 12880 | *var_type_feedback = SmiConstant( | |||
| 12881 | CompareOperationFeedback::kReceiverOrNullOrUndefined); | |||
| 12882 | } | |||
| 12883 | Branch(IsUndetectableMap(left_map), &if_equal, &if_notequal); | |||
| 12884 | } | |||
| 12885 | ||||
| 12886 | BIND(&if_right_not_undetectable)Bind(&if_right_not_undetectable); | |||
| 12887 | { | |||
| 12888 | // {right} is a Primitive, and neither Null or Undefined; | |||
| 12889 | // convert {left} to Primitive too. | |||
| 12890 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 12891 | Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate()); | |||
| 12892 | var_left = CallStub(callable, context(), left); | |||
| 12893 | Goto(&loop); | |||
| 12894 | } | |||
| 12895 | } | |||
| 12896 | } | |||
| 12897 | } | |||
| 12898 | ||||
| 12899 | BIND(&do_right_stringtonumber)Bind(&do_right_stringtonumber); | |||
| 12900 | { | |||
| 12901 | if (var_type_feedback != nullptr) { | |||
| 12902 | TNode<Map> right_map = LoadMap(CAST(right)Cast(right)); | |||
| 12903 | TNode<Uint16T> right_type = LoadMapInstanceType(right_map); | |||
| 12904 | CombineFeedback(var_type_feedback, | |||
| 12905 | CollectFeedbackForString(right_type)); | |||
| 12906 | } | |||
| 12907 | var_right = CallBuiltin(Builtin::kStringToNumber, context(), right); | |||
| 12908 | Goto(&loop); | |||
| 12909 | } | |||
| 12910 | ||||
| 12911 | BIND(&use_symmetry)Bind(&use_symmetry); | |||
| 12912 | { | |||
| 12913 | var_left = right; | |||
| 12914 | var_right = left; | |||
| 12915 | Goto(&loop); | |||
| 12916 | } | |||
| 12917 | } | |||
| 12918 | ||||
| 12919 | BIND(&do_float_comparison)Bind(&do_float_comparison); | |||
| 12920 | { | |||
| 12921 | Branch(Float64Equal(var_left_float.value(), var_right_float.value()), | |||
| 12922 | &if_equal, &if_notequal); | |||
| 12923 | } | |||
| 12924 | ||||
| 12925 | BIND(&if_equal)Bind(&if_equal); | |||
| 12926 | { | |||
| 12927 | result = TrueConstant(); | |||
| 12928 | Goto(&end); | |||
| 12929 | } | |||
| 12930 | ||||
| 12931 | BIND(&if_notequal)Bind(&if_notequal); | |||
| 12932 | { | |||
| 12933 | result = FalseConstant(); | |||
| 12934 | Goto(&end); | |||
| 12935 | } | |||
| 12936 | ||||
| 12937 | BIND(&end)Bind(&end); | |||
| 12938 | return result.value(); | |||
| 12939 | } | |||
| 12940 | ||||
| 12941 | TNode<Oddball> CodeStubAssembler::StrictEqual( | |||
| 12942 | TNode<Object> lhs, TNode<Object> rhs, TVariable<Smi>* var_type_feedback) { | |||
| 12943 | // Pseudo-code for the algorithm below: | |||
| 12944 | // | |||
| 12945 | // if (lhs == rhs) { | |||
| 12946 | // if (lhs->IsHeapNumber()) return HeapNumber::cast(lhs)->value() != NaN; | |||
| 12947 | // return true; | |||
| 12948 | // } | |||
| 12949 | // if (!lhs->IsSmi()) { | |||
| 12950 | // if (lhs->IsHeapNumber()) { | |||
| 12951 | // if (rhs->IsSmi()) { | |||
| 12952 | // return Smi::ToInt(rhs) == HeapNumber::cast(lhs)->value(); | |||
| 12953 | // } else if (rhs->IsHeapNumber()) { | |||
| 12954 | // return HeapNumber::cast(rhs)->value() == | |||
| 12955 | // HeapNumber::cast(lhs)->value(); | |||
| 12956 | // } else { | |||
| 12957 | // return false; | |||
| 12958 | // } | |||
| 12959 | // } else { | |||
| 12960 | // if (rhs->IsSmi()) { | |||
| 12961 | // return false; | |||
| 12962 | // } else { | |||
| 12963 | // if (lhs->IsString()) { | |||
| 12964 | // if (rhs->IsString()) { | |||
| 12965 | // return %StringEqual(lhs, rhs); | |||
| 12966 | // } else { | |||
| 12967 | // return false; | |||
| 12968 | // } | |||
| 12969 | // } else if (lhs->IsBigInt()) { | |||
| 12970 | // if (rhs->IsBigInt()) { | |||
| 12971 | // return %BigIntEqualToBigInt(lhs, rhs); | |||
| 12972 | // } else { | |||
| 12973 | // return false; | |||
| 12974 | // } | |||
| 12975 | // } else { | |||
| 12976 | // return false; | |||
| 12977 | // } | |||
| 12978 | // } | |||
| 12979 | // } | |||
| 12980 | // } else { | |||
| 12981 | // if (rhs->IsSmi()) { | |||
| 12982 | // return false; | |||
| 12983 | // } else { | |||
| 12984 | // if (rhs->IsHeapNumber()) { | |||
| 12985 | // return Smi::ToInt(lhs) == HeapNumber::cast(rhs)->value(); | |||
| 12986 | // } else { | |||
| 12987 | // return false; | |||
| 12988 | // } | |||
| 12989 | // } | |||
| 12990 | // } | |||
| 12991 | ||||
| 12992 | Label if_equal(this), if_notequal(this), if_not_equivalent_types(this), | |||
| 12993 | end(this); | |||
| 12994 | TVARIABLE(Oddball, result)TVariable<Oddball> result(this); | |||
| 12995 | ||||
| 12996 | OverwriteFeedback(var_type_feedback, CompareOperationFeedback::kNone); | |||
| 12997 | ||||
| 12998 | // Check if {lhs} and {rhs} refer to the same object. | |||
| 12999 | Label if_same(this), if_notsame(this); | |||
| 13000 | Branch(TaggedEqual(lhs, rhs), &if_same, &if_notsame); | |||
| 13001 | ||||
| 13002 | BIND(&if_same)Bind(&if_same); | |||
| 13003 | { | |||
| 13004 | // The {lhs} and {rhs} reference the exact same value, yet we need special | |||
| 13005 | // treatment for HeapNumber, as NaN is not equal to NaN. | |||
| 13006 | GenerateEqual_Same(lhs, &if_equal, &if_notequal, var_type_feedback); | |||
| 13007 | } | |||
| 13008 | ||||
| 13009 | BIND(&if_notsame)Bind(&if_notsame); | |||
| 13010 | { | |||
| 13011 | // The {lhs} and {rhs} reference different objects, yet for Smi, HeapNumber, | |||
| 13012 | // BigInt and String they can still be considered equal. | |||
| 13013 | ||||
| 13014 | // Check if {lhs} is a Smi or a HeapObject. | |||
| 13015 | Label if_lhsissmi(this), if_lhsisnotsmi(this); | |||
| 13016 | Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisnotsmi); | |||
| 13017 | ||||
| 13018 | BIND(&if_lhsisnotsmi)Bind(&if_lhsisnotsmi); | |||
| 13019 | { | |||
| 13020 | // Load the map of {lhs}. | |||
| 13021 | TNode<Map> lhs_map = LoadMap(CAST(lhs)Cast(lhs)); | |||
| 13022 | ||||
| 13023 | // Check if {lhs} is a HeapNumber. | |||
| 13024 | Label if_lhsisnumber(this), if_lhsisnotnumber(this); | |||
| 13025 | Branch(IsHeapNumberMap(lhs_map), &if_lhsisnumber, &if_lhsisnotnumber); | |||
| 13026 | ||||
| 13027 | BIND(&if_lhsisnumber)Bind(&if_lhsisnumber); | |||
| 13028 | { | |||
| 13029 | // Check if {rhs} is a Smi or a HeapObject. | |||
| 13030 | Label if_rhsissmi(this), if_rhsisnotsmi(this); | |||
| 13031 | Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); | |||
| 13032 | ||||
| 13033 | BIND(&if_rhsissmi)Bind(&if_rhsissmi); | |||
| 13034 | { | |||
| 13035 | // Convert {lhs} and {rhs} to floating point values. | |||
| 13036 | TNode<Float64T> lhs_value = LoadHeapNumberValue(CAST(lhs)Cast(lhs)); | |||
| 13037 | TNode<Float64T> rhs_value = SmiToFloat64(CAST(rhs)Cast(rhs)); | |||
| 13038 | ||||
| 13039 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 13040 | ||||
| 13041 | // Perform a floating point comparison of {lhs} and {rhs}. | |||
| 13042 | Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); | |||
| 13043 | } | |||
| 13044 | ||||
| 13045 | BIND(&if_rhsisnotsmi)Bind(&if_rhsisnotsmi); | |||
| 13046 | { | |||
| 13047 | TNode<HeapObject> rhs_ho = CAST(rhs)Cast(rhs); | |||
| 13048 | // Load the map of {rhs}. | |||
| 13049 | TNode<Map> rhs_map = LoadMap(rhs_ho); | |||
| 13050 | ||||
| 13051 | // Check if {rhs} is also a HeapNumber. | |||
| 13052 | Label if_rhsisnumber(this), if_rhsisnotnumber(this); | |||
| 13053 | Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); | |||
| 13054 | ||||
| 13055 | BIND(&if_rhsisnumber)Bind(&if_rhsisnumber); | |||
| 13056 | { | |||
| 13057 | // Convert {lhs} and {rhs} to floating point values. | |||
| 13058 | TNode<Float64T> lhs_value = LoadHeapNumberValue(CAST(lhs)Cast(lhs)); | |||
| 13059 | TNode<Float64T> rhs_value = LoadHeapNumberValue(CAST(rhs)Cast(rhs)); | |||
| 13060 | ||||
| 13061 | CombineFeedback(var_type_feedback, | |||
| 13062 | CompareOperationFeedback::kNumber); | |||
| 13063 | ||||
| 13064 | // Perform a floating point comparison of {lhs} and {rhs}. | |||
| 13065 | Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); | |||
| 13066 | } | |||
| 13067 | ||||
| 13068 | BIND(&if_rhsisnotnumber)Bind(&if_rhsisnotnumber); | |||
| 13069 | Goto(&if_not_equivalent_types); | |||
| 13070 | } | |||
| 13071 | } | |||
| 13072 | ||||
| 13073 | BIND(&if_lhsisnotnumber)Bind(&if_lhsisnotnumber); | |||
| 13074 | { | |||
| 13075 | // Check if {rhs} is a Smi or a HeapObject. | |||
| 13076 | Label if_rhsissmi(this), if_rhsisnotsmi(this); | |||
| 13077 | Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); | |||
| 13078 | ||||
| 13079 | BIND(&if_rhsissmi)Bind(&if_rhsissmi); | |||
| 13080 | Goto(&if_not_equivalent_types); | |||
| 13081 | ||||
| 13082 | BIND(&if_rhsisnotsmi)Bind(&if_rhsisnotsmi); | |||
| 13083 | { | |||
| 13084 | // Load the instance type of {lhs}. | |||
| 13085 | TNode<Uint16T> lhs_instance_type = LoadMapInstanceType(lhs_map); | |||
| 13086 | ||||
| 13087 | // Check if {lhs} is a String. | |||
| 13088 | Label if_lhsisstring(this, Label::kDeferred), if_lhsisnotstring(this); | |||
| 13089 | Branch(IsStringInstanceType(lhs_instance_type), &if_lhsisstring, | |||
| 13090 | &if_lhsisnotstring); | |||
| 13091 | ||||
| 13092 | BIND(&if_lhsisstring)Bind(&if_lhsisstring); | |||
| 13093 | { | |||
| 13094 | // Load the instance type of {rhs}. | |||
| 13095 | TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs)Cast(rhs)); | |||
| 13096 | ||||
| 13097 | // Check if {rhs} is also a String. | |||
| 13098 | Label if_rhsisstring(this, Label::kDeferred), | |||
| 13099 | if_rhsisnotstring(this); | |||
| 13100 | Branch(IsStringInstanceType(rhs_instance_type), &if_rhsisstring, | |||
| 13101 | &if_rhsisnotstring); | |||
| 13102 | ||||
| 13103 | BIND(&if_rhsisstring)Bind(&if_rhsisstring); | |||
| 13104 | { | |||
| 13105 | if (var_type_feedback != nullptr) { | |||
| 13106 | TNode<Smi> lhs_feedback = | |||
| 13107 | CollectFeedbackForString(lhs_instance_type); | |||
| 13108 | TNode<Smi> rhs_feedback = | |||
| 13109 | CollectFeedbackForString(rhs_instance_type); | |||
| 13110 | *var_type_feedback = SmiOr(lhs_feedback, rhs_feedback); | |||
| 13111 | } | |||
| 13112 | result = CAST(CallBuiltin(Builtin::kStringEqual,Cast(CallBuiltin(Builtin::kStringEqual, NoContextConstant(), lhs , rhs)) | |||
| 13113 | NoContextConstant(), lhs, rhs))Cast(CallBuiltin(Builtin::kStringEqual, NoContextConstant(), lhs , rhs)); | |||
| 13114 | Goto(&end); | |||
| 13115 | } | |||
| 13116 | ||||
| 13117 | BIND(&if_rhsisnotstring)Bind(&if_rhsisnotstring); | |||
| 13118 | Goto(&if_not_equivalent_types); | |||
| 13119 | } | |||
| 13120 | ||||
| 13121 | BIND(&if_lhsisnotstring)Bind(&if_lhsisnotstring); | |||
| 13122 | { | |||
| 13123 | // Check if {lhs} is a BigInt. | |||
| 13124 | Label if_lhsisbigint(this), if_lhsisnotbigint(this); | |||
| 13125 | Branch(IsBigIntInstanceType(lhs_instance_type), &if_lhsisbigint, | |||
| 13126 | &if_lhsisnotbigint); | |||
| 13127 | ||||
| 13128 | BIND(&if_lhsisbigint)Bind(&if_lhsisbigint); | |||
| 13129 | { | |||
| 13130 | // Load the instance type of {rhs}. | |||
| 13131 | TNode<Uint16T> rhs_instance_type = LoadInstanceType(CAST(rhs)Cast(rhs)); | |||
| 13132 | ||||
| 13133 | // Check if {rhs} is also a BigInt. | |||
| 13134 | Label if_rhsisbigint(this, Label::kDeferred), | |||
| 13135 | if_rhsisnotbigint(this); | |||
| 13136 | Branch(IsBigIntInstanceType(rhs_instance_type), &if_rhsisbigint, | |||
| 13137 | &if_rhsisnotbigint); | |||
| 13138 | ||||
| 13139 | BIND(&if_rhsisbigint)Bind(&if_rhsisbigint); | |||
| 13140 | { | |||
| 13141 | CombineFeedback(var_type_feedback, | |||
| 13142 | CompareOperationFeedback::kBigInt); | |||
| 13143 | result = CAST(CallRuntime(Runtime::kBigIntEqualToBigInt,Cast(CallRuntime(Runtime::kBigIntEqualToBigInt, NoContextConstant (), lhs, rhs)) | |||
| 13144 | NoContextConstant(), lhs, rhs))Cast(CallRuntime(Runtime::kBigIntEqualToBigInt, NoContextConstant (), lhs, rhs)); | |||
| 13145 | Goto(&end); | |||
| 13146 | } | |||
| 13147 | ||||
| 13148 | BIND(&if_rhsisnotbigint)Bind(&if_rhsisnotbigint); | |||
| 13149 | Goto(&if_not_equivalent_types); | |||
| 13150 | } | |||
| 13151 | ||||
| 13152 | BIND(&if_lhsisnotbigint)Bind(&if_lhsisnotbigint); | |||
| 13153 | if (var_type_feedback != nullptr) { | |||
| 13154 | // Load the instance type of {rhs}. | |||
| 13155 | TNode<Map> rhs_map = LoadMap(CAST(rhs)Cast(rhs)); | |||
| 13156 | TNode<Uint16T> rhs_instance_type = LoadMapInstanceType(rhs_map); | |||
| 13157 | ||||
| 13158 | Label if_lhsissymbol(this), if_lhsisreceiver(this), | |||
| 13159 | if_lhsisoddball(this); | |||
| 13160 | GotoIf(IsJSReceiverInstanceType(lhs_instance_type), | |||
| 13161 | &if_lhsisreceiver); | |||
| 13162 | GotoIf(IsBooleanMap(lhs_map), &if_not_equivalent_types); | |||
| 13163 | GotoIf(IsOddballInstanceType(lhs_instance_type), | |||
| 13164 | &if_lhsisoddball); | |||
| 13165 | Branch(IsSymbolInstanceType(lhs_instance_type), &if_lhsissymbol, | |||
| 13166 | &if_not_equivalent_types); | |||
| 13167 | ||||
| 13168 | BIND(&if_lhsisreceiver)Bind(&if_lhsisreceiver); | |||
| 13169 | { | |||
| 13170 | GotoIf(IsBooleanMap(rhs_map), &if_not_equivalent_types); | |||
| 13171 | OverwriteFeedback(var_type_feedback, | |||
| 13172 | CompareOperationFeedback::kReceiver); | |||
| 13173 | GotoIf(IsJSReceiverInstanceType(rhs_instance_type), | |||
| 13174 | &if_notequal); | |||
| 13175 | OverwriteFeedback( | |||
| 13176 | var_type_feedback, | |||
| 13177 | CompareOperationFeedback::kReceiverOrNullOrUndefined); | |||
| 13178 | GotoIf(IsOddballInstanceType(rhs_instance_type), &if_notequal); | |||
| 13179 | Goto(&if_not_equivalent_types); | |||
| 13180 | } | |||
| 13181 | ||||
| 13182 | BIND(&if_lhsisoddball)Bind(&if_lhsisoddball); | |||
| 13183 | { | |||
| 13184 | Label if_lhsisboolean(this), if_lhsisnotboolean(this); | |||
| 13185 | Branch(IsBooleanMap(lhs_map), &if_lhsisboolean, | |||
| 13186 | &if_lhsisnotboolean); | |||
| 13187 | ||||
| 13188 | BIND(&if_lhsisboolean)Bind(&if_lhsisboolean); | |||
| 13189 | { | |||
| 13190 | OverwriteFeedback(var_type_feedback, | |||
| 13191 | CompareOperationFeedback::kNumberOrOddball); | |||
| 13192 | GotoIf(IsBooleanMap(rhs_map), &if_notequal); | |||
| 13193 | Goto(&if_not_equivalent_types); | |||
| 13194 | } | |||
| 13195 | ||||
| 13196 | BIND(&if_lhsisnotboolean)Bind(&if_lhsisnotboolean); | |||
| 13197 | { | |||
| 13198 | Label if_rhsisheapnumber(this), if_rhsisnotheapnumber(this); | |||
| 13199 | ||||
| 13200 | STATIC_ASSERT(LAST_PRIMITIVE_HEAP_OBJECT_TYPE ==static_assert(LAST_PRIMITIVE_HEAP_OBJECT_TYPE == ODDBALL_TYPE , "LAST_PRIMITIVE_HEAP_OBJECT_TYPE == ODDBALL_TYPE") | |||
| 13201 | ODDBALL_TYPE)static_assert(LAST_PRIMITIVE_HEAP_OBJECT_TYPE == ODDBALL_TYPE , "LAST_PRIMITIVE_HEAP_OBJECT_TYPE == ODDBALL_TYPE"); | |||
| 13202 | GotoIf(Int32LessThan(rhs_instance_type, | |||
| 13203 | Int32Constant(ODDBALL_TYPE)), | |||
| 13204 | &if_not_equivalent_types); | |||
| 13205 | ||||
| 13206 | Branch(IsHeapNumberMap(rhs_map), &if_rhsisheapnumber, | |||
| 13207 | &if_rhsisnotheapnumber); | |||
| 13208 | ||||
| 13209 | BIND(&if_rhsisheapnumber)Bind(&if_rhsisheapnumber); | |||
| 13210 | { | |||
| 13211 | OverwriteFeedback( | |||
| 13212 | var_type_feedback, | |||
| 13213 | CompareOperationFeedback::kNumberOrOddball); | |||
| 13214 | Goto(&if_not_equivalent_types); | |||
| 13215 | } | |||
| 13216 | ||||
| 13217 | BIND(&if_rhsisnotheapnumber)Bind(&if_rhsisnotheapnumber); | |||
| 13218 | { | |||
| 13219 | OverwriteFeedback( | |||
| 13220 | var_type_feedback, | |||
| 13221 | CompareOperationFeedback::kReceiverOrNullOrUndefined); | |||
| 13222 | Goto(&if_notequal); | |||
| 13223 | } | |||
| 13224 | } | |||
| 13225 | } | |||
| 13226 | ||||
| 13227 | BIND(&if_lhsissymbol)Bind(&if_lhsissymbol); | |||
| 13228 | { | |||
| 13229 | GotoIfNot(IsSymbolInstanceType(rhs_instance_type), | |||
| 13230 | &if_not_equivalent_types); | |||
| 13231 | OverwriteFeedback(var_type_feedback, | |||
| 13232 | CompareOperationFeedback::kSymbol); | |||
| 13233 | Goto(&if_notequal); | |||
| 13234 | } | |||
| 13235 | } else { | |||
| 13236 | Goto(&if_notequal); | |||
| 13237 | } | |||
| 13238 | } | |||
| 13239 | } | |||
| 13240 | } | |||
| 13241 | } | |||
| 13242 | ||||
| 13243 | BIND(&if_lhsissmi)Bind(&if_lhsissmi); | |||
| 13244 | { | |||
| 13245 | // We already know that {lhs} and {rhs} are not reference equal, and {lhs} | |||
| 13246 | // is a Smi; so {lhs} and {rhs} can only be strictly equal if {rhs} is a | |||
| 13247 | // HeapNumber with an equal floating point value. | |||
| 13248 | ||||
| 13249 | // Check if {rhs} is a Smi or a HeapObject. | |||
| 13250 | Label if_rhsissmi(this), if_rhsisnotsmi(this); | |||
| 13251 | Branch(TaggedIsSmi(rhs), &if_rhsissmi, &if_rhsisnotsmi); | |||
| 13252 | ||||
| 13253 | BIND(&if_rhsissmi)Bind(&if_rhsissmi); | |||
| 13254 | CombineFeedback(var_type_feedback, | |||
| 13255 | CompareOperationFeedback::kSignedSmall); | |||
| 13256 | Goto(&if_notequal); | |||
| 13257 | ||||
| 13258 | BIND(&if_rhsisnotsmi)Bind(&if_rhsisnotsmi); | |||
| 13259 | { | |||
| 13260 | // Load the map of the {rhs}. | |||
| 13261 | TNode<Map> rhs_map = LoadMap(CAST(rhs)Cast(rhs)); | |||
| 13262 | ||||
| 13263 | // The {rhs} could be a HeapNumber with the same value as {lhs}. | |||
| 13264 | Label if_rhsisnumber(this), if_rhsisnotnumber(this); | |||
| 13265 | Branch(IsHeapNumberMap(rhs_map), &if_rhsisnumber, &if_rhsisnotnumber); | |||
| 13266 | ||||
| 13267 | BIND(&if_rhsisnumber)Bind(&if_rhsisnumber); | |||
| 13268 | { | |||
| 13269 | // Convert {lhs} and {rhs} to floating point values. | |||
| 13270 | TNode<Float64T> lhs_value = SmiToFloat64(CAST(lhs)Cast(lhs)); | |||
| 13271 | TNode<Float64T> rhs_value = LoadHeapNumberValue(CAST(rhs)Cast(rhs)); | |||
| 13272 | ||||
| 13273 | CombineFeedback(var_type_feedback, CompareOperationFeedback::kNumber); | |||
| 13274 | ||||
| 13275 | // Perform a floating point comparison of {lhs} and {rhs}. | |||
| 13276 | Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); | |||
| 13277 | } | |||
| 13278 | ||||
| 13279 | BIND(&if_rhsisnotnumber)Bind(&if_rhsisnotnumber); | |||
| 13280 | { | |||
| 13281 | TNode<Uint16T> rhs_instance_type = LoadMapInstanceType(rhs_map); | |||
| 13282 | GotoIfNot(IsOddballInstanceType(rhs_instance_type), | |||
| 13283 | &if_not_equivalent_types); | |||
| 13284 | OverwriteFeedback(var_type_feedback, | |||
| 13285 | CompareOperationFeedback::kNumberOrOddball); | |||
| 13286 | Goto(&if_notequal); | |||
| 13287 | } | |||
| 13288 | } | |||
| 13289 | } | |||
| 13290 | } | |||
| 13291 | ||||
| 13292 | BIND(&if_equal)Bind(&if_equal); | |||
| 13293 | { | |||
| 13294 | result = TrueConstant(); | |||
| 13295 | Goto(&end); | |||
| 13296 | } | |||
| 13297 | ||||
| 13298 | BIND(&if_not_equivalent_types)Bind(&if_not_equivalent_types); | |||
| 13299 | { | |||
| 13300 | OverwriteFeedback(var_type_feedback, CompareOperationFeedback::kAny); | |||
| 13301 | Goto(&if_notequal); | |||
| 13302 | } | |||
| 13303 | ||||
| 13304 | BIND(&if_notequal)Bind(&if_notequal); | |||
| 13305 | { | |||
| 13306 | result = FalseConstant(); | |||
| 13307 | Goto(&end); | |||
| 13308 | } | |||
| 13309 | ||||
| 13310 | BIND(&end)Bind(&end); | |||
| 13311 | return result.value(); | |||
| 13312 | } | |||
| 13313 | ||||
| 13314 | // ECMA#sec-samevalue | |||
| 13315 | // This algorithm differs from the Strict Equality Comparison Algorithm in its | |||
| 13316 | // treatment of signed zeroes and NaNs. | |||
| 13317 | void CodeStubAssembler::BranchIfSameValue(TNode<Object> lhs, TNode<Object> rhs, | |||
| 13318 | Label* if_true, Label* if_false, | |||
| 13319 | SameValueMode mode) { | |||
| 13320 | TVARIABLE(Float64T, var_lhs_value)TVariable<Float64T> var_lhs_value(this); | |||
| 13321 | TVARIABLE(Float64T, var_rhs_value)TVariable<Float64T> var_rhs_value(this); | |||
| 13322 | Label do_fcmp(this); | |||
| 13323 | ||||
| 13324 | // Immediately jump to {if_true} if {lhs} == {rhs}, because - unlike | |||
| 13325 | // StrictEqual - SameValue considers two NaNs to be equal. | |||
| 13326 | GotoIf(TaggedEqual(lhs, rhs), if_true); | |||
| 13327 | ||||
| 13328 | // Check if the {lhs} is a Smi. | |||
| 13329 | Label if_lhsissmi(this), if_lhsisheapobject(this); | |||
| 13330 | Branch(TaggedIsSmi(lhs), &if_lhsissmi, &if_lhsisheapobject); | |||
| 13331 | ||||
| 13332 | BIND(&if_lhsissmi)Bind(&if_lhsissmi); | |||
| 13333 | { | |||
| 13334 | // Since {lhs} is a Smi, the comparison can only yield true | |||
| 13335 | // iff the {rhs} is a HeapNumber with the same float64 value. | |||
| 13336 | Branch(TaggedIsSmi(rhs), if_false, [&] { | |||
| 13337 | GotoIfNot(IsHeapNumber(CAST(rhs)Cast(rhs)), if_false); | |||
| 13338 | var_lhs_value = SmiToFloat64(CAST(lhs)Cast(lhs)); | |||
| 13339 | var_rhs_value = LoadHeapNumberValue(CAST(rhs)Cast(rhs)); | |||
| 13340 | Goto(&do_fcmp); | |||
| 13341 | }); | |||
| 13342 | } | |||
| 13343 | ||||
| 13344 | BIND(&if_lhsisheapobject)Bind(&if_lhsisheapobject); | |||
| 13345 | { | |||
| 13346 | // Check if the {rhs} is a Smi. | |||
| 13347 | Branch( | |||
| 13348 | TaggedIsSmi(rhs), | |||
| 13349 | [&] { | |||
| 13350 | // Since {rhs} is a Smi, the comparison can only yield true | |||
| 13351 | // iff the {lhs} is a HeapNumber with the same float64 value. | |||
| 13352 | GotoIfNot(IsHeapNumber(CAST(lhs)Cast(lhs)), if_false); | |||
| 13353 | var_lhs_value = LoadHeapNumberValue(CAST(lhs)Cast(lhs)); | |||
| 13354 | var_rhs_value = SmiToFloat64(CAST(rhs)Cast(rhs)); | |||
| 13355 | Goto(&do_fcmp); | |||
| 13356 | }, | |||
| 13357 | [&] { | |||
| 13358 | // Now this can only yield true if either both {lhs} and {rhs} are | |||
| 13359 | // HeapNumbers with the same value, or both are Strings with the | |||
| 13360 | // same character sequence, or both are BigInts with the same | |||
| 13361 | // value. | |||
| 13362 | Label if_lhsisheapnumber(this), if_lhsisstring(this), | |||
| 13363 | if_lhsisbigint(this); | |||
| 13364 | const TNode<Map> lhs_map = LoadMap(CAST(lhs)Cast(lhs)); | |||
| 13365 | GotoIf(IsHeapNumberMap(lhs_map), &if_lhsisheapnumber); | |||
| 13366 | if (mode != SameValueMode::kNumbersOnly) { | |||
| 13367 | const TNode<Uint16T> lhs_instance_type = | |||
| 13368 | LoadMapInstanceType(lhs_map); | |||
| 13369 | GotoIf(IsStringInstanceType(lhs_instance_type), &if_lhsisstring); | |||
| 13370 | GotoIf(IsBigIntInstanceType(lhs_instance_type), &if_lhsisbigint); | |||
| 13371 | } | |||
| 13372 | Goto(if_false); | |||
| 13373 | ||||
| 13374 | BIND(&if_lhsisheapnumber)Bind(&if_lhsisheapnumber); | |||
| 13375 | { | |||
| 13376 | GotoIfNot(IsHeapNumber(CAST(rhs)Cast(rhs)), if_false); | |||
| 13377 | var_lhs_value = LoadHeapNumberValue(CAST(lhs)Cast(lhs)); | |||
| 13378 | var_rhs_value = LoadHeapNumberValue(CAST(rhs)Cast(rhs)); | |||
| 13379 | Goto(&do_fcmp); | |||
| 13380 | } | |||
| 13381 | ||||
| 13382 | if (mode != SameValueMode::kNumbersOnly) { | |||
| 13383 | BIND(&if_lhsisstring)Bind(&if_lhsisstring); | |||
| 13384 | { | |||
| 13385 | // Now we can only yield true if {rhs} is also a String | |||
| 13386 | // with the same sequence of characters. | |||
| 13387 | GotoIfNot(IsString(CAST(rhs)Cast(rhs)), if_false); | |||
| 13388 | const TNode<Object> result = CallBuiltin( | |||
| 13389 | Builtin::kStringEqual, NoContextConstant(), lhs, rhs); | |||
| 13390 | Branch(IsTrue(result), if_true, if_false); | |||
| 13391 | } | |||
| 13392 | ||||
| 13393 | BIND(&if_lhsisbigint)Bind(&if_lhsisbigint); | |||
| 13394 | { | |||
| 13395 | GotoIfNot(IsBigInt(CAST(rhs)Cast(rhs)), if_false); | |||
| 13396 | const TNode<Object> result = CallRuntime( | |||
| 13397 | Runtime::kBigIntEqualToBigInt, NoContextConstant(), lhs, rhs); | |||
| 13398 | Branch(IsTrue(result), if_true, if_false); | |||
| 13399 | } | |||
| 13400 | } | |||
| 13401 | }); | |||
| 13402 | } | |||
| 13403 | ||||
| 13404 | BIND(&do_fcmp)Bind(&do_fcmp); | |||
| 13405 | { | |||
| 13406 | TNode<Float64T> lhs_value = UncheckedCast<Float64T>(var_lhs_value.value()); | |||
| 13407 | TNode<Float64T> rhs_value = UncheckedCast<Float64T>(var_rhs_value.value()); | |||
| 13408 | BranchIfSameNumberValue(lhs_value, rhs_value, if_true, if_false); | |||
| 13409 | } | |||
| 13410 | } | |||
| 13411 | ||||
| 13412 | void CodeStubAssembler::BranchIfSameNumberValue(TNode<Float64T> lhs_value, | |||
| 13413 | TNode<Float64T> rhs_value, | |||
| 13414 | Label* if_true, | |||
| 13415 | Label* if_false) { | |||
| 13416 | Label if_equal(this), if_notequal(this); | |||
| 13417 | Branch(Float64Equal(lhs_value, rhs_value), &if_equal, &if_notequal); | |||
| 13418 | ||||
| 13419 | BIND(&if_equal)Bind(&if_equal); | |||
| 13420 | { | |||
| 13421 | // We still need to handle the case when {lhs} and {rhs} are -0.0 and | |||
| 13422 | // 0.0 (or vice versa). Compare the high word to | |||
| 13423 | // distinguish between the two. | |||
| 13424 | const TNode<Uint32T> lhs_hi_word = Float64ExtractHighWord32(lhs_value); | |||
| 13425 | const TNode<Uint32T> rhs_hi_word = Float64ExtractHighWord32(rhs_value); | |||
| 13426 | ||||
| 13427 | // If x is +0 and y is -0, return false. | |||
| 13428 | // If x is -0 and y is +0, return false. | |||
| 13429 | Branch(Word32Equal(lhs_hi_word, rhs_hi_word), if_true, if_false); | |||
| 13430 | } | |||
| 13431 | ||||
| 13432 | BIND(&if_notequal)Bind(&if_notequal); | |||
| 13433 | { | |||
| 13434 | // Return true iff both {rhs} and {lhs} are NaN. | |||
| 13435 | GotoIf(Float64Equal(lhs_value, lhs_value), if_false); | |||
| 13436 | Branch(Float64Equal(rhs_value, rhs_value), if_false, if_true); | |||
| 13437 | } | |||
| 13438 | } | |||
| 13439 | ||||
| 13440 | TNode<Oddball> CodeStubAssembler::HasProperty(TNode<Context> context, | |||
| 13441 | TNode<Object> object, | |||
| 13442 | TNode<Object> key, | |||
| 13443 | HasPropertyLookupMode mode) { | |||
| 13444 | Label call_runtime(this, Label::kDeferred), return_true(this), | |||
| 13445 | return_false(this), end(this), if_proxy(this, Label::kDeferred); | |||
| 13446 | ||||
| 13447 | CodeStubAssembler::LookupPropertyInHolder lookup_property_in_holder = | |||
| 13448 | [this, &return_true]( | |||
| 13449 | TNode<HeapObject> receiver, TNode<HeapObject> holder, | |||
| 13450 | TNode<Map> holder_map, TNode<Int32T> holder_instance_type, | |||
| 13451 | TNode<Name> unique_name, Label* next_holder, Label* if_bailout) { | |||
| 13452 | TryHasOwnProperty(holder, holder_map, holder_instance_type, unique_name, | |||
| 13453 | &return_true, next_holder, if_bailout); | |||
| 13454 | }; | |||
| 13455 | ||||
| 13456 | CodeStubAssembler::LookupElementInHolder lookup_element_in_holder = | |||
| 13457 | [this, &return_true, &return_false]( | |||
| 13458 | TNode<HeapObject> receiver, TNode<HeapObject> holder, | |||
| 13459 | TNode<Map> holder_map, TNode<Int32T> holder_instance_type, | |||
| 13460 | TNode<IntPtrT> index, Label* next_holder, Label* if_bailout) { | |||
| 13461 | TryLookupElement(holder, holder_map, holder_instance_type, index, | |||
| 13462 | &return_true, &return_false, next_holder, if_bailout); | |||
| 13463 | }; | |||
| 13464 | ||||
| 13465 | const bool kHandlePrivateNames = mode == HasPropertyLookupMode::kHasProperty; | |||
| 13466 | TryPrototypeChainLookup(object, object, key, lookup_property_in_holder, | |||
| 13467 | lookup_element_in_holder, &return_false, | |||
| 13468 | &call_runtime, &if_proxy, kHandlePrivateNames); | |||
| 13469 | ||||
| 13470 | TVARIABLE(Oddball, result)TVariable<Oddball> result(this); | |||
| 13471 | ||||
| 13472 | BIND(&if_proxy)Bind(&if_proxy); | |||
| 13473 | { | |||
| 13474 | TNode<Name> name = CAST(CallBuiltin(Builtin::kToName, context, key))Cast(CallBuiltin(Builtin::kToName, context, key)); | |||
| 13475 | switch (mode) { | |||
| 13476 | case kHasProperty: | |||
| 13477 | GotoIf(IsPrivateSymbol(name), &call_runtime); | |||
| 13478 | ||||
| 13479 | result = CAST(Cast(CallBuiltin(Builtin::kProxyHasProperty, context, object, name)) | |||
| 13480 | CallBuiltin(Builtin::kProxyHasProperty, context, object, name))Cast(CallBuiltin(Builtin::kProxyHasProperty, context, object, name)); | |||
| 13481 | Goto(&end); | |||
| 13482 | break; | |||
| 13483 | case kForInHasProperty: | |||
| 13484 | Goto(&call_runtime); | |||
| 13485 | break; | |||
| 13486 | } | |||
| 13487 | } | |||
| 13488 | ||||
| 13489 | BIND(&return_true)Bind(&return_true); | |||
| 13490 | { | |||
| 13491 | result = TrueConstant(); | |||
| 13492 | Goto(&end); | |||
| 13493 | } | |||
| 13494 | ||||
| 13495 | BIND(&return_false)Bind(&return_false); | |||
| 13496 | { | |||
| 13497 | result = FalseConstant(); | |||
| 13498 | Goto(&end); | |||
| 13499 | } | |||
| 13500 | ||||
| 13501 | BIND(&call_runtime)Bind(&call_runtime); | |||
| 13502 | { | |||
| 13503 | Runtime::FunctionId fallback_runtime_function_id; | |||
| 13504 | switch (mode) { | |||
| 13505 | case kHasProperty: | |||
| 13506 | fallback_runtime_function_id = Runtime::kHasProperty; | |||
| 13507 | break; | |||
| 13508 | case kForInHasProperty: | |||
| 13509 | fallback_runtime_function_id = Runtime::kForInHasProperty; | |||
| 13510 | break; | |||
| 13511 | } | |||
| 13512 | ||||
| 13513 | result = | |||
| 13514 | CAST(CallRuntime(fallback_runtime_function_id, context, object, key))Cast(CallRuntime(fallback_runtime_function_id, context, object , key)); | |||
| 13515 | Goto(&end); | |||
| 13516 | } | |||
| 13517 | ||||
| 13518 | BIND(&end)Bind(&end); | |||
| 13519 | CSA_DCHECK(this, IsBoolean(result.value()))((void)0); | |||
| 13520 | return result.value(); | |||
| 13521 | } | |||
| 13522 | ||||
| 13523 | void CodeStubAssembler::ForInPrepare(TNode<HeapObject> enumerator, | |||
| 13524 | TNode<UintPtrT> slot, | |||
| 13525 | TNode<HeapObject> maybe_feedback_vector, | |||
| 13526 | TNode<FixedArray>* cache_array_out, | |||
| 13527 | TNode<Smi>* cache_length_out, | |||
| 13528 | UpdateFeedbackMode update_feedback_mode) { | |||
| 13529 | // Check if we're using an enum cache. | |||
| 13530 | TVARIABLE(FixedArray, cache_array)TVariable<FixedArray> cache_array(this); | |||
| 13531 | TVARIABLE(Smi, cache_length)TVariable<Smi> cache_length(this); | |||
| 13532 | Label if_fast(this), if_slow(this, Label::kDeferred), out(this); | |||
| 13533 | Branch(IsMap(enumerator), &if_fast, &if_slow); | |||
| 13534 | ||||
| 13535 | BIND(&if_fast)Bind(&if_fast); | |||
| 13536 | { | |||
| 13537 | // Load the enumeration length and cache from the {enumerator}. | |||
| 13538 | TNode<Map> map_enumerator = CAST(enumerator)Cast(enumerator); | |||
| 13539 | TNode<WordT> enum_length = LoadMapEnumLength(map_enumerator); | |||
| 13540 | CSA_DCHECK(this, WordNotEqual(enum_length,((void)0) | |||
| 13541 | IntPtrConstant(kInvalidEnumCacheSentinel)))((void)0); | |||
| 13542 | TNode<DescriptorArray> descriptors = LoadMapDescriptors(map_enumerator); | |||
| 13543 | TNode<EnumCache> enum_cache = LoadObjectField<EnumCache>( | |||
| 13544 | descriptors, DescriptorArray::kEnumCacheOffset); | |||
| 13545 | TNode<FixedArray> enum_keys = | |||
| 13546 | LoadObjectField<FixedArray>(enum_cache, EnumCache::kKeysOffset); | |||
| 13547 | ||||
| 13548 | // Check if we have enum indices available. | |||
| 13549 | TNode<FixedArray> enum_indices = | |||
| 13550 | LoadObjectField<FixedArray>(enum_cache, EnumCache::kIndicesOffset); | |||
| 13551 | TNode<IntPtrT> enum_indices_length = | |||
| 13552 | LoadAndUntagFixedArrayBaseLength(enum_indices); | |||
| 13553 | TNode<Smi> feedback = SelectSmiConstant( | |||
| 13554 | IntPtrLessThanOrEqual(enum_length, enum_indices_length), | |||
| 13555 | static_cast<int>(ForInFeedback::kEnumCacheKeysAndIndices), | |||
| 13556 | static_cast<int>(ForInFeedback::kEnumCacheKeys)); | |||
| 13557 | UpdateFeedback(feedback, maybe_feedback_vector, slot, update_feedback_mode); | |||
| 13558 | ||||
| 13559 | cache_array = enum_keys; | |||
| 13560 | cache_length = SmiTag(Signed(enum_length)); | |||
| 13561 | Goto(&out); | |||
| 13562 | } | |||
| 13563 | ||||
| 13564 | BIND(&if_slow)Bind(&if_slow); | |||
| 13565 | { | |||
| 13566 | // The {enumerator} is a FixedArray with all the keys to iterate. | |||
| 13567 | TNode<FixedArray> array_enumerator = CAST(enumerator)Cast(enumerator); | |||
| 13568 | ||||
| 13569 | // Record the fact that we hit the for-in slow-path. | |||
| 13570 | UpdateFeedback(SmiConstant(ForInFeedback::kAny), maybe_feedback_vector, | |||
| 13571 | slot, update_feedback_mode); | |||
| 13572 | ||||
| 13573 | cache_array = array_enumerator; | |||
| 13574 | cache_length = LoadFixedArrayBaseLength(array_enumerator); | |||
| 13575 | Goto(&out); | |||
| 13576 | } | |||
| 13577 | ||||
| 13578 | BIND(&out)Bind(&out); | |||
| 13579 | *cache_array_out = cache_array.value(); | |||
| 13580 | *cache_length_out = cache_length.value(); | |||
| 13581 | } | |||
| 13582 | ||||
| 13583 | TNode<String> CodeStubAssembler::Typeof(TNode<Object> value) { | |||
| 13584 | TVARIABLE(String, result_var)TVariable<String> result_var(this); | |||
| 13585 | ||||
| 13586 | Label return_number(this, Label::kDeferred), if_oddball(this), | |||
| 13587 | return_function(this), return_undefined(this), return_object(this), | |||
| 13588 | return_string(this), return_bigint(this), return_result(this); | |||
| 13589 | ||||
| 13590 | GotoIf(TaggedIsSmi(value), &return_number); | |||
| 13591 | ||||
| 13592 | TNode<HeapObject> value_heap_object = CAST(value)Cast(value); | |||
| 13593 | TNode<Map> map = LoadMap(value_heap_object); | |||
| 13594 | ||||
| 13595 | GotoIf(IsHeapNumberMap(map), &return_number); | |||
| 13596 | ||||
| 13597 | TNode<Uint16T> instance_type = LoadMapInstanceType(map); | |||
| 13598 | ||||
| 13599 | GotoIf(InstanceTypeEqual(instance_type, ODDBALL_TYPE), &if_oddball); | |||
| 13600 | ||||
| 13601 | TNode<Int32T> callable_or_undetectable_mask = | |||
| 13602 | Word32And(LoadMapBitField(map), | |||
| 13603 | Int32Constant(Map::Bits1::IsCallableBit::kMask | | |||
| 13604 | Map::Bits1::IsUndetectableBit::kMask)); | |||
| 13605 | ||||
| 13606 | GotoIf(Word32Equal(callable_or_undetectable_mask, | |||
| 13607 | Int32Constant(Map::Bits1::IsCallableBit::kMask)), | |||
| 13608 | &return_function); | |||
| 13609 | ||||
| 13610 | GotoIfNot(Word32Equal(callable_or_undetectable_mask, Int32Constant(0)), | |||
| 13611 | &return_undefined); | |||
| 13612 | ||||
| 13613 | GotoIf(IsJSReceiverInstanceType(instance_type), &return_object); | |||
| 13614 | ||||
| 13615 | GotoIf(IsStringInstanceType(instance_type), &return_string); | |||
| 13616 | ||||
| 13617 | GotoIf(IsBigIntInstanceType(instance_type), &return_bigint); | |||
| 13618 | ||||
| 13619 | CSA_DCHECK(this, InstanceTypeEqual(instance_type, SYMBOL_TYPE))((void)0); | |||
| 13620 | result_var = HeapConstant(isolate()->factory()->symbol_string()); | |||
| 13621 | Goto(&return_result); | |||
| 13622 | ||||
| 13623 | BIND(&return_number)Bind(&return_number); | |||
| 13624 | { | |||
| 13625 | result_var = HeapConstant(isolate()->factory()->number_string()); | |||
| 13626 | Goto(&return_result); | |||
| 13627 | } | |||
| 13628 | ||||
| 13629 | BIND(&if_oddball)Bind(&if_oddball); | |||
| 13630 | { | |||
| 13631 | TNode<String> type = | |||
| 13632 | CAST(LoadObjectField(value_heap_object, Oddball::kTypeOfOffset))Cast(LoadObjectField(value_heap_object, Oddball::kTypeOfOffset )); | |||
| 13633 | result_var = type; | |||
| 13634 | Goto(&return_result); | |||
| 13635 | } | |||
| 13636 | ||||
| 13637 | BIND(&return_function)Bind(&return_function); | |||
| 13638 | { | |||
| 13639 | result_var = HeapConstant(isolate()->factory()->function_string()); | |||
| 13640 | Goto(&return_result); | |||
| 13641 | } | |||
| 13642 | ||||
| 13643 | BIND(&return_undefined)Bind(&return_undefined); | |||
| 13644 | { | |||
| 13645 | result_var = HeapConstant(isolate()->factory()->undefined_string()); | |||
| 13646 | Goto(&return_result); | |||
| 13647 | } | |||
| 13648 | ||||
| 13649 | BIND(&return_object)Bind(&return_object); | |||
| 13650 | { | |||
| 13651 | result_var = HeapConstant(isolate()->factory()->object_string()); | |||
| 13652 | Goto(&return_result); | |||
| 13653 | } | |||
| 13654 | ||||
| 13655 | BIND(&return_string)Bind(&return_string); | |||
| 13656 | { | |||
| 13657 | result_var = HeapConstant(isolate()->factory()->string_string()); | |||
| 13658 | Goto(&return_result); | |||
| 13659 | } | |||
| 13660 | ||||
| 13661 | BIND(&return_bigint)Bind(&return_bigint); | |||
| 13662 | { | |||
| 13663 | result_var = HeapConstant(isolate()->factory()->bigint_string()); | |||
| 13664 | Goto(&return_result); | |||
| 13665 | } | |||
| 13666 | ||||
| 13667 | BIND(&return_result)Bind(&return_result); | |||
| 13668 | return result_var.value(); | |||
| 13669 | } | |||
| 13670 | ||||
| 13671 | TNode<HeapObject> CodeStubAssembler::GetSuperConstructor( | |||
| 13672 | TNode<JSFunction> active_function) { | |||
| 13673 | TNode<Map> map = LoadMap(active_function); | |||
| 13674 | return LoadMapPrototype(map); | |||
| 13675 | } | |||
| 13676 | ||||
| 13677 | TNode<JSReceiver> CodeStubAssembler::SpeciesConstructor( | |||
| 13678 | TNode<Context> context, TNode<Object> object, | |||
| 13679 | TNode<JSReceiver> default_constructor) { | |||
| 13680 | Isolate* isolate = this->isolate(); | |||
| 13681 | TVARIABLE(JSReceiver, var_result, default_constructor)TVariable<JSReceiver> var_result(default_constructor, this ); | |||
| 13682 | ||||
| 13683 | // 2. Let C be ? Get(O, "constructor"). | |||
| 13684 | TNode<Object> constructor = | |||
| 13685 | GetProperty(context, object, isolate->factory()->constructor_string()); | |||
| 13686 | ||||
| 13687 | // 3. If C is undefined, return defaultConstructor. | |||
| 13688 | Label out(this); | |||
| 13689 | GotoIf(IsUndefined(constructor), &out); | |||
| 13690 | ||||
| 13691 | // 4. If Type(C) is not Object, throw a TypeError exception. | |||
| 13692 | ThrowIfNotJSReceiver(context, constructor, | |||
| 13693 | MessageTemplate::kConstructorNotReceiver, ""); | |||
| 13694 | ||||
| 13695 | // 5. Let S be ? Get(C, @@species). | |||
| 13696 | TNode<Object> species = | |||
| 13697 | GetProperty(context, constructor, isolate->factory()->species_symbol()); | |||
| 13698 | ||||
| 13699 | // 6. If S is either undefined or null, return defaultConstructor. | |||
| 13700 | GotoIf(IsNullOrUndefined(species), &out); | |||
| 13701 | ||||
| 13702 | // 7. If IsConstructor(S) is true, return S. | |||
| 13703 | Label throw_error(this); | |||
| 13704 | GotoIf(TaggedIsSmi(species), &throw_error); | |||
| 13705 | GotoIfNot(IsConstructorMap(LoadMap(CAST(species)Cast(species))), &throw_error); | |||
| 13706 | var_result = CAST(species)Cast(species); | |||
| 13707 | Goto(&out); | |||
| 13708 | ||||
| 13709 | // 8. Throw a TypeError exception. | |||
| 13710 | BIND(&throw_error)Bind(&throw_error); | |||
| 13711 | ThrowTypeError(context, MessageTemplate::kSpeciesNotConstructor); | |||
| 13712 | ||||
| 13713 | BIND(&out)Bind(&out); | |||
| 13714 | return var_result.value(); | |||
| 13715 | } | |||
| 13716 | ||||
| 13717 | TNode<Oddball> CodeStubAssembler::InstanceOf(TNode<Object> object, | |||
| 13718 | TNode<Object> callable, | |||
| 13719 | TNode<Context> context) { | |||
| 13720 | TVARIABLE(Oddball, var_result)TVariable<Oddball> var_result(this); | |||
| 13721 | Label if_notcallable(this, Label::kDeferred), | |||
| 13722 | if_notreceiver(this, Label::kDeferred), if_otherhandler(this), | |||
| 13723 | if_nohandler(this, Label::kDeferred), return_true(this), | |||
| 13724 | return_false(this), return_result(this, &var_result); | |||
| 13725 | ||||
| 13726 | // Ensure that the {callable} is actually a JSReceiver. | |||
| 13727 | GotoIf(TaggedIsSmi(callable), &if_notreceiver); | |||
| 13728 | GotoIfNot(IsJSReceiver(CAST(callable)Cast(callable)), &if_notreceiver); | |||
| 13729 | ||||
| 13730 | // Load the @@hasInstance property from {callable}. | |||
| 13731 | TNode<Object> inst_of_handler = | |||
| 13732 | GetProperty(context, callable, HasInstanceSymbolConstant()); | |||
| 13733 | ||||
| 13734 | // Optimize for the likely case where {inst_of_handler} is the builtin | |||
| 13735 | // Function.prototype[@@hasInstance] method, and emit a direct call in | |||
| 13736 | // that case without any additional checking. | |||
| 13737 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 13738 | TNode<Object> function_has_instance = | |||
| 13739 | LoadContextElement(native_context, Context::FUNCTION_HAS_INSTANCE_INDEX); | |||
| 13740 | GotoIfNot(TaggedEqual(inst_of_handler, function_has_instance), | |||
| 13741 | &if_otherhandler); | |||
| 13742 | { | |||
| 13743 | // Call to Function.prototype[@@hasInstance] directly. | |||
| 13744 | Callable builtin(BUILTIN_CODE(isolate(), FunctionPrototypeHasInstance)(isolate())->builtins()->code_handle(i::Builtin::kFunctionPrototypeHasInstance ), | |||
| 13745 | CallTrampolineDescriptor{}); | |||
| 13746 | var_result = | |||
| 13747 | CAST(CallJS(builtin, context, inst_of_handler, callable, object))Cast(CallJS(builtin, context, inst_of_handler, callable, object )); | |||
| 13748 | Goto(&return_result); | |||
| 13749 | } | |||
| 13750 | ||||
| 13751 | BIND(&if_otherhandler)Bind(&if_otherhandler); | |||
| 13752 | { | |||
| 13753 | // Check if there's actually an {inst_of_handler}. | |||
| 13754 | GotoIf(IsNull(inst_of_handler), &if_nohandler); | |||
| 13755 | GotoIf(IsUndefined(inst_of_handler), &if_nohandler); | |||
| 13756 | ||||
| 13757 | // Call the {inst_of_handler} for {callable} and {object}. | |||
| 13758 | TNode<Object> result = Call(context, inst_of_handler, callable, object); | |||
| 13759 | ||||
| 13760 | // Convert the {result} to a Boolean. | |||
| 13761 | BranchIfToBooleanIsTrue(result, &return_true, &return_false); | |||
| 13762 | } | |||
| 13763 | ||||
| 13764 | BIND(&if_nohandler)Bind(&if_nohandler); | |||
| 13765 | { | |||
| 13766 | // Ensure that the {callable} is actually Callable. | |||
| 13767 | GotoIfNot(IsCallable(CAST(callable)Cast(callable)), &if_notcallable); | |||
| 13768 | ||||
| 13769 | // Use the OrdinaryHasInstance algorithm. | |||
| 13770 | var_result = CAST(Cast(CallBuiltin(Builtin::kOrdinaryHasInstance, context, callable , object)) | |||
| 13771 | CallBuiltin(Builtin::kOrdinaryHasInstance, context, callable, object))Cast(CallBuiltin(Builtin::kOrdinaryHasInstance, context, callable , object)); | |||
| 13772 | Goto(&return_result); | |||
| 13773 | } | |||
| 13774 | ||||
| 13775 | BIND(&if_notcallable)Bind(&if_notcallable); | |||
| 13776 | { ThrowTypeError(context, MessageTemplate::kNonCallableInInstanceOfCheck); } | |||
| 13777 | ||||
| 13778 | BIND(&if_notreceiver)Bind(&if_notreceiver); | |||
| 13779 | { ThrowTypeError(context, MessageTemplate::kNonObjectInInstanceOfCheck); } | |||
| 13780 | ||||
| 13781 | BIND(&return_true)Bind(&return_true); | |||
| 13782 | var_result = TrueConstant(); | |||
| 13783 | Goto(&return_result); | |||
| 13784 | ||||
| 13785 | BIND(&return_false)Bind(&return_false); | |||
| 13786 | var_result = FalseConstant(); | |||
| 13787 | Goto(&return_result); | |||
| 13788 | ||||
| 13789 | BIND(&return_result)Bind(&return_result); | |||
| 13790 | return var_result.value(); | |||
| 13791 | } | |||
| 13792 | ||||
| 13793 | TNode<Number> CodeStubAssembler::NumberInc(TNode<Number> value) { | |||
| 13794 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 13795 | TVARIABLE(Float64T, var_finc_value)TVariable<Float64T> var_finc_value(this); | |||
| 13796 | Label if_issmi(this), if_isnotsmi(this), do_finc(this), end(this); | |||
| 13797 | Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi); | |||
| 13798 | ||||
| 13799 | BIND(&if_issmi)Bind(&if_issmi); | |||
| 13800 | { | |||
| 13801 | Label if_overflow(this); | |||
| 13802 | TNode<Smi> smi_value = CAST(value)Cast(value); | |||
| 13803 | TNode<Smi> one = SmiConstant(1); | |||
| 13804 | var_result = TrySmiAdd(smi_value, one, &if_overflow); | |||
| 13805 | Goto(&end); | |||
| 13806 | ||||
| 13807 | BIND(&if_overflow)Bind(&if_overflow); | |||
| 13808 | { | |||
| 13809 | var_finc_value = SmiToFloat64(smi_value); | |||
| 13810 | Goto(&do_finc); | |||
| 13811 | } | |||
| 13812 | } | |||
| 13813 | ||||
| 13814 | BIND(&if_isnotsmi)Bind(&if_isnotsmi); | |||
| 13815 | { | |||
| 13816 | TNode<HeapNumber> heap_number_value = CAST(value)Cast(value); | |||
| 13817 | ||||
| 13818 | // Load the HeapNumber value. | |||
| 13819 | var_finc_value = LoadHeapNumberValue(heap_number_value); | |||
| 13820 | Goto(&do_finc); | |||
| 13821 | } | |||
| 13822 | ||||
| 13823 | BIND(&do_finc)Bind(&do_finc); | |||
| 13824 | { | |||
| 13825 | TNode<Float64T> finc_value = var_finc_value.value(); | |||
| 13826 | TNode<Float64T> one = Float64Constant(1.0); | |||
| 13827 | TNode<Float64T> finc_result = Float64Add(finc_value, one); | |||
| 13828 | var_result = AllocateHeapNumberWithValue(finc_result); | |||
| 13829 | Goto(&end); | |||
| 13830 | } | |||
| 13831 | ||||
| 13832 | BIND(&end)Bind(&end); | |||
| 13833 | return var_result.value(); | |||
| 13834 | } | |||
| 13835 | ||||
| 13836 | TNode<Number> CodeStubAssembler::NumberDec(TNode<Number> value) { | |||
| 13837 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 13838 | TVARIABLE(Float64T, var_fdec_value)TVariable<Float64T> var_fdec_value(this); | |||
| 13839 | Label if_issmi(this), if_isnotsmi(this), do_fdec(this), end(this); | |||
| 13840 | Branch(TaggedIsSmi(value), &if_issmi, &if_isnotsmi); | |||
| 13841 | ||||
| 13842 | BIND(&if_issmi)Bind(&if_issmi); | |||
| 13843 | { | |||
| 13844 | TNode<Smi> smi_value = CAST(value)Cast(value); | |||
| 13845 | TNode<Smi> one = SmiConstant(1); | |||
| 13846 | Label if_overflow(this); | |||
| 13847 | var_result = TrySmiSub(smi_value, one, &if_overflow); | |||
| 13848 | Goto(&end); | |||
| 13849 | ||||
| 13850 | BIND(&if_overflow)Bind(&if_overflow); | |||
| 13851 | { | |||
| 13852 | var_fdec_value = SmiToFloat64(smi_value); | |||
| 13853 | Goto(&do_fdec); | |||
| 13854 | } | |||
| 13855 | } | |||
| 13856 | ||||
| 13857 | BIND(&if_isnotsmi)Bind(&if_isnotsmi); | |||
| 13858 | { | |||
| 13859 | TNode<HeapNumber> heap_number_value = CAST(value)Cast(value); | |||
| 13860 | ||||
| 13861 | // Load the HeapNumber value. | |||
| 13862 | var_fdec_value = LoadHeapNumberValue(heap_number_value); | |||
| 13863 | Goto(&do_fdec); | |||
| 13864 | } | |||
| 13865 | ||||
| 13866 | BIND(&do_fdec)Bind(&do_fdec); | |||
| 13867 | { | |||
| 13868 | TNode<Float64T> fdec_value = var_fdec_value.value(); | |||
| 13869 | TNode<Float64T> minus_one = Float64Constant(-1.0); | |||
| 13870 | TNode<Float64T> fdec_result = Float64Add(fdec_value, minus_one); | |||
| 13871 | var_result = AllocateHeapNumberWithValue(fdec_result); | |||
| 13872 | Goto(&end); | |||
| 13873 | } | |||
| 13874 | ||||
| 13875 | BIND(&end)Bind(&end); | |||
| 13876 | return var_result.value(); | |||
| 13877 | } | |||
| 13878 | ||||
| 13879 | TNode<Number> CodeStubAssembler::NumberAdd(TNode<Number> a, TNode<Number> b) { | |||
| 13880 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 13881 | Label float_add(this, Label::kDeferred), end(this); | |||
| 13882 | GotoIf(TaggedIsNotSmi(a), &float_add); | |||
| 13883 | GotoIf(TaggedIsNotSmi(b), &float_add); | |||
| 13884 | ||||
| 13885 | // Try fast Smi addition first. | |||
| 13886 | var_result = TrySmiAdd(CAST(a)Cast(a), CAST(b)Cast(b), &float_add); | |||
| 13887 | Goto(&end); | |||
| 13888 | ||||
| 13889 | BIND(&float_add)Bind(&float_add); | |||
| 13890 | { | |||
| 13891 | var_result = ChangeFloat64ToTagged( | |||
| 13892 | Float64Add(ChangeNumberToFloat64(a), ChangeNumberToFloat64(b))); | |||
| 13893 | Goto(&end); | |||
| 13894 | } | |||
| 13895 | ||||
| 13896 | BIND(&end)Bind(&end); | |||
| 13897 | return var_result.value(); | |||
| 13898 | } | |||
| 13899 | ||||
| 13900 | TNode<Number> CodeStubAssembler::NumberSub(TNode<Number> a, TNode<Number> b) { | |||
| 13901 | TVARIABLE(Number, var_result)TVariable<Number> var_result(this); | |||
| 13902 | Label float_sub(this, Label::kDeferred), end(this); | |||
| 13903 | GotoIf(TaggedIsNotSmi(a), &float_sub); | |||
| 13904 | GotoIf(TaggedIsNotSmi(b), &float_sub); | |||
| 13905 | ||||
| 13906 | // Try fast Smi subtraction first. | |||
| 13907 | var_result = TrySmiSub(CAST(a)Cast(a), CAST(b)Cast(b), &float_sub); | |||
| 13908 | Goto(&end); | |||
| 13909 | ||||
| 13910 | BIND(&float_sub)Bind(&float_sub); | |||
| 13911 | { | |||
| 13912 | var_result = ChangeFloat64ToTagged( | |||
| 13913 | Float64Sub(ChangeNumberToFloat64(a), ChangeNumberToFloat64(b))); | |||
| 13914 | Goto(&end); | |||
| 13915 | } | |||
| 13916 | ||||
| 13917 | BIND(&end)Bind(&end); | |||
| 13918 | return var_result.value(); | |||
| 13919 | } | |||
| 13920 | ||||
| 13921 | void CodeStubAssembler::GotoIfNotNumber(TNode<Object> input, | |||
| 13922 | Label* is_not_number) { | |||
| 13923 | Label is_number(this); | |||
| 13924 | GotoIf(TaggedIsSmi(input), &is_number); | |||
| 13925 | Branch(IsHeapNumber(CAST(input)Cast(input)), &is_number, is_not_number); | |||
| 13926 | BIND(&is_number)Bind(&is_number); | |||
| 13927 | } | |||
| 13928 | ||||
| 13929 | void CodeStubAssembler::GotoIfNumber(TNode<Object> input, Label* is_number) { | |||
| 13930 | GotoIf(TaggedIsSmi(input), is_number); | |||
| 13931 | GotoIf(IsHeapNumber(CAST(input)Cast(input)), is_number); | |||
| 13932 | } | |||
| 13933 | ||||
| 13934 | TNode<Number> CodeStubAssembler::BitwiseOp(TNode<Word32T> left32, | |||
| 13935 | TNode<Word32T> right32, | |||
| 13936 | Operation bitwise_op) { | |||
| 13937 | switch (bitwise_op) { | |||
| 13938 | case Operation::kBitwiseAnd: | |||
| 13939 | return ChangeInt32ToTagged(Signed(Word32And(left32, right32))); | |||
| 13940 | case Operation::kBitwiseOr: | |||
| 13941 | return ChangeInt32ToTagged(Signed(Word32Or(left32, right32))); | |||
| 13942 | case Operation::kBitwiseXor: | |||
| 13943 | return ChangeInt32ToTagged(Signed(Word32Xor(left32, right32))); | |||
| 13944 | case Operation::kShiftLeft: | |||
| 13945 | if (!Word32ShiftIsSafe()) { | |||
| 13946 | right32 = Word32And(right32, Int32Constant(0x1F)); | |||
| 13947 | } | |||
| 13948 | return ChangeInt32ToTagged(Signed(Word32Shl(left32, right32))); | |||
| 13949 | case Operation::kShiftRight: | |||
| 13950 | if (!Word32ShiftIsSafe()) { | |||
| 13951 | right32 = Word32And(right32, Int32Constant(0x1F)); | |||
| 13952 | } | |||
| 13953 | return ChangeInt32ToTagged(Signed(Word32Sar(left32, right32))); | |||
| 13954 | case Operation::kShiftRightLogical: | |||
| 13955 | if (!Word32ShiftIsSafe()) { | |||
| 13956 | right32 = Word32And(right32, Int32Constant(0x1F)); | |||
| 13957 | } | |||
| 13958 | return ChangeUint32ToTagged(Unsigned(Word32Shr(left32, right32))); | |||
| 13959 | default: | |||
| 13960 | break; | |||
| 13961 | } | |||
| 13962 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 13963 | } | |||
| 13964 | ||||
| 13965 | TNode<Number> CodeStubAssembler::BitwiseSmiOp(TNode<Smi> left, TNode<Smi> right, | |||
| 13966 | Operation bitwise_op) { | |||
| 13967 | switch (bitwise_op) { | |||
| 13968 | case Operation::kBitwiseAnd: | |||
| 13969 | return SmiAnd(left, right); | |||
| 13970 | case Operation::kBitwiseOr: | |||
| 13971 | return SmiOr(left, right); | |||
| 13972 | case Operation::kBitwiseXor: | |||
| 13973 | return SmiXor(left, right); | |||
| 13974 | // Smi shift left and logical shift rihgt can have (Heap)Number output, so | |||
| 13975 | // perform int32 operation. | |||
| 13976 | case Operation::kShiftLeft: | |||
| 13977 | case Operation::kShiftRightLogical: | |||
| 13978 | return BitwiseOp(SmiToInt32(left), SmiToInt32(right), bitwise_op); | |||
| 13979 | // Arithmetic shift right of a Smi can't overflow to the heap number, so | |||
| 13980 | // perform int32 operation but don't check for overflow. | |||
| 13981 | case Operation::kShiftRight: { | |||
| 13982 | TNode<Int32T> left32 = SmiToInt32(left); | |||
| 13983 | TNode<Int32T> right32 = SmiToInt32(right); | |||
| 13984 | if (!Word32ShiftIsSafe()) { | |||
| 13985 | right32 = Word32And(right32, Int32Constant(0x1F)); | |||
| 13986 | } | |||
| 13987 | return ChangeInt32ToTaggedNoOverflow(Word32Sar(left32, right32)); | |||
| 13988 | } | |||
| 13989 | default: | |||
| 13990 | break; | |||
| 13991 | } | |||
| 13992 | UNREACHABLE()V8_Fatal("unreachable code"); | |||
| 13993 | } | |||
| 13994 | ||||
| 13995 | TNode<JSObject> CodeStubAssembler::AllocateJSIteratorResult( | |||
| 13996 | TNode<Context> context, TNode<Object> value, TNode<Oddball> done) { | |||
| 13997 | CSA_DCHECK(this, IsBoolean(done))((void)0); | |||
| 13998 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 13999 | TNode<Map> map = CAST(Cast(LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX )) | |||
| 14000 | LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX))Cast(LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX )); | |||
| 14001 | TNode<HeapObject> result = Allocate(JSIteratorResult::kSize); | |||
| 14002 | StoreMapNoWriteBarrier(result, map); | |||
| 14003 | StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOrHashOffset, | |||
| 14004 | RootIndex::kEmptyFixedArray); | |||
| 14005 | StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset, | |||
| 14006 | RootIndex::kEmptyFixedArray); | |||
| 14007 | StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, value); | |||
| 14008 | StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset, done); | |||
| 14009 | return CAST(result)Cast(result); | |||
| 14010 | } | |||
| 14011 | ||||
| 14012 | TNode<JSObject> CodeStubAssembler::AllocateJSIteratorResultForEntry( | |||
| 14013 | TNode<Context> context, TNode<Object> key, TNode<Object> value) { | |||
| 14014 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 14015 | TNode<Smi> length = SmiConstant(2); | |||
| 14016 | int const elements_size = FixedArray::SizeFor(2); | |||
| 14017 | TNode<FixedArray> elements = | |||
| 14018 | UncheckedCast<FixedArray>(Allocate(elements_size)); | |||
| 14019 | StoreObjectFieldRoot(elements, FixedArray::kMapOffset, | |||
| 14020 | RootIndex::kFixedArrayMap); | |||
| 14021 | StoreObjectFieldNoWriteBarrier(elements, FixedArray::kLengthOffset, length); | |||
| 14022 | StoreFixedArrayElement(elements, 0, key); | |||
| 14023 | StoreFixedArrayElement(elements, 1, value); | |||
| 14024 | TNode<Map> array_map = CAST(LoadContextElement(Cast(LoadContextElement( native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX )) | |||
| 14025 | native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX))Cast(LoadContextElement( native_context, Context::JS_ARRAY_PACKED_ELEMENTS_MAP_INDEX )); | |||
| 14026 | TNode<HeapObject> array = Allocate(JSArray::kHeaderSize); | |||
| 14027 | StoreMapNoWriteBarrier(array, array_map); | |||
| 14028 | StoreObjectFieldRoot(array, JSArray::kPropertiesOrHashOffset, | |||
| 14029 | RootIndex::kEmptyFixedArray); | |||
| 14030 | StoreObjectFieldNoWriteBarrier(array, JSArray::kElementsOffset, elements); | |||
| 14031 | StoreObjectFieldNoWriteBarrier(array, JSArray::kLengthOffset, length); | |||
| 14032 | TNode<Map> iterator_map = CAST(Cast(LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX )) | |||
| 14033 | LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX))Cast(LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX )); | |||
| 14034 | TNode<HeapObject> result = Allocate(JSIteratorResult::kSize); | |||
| 14035 | StoreMapNoWriteBarrier(result, iterator_map); | |||
| 14036 | StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOrHashOffset, | |||
| 14037 | RootIndex::kEmptyFixedArray); | |||
| 14038 | StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset, | |||
| 14039 | RootIndex::kEmptyFixedArray); | |||
| 14040 | StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset, array); | |||
| 14041 | StoreObjectFieldRoot(result, JSIteratorResult::kDoneOffset, | |||
| 14042 | RootIndex::kFalseValue); | |||
| 14043 | return CAST(result)Cast(result); | |||
| 14044 | } | |||
| 14045 | ||||
| 14046 | TNode<JSReceiver> CodeStubAssembler::ArraySpeciesCreate(TNode<Context> context, | |||
| 14047 | TNode<Object> o, | |||
| 14048 | TNode<Number> len) { | |||
| 14049 | TNode<JSReceiver> constructor = | |||
| 14050 | CAST(CallRuntime(Runtime::kArraySpeciesConstructor, context, o))Cast(CallRuntime(Runtime::kArraySpeciesConstructor, context, o )); | |||
| 14051 | return Construct(context, constructor, len); | |||
| 14052 | } | |||
| 14053 | ||||
| 14054 | void CodeStubAssembler::ThrowIfArrayBufferIsDetached( | |||
| 14055 | TNode<Context> context, TNode<JSArrayBuffer> array_buffer, | |||
| 14056 | const char* method_name) { | |||
| 14057 | Label if_detached(this, Label::kDeferred), if_not_detached(this); | |||
| 14058 | Branch(IsDetachedBuffer(array_buffer), &if_detached, &if_not_detached); | |||
| 14059 | BIND(&if_detached)Bind(&if_detached); | |||
| 14060 | ThrowTypeError(context, MessageTemplate::kDetachedOperation, method_name); | |||
| 14061 | BIND(&if_not_detached)Bind(&if_not_detached); | |||
| 14062 | } | |||
| 14063 | ||||
| 14064 | void CodeStubAssembler::ThrowIfArrayBufferViewBufferIsDetached( | |||
| 14065 | TNode<Context> context, TNode<JSArrayBufferView> array_buffer_view, | |||
| 14066 | const char* method_name) { | |||
| 14067 | TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array_buffer_view); | |||
| 14068 | ThrowIfArrayBufferIsDetached(context, buffer, method_name); | |||
| 14069 | } | |||
| 14070 | ||||
| 14071 | TNode<RawPtrT> CodeStubAssembler::LoadJSArrayBufferBackingStorePtr( | |||
| 14072 | TNode<JSArrayBuffer> array_buffer) { | |||
| 14073 | return LoadSandboxedPointerFromObject(array_buffer, | |||
| 14074 | JSArrayBuffer::kBackingStoreOffset); | |||
| 14075 | } | |||
| 14076 | ||||
| 14077 | TNode<JSArrayBuffer> CodeStubAssembler::LoadJSArrayBufferViewBuffer( | |||
| 14078 | TNode<JSArrayBufferView> array_buffer_view) { | |||
| 14079 | return LoadObjectField<JSArrayBuffer>(array_buffer_view, | |||
| 14080 | JSArrayBufferView::kBufferOffset); | |||
| 14081 | } | |||
| 14082 | ||||
| 14083 | TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferViewByteLength( | |||
| 14084 | TNode<JSArrayBufferView> array_buffer_view) { | |||
| 14085 | return LoadObjectField<UintPtrT>(array_buffer_view, | |||
| 14086 | JSArrayBufferView::kByteLengthOffset); | |||
| 14087 | } | |||
| 14088 | ||||
| 14089 | TNode<UintPtrT> CodeStubAssembler::LoadJSArrayBufferViewByteOffset( | |||
| 14090 | TNode<JSArrayBufferView> array_buffer_view) { | |||
| 14091 | return LoadObjectField<UintPtrT>(array_buffer_view, | |||
| 14092 | JSArrayBufferView::kByteOffsetOffset); | |||
| 14093 | } | |||
| 14094 | ||||
| 14095 | TNode<UintPtrT> CodeStubAssembler::LoadJSTypedArrayLengthAndCheckDetached( | |||
| 14096 | TNode<JSTypedArray> typed_array, Label* detached) { | |||
| 14097 | TVARIABLE(UintPtrT, result)TVariable<UintPtrT> result(this); | |||
| 14098 | TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(typed_array); | |||
| 14099 | ||||
| 14100 | Label variable_length(this), fixed_length(this), end(this); | |||
| 14101 | Branch(IsVariableLengthJSArrayBufferView(typed_array), &variable_length, | |||
| 14102 | &fixed_length); | |||
| 14103 | BIND(&variable_length)Bind(&variable_length); | |||
| 14104 | { | |||
| 14105 | result = | |||
| 14106 | LoadVariableLengthJSTypedArrayLength(typed_array, buffer, detached); | |||
| 14107 | Goto(&end); | |||
| 14108 | } | |||
| 14109 | ||||
| 14110 | BIND(&fixed_length)Bind(&fixed_length); | |||
| 14111 | { | |||
| 14112 | Label not_detached(this); | |||
| 14113 | Branch(IsDetachedBuffer(buffer), detached, ¬_detached); | |||
| 14114 | BIND(¬_detached)Bind(¬_detached); | |||
| 14115 | result = LoadJSTypedArrayLength(typed_array); | |||
| 14116 | Goto(&end); | |||
| 14117 | } | |||
| 14118 | BIND(&end)Bind(&end); | |||
| 14119 | return result.value(); | |||
| 14120 | } | |||
| 14121 | ||||
| 14122 | // ES #sec-integerindexedobjectlength | |||
| 14123 | TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayLength( | |||
| 14124 | TNode<JSTypedArray> array, TNode<JSArrayBuffer> buffer, | |||
| 14125 | Label* detached_or_out_of_bounds) { | |||
| 14126 | // byte_length already takes array's offset into account. | |||
| 14127 | TNode<UintPtrT> byte_length = LoadVariableLengthJSArrayBufferViewByteLength( | |||
| 14128 | array, buffer, detached_or_out_of_bounds); | |||
| 14129 | TNode<IntPtrT> element_size = | |||
| 14130 | RabGsabElementsKindToElementByteSize(LoadElementsKind(array)); | |||
| 14131 | return Unsigned(IntPtrDiv(Signed(byte_length), element_size)); | |||
| 14132 | } | |||
| 14133 | ||||
| 14134 | TNode<UintPtrT> | |||
| 14135 | CodeStubAssembler::LoadVariableLengthJSArrayBufferViewByteLength( | |||
| 14136 | TNode<JSArrayBufferView> array, TNode<JSArrayBuffer> buffer, | |||
| 14137 | Label* detached_or_out_of_bounds) { | |||
| 14138 | Label is_gsab(this), is_rab(this), end(this); | |||
| 14139 | TVARIABLE(UintPtrT, result)TVariable<UintPtrT> result(this); | |||
| 14140 | TNode<UintPtrT> array_byte_offset = LoadJSArrayBufferViewByteOffset(array); | |||
| 14141 | ||||
| 14142 | Branch(IsSharedArrayBuffer(buffer), &is_gsab, &is_rab); | |||
| 14143 | BIND(&is_gsab)Bind(&is_gsab); | |||
| 14144 | { | |||
| 14145 | // Non-length-tracking GSAB-backed ArrayBufferViews shouldn't end up here. | |||
| 14146 | CSA_DCHECK(this, IsLengthTrackingJSArrayBufferView(array))((void)0); | |||
| 14147 | // Read the byte length from the BackingStore. | |||
| 14148 | const TNode<ExternalReference> byte_length_function = | |||
| 14149 | ExternalConstant(ExternalReference::gsab_byte_length()); | |||
| 14150 | TNode<ExternalReference> isolate_ptr = | |||
| 14151 | ExternalConstant(ExternalReference::isolate_address(isolate())); | |||
| 14152 | TNode<UintPtrT> buffer_byte_length = UncheckedCast<UintPtrT>( | |||
| 14153 | CallCFunction(byte_length_function, MachineType::UintPtr(), | |||
| 14154 | std::make_pair(MachineType::Pointer(), isolate_ptr), | |||
| 14155 | std::make_pair(MachineType::AnyTagged(), buffer))); | |||
| 14156 | // Since the SharedArrayBuffer can't shrink, and we've managed to create | |||
| 14157 | // this JSArrayBufferDataView without throwing an exception, we know that | |||
| 14158 | // buffer_byte_length >= array_byte_offset. | |||
| 14159 | CSA_CHECK(this,(this)->FastCheck(UintPtrGreaterThanOrEqual(buffer_byte_length , array_byte_offset)) | |||
| 14160 | UintPtrGreaterThanOrEqual(buffer_byte_length, array_byte_offset))(this)->FastCheck(UintPtrGreaterThanOrEqual(buffer_byte_length , array_byte_offset)); | |||
| 14161 | result = UintPtrSub(buffer_byte_length, array_byte_offset); | |||
| 14162 | Goto(&end); | |||
| 14163 | } | |||
| 14164 | ||||
| 14165 | BIND(&is_rab)Bind(&is_rab); | |||
| 14166 | { | |||
| 14167 | GotoIf(IsDetachedBuffer(buffer), detached_or_out_of_bounds); | |||
| 14168 | ||||
| 14169 | TNode<UintPtrT> buffer_byte_length = LoadJSArrayBufferByteLength(buffer); | |||
| 14170 | ||||
| 14171 | Label is_length_tracking(this), not_length_tracking(this); | |||
| 14172 | Branch(IsLengthTrackingJSArrayBufferView(array), &is_length_tracking, | |||
| 14173 | ¬_length_tracking); | |||
| 14174 | ||||
| 14175 | BIND(&is_length_tracking)Bind(&is_length_tracking); | |||
| 14176 | { | |||
| 14177 | // The backing RAB might have been shrunk so that the start of the | |||
| 14178 | // TypedArray is already out of bounds. | |||
| 14179 | GotoIfNot(UintPtrLessThanOrEqual(array_byte_offset, buffer_byte_length), | |||
| 14180 | detached_or_out_of_bounds); | |||
| 14181 | result = UintPtrSub(buffer_byte_length, array_byte_offset); | |||
| 14182 | Goto(&end); | |||
| 14183 | } | |||
| 14184 | ||||
| 14185 | BIND(¬_length_tracking)Bind(¬_length_tracking); | |||
| 14186 | { | |||
| 14187 | // Check if the backing RAB has shrunk so that the buffer is out of | |||
| 14188 | // bounds. | |||
| 14189 | TNode<UintPtrT> array_byte_length = | |||
| 14190 | LoadJSArrayBufferViewByteLength(array); | |||
| 14191 | GotoIfNot(UintPtrGreaterThanOrEqual( | |||
| 14192 | buffer_byte_length, | |||
| 14193 | UintPtrAdd(array_byte_offset, array_byte_length)), | |||
| 14194 | detached_or_out_of_bounds); | |||
| 14195 | result = array_byte_length; | |||
| 14196 | Goto(&end); | |||
| 14197 | } | |||
| 14198 | } | |||
| 14199 | BIND(&end)Bind(&end); | |||
| 14200 | return result.value(); | |||
| 14201 | } | |||
| 14202 | ||||
| 14203 | void CodeStubAssembler::IsJSArrayBufferViewDetachedOrOutOfBounds( | |||
| 14204 | TNode<JSArrayBufferView> array_buffer_view, Label* detached_or_oob, | |||
| 14205 | Label* not_detached_nor_oob) { | |||
| 14206 | TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array_buffer_view); | |||
| 14207 | ||||
| 14208 | GotoIf(IsDetachedBuffer(buffer), detached_or_oob); | |||
| 14209 | GotoIfNot(IsVariableLengthJSArrayBufferView(array_buffer_view), | |||
| 14210 | not_detached_nor_oob); | |||
| 14211 | GotoIf(IsSharedArrayBuffer(buffer), not_detached_nor_oob); | |||
| 14212 | ||||
| 14213 | { | |||
| 14214 | TNode<UintPtrT> buffer_byte_length = LoadJSArrayBufferByteLength(buffer); | |||
| 14215 | TNode<UintPtrT> array_byte_offset = | |||
| 14216 | LoadJSArrayBufferViewByteOffset(array_buffer_view); | |||
| 14217 | ||||
| 14218 | Label length_tracking(this), not_length_tracking(this); | |||
| 14219 | Branch(IsLengthTrackingJSArrayBufferView(array_buffer_view), | |||
| 14220 | &length_tracking, ¬_length_tracking); | |||
| 14221 | ||||
| 14222 | BIND(&length_tracking)Bind(&length_tracking); | |||
| 14223 | { | |||
| 14224 | // The backing RAB might have been shrunk so that the start of the | |||
| 14225 | // TypedArray is already out of bounds. | |||
| 14226 | Branch(UintPtrLessThanOrEqual(array_byte_offset, buffer_byte_length), | |||
| 14227 | not_detached_nor_oob, detached_or_oob); | |||
| 14228 | } | |||
| 14229 | ||||
| 14230 | BIND(¬_length_tracking)Bind(¬_length_tracking); | |||
| 14231 | { | |||
| 14232 | // Check if the backing RAB has shrunk so that the buffer is out of | |||
| 14233 | // bounds. | |||
| 14234 | TNode<UintPtrT> array_byte_length = | |||
| 14235 | LoadJSArrayBufferViewByteLength(array_buffer_view); | |||
| 14236 | Branch(UintPtrGreaterThanOrEqual( | |||
| 14237 | buffer_byte_length, | |||
| 14238 | UintPtrAdd(array_byte_offset, array_byte_length)), | |||
| 14239 | not_detached_nor_oob, detached_or_oob); | |||
| 14240 | } | |||
| 14241 | } | |||
| 14242 | } | |||
| 14243 | ||||
| 14244 | TNode<BoolT> CodeStubAssembler::IsJSArrayBufferViewDetachedOrOutOfBoundsBoolean( | |||
| 14245 | TNode<JSArrayBufferView> array_buffer_view) { | |||
| 14246 | Label is_detached_or_out_of_bounds(this), | |||
| 14247 | not_detached_nor_out_of_bounds(this), end(this); | |||
| 14248 | TVARIABLE(BoolT, result)TVariable<BoolT> result(this); | |||
| 14249 | ||||
| 14250 | IsJSArrayBufferViewDetachedOrOutOfBounds(array_buffer_view, | |||
| 14251 | &is_detached_or_out_of_bounds, | |||
| 14252 | ¬_detached_nor_out_of_bounds); | |||
| 14253 | BIND(&is_detached_or_out_of_bounds)Bind(&is_detached_or_out_of_bounds); | |||
| 14254 | { | |||
| 14255 | result = BoolConstant(true); | |||
| 14256 | Goto(&end); | |||
| 14257 | } | |||
| 14258 | BIND(¬_detached_nor_out_of_bounds)Bind(¬_detached_nor_out_of_bounds); | |||
| 14259 | { | |||
| 14260 | result = BoolConstant(false); | |||
| 14261 | Goto(&end); | |||
| 14262 | } | |||
| 14263 | BIND(&end)Bind(&end); | |||
| 14264 | return result.value(); | |||
| 14265 | } | |||
| 14266 | ||||
| 14267 | void CodeStubAssembler::CheckJSTypedArrayIndex( | |||
| 14268 | TNode<UintPtrT> index, TNode<JSTypedArray> typed_array, | |||
| 14269 | Label* detached_or_out_of_bounds) { | |||
| 14270 | TNode<UintPtrT> len = LoadJSTypedArrayLengthAndCheckDetached( | |||
| 14271 | typed_array, detached_or_out_of_bounds); | |||
| 14272 | ||||
| 14273 | GotoIf(UintPtrGreaterThanOrEqual(index, len), detached_or_out_of_bounds); | |||
| 14274 | } | |||
| 14275 | ||||
| 14276 | // ES #sec-integerindexedobjectbytelength | |||
| 14277 | TNode<UintPtrT> CodeStubAssembler::LoadVariableLengthJSTypedArrayByteLength( | |||
| 14278 | TNode<Context> context, TNode<JSTypedArray> array, | |||
| 14279 | TNode<JSArrayBuffer> buffer) { | |||
| 14280 | Label miss(this), end(this); | |||
| 14281 | TVARIABLE(UintPtrT, result)TVariable<UintPtrT> result(this); | |||
| 14282 | ||||
| 14283 | TNode<UintPtrT> length = | |||
| 14284 | LoadVariableLengthJSTypedArrayLength(array, buffer, &miss); | |||
| 14285 | TNode<IntPtrT> element_size = | |||
| 14286 | RabGsabElementsKindToElementByteSize(LoadElementsKind(array)); | |||
| 14287 | // Conversion to signed is OK since length < JSArrayBuffer::kMaxByteLength. | |||
| 14288 | TNode<IntPtrT> byte_length = IntPtrMul(Signed(length), element_size); | |||
| 14289 | result = Unsigned(byte_length); | |||
| 14290 | Goto(&end); | |||
| 14291 | BIND(&miss)Bind(&miss); | |||
| 14292 | { | |||
| 14293 | result = UintPtrConstant(0); | |||
| 14294 | Goto(&end); | |||
| 14295 | } | |||
| 14296 | BIND(&end)Bind(&end); | |||
| 14297 | return result.value(); | |||
| 14298 | } | |||
| 14299 | ||||
| 14300 | TNode<IntPtrT> CodeStubAssembler::RabGsabElementsKindToElementByteSize( | |||
| 14301 | TNode<Int32T> elements_kind) { | |||
| 14302 | TVARIABLE(IntPtrT, result)TVariable<IntPtrT> result(this); | |||
| 14303 | Label elements_8(this), elements_16(this), elements_32(this), | |||
| 14304 | elements_64(this), not_found(this), end(this); | |||
| 14305 | int32_t elements_kinds[] = { | |||
| 14306 | RAB_GSAB_UINT8_ELEMENTS, RAB_GSAB_UINT8_CLAMPED_ELEMENTS, | |||
| 14307 | RAB_GSAB_INT8_ELEMENTS, RAB_GSAB_UINT16_ELEMENTS, | |||
| 14308 | RAB_GSAB_INT16_ELEMENTS, RAB_GSAB_UINT32_ELEMENTS, | |||
| 14309 | RAB_GSAB_INT32_ELEMENTS, RAB_GSAB_FLOAT32_ELEMENTS, | |||
| 14310 | RAB_GSAB_FLOAT64_ELEMENTS, RAB_GSAB_BIGINT64_ELEMENTS, | |||
| 14311 | RAB_GSAB_BIGUINT64_ELEMENTS}; | |||
| 14312 | Label* elements_kind_labels[] = {&elements_8, &elements_8, &elements_8, | |||
| 14313 | &elements_16, &elements_16, &elements_32, | |||
| 14314 | &elements_32, &elements_32, &elements_64, | |||
| 14315 | &elements_64, &elements_64}; | |||
| 14316 | const size_t kTypedElementsKindCount = | |||
| 14317 | LAST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND - | |||
| 14318 | FIRST_RAB_GSAB_FIXED_TYPED_ARRAY_ELEMENTS_KIND + 1; | |||
| 14319 | DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kinds))((void) 0); | |||
| 14320 | DCHECK_EQ(kTypedElementsKindCount, arraysize(elements_kind_labels))((void) 0); | |||
| 14321 | Switch(elements_kind, ¬_found, elements_kinds, elements_kind_labels, | |||
| 14322 | kTypedElementsKindCount); | |||
| 14323 | BIND(&elements_8)Bind(&elements_8); | |||
| 14324 | { | |||
| 14325 | result = IntPtrConstant(1); | |||
| 14326 | Goto(&end); | |||
| 14327 | } | |||
| 14328 | BIND(&elements_16)Bind(&elements_16); | |||
| 14329 | { | |||
| 14330 | result = IntPtrConstant(2); | |||
| 14331 | Goto(&end); | |||
| 14332 | } | |||
| 14333 | BIND(&elements_32)Bind(&elements_32); | |||
| 14334 | { | |||
| 14335 | result = IntPtrConstant(4); | |||
| 14336 | Goto(&end); | |||
| 14337 | } | |||
| 14338 | BIND(&elements_64)Bind(&elements_64); | |||
| 14339 | { | |||
| 14340 | result = IntPtrConstant(8); | |||
| 14341 | Goto(&end); | |||
| 14342 | } | |||
| 14343 | BIND(¬_found)Bind(¬_found); | |||
| 14344 | { Unreachable(); } | |||
| 14345 | BIND(&end)Bind(&end); | |||
| 14346 | return result.value(); | |||
| 14347 | } | |||
| 14348 | ||||
| 14349 | TNode<JSArrayBuffer> CodeStubAssembler::GetTypedArrayBuffer( | |||
| 14350 | TNode<Context> context, TNode<JSTypedArray> array) { | |||
| 14351 | Label call_runtime(this), done(this); | |||
| 14352 | TVARIABLE(Object, var_result)TVariable<Object> var_result(this); | |||
| 14353 | ||||
| 14354 | GotoIf(IsOnHeapTypedArray(array), &call_runtime); | |||
| 14355 | ||||
| 14356 | TNode<JSArrayBuffer> buffer = LoadJSArrayBufferViewBuffer(array); | |||
| 14357 | GotoIf(IsDetachedBuffer(buffer), &call_runtime); | |||
| 14358 | var_result = buffer; | |||
| 14359 | Goto(&done); | |||
| 14360 | ||||
| 14361 | BIND(&call_runtime)Bind(&call_runtime); | |||
| 14362 | { | |||
| 14363 | var_result = CallRuntime(Runtime::kTypedArrayGetBuffer, context, array); | |||
| 14364 | Goto(&done); | |||
| 14365 | } | |||
| 14366 | ||||
| 14367 | BIND(&done)Bind(&done); | |||
| 14368 | return CAST(var_result.value())Cast(var_result.value()); | |||
| 14369 | } | |||
| 14370 | ||||
| 14371 | CodeStubArguments::CodeStubArguments(CodeStubAssembler* assembler, | |||
| 14372 | TNode<IntPtrT> argc, TNode<RawPtrT> fp) | |||
| 14373 | : assembler_(assembler), | |||
| 14374 | argc_(argc), | |||
| 14375 | base_(), | |||
| 14376 | fp_(fp != nullptr ? fp : assembler_->LoadFramePointer()) { | |||
| 14377 | TNode<IntPtrT> offset = assembler_->IntPtrConstant( | |||
| 14378 | (StandardFrameConstants::kFixedSlotCountAboveFp + 1) * | |||
| 14379 | kSystemPointerSize); | |||
| 14380 | DCHECK_NOT_NULL(argc_)((void) 0); | |||
| 14381 | // base_ points to the first argument, not the receiver | |||
| 14382 | // whether present or not. | |||
| 14383 | base_ = assembler_->RawPtrAdd(fp_, offset); | |||
| 14384 | } | |||
| 14385 | ||||
| 14386 | TNode<Object> CodeStubArguments::GetReceiver() const { | |||
| 14387 | intptr_t offset = -kSystemPointerSize; | |||
| 14388 | return assembler_->LoadFullTagged(base_, assembler_->IntPtrConstant(offset)); | |||
| 14389 | } | |||
| 14390 | ||||
| 14391 | void CodeStubArguments::SetReceiver(TNode<Object> object) const { | |||
| 14392 | intptr_t offset = -kSystemPointerSize; | |||
| 14393 | assembler_->StoreFullTaggedNoWriteBarrier( | |||
| 14394 | base_, assembler_->IntPtrConstant(offset), object); | |||
| 14395 | } | |||
| 14396 | ||||
| 14397 | TNode<RawPtrT> CodeStubArguments::AtIndexPtr(TNode<IntPtrT> index) const { | |||
| 14398 | TNode<IntPtrT> offset = | |||
| 14399 | assembler_->ElementOffsetFromIndex(index, SYSTEM_POINTER_ELEMENTS, 0); | |||
| 14400 | return assembler_->RawPtrAdd(base_, offset); | |||
| 14401 | } | |||
| 14402 | ||||
| 14403 | TNode<Object> CodeStubArguments::AtIndex(TNode<IntPtrT> index) const { | |||
| 14404 | CSA_DCHECK(assembler_, assembler_->UintPtrOrSmiLessThan(((void)0) | |||
| 14405 | index, GetLengthWithoutReceiver()))((void)0); | |||
| 14406 | return assembler_->LoadFullTagged(AtIndexPtr(index)); | |||
| 14407 | } | |||
| 14408 | ||||
| 14409 | TNode<Object> CodeStubArguments::AtIndex(int index) const { | |||
| 14410 | return AtIndex(assembler_->IntPtrConstant(index)); | |||
| 14411 | } | |||
| 14412 | ||||
| 14413 | TNode<IntPtrT> CodeStubArguments::GetLengthWithoutReceiver() const { | |||
| 14414 | return assembler_->IntPtrSub( | |||
| 14415 | argc_, assembler_->IntPtrConstant(kJSArgcReceiverSlots)); | |||
| 14416 | } | |||
| 14417 | ||||
| 14418 | TNode<IntPtrT> CodeStubArguments::GetLengthWithReceiver() const { | |||
| 14419 | return argc_; | |||
| 14420 | } | |||
| 14421 | ||||
| 14422 | TNode<Object> CodeStubArguments::GetOptionalArgumentValue( | |||
| 14423 | TNode<IntPtrT> index, TNode<Object> default_value) { | |||
| 14424 | CodeStubAssembler::TVariable<Object> result(assembler_); | |||
| 14425 | CodeStubAssembler::Label argument_missing(assembler_), | |||
| 14426 | argument_done(assembler_, &result); | |||
| 14427 | ||||
| 14428 | assembler_->GotoIf( | |||
| 14429 | assembler_->UintPtrGreaterThanOrEqual(index, GetLengthWithoutReceiver()), | |||
| 14430 | &argument_missing); | |||
| 14431 | result = AtIndex(index); | |||
| 14432 | assembler_->Goto(&argument_done); | |||
| 14433 | ||||
| 14434 | assembler_->BIND(&argument_missing)Bind(&argument_missing); | |||
| 14435 | result = default_value; | |||
| 14436 | assembler_->Goto(&argument_done); | |||
| 14437 | ||||
| 14438 | assembler_->BIND(&argument_done)Bind(&argument_done); | |||
| 14439 | return result.value(); | |||
| 14440 | } | |||
| 14441 | ||||
| 14442 | void CodeStubArguments::ForEach( | |||
| 14443 | const CodeStubAssembler::VariableList& vars, | |||
| 14444 | const CodeStubArguments::ForEachBodyFunction& body, TNode<IntPtrT> first, | |||
| 14445 | TNode<IntPtrT> last) const { | |||
| 14446 | assembler_->Comment("CodeStubArguments::ForEach"); | |||
| 14447 | if (first == nullptr) { | |||
| 14448 | first = assembler_->IntPtrConstant(0); | |||
| 14449 | } | |||
| 14450 | if (last == nullptr) { | |||
| 14451 | last = GetLengthWithoutReceiver(); | |||
| 14452 | } | |||
| 14453 | TNode<RawPtrT> start = AtIndexPtr(first); | |||
| 14454 | TNode<RawPtrT> end = AtIndexPtr(last); | |||
| 14455 | const int increment = kSystemPointerSize; | |||
| 14456 | assembler_->BuildFastLoop<RawPtrT>( | |||
| 14457 | vars, start, end, | |||
| 14458 | [&](TNode<RawPtrT> current) { | |||
| 14459 | TNode<Object> arg = assembler_->LoadFullTagged(current); | |||
| 14460 | body(arg); | |||
| 14461 | }, | |||
| 14462 | increment, CodeStubAssembler::IndexAdvanceMode::kPost); | |||
| 14463 | } | |||
| 14464 | ||||
| 14465 | void CodeStubArguments::PopAndReturn(TNode<Object> value) { | |||
| 14466 | TNode<IntPtrT> pop_count = GetLengthWithReceiver(); | |||
| 14467 | assembler_->PopAndReturn(pop_count, value); | |||
| 14468 | } | |||
| 14469 | ||||
| 14470 | TNode<BoolT> CodeStubAssembler::IsFastElementsKind( | |||
| 14471 | TNode<Int32T> elements_kind) { | |||
| 14472 | STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND)static_assert(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND , "FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND"); | |||
| 14473 | return Uint32LessThanOrEqual(elements_kind, | |||
| 14474 | Int32Constant(LAST_FAST_ELEMENTS_KIND)); | |||
| 14475 | } | |||
| 14476 | ||||
| 14477 | TNode<BoolT> CodeStubAssembler::IsFastOrNonExtensibleOrSealedElementsKind( | |||
| 14478 | TNode<Int32T> elements_kind) { | |||
| 14479 | STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND)static_assert(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND , "FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND"); | |||
| 14480 | STATIC_ASSERT(LAST_FAST_ELEMENTS_KIND + 1 == PACKED_NONEXTENSIBLE_ELEMENTS)static_assert(LAST_FAST_ELEMENTS_KIND + 1 == PACKED_NONEXTENSIBLE_ELEMENTS , "LAST_FAST_ELEMENTS_KIND + 1 == PACKED_NONEXTENSIBLE_ELEMENTS" ); | |||
| 14481 | STATIC_ASSERT(PACKED_NONEXTENSIBLE_ELEMENTS + 1 ==static_assert(PACKED_NONEXTENSIBLE_ELEMENTS + 1 == HOLEY_NONEXTENSIBLE_ELEMENTS , "PACKED_NONEXTENSIBLE_ELEMENTS + 1 == HOLEY_NONEXTENSIBLE_ELEMENTS" ) | |||
| 14482 | HOLEY_NONEXTENSIBLE_ELEMENTS)static_assert(PACKED_NONEXTENSIBLE_ELEMENTS + 1 == HOLEY_NONEXTENSIBLE_ELEMENTS , "PACKED_NONEXTENSIBLE_ELEMENTS + 1 == HOLEY_NONEXTENSIBLE_ELEMENTS" ); | |||
| 14483 | STATIC_ASSERT(HOLEY_NONEXTENSIBLE_ELEMENTS + 1 == PACKED_SEALED_ELEMENTS)static_assert(HOLEY_NONEXTENSIBLE_ELEMENTS + 1 == PACKED_SEALED_ELEMENTS , "HOLEY_NONEXTENSIBLE_ELEMENTS + 1 == PACKED_SEALED_ELEMENTS" ); | |||
| 14484 | STATIC_ASSERT(PACKED_SEALED_ELEMENTS + 1 == HOLEY_SEALED_ELEMENTS)static_assert(PACKED_SEALED_ELEMENTS + 1 == HOLEY_SEALED_ELEMENTS , "PACKED_SEALED_ELEMENTS + 1 == HOLEY_SEALED_ELEMENTS"); | |||
| 14485 | return Uint32LessThanOrEqual(elements_kind, | |||
| 14486 | Int32Constant(HOLEY_SEALED_ELEMENTS)); | |||
| 14487 | } | |||
| 14488 | ||||
| 14489 | TNode<BoolT> CodeStubAssembler::IsDoubleElementsKind( | |||
| 14490 | TNode<Int32T> elements_kind) { | |||
| 14491 | STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND)static_assert(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND , "FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND"); | |||
| 14492 | STATIC_ASSERT((PACKED_DOUBLE_ELEMENTS & 1) == 0)static_assert((PACKED_DOUBLE_ELEMENTS & 1) == 0, "(PACKED_DOUBLE_ELEMENTS & 1) == 0" ); | |||
| 14493 | STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS + 1 == HOLEY_DOUBLE_ELEMENTS)static_assert(PACKED_DOUBLE_ELEMENTS + 1 == HOLEY_DOUBLE_ELEMENTS , "PACKED_DOUBLE_ELEMENTS + 1 == HOLEY_DOUBLE_ELEMENTS"); | |||
| 14494 | return Word32Equal(Word32Shr(elements_kind, Int32Constant(1)), | |||
| 14495 | Int32Constant(PACKED_DOUBLE_ELEMENTS / 2)); | |||
| 14496 | } | |||
| 14497 | ||||
| 14498 | TNode<BoolT> CodeStubAssembler::IsFastSmiOrTaggedElementsKind( | |||
| 14499 | TNode<Int32T> elements_kind) { | |||
| 14500 | STATIC_ASSERT(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND)static_assert(FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND , "FIRST_ELEMENTS_KIND == FIRST_FAST_ELEMENTS_KIND"); | |||
| 14501 | STATIC_ASSERT(PACKED_DOUBLE_ELEMENTS > TERMINAL_FAST_ELEMENTS_KIND)static_assert(PACKED_DOUBLE_ELEMENTS > TERMINAL_FAST_ELEMENTS_KIND , "PACKED_DOUBLE_ELEMENTS > TERMINAL_FAST_ELEMENTS_KIND"); | |||
| 14502 | STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS > TERMINAL_FAST_ELEMENTS_KIND)static_assert(HOLEY_DOUBLE_ELEMENTS > TERMINAL_FAST_ELEMENTS_KIND , "HOLEY_DOUBLE_ELEMENTS > TERMINAL_FAST_ELEMENTS_KIND"); | |||
| 14503 | return Uint32LessThanOrEqual(elements_kind, | |||
| 14504 | Int32Constant(TERMINAL_FAST_ELEMENTS_KIND)); | |||
| 14505 | } | |||
| 14506 | ||||
| 14507 | TNode<BoolT> CodeStubAssembler::IsFastSmiElementsKind( | |||
| 14508 | TNode<Int32T> elements_kind) { | |||
| 14509 | return Uint32LessThanOrEqual(elements_kind, | |||
| 14510 | Int32Constant(HOLEY_SMI_ELEMENTS)); | |||
| 14511 | } | |||
| 14512 | ||||
| 14513 | TNode<BoolT> CodeStubAssembler::IsHoleyFastElementsKind( | |||
| 14514 | TNode<Int32T> elements_kind) { | |||
| 14515 | CSA_DCHECK(this, IsFastElementsKind(elements_kind))((void)0); | |||
| 14516 | ||||
| 14517 | STATIC_ASSERT(HOLEY_SMI_ELEMENTS == (PACKED_SMI_ELEMENTS | 1))static_assert(HOLEY_SMI_ELEMENTS == (PACKED_SMI_ELEMENTS | 1) , "HOLEY_SMI_ELEMENTS == (PACKED_SMI_ELEMENTS | 1)"); | |||
| 14518 | STATIC_ASSERT(HOLEY_ELEMENTS == (PACKED_ELEMENTS | 1))static_assert(HOLEY_ELEMENTS == (PACKED_ELEMENTS | 1), "HOLEY_ELEMENTS == (PACKED_ELEMENTS | 1)" ); | |||
| 14519 | STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == (PACKED_DOUBLE_ELEMENTS | 1))static_assert(HOLEY_DOUBLE_ELEMENTS == (PACKED_DOUBLE_ELEMENTS | 1), "HOLEY_DOUBLE_ELEMENTS == (PACKED_DOUBLE_ELEMENTS | 1)" ); | |||
| 14520 | return IsSetWord32(elements_kind, 1); | |||
| 14521 | } | |||
| 14522 | ||||
| 14523 | TNode<BoolT> CodeStubAssembler::IsHoleyFastElementsKindForRead( | |||
| 14524 | TNode<Int32T> elements_kind) { | |||
| 14525 | CSA_DCHECK(this, Uint32LessThanOrEqual(((void)0) | |||
| 14526 | elements_kind,((void)0) | |||
| 14527 | Int32Constant(LAST_ANY_NONEXTENSIBLE_ELEMENTS_KIND)))((void)0); | |||
| 14528 | ||||
| 14529 | STATIC_ASSERT(HOLEY_SMI_ELEMENTS == (PACKED_SMI_ELEMENTS | 1))static_assert(HOLEY_SMI_ELEMENTS == (PACKED_SMI_ELEMENTS | 1) , "HOLEY_SMI_ELEMENTS == (PACKED_SMI_ELEMENTS | 1)"); | |||
| 14530 | STATIC_ASSERT(HOLEY_ELEMENTS == (PACKED_ELEMENTS | 1))static_assert(HOLEY_ELEMENTS == (PACKED_ELEMENTS | 1), "HOLEY_ELEMENTS == (PACKED_ELEMENTS | 1)" ); | |||
| 14531 | STATIC_ASSERT(HOLEY_DOUBLE_ELEMENTS == (PACKED_DOUBLE_ELEMENTS | 1))static_assert(HOLEY_DOUBLE_ELEMENTS == (PACKED_DOUBLE_ELEMENTS | 1), "HOLEY_DOUBLE_ELEMENTS == (PACKED_DOUBLE_ELEMENTS | 1)" ); | |||
| 14532 | STATIC_ASSERT(HOLEY_NONEXTENSIBLE_ELEMENTS ==static_assert(HOLEY_NONEXTENSIBLE_ELEMENTS == (PACKED_NONEXTENSIBLE_ELEMENTS | 1), "HOLEY_NONEXTENSIBLE_ELEMENTS == (PACKED_NONEXTENSIBLE_ELEMENTS | 1)" ) | |||
| 14533 | (PACKED_NONEXTENSIBLE_ELEMENTS | 1))static_assert(HOLEY_NONEXTENSIBLE_ELEMENTS == (PACKED_NONEXTENSIBLE_ELEMENTS | 1), "HOLEY_NONEXTENSIBLE_ELEMENTS == (PACKED_NONEXTENSIBLE_ELEMENTS | 1)" ); | |||
| 14534 | STATIC_ASSERT(HOLEY_SEALED_ELEMENTS == (PACKED_SEALED_ELEMENTS | 1))static_assert(HOLEY_SEALED_ELEMENTS == (PACKED_SEALED_ELEMENTS | 1), "HOLEY_SEALED_ELEMENTS == (PACKED_SEALED_ELEMENTS | 1)" ); | |||
| 14535 | STATIC_ASSERT(HOLEY_FROZEN_ELEMENTS == (PACKED_FROZEN_ELEMENTS | 1))static_assert(HOLEY_FROZEN_ELEMENTS == (PACKED_FROZEN_ELEMENTS | 1), "HOLEY_FROZEN_ELEMENTS == (PACKED_FROZEN_ELEMENTS | 1)" ); | |||
| 14536 | return IsSetWord32(elements_kind, 1); | |||
| 14537 | } | |||
| 14538 | ||||
| 14539 | TNode<BoolT> CodeStubAssembler::IsElementsKindGreaterThan( | |||
| 14540 | TNode<Int32T> target_kind, ElementsKind reference_kind) { | |||
| 14541 | return Int32GreaterThan(target_kind, Int32Constant(reference_kind)); | |||
| 14542 | } | |||
| 14543 | ||||
| 14544 | TNode<BoolT> CodeStubAssembler::IsElementsKindGreaterThanOrEqual( | |||
| 14545 | TNode<Int32T> target_kind, ElementsKind reference_kind) { | |||
| 14546 | return Int32GreaterThanOrEqual(target_kind, Int32Constant(reference_kind)); | |||
| 14547 | } | |||
| 14548 | ||||
| 14549 | TNode<BoolT> CodeStubAssembler::IsElementsKindLessThanOrEqual( | |||
| 14550 | TNode<Int32T> target_kind, ElementsKind reference_kind) { | |||
| 14551 | return Int32LessThanOrEqual(target_kind, Int32Constant(reference_kind)); | |||
| 14552 | } | |||
| 14553 | ||||
| 14554 | TNode<BoolT> CodeStubAssembler::IsDebugActive() { | |||
| 14555 | TNode<Uint8T> is_debug_active = Load<Uint8T>( | |||
| 14556 | ExternalConstant(ExternalReference::debug_is_active_address(isolate()))); | |||
| 14557 | return Word32NotEqual(is_debug_active, Int32Constant(0)); | |||
| 14558 | } | |||
| 14559 | ||||
| 14560 | TNode<BoolT> CodeStubAssembler::IsSideEffectFreeDebuggingActive() { | |||
| 14561 | TNode<Uint8T> debug_execution_mode = Load<Uint8T>(ExternalConstant( | |||
| 14562 | ExternalReference::debug_execution_mode_address(isolate()))); | |||
| 14563 | ||||
| 14564 | TNode<BoolT> is_active = | |||
| 14565 | Word32Equal(debug_execution_mode, | |||
| 14566 | Int32Constant(DebugInfo::ExecutionMode::kSideEffects)); | |||
| 14567 | ||||
| 14568 | return is_active; | |||
| 14569 | } | |||
| 14570 | ||||
| 14571 | TNode<BoolT> CodeStubAssembler::HasAsyncEventDelegate() { | |||
| 14572 | const TNode<RawPtrT> async_event_delegate = Load<RawPtrT>(ExternalConstant( | |||
| 14573 | ExternalReference::async_event_delegate_address(isolate()))); | |||
| 14574 | return WordNotEqual(async_event_delegate, IntPtrConstant(0)); | |||
| 14575 | } | |||
| 14576 | ||||
| 14577 | TNode<Uint32T> CodeStubAssembler::PromiseHookFlags() { | |||
| 14578 | return Load<Uint32T>(ExternalConstant( | |||
| 14579 | ExternalReference::promise_hook_flags_address(isolate()))); | |||
| 14580 | } | |||
| 14581 | ||||
| 14582 | TNode<BoolT> CodeStubAssembler::IsAnyPromiseHookEnabled(TNode<Uint32T> flags) { | |||
| 14583 | uint32_t mask = Isolate::PromiseHookFields::HasContextPromiseHook::kMask | | |||
| 14584 | Isolate::PromiseHookFields::HasIsolatePromiseHook::kMask; | |||
| 14585 | return IsSetWord32(flags, mask); | |||
| 14586 | } | |||
| 14587 | ||||
| 14588 | TNode<BoolT> CodeStubAssembler::IsIsolatePromiseHookEnabled( | |||
| 14589 | TNode<Uint32T> flags) { | |||
| 14590 | return IsSetWord32<Isolate::PromiseHookFields::HasIsolatePromiseHook>(flags); | |||
| 14591 | } | |||
| 14592 | ||||
| 14593 | #ifdef V8_ENABLE_JAVASCRIPT_PROMISE_HOOKS1 | |||
| 14594 | TNode<BoolT> CodeStubAssembler::IsContextPromiseHookEnabled( | |||
| 14595 | TNode<Uint32T> flags) { | |||
| 14596 | return IsSetWord32<Isolate::PromiseHookFields::HasContextPromiseHook>(flags); | |||
| 14597 | } | |||
| 14598 | #endif | |||
| 14599 | ||||
| 14600 | TNode<BoolT> CodeStubAssembler:: | |||
| 14601 | IsIsolatePromiseHookEnabledOrHasAsyncEventDelegate(TNode<Uint32T> flags) { | |||
| 14602 | uint32_t mask = Isolate::PromiseHookFields::HasIsolatePromiseHook::kMask | | |||
| 14603 | Isolate::PromiseHookFields::HasAsyncEventDelegate::kMask; | |||
| 14604 | return IsSetWord32(flags, mask); | |||
| 14605 | } | |||
| 14606 | ||||
| 14607 | TNode<BoolT> CodeStubAssembler:: | |||
| 14608 | IsIsolatePromiseHookEnabledOrDebugIsActiveOrHasAsyncEventDelegate( | |||
| 14609 | TNode<Uint32T> flags) { | |||
| 14610 | uint32_t mask = Isolate::PromiseHookFields::HasIsolatePromiseHook::kMask | | |||
| 14611 | Isolate::PromiseHookFields::HasAsyncEventDelegate::kMask | | |||
| 14612 | Isolate::PromiseHookFields::IsDebugActive::kMask; | |||
| 14613 | return IsSetWord32(flags, mask); | |||
| 14614 | } | |||
| 14615 | ||||
| 14616 | TNode<BoolT> CodeStubAssembler::NeedsAnyPromiseHooks(TNode<Uint32T> flags) { | |||
| 14617 | return Word32NotEqual(flags, Int32Constant(0)); | |||
| 14618 | } | |||
| 14619 | ||||
| 14620 | TNode<CodeT> CodeStubAssembler::LoadBuiltin(TNode<Smi> builtin_id) { | |||
| 14621 | CSA_DCHECK(this, SmiBelow(builtin_id, SmiConstant(Builtins::kBuiltinCount)))((void)0); | |||
| 14622 | ||||
| 14623 | TNode<IntPtrT> offset = | |||
| 14624 | ElementOffsetFromIndex(SmiToBInt(builtin_id), SYSTEM_POINTER_ELEMENTS); | |||
| 14625 | ||||
| 14626 | TNode<ExternalReference> table = | |||
| 14627 | ExternalConstant(ExternalReference::builtins_table(isolate())); | |||
| 14628 | ||||
| 14629 | return CAST(BitcastWordToTagged(Load<RawPtrT>(table, offset)))Cast(BitcastWordToTagged(Load<RawPtrT>(table, offset))); | |||
| 14630 | } | |||
| 14631 | ||||
| 14632 | TNode<CodeT> CodeStubAssembler::GetSharedFunctionInfoCode( | |||
| 14633 | TNode<SharedFunctionInfo> shared_info, TVariable<Uint16T>* data_type_out, | |||
| 14634 | Label* if_compile_lazy) { | |||
| 14635 | TNode<Object> sfi_data = | |||
| 14636 | LoadObjectField(shared_info, SharedFunctionInfo::kFunctionDataOffset); | |||
| 14637 | ||||
| 14638 | TVARIABLE(CodeT, sfi_code)TVariable<CodeT> sfi_code(this); | |||
| 14639 | ||||
| 14640 | Label done(this); | |||
| 14641 | Label check_instance_type(this); | |||
| 14642 | ||||
| 14643 | // IsSmi: Is builtin | |||
| 14644 | GotoIf(TaggedIsNotSmi(sfi_data), &check_instance_type); | |||
| 14645 | if (data_type_out) { | |||
| 14646 | *data_type_out = Uint16Constant(0); | |||
| 14647 | } | |||
| 14648 | if (if_compile_lazy) { | |||
| 14649 | GotoIf(SmiEqual(CAST(sfi_data)Cast(sfi_data), SmiConstant(Builtin::kCompileLazy)), | |||
| 14650 | if_compile_lazy); | |||
| 14651 | } | |||
| 14652 | sfi_code = LoadBuiltin(CAST(sfi_data)Cast(sfi_data)); | |||
| 14653 | Goto(&done); | |||
| 14654 | ||||
| 14655 | // Switch on data's instance type. | |||
| 14656 | BIND(&check_instance_type)Bind(&check_instance_type); | |||
| 14657 | TNode<Uint16T> data_type = LoadInstanceType(CAST(sfi_data)Cast(sfi_data)); | |||
| 14658 | if (data_type_out) { | |||
| 14659 | *data_type_out = data_type; | |||
| 14660 | } | |||
| 14661 | ||||
| 14662 | int32_t case_values[] = { | |||
| 14663 | BYTECODE_ARRAY_TYPE, | |||
| 14664 | CODET_TYPE, | |||
| 14665 | UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_TYPE, | |||
| 14666 | UNCOMPILED_DATA_WITH_PREPARSE_DATA_TYPE, | |||
| 14667 | UNCOMPILED_DATA_WITHOUT_PREPARSE_DATA_WITH_JOB_TYPE, | |||
| 14668 | UNCOMPILED_DATA_WITH_PREPARSE_DATA_AND_JOB_TYPE, | |||
| 14669 | FUNCTION_TEMPLATE_INFO_TYPE, | |||
| 14670 | #if V8_ENABLE_WEBASSEMBLY1 | |||
| 14671 | WASM_CAPI_FUNCTION_DATA_TYPE, | |||
| 14672 | WASM_EXPORTED_FUNCTION_DATA_TYPE, | |||
| 14673 | WASM_JS_FUNCTION_DATA_TYPE, | |||
| 14674 | ASM_WASM_DATA_TYPE, | |||
| 14675 | WASM_ON_FULFILLED_DATA_TYPE, | |||
| 14676 | #endif // V8_ENABLE_WEBASSEMBLY | |||
| 14677 | }; | |||
| 14678 | Label check_is_bytecode_array(this); | |||
| 14679 | Label check_is_baseline_data(this); | |||
| 14680 | Label check_is_asm_wasm_data(this); | |||
| 14681 | Label check_is_uncompiled_data(this); | |||
| 14682 | Label check_is_function_template_info(this); | |||
| 14683 | Label check_is_interpreter_data(this); | |||
| 14684 | Label check_is_wasm_function_data(this); | |||
| 14685 | Label check_is_wasm_on_fulfilled(this); | |||
| 14686 | Label* case_labels[] = { | |||
| 14687 | &check_is_bytecode_array, | |||
| 14688 | &check_is_baseline_data, | |||
| 14689 | &check_is_uncompiled_data, | |||
| 14690 | &check_is_uncompiled_data, | |||
| 14691 | &check_is_uncompiled_data, | |||
| 14692 | &check_is_uncompiled_data, | |||
| 14693 | &check_is_function_template_info, | |||
| 14694 | #if V8_ENABLE_WEBASSEMBLY1 | |||
| 14695 | &check_is_wasm_function_data, | |||
| 14696 | &check_is_wasm_function_data, | |||
| 14697 | &check_is_wasm_function_data, | |||
| 14698 | &check_is_asm_wasm_data, | |||
| 14699 | &check_is_wasm_on_fulfilled, | |||
| 14700 | #endif // V8_ENABLE_WEBASSEMBLY | |||
| 14701 | }; | |||
| 14702 | STATIC_ASSERT(arraysize(case_values) == arraysize(case_labels))static_assert((sizeof(ArraySizeHelper(case_values))) == (sizeof (ArraySizeHelper(case_labels))), "arraysize(case_values) == arraysize(case_labels)" ); | |||
| 14703 | Switch(data_type, &check_is_interpreter_data, case_values, case_labels, | |||
| 14704 | arraysize(case_labels)(sizeof(ArraySizeHelper(case_labels)))); | |||
| 14705 | ||||
| 14706 | // IsBytecodeArray: Interpret bytecode | |||
| 14707 | BIND(&check_is_bytecode_array)Bind(&check_is_bytecode_array); | |||
| 14708 | sfi_code = HeapConstant(BUILTIN_CODE(isolate(), InterpreterEntryTrampoline)(isolate())->builtins()->code_handle(i::Builtin::kInterpreterEntryTrampoline )); | |||
| 14709 | Goto(&done); | |||
| 14710 | ||||
| 14711 | // IsBaselineData: Execute baseline code | |||
| 14712 | BIND(&check_is_baseline_data)Bind(&check_is_baseline_data); | |||
| 14713 | { | |||
| 14714 | TNode<CodeT> baseline_code = CAST(sfi_data)Cast(sfi_data); | |||
| 14715 | sfi_code = baseline_code; | |||
| 14716 | Goto(&done); | |||
| 14717 | } | |||
| 14718 | ||||
| 14719 | // IsUncompiledDataWithPreparseData | IsUncompiledDataWithoutPreparseData: | |||
| 14720 | // Compile lazy | |||
| 14721 | BIND(&check_is_uncompiled_data)Bind(&check_is_uncompiled_data); | |||
| 14722 | sfi_code = HeapConstant(BUILTIN_CODE(isolate(), CompileLazy)(isolate())->builtins()->code_handle(i::Builtin::kCompileLazy )); | |||
| 14723 | Goto(if_compile_lazy ? if_compile_lazy : &done); | |||
| 14724 | ||||
| 14725 | // IsFunctionTemplateInfo: API call | |||
| 14726 | BIND(&check_is_function_template_info)Bind(&check_is_function_template_info); | |||
| 14727 | sfi_code = HeapConstant(BUILTIN_CODE(isolate(), HandleApiCall)(isolate())->builtins()->code_handle(i::Builtin::kHandleApiCall )); | |||
| 14728 | Goto(&done); | |||
| 14729 | ||||
| 14730 | // IsInterpreterData: Interpret bytecode | |||
| 14731 | BIND(&check_is_interpreter_data)Bind(&check_is_interpreter_data); | |||
| 14732 | // This is the default branch, so assert that we have the expected data type. | |||
| 14733 | CSA_DCHECK(this,((void)0) | |||
| 14734 | Word32Equal(data_type, Int32Constant(INTERPRETER_DATA_TYPE)))((void)0); | |||
| 14735 | { | |||
| 14736 | TNode<CodeT> trampoline = | |||
| 14737 | LoadInterpreterDataInterpreterTrampoline(CAST(sfi_data)Cast(sfi_data)); | |||
| 14738 | sfi_code = trampoline; | |||
| 14739 | } | |||
| 14740 | Goto(&done); | |||
| 14741 | ||||
| 14742 | #if V8_ENABLE_WEBASSEMBLY1 | |||
| 14743 | // IsWasmFunctionData: Use the wrapper code | |||
| 14744 | BIND(&check_is_wasm_function_data)Bind(&check_is_wasm_function_data); | |||
| 14745 | sfi_code = CAST(LoadObjectField(Cast(LoadObjectField( Cast(sfi_data), WasmExportedFunctionData ::kWrapperCodeOffset)) | |||
| 14746 | CAST(sfi_data), WasmExportedFunctionData::kWrapperCodeOffset))Cast(LoadObjectField( Cast(sfi_data), WasmExportedFunctionData ::kWrapperCodeOffset)); | |||
| 14747 | Goto(&done); | |||
| 14748 | ||||
| 14749 | // IsAsmWasmData: Instantiate using AsmWasmData | |||
| 14750 | BIND(&check_is_asm_wasm_data)Bind(&check_is_asm_wasm_data); | |||
| 14751 | sfi_code = HeapConstant(BUILTIN_CODE(isolate(), InstantiateAsmJs)(isolate())->builtins()->code_handle(i::Builtin::kInstantiateAsmJs )); | |||
| 14752 | Goto(&done); | |||
| 14753 | ||||
| 14754 | // IsWasmOnFulfilledData: Resume the suspended wasm continuation. | |||
| 14755 | BIND(&check_is_wasm_on_fulfilled)Bind(&check_is_wasm_on_fulfilled); | |||
| 14756 | sfi_code = HeapConstant(BUILTIN_CODE(isolate(), WasmResume)(isolate())->builtins()->code_handle(i::Builtin::kWasmResume )); | |||
| 14757 | Goto(&done); | |||
| 14758 | #endif // V8_ENABLE_WEBASSEMBLY | |||
| 14759 | ||||
| 14760 | BIND(&done)Bind(&done); | |||
| 14761 | return sfi_code.value(); | |||
| 14762 | } | |||
| 14763 | ||||
| 14764 | TNode<RawPtrT> CodeStubAssembler::GetCodeEntry(TNode<CodeT> code) { | |||
| 14765 | #ifdef V8_EXTERNAL_CODE_SPACE | |||
| 14766 | TNode<CodeDataContainer> cdc = CodeDataContainerFromCodeT(code); | |||
| 14767 | return LoadExternalPointerFromObject( | |||
| 14768 | cdc, IntPtrConstant(CodeDataContainer::kCodeEntryPointOffset), | |||
| 14769 | kCodeEntryPointTag); | |||
| 14770 | #else | |||
| 14771 | TNode<IntPtrT> object = BitcastTaggedToWord(code); | |||
| 14772 | return ReinterpretCast<RawPtrT>( | |||
| 14773 | IntPtrAdd(object, IntPtrConstant(Code::kHeaderSize - kHeapObjectTag))); | |||
| 14774 | #endif | |||
| 14775 | } | |||
| 14776 | ||||
| 14777 | TNode<JSFunction> CodeStubAssembler::AllocateFunctionWithMapAndContext( | |||
| 14778 | TNode<Map> map, TNode<SharedFunctionInfo> shared_info, | |||
| 14779 | TNode<Context> context) { | |||
| 14780 | const TNode<CodeT> code = GetSharedFunctionInfoCode(shared_info); | |||
| 14781 | ||||
| 14782 | // TODO(ishell): All the callers of this function pass map loaded from | |||
| 14783 | // Context::STRICT_FUNCTION_WITHOUT_PROTOTYPE_MAP_INDEX. So we can remove | |||
| 14784 | // map parameter. | |||
| 14785 | CSA_DCHECK(this, Word32BinaryNot(IsConstructorMap(map)))((void)0); | |||
| 14786 | CSA_DCHECK(this, Word32BinaryNot(IsFunctionWithPrototypeSlotMap(map)))((void)0); | |||
| 14787 | const TNode<HeapObject> fun = Allocate(JSFunction::kSizeWithoutPrototype); | |||
| 14788 | STATIC_ASSERT(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize)static_assert(JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize , "JSFunction::kSizeWithoutPrototype == 7 * kTaggedSize"); | |||
| 14789 | StoreMapNoWriteBarrier(fun, map); | |||
| 14790 | StoreObjectFieldRoot(fun, JSObject::kPropertiesOrHashOffset, | |||
| 14791 | RootIndex::kEmptyFixedArray); | |||
| 14792 | StoreObjectFieldRoot(fun, JSObject::kElementsOffset, | |||
| 14793 | RootIndex::kEmptyFixedArray); | |||
| 14794 | StoreObjectFieldRoot(fun, JSFunction::kFeedbackCellOffset, | |||
| 14795 | RootIndex::kManyClosuresCell); | |||
| 14796 | StoreObjectFieldNoWriteBarrier(fun, JSFunction::kSharedFunctionInfoOffset, | |||
| 14797 | shared_info); | |||
| 14798 | StoreObjectFieldNoWriteBarrier(fun, JSFunction::kContextOffset, context); | |||
| 14799 | StoreObjectField(fun, JSFunction::kCodeOffset, code); | |||
| 14800 | return CAST(fun)Cast(fun); | |||
| 14801 | } | |||
| 14802 | ||||
| 14803 | void CodeStubAssembler::CheckPrototypeEnumCache(TNode<JSReceiver> receiver, | |||
| 14804 | TNode<Map> receiver_map, | |||
| 14805 | Label* if_fast, | |||
| 14806 | Label* if_slow) { | |||
| 14807 | TVARIABLE(JSReceiver, var_object, receiver)TVariable<JSReceiver> var_object(receiver, this); | |||
| 14808 | TVARIABLE(Map, object_map, receiver_map)TVariable<Map> object_map(receiver_map, this); | |||
| 14809 | ||||
| 14810 | Label loop(this, {&var_object, &object_map}), done_loop(this); | |||
| 14811 | Goto(&loop); | |||
| 14812 | BIND(&loop)Bind(&loop); | |||
| 14813 | { | |||
| 14814 | // Check that there are no elements on the current {var_object}. | |||
| 14815 | Label if_no_elements(this); | |||
| 14816 | ||||
| 14817 | // The following relies on the elements only aliasing with JSProxy::target, | |||
| 14818 | // which is a JavaScript value and hence cannot be confused with an elements | |||
| 14819 | // backing store. | |||
| 14820 | STATIC_ASSERT(static_cast<int>(JSObject::kElementsOffset) ==static_assert(static_cast<int>(JSObject::kElementsOffset ) == static_cast<int>(JSProxy::kTargetOffset), "static_cast<int>(JSObject::kElementsOffset) == static_cast<int>(JSProxy::kTargetOffset)" ) | |||
| 14821 | static_cast<int>(JSProxy::kTargetOffset))static_assert(static_cast<int>(JSObject::kElementsOffset ) == static_cast<int>(JSProxy::kTargetOffset), "static_cast<int>(JSObject::kElementsOffset) == static_cast<int>(JSProxy::kTargetOffset)" ); | |||
| 14822 | TNode<Object> object_elements = | |||
| 14823 | LoadObjectField(var_object.value(), JSObject::kElementsOffset); | |||
| 14824 | GotoIf(IsEmptyFixedArray(object_elements), &if_no_elements); | |||
| 14825 | GotoIf(IsEmptySlowElementDictionary(object_elements), &if_no_elements); | |||
| 14826 | ||||
| 14827 | // It might still be an empty JSArray. | |||
| 14828 | GotoIfNot(IsJSArrayMap(object_map.value()), if_slow); | |||
| 14829 | TNode<Number> object_length = LoadJSArrayLength(CAST(var_object.value())Cast(var_object.value())); | |||
| 14830 | Branch(TaggedEqual(object_length, SmiConstant(0)), &if_no_elements, | |||
| 14831 | if_slow); | |||
| 14832 | ||||
| 14833 | // Continue with {var_object}'s prototype. | |||
| 14834 | BIND(&if_no_elements)Bind(&if_no_elements); | |||
| 14835 | TNode<HeapObject> object = LoadMapPrototype(object_map.value()); | |||
| 14836 | GotoIf(IsNull(object), if_fast); | |||
| 14837 | ||||
| 14838 | // For all {object}s but the {receiver}, check that the cache is empty. | |||
| 14839 | var_object = CAST(object)Cast(object); | |||
| 14840 | object_map = LoadMap(object); | |||
| 14841 | TNode<WordT> object_enum_length = LoadMapEnumLength(object_map.value()); | |||
| 14842 | Branch(WordEqual(object_enum_length, IntPtrConstant(0)), &loop, if_slow); | |||
| 14843 | } | |||
| 14844 | } | |||
| 14845 | ||||
| 14846 | TNode<Map> CodeStubAssembler::CheckEnumCache(TNode<JSReceiver> receiver, | |||
| 14847 | Label* if_empty, | |||
| 14848 | Label* if_runtime) { | |||
| 14849 | Label if_fast(this), if_cache(this), if_no_cache(this, Label::kDeferred); | |||
| 14850 | TNode<Map> receiver_map = LoadMap(receiver); | |||
| 14851 | ||||
| 14852 | // Check if the enum length field of the {receiver} is properly initialized, | |||
| 14853 | // indicating that there is an enum cache. | |||
| 14854 | TNode<WordT> receiver_enum_length = LoadMapEnumLength(receiver_map); | |||
| 14855 | Branch(WordEqual(receiver_enum_length, | |||
| 14856 | IntPtrConstant(kInvalidEnumCacheSentinel)), | |||
| 14857 | &if_no_cache, &if_cache); | |||
| 14858 | ||||
| 14859 | BIND(&if_no_cache)Bind(&if_no_cache); | |||
| 14860 | { | |||
| 14861 | // Avoid runtime-call for empty dictionary receivers. | |||
| 14862 | GotoIfNot(IsDictionaryMap(receiver_map), if_runtime); | |||
| 14863 | TNode<Smi> length; | |||
| 14864 | TNode<HeapObject> properties = LoadSlowProperties(receiver); | |||
| 14865 | ||||
| 14866 | if (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOLfalse) { | |||
| 14867 | CSA_DCHECK(this, Word32Or(IsSwissNameDictionary(properties),((void)0) | |||
| 14868 | IsGlobalDictionary(properties)))((void)0); | |||
| 14869 | ||||
| 14870 | length = Select<Smi>( | |||
| 14871 | IsSwissNameDictionary(properties), | |||
| 14872 | [=] { | |||
| 14873 | return GetNumberOfElements( | |||
| 14874 | UncheckedCast<SwissNameDictionary>(properties)); | |||
| 14875 | }, | |||
| 14876 | [=] { | |||
| 14877 | return GetNumberOfElements( | |||
| 14878 | UncheckedCast<GlobalDictionary>(properties)); | |||
| 14879 | }); | |||
| 14880 | ||||
| 14881 | } else { | |||
| 14882 | CSA_DCHECK(this, Word32Or(IsNameDictionary(properties),((void)0) | |||
| 14883 | IsGlobalDictionary(properties)))((void)0); | |||
| 14884 | STATIC_ASSERT(static_cast<int>(NameDictionary::kNumberOfElementsIndex) ==static_assert(static_cast<int>(NameDictionary::kNumberOfElementsIndex ) == static_cast<int>(GlobalDictionary::kNumberOfElementsIndex ), "static_cast<int>(NameDictionary::kNumberOfElementsIndex) == static_cast<int>(GlobalDictionary::kNumberOfElementsIndex)" ) | |||
| 14885 | static_cast<int>(GlobalDictionary::kNumberOfElementsIndex))static_assert(static_cast<int>(NameDictionary::kNumberOfElementsIndex ) == static_cast<int>(GlobalDictionary::kNumberOfElementsIndex ), "static_cast<int>(NameDictionary::kNumberOfElementsIndex) == static_cast<int>(GlobalDictionary::kNumberOfElementsIndex)" ); | |||
| 14886 | length = GetNumberOfElements(UncheckedCast<HashTableBase>(properties)); | |||
| 14887 | } | |||
| 14888 | ||||
| 14889 | GotoIfNot(TaggedEqual(length, SmiConstant(0)), if_runtime); | |||
| 14890 | // Check that there are no elements on the {receiver} and its prototype | |||
| 14891 | // chain. Given that we do not create an EnumCache for dict-mode objects, | |||
| 14892 | // directly jump to {if_empty} if there are no elements and no properties | |||
| 14893 | // on the {receiver}. | |||
| 14894 | CheckPrototypeEnumCache(receiver, receiver_map, if_empty, if_runtime); | |||
| 14895 | } | |||
| 14896 | ||||
| 14897 | // Check that there are no elements on the fast {receiver} and its | |||
| 14898 | // prototype chain. | |||
| 14899 | BIND(&if_cache)Bind(&if_cache); | |||
| 14900 | CheckPrototypeEnumCache(receiver, receiver_map, &if_fast, if_runtime); | |||
| 14901 | ||||
| 14902 | BIND(&if_fast)Bind(&if_fast); | |||
| 14903 | return receiver_map; | |||
| 14904 | } | |||
| 14905 | ||||
| 14906 | TNode<Object> CodeStubAssembler::GetArgumentValue(TorqueStructArguments args, | |||
| 14907 | TNode<IntPtrT> index) { | |||
| 14908 | return CodeStubArguments(this, args).GetOptionalArgumentValue(index); | |||
| 14909 | } | |||
| 14910 | ||||
| 14911 | TorqueStructArguments CodeStubAssembler::GetFrameArguments( | |||
| 14912 | TNode<RawPtrT> frame, TNode<IntPtrT> argc, | |||
| 14913 | FrameArgumentsArgcType argc_type) { | |||
| 14914 | if (argc_type == FrameArgumentsArgcType::kCountExcludesReceiver) { | |||
| 14915 | argc = IntPtrAdd(argc, IntPtrConstant(kJSArgcReceiverSlots)); | |||
| 14916 | } | |||
| 14917 | return CodeStubArguments(this, argc, frame).GetTorqueArguments(); | |||
| 14918 | } | |||
| 14919 | ||||
| 14920 | void CodeStubAssembler::Print(const char* s) { | |||
| 14921 | std::string formatted(s); | |||
| 14922 | formatted += "\n"; | |||
| 14923 | CallRuntime(Runtime::kGlobalPrint, NoContextConstant(), | |||
| 14924 | StringConstant(formatted.c_str())); | |||
| 14925 | } | |||
| 14926 | ||||
| 14927 | void CodeStubAssembler::Print(const char* prefix, | |||
| 14928 | TNode<MaybeObject> tagged_value) { | |||
| 14929 | if (prefix != nullptr) { | |||
| 14930 | std::string formatted(prefix); | |||
| 14931 | formatted += ": "; | |||
| 14932 | Handle<String> string = isolate()->factory()->NewStringFromAsciiChecked( | |||
| 14933 | formatted.c_str(), AllocationType::kOld); | |||
| 14934 | CallRuntime(Runtime::kGlobalPrint, NoContextConstant(), | |||
| 14935 | HeapConstant(string)); | |||
| 14936 | } | |||
| 14937 | // CallRuntime only accepts Objects, so do an UncheckedCast to object. | |||
| 14938 | // DebugPrint explicitly checks whether the tagged value is a MaybeObject. | |||
| 14939 | TNode<Object> arg = UncheckedCast<Object>(tagged_value); | |||
| 14940 | CallRuntime(Runtime::kDebugPrint, NoContextConstant(), arg); | |||
| 14941 | } | |||
| 14942 | ||||
| 14943 | IntegerLiteral CodeStubAssembler::ConstexprIntegerLiteralAdd( | |||
| 14944 | const IntegerLiteral& lhs, const IntegerLiteral& rhs) { | |||
| 14945 | return lhs + rhs; | |||
| 14946 | } | |||
| 14947 | IntegerLiteral CodeStubAssembler::ConstexprIntegerLiteralLeftShift( | |||
| 14948 | const IntegerLiteral& lhs, const IntegerLiteral& rhs) { | |||
| 14949 | return lhs << rhs; | |||
| 14950 | } | |||
| 14951 | IntegerLiteral CodeStubAssembler::ConstexprIntegerLiteralBitwiseOr( | |||
| 14952 | const IntegerLiteral& lhs, const IntegerLiteral& rhs) { | |||
| 14953 | return lhs | rhs; | |||
| 14954 | } | |||
| 14955 | ||||
| 14956 | void CodeStubAssembler::PerformStackCheck(TNode<Context> context) { | |||
| 14957 | Label ok(this), stack_check_interrupt(this, Label::kDeferred); | |||
| 14958 | ||||
| 14959 | TNode<UintPtrT> stack_limit = UncheckedCast<UintPtrT>( | |||
| 14960 | Load(MachineType::Pointer(), | |||
| 14961 | ExternalConstant(ExternalReference::address_of_jslimit(isolate())))); | |||
| 14962 | TNode<BoolT> sp_within_limit = StackPointerGreaterThan(stack_limit); | |||
| 14963 | ||||
| 14964 | Branch(sp_within_limit, &ok, &stack_check_interrupt); | |||
| 14965 | ||||
| 14966 | BIND(&stack_check_interrupt)Bind(&stack_check_interrupt); | |||
| 14967 | CallRuntime(Runtime::kStackGuard, context); | |||
| 14968 | Goto(&ok); | |||
| 14969 | ||||
| 14970 | BIND(&ok)Bind(&ok); | |||
| 14971 | } | |||
| 14972 | ||||
| 14973 | TNode<Object> CodeStubAssembler::CallApiCallback( | |||
| 14974 | TNode<Object> context, TNode<RawPtrT> callback, TNode<IntPtrT> argc, | |||
| 14975 | TNode<Object> data, TNode<Object> holder, TNode<Object> receiver) { | |||
| 14976 | Callable callable = CodeFactory::CallApiCallback(isolate()); | |||
| 14977 | return CallStub(callable, context, callback, argc, data, holder, receiver); | |||
| 14978 | } | |||
| 14979 | ||||
| 14980 | TNode<Object> CodeStubAssembler::CallApiCallback( | |||
| 14981 | TNode<Object> context, TNode<RawPtrT> callback, TNode<IntPtrT> argc, | |||
| 14982 | TNode<Object> data, TNode<Object> holder, TNode<Object> receiver, | |||
| 14983 | TNode<Object> value) { | |||
| 14984 | Callable callable = CodeFactory::CallApiCallback(isolate()); | |||
| 14985 | return CallStub(callable, context, callback, argc, data, holder, receiver, | |||
| 14986 | value); | |||
| 14987 | } | |||
| 14988 | ||||
| 14989 | TNode<Object> CodeStubAssembler::CallRuntimeNewArray( | |||
| 14990 | TNode<Context> context, TNode<Object> receiver, TNode<Object> length, | |||
| 14991 | TNode<Object> new_target, TNode<Object> allocation_site) { | |||
| 14992 | // Runtime_NewArray receives arguments in the JS order (to avoid unnecessary | |||
| 14993 | // copy). Except the last two (new_target and allocation_site) which are add | |||
| 14994 | // on top of the stack later. | |||
| 14995 | return CallRuntime(Runtime::kNewArray, context, length, receiver, new_target, | |||
| 14996 | allocation_site); | |||
| 14997 | } | |||
| 14998 | ||||
| 14999 | void CodeStubAssembler::TailCallRuntimeNewArray(TNode<Context> context, | |||
| 15000 | TNode<Object> receiver, | |||
| 15001 | TNode<Object> length, | |||
| 15002 | TNode<Object> new_target, | |||
| 15003 | TNode<Object> allocation_site) { | |||
| 15004 | // Runtime_NewArray receives arguments in the JS order (to avoid unnecessary | |||
| 15005 | // copy). Except the last two (new_target and allocation_site) which are add | |||
| 15006 | // on top of the stack later. | |||
| 15007 | return TailCallRuntime(Runtime::kNewArray, context, length, receiver, | |||
| 15008 | new_target, allocation_site); | |||
| 15009 | } | |||
| 15010 | ||||
| 15011 | TNode<JSArray> CodeStubAssembler::ArrayCreate(TNode<Context> context, | |||
| 15012 | TNode<Number> length) { | |||
| 15013 | TVARIABLE(JSArray, array)TVariable<JSArray> array(this); | |||
| 15014 | Label allocate_js_array(this); | |||
| 15015 | ||||
| 15016 | Label done(this), next(this), runtime(this, Label::kDeferred); | |||
| 15017 | TNode<Smi> limit = SmiConstant(JSArray::kInitialMaxFastElementArray); | |||
| 15018 | CSA_DCHECK_BRANCH(this, [=](Label* ok, Label* not_ok) {((void)0) | |||
| 15019 | BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, length,((void)0) | |||
| 15020 | SmiConstant(0), ok, not_ok);((void)0) | |||
| 15021 | })((void)0); | |||
| 15022 | // This check also transitively covers the case where length is too big | |||
| 15023 | // to be representable by a SMI and so is not usable with | |||
| 15024 | // AllocateJSArray. | |||
| 15025 | BranchIfNumberRelationalComparison(Operation::kGreaterThanOrEqual, length, | |||
| 15026 | limit, &runtime, &next); | |||
| 15027 | ||||
| 15028 | BIND(&runtime)Bind(&runtime); | |||
| 15029 | { | |||
| 15030 | TNode<NativeContext> native_context = LoadNativeContext(context); | |||
| 15031 | TNode<JSFunction> array_function = | |||
| 15032 | CAST(LoadContextElement(native_context, Context::ARRAY_FUNCTION_INDEX))Cast(LoadContextElement(native_context, Context::ARRAY_FUNCTION_INDEX )); | |||
| 15033 | array = CAST(CallRuntimeNewArray(context, array_function, length,Cast(CallRuntimeNewArray(context, array_function, length, array_function , UndefinedConstant())) | |||
| 15034 | array_function, UndefinedConstant()))Cast(CallRuntimeNewArray(context, array_function, length, array_function , UndefinedConstant())); | |||
| 15035 | Goto(&done); | |||
| 15036 | } | |||
| 15037 | ||||
| 15038 | BIND(&next)Bind(&next); | |||
| 15039 | TNode<Smi> length_smi = CAST(length)Cast(length); | |||
| 15040 | ||||
| 15041 | TNode<Map> array_map = CAST(LoadContextElement(Cast(LoadContextElement( context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX )) | |||
| 15042 | context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX))Cast(LoadContextElement( context, Context::JS_ARRAY_PACKED_SMI_ELEMENTS_MAP_INDEX )); | |||
| 15043 | ||||
| 15044 | // TODO(delphick): Consider using | |||
| 15045 | // AllocateUninitializedJSArrayWithElements to avoid initializing an | |||
| 15046 | // array and then writing over it. | |||
| 15047 | array = AllocateJSArray(PACKED_SMI_ELEMENTS, array_map, length_smi, | |||
| 15048 | SmiConstant(0)); | |||
| 15049 | Goto(&done); | |||
| 15050 | ||||
| 15051 | BIND(&done)Bind(&done); | |||
| 15052 | return array.value(); | |||
| 15053 | } | |||
| 15054 | ||||
| 15055 | void CodeStubAssembler::SetPropertyLength(TNode<Context> context, | |||
| 15056 | TNode<Object> array, | |||
| 15057 | TNode<Number> length) { | |||
| 15058 | SetPropertyStrict(context, array, CodeStubAssembler::LengthStringConstant(), | |||
| 15059 | length); | |||
| 15060 | } | |||
| 15061 | ||||
| 15062 | TNode<Smi> CodeStubAssembler::RefillMathRandom( | |||
| 15063 | TNode<NativeContext> native_context) { | |||
| 15064 | // Cache exhausted, populate the cache. Return value is the new index. | |||
| 15065 | const TNode<ExternalReference> refill_math_random = | |||
| 15066 | ExternalConstant(ExternalReference::refill_math_random()); | |||
| 15067 | const TNode<ExternalReference> isolate_ptr = | |||
| 15068 | ExternalConstant(ExternalReference::isolate_address(isolate())); | |||
| 15069 | MachineType type_tagged = MachineType::AnyTagged(); | |||
| 15070 | MachineType type_ptr = MachineType::Pointer(); | |||
| 15071 | ||||
| 15072 | return CAST(CallCFunction(refill_math_random, type_tagged,Cast(CallCFunction(refill_math_random, type_tagged, std::make_pair (type_ptr, isolate_ptr), std::make_pair(type_tagged, native_context ))) | |||
| 15073 | std::make_pair(type_ptr, isolate_ptr),Cast(CallCFunction(refill_math_random, type_tagged, std::make_pair (type_ptr, isolate_ptr), std::make_pair(type_tagged, native_context ))) | |||
| 15074 | std::make_pair(type_tagged, native_context)))Cast(CallCFunction(refill_math_random, type_tagged, std::make_pair (type_ptr, isolate_ptr), std::make_pair(type_tagged, native_context ))); | |||
| 15075 | } | |||
| 15076 | ||||
| 15077 | TNode<String> CodeStubAssembler::TaggedToDirectString(TNode<Object> value, | |||
| 15078 | Label* fail) { | |||
| 15079 | ToDirectStringAssembler to_direct(state(), CAST(value)Cast(value)); | |||
| 15080 | to_direct.TryToDirect(fail); | |||
| 15081 | to_direct.PointerToData(fail); | |||
| 15082 | return CAST(value)Cast(value); | |||
| 15083 | } | |||
| 15084 | ||||
| 15085 | void CodeStubAssembler::RemoveFinalizationRegistryCellFromUnregisterTokenMap( | |||
| 15086 | TNode<JSFinalizationRegistry> finalization_registry, | |||
| 15087 | TNode<WeakCell> weak_cell) { | |||
| 15088 | const TNode<ExternalReference> remove_cell = ExternalConstant( | |||
| 15089 | ExternalReference:: | |||
| 15090 | js_finalization_registry_remove_cell_from_unregister_token_map()); | |||
| 15091 | const TNode<ExternalReference> isolate_ptr = | |||
| 15092 | ExternalConstant(ExternalReference::isolate_address(isolate())); | |||
| 15093 | ||||
| 15094 | CallCFunction(remove_cell, MachineType::Pointer(), | |||
| 15095 | std::make_pair(MachineType::Pointer(), isolate_ptr), | |||
| 15096 | std::make_pair(MachineType::AnyTagged(), finalization_registry), | |||
| 15097 | std::make_pair(MachineType::AnyTagged(), weak_cell)); | |||
| 15098 | } | |||
| 15099 | ||||
| 15100 | PrototypeCheckAssembler::PrototypeCheckAssembler( | |||
| 15101 | compiler::CodeAssemblerState* state, Flags flags, | |||
| 15102 | TNode<NativeContext> native_context, TNode<Map> initial_prototype_map, | |||
| 15103 | base::Vector<DescriptorIndexNameValue> properties) | |||
| 15104 | : CodeStubAssembler(state), | |||
| 15105 | flags_(flags), | |||
| 15106 | native_context_(native_context), | |||
| 15107 | initial_prototype_map_(initial_prototype_map), | |||
| 15108 | properties_(properties) {} | |||
| 15109 | ||||
| 15110 | void PrototypeCheckAssembler::CheckAndBranch(TNode<HeapObject> prototype, | |||
| 15111 | Label* if_unmodified, | |||
| 15112 | Label* if_modified) { | |||
| 15113 | TNode<Map> prototype_map = LoadMap(prototype); | |||
| 15114 | TNode<DescriptorArray> descriptors = LoadMapDescriptors(prototype_map); | |||
| 15115 | ||||
| 15116 | // The continuation of a failed fast check: if property identity checks are | |||
| 15117 | // enabled, we continue there (since they may still classify the prototype as | |||
| 15118 | // fast), otherwise we bail out. | |||
| 15119 | Label property_identity_check(this, Label::kDeferred); | |||
| 15120 | Label* if_fast_check_failed = | |||
| 15121 | ((flags_ & kCheckPrototypePropertyIdentity) == 0) | |||
| 15122 | ? if_modified | |||
| 15123 | : &property_identity_check; | |||
| 15124 | ||||
| 15125 | if ((flags_ & kCheckPrototypePropertyConstness) != 0) { | |||
| 15126 | // A simple prototype map identity check. Note that map identity does not | |||
| 15127 | // guarantee unmodified properties. It does guarantee that no new properties | |||
| 15128 | // have been added, or old properties deleted. | |||
| 15129 | ||||
| 15130 | GotoIfNot(TaggedEqual(prototype_map, initial_prototype_map_), | |||
| 15131 | if_fast_check_failed); | |||
| 15132 | ||||
| 15133 | // We need to make sure that relevant properties in the prototype have | |||
| 15134 | // not been tampered with. We do this by checking that their slots | |||
| 15135 | // in the prototype's descriptor array are still marked as const. | |||
| 15136 | ||||
| 15137 | TNode<Uint32T> combined_details; | |||
| 15138 | for (int i = 0; i < properties_.length(); i++) { | |||
| 15139 | // Assert the descriptor index is in-bounds. | |||
| 15140 | int descriptor = properties_[i].descriptor_index; | |||
| 15141 | CSA_DCHECK(this, Int32LessThan(Int32Constant(descriptor),((void)0) | |||
| 15142 | LoadNumberOfDescriptors(descriptors)))((void)0); | |||
| 15143 | ||||
| 15144 | // Assert that the name is correct. This essentially checks that | |||
| 15145 | // the descriptor index corresponds to the insertion order in | |||
| 15146 | // the bootstrapper. | |||
| 15147 | CSA_DCHECK(((void)0) | |||
| 15148 | this,((void)0) | |||
| 15149 | TaggedEqual(LoadKeyByDescriptorEntry(descriptors, descriptor),((void)0) | |||
| 15150 | CodeAssembler::LoadRoot(properties_[i].name_root_index)))((void)0); | |||
| 15151 | ||||
| 15152 | TNode<Uint32T> details = | |||
| 15153 | DescriptorArrayGetDetails(descriptors, Uint32Constant(descriptor)); | |||
| 15154 | ||||
| 15155 | if (i == 0) { | |||
| 15156 | combined_details = details; | |||
| 15157 | } else { | |||
| 15158 | combined_details = Word32And(combined_details, details); | |||
| 15159 | } | |||
| 15160 | } | |||
| 15161 | ||||
| 15162 | TNode<Uint32T> constness = | |||
| 15163 | DecodeWord32<PropertyDetails::ConstnessField>(combined_details); | |||
| 15164 | ||||
| 15165 | Branch( | |||
| 15166 | Word32Equal(constness, | |||
| 15167 | Int32Constant(static_cast<int>(PropertyConstness::kConst))), | |||
| 15168 | if_unmodified, if_fast_check_failed); | |||
| 15169 | } | |||
| 15170 | ||||
| 15171 | if ((flags_ & kCheckPrototypePropertyIdentity) != 0) { | |||
| 15172 | // The above checks have failed, for whatever reason (maybe the prototype | |||
| 15173 | // map has changed, or a property is no longer const). This block implements | |||
| 15174 | // a more thorough check that can also accept maps which 1. do not have the | |||
| 15175 | // initial map, 2. have mutable relevant properties, but 3. still match the | |||
| 15176 | // expected value for all relevant properties. | |||
| 15177 | ||||
| 15178 | BIND(&property_identity_check)Bind(&property_identity_check); | |||
| 15179 | ||||
| 15180 | int max_descriptor_index = -1; | |||
| 15181 | for (int i = 0; i < properties_.length(); i++) { | |||
| 15182 | max_descriptor_index = | |||
| 15183 | std::max(max_descriptor_index, properties_[i].descriptor_index); | |||
| 15184 | } | |||
| 15185 | ||||
| 15186 | // If the greatest descriptor index is out of bounds, the map cannot be | |||
| 15187 | // fast. | |||
| 15188 | GotoIfNot(Int32LessThan(Int32Constant(max_descriptor_index), | |||
| 15189 | LoadNumberOfDescriptors(descriptors)), | |||
| 15190 | if_modified); | |||
| 15191 | ||||
| 15192 | // Logic below only handles maps with fast properties. | |||
| 15193 | GotoIfMapHasSlowProperties(prototype_map, if_modified); | |||
| 15194 | ||||
| 15195 | for (int i = 0; i < properties_.length(); i++) { | |||
| 15196 | const DescriptorIndexNameValue& p = properties_[i]; | |||
| 15197 | const int descriptor = p.descriptor_index; | |||
| 15198 | ||||
| 15199 | // Check if the name is correct. This essentially checks that | |||
| 15200 | // the descriptor index corresponds to the insertion order in | |||
| 15201 | // the bootstrapper. | |||
| 15202 | GotoIfNot(TaggedEqual(LoadKeyByDescriptorEntry(descriptors, descriptor), | |||
| 15203 | CodeAssembler::LoadRoot(p.name_root_index)), | |||
| 15204 | if_modified); | |||
| 15205 | ||||
| 15206 | // Finally, check whether the actual value equals the expected value. | |||
| 15207 | TNode<Uint32T> details = | |||
| 15208 | DescriptorArrayGetDetails(descriptors, Uint32Constant(descriptor)); | |||
| 15209 | TVARIABLE(Uint32T, var_details, details)TVariable<Uint32T> var_details(details, this); | |||
| 15210 | TVARIABLE(Object, var_value)TVariable<Object> var_value(this); | |||
| 15211 | ||||
| 15212 | const int key_index = DescriptorArray::ToKeyIndex(descriptor); | |||
| 15213 | LoadPropertyFromFastObject(prototype, prototype_map, descriptors, | |||
| 15214 | IntPtrConstant(key_index), &var_details, | |||
| 15215 | &var_value); | |||
| 15216 | ||||
| 15217 | TNode<Object> actual_value = var_value.value(); | |||
| 15218 | TNode<Object> expected_value = | |||
| 15219 | LoadContextElement(native_context_, p.expected_value_context_index); | |||
| 15220 | GotoIfNot(TaggedEqual(actual_value, expected_value), if_modified); | |||
| 15221 | } | |||
| 15222 | ||||
| 15223 | Goto(if_unmodified); | |||
| 15224 | } | |||
| 15225 | } | |||
| 15226 | ||||
| 15227 | // | |||
| 15228 | // Begin of SwissNameDictionary macros | |||
| 15229 | // | |||
| 15230 | ||||
| 15231 | namespace { | |||
| 15232 | ||||
| 15233 | // Provides load and store functions that abstract over the details of accessing | |||
| 15234 | // the meta table in memory. Instead they allow using logical indices that are | |||
| 15235 | // independent from the underlying entry size in the meta table of a | |||
| 15236 | // SwissNameDictionary. | |||
| 15237 | class MetaTableAccessor { | |||
| 15238 | public: | |||
| 15239 | MetaTableAccessor(CodeStubAssembler& csa, MachineType mt) | |||
| 15240 | : csa{csa}, mt{mt} {} | |||
| 15241 | ||||
| 15242 | TNode<Uint32T> Load(TNode<ByteArray> meta_table, TNode<IntPtrT> index) { | |||
| 15243 | TNode<IntPtrT> offset = OverallOffset(meta_table, index); | |||
| 15244 | ||||
| 15245 | return csa.UncheckedCast<Uint32T>( | |||
| 15246 | csa.LoadFromObject(mt, meta_table, offset)); | |||
| 15247 | } | |||
| 15248 | ||||
| 15249 | TNode<Uint32T> Load(TNode<ByteArray> meta_table, int index) { | |||
| 15250 | return Load(meta_table, csa.IntPtrConstant(index)); | |||
| 15251 | } | |||
| 15252 | ||||
| 15253 | void Store(TNode<ByteArray> meta_table, TNode<IntPtrT> index, | |||
| 15254 | TNode<Uint32T> data) { | |||
| 15255 | TNode<IntPtrT> offset = OverallOffset(meta_table, index); | |||
| 15256 | ||||
| 15257 | #ifdef DEBUG | |||
| 15258 | int bits = mt.MemSize() * 8; | |||
| 15259 | TNode<UintPtrT> max_value = csa.UintPtrConstant((1ULL << bits) - 1); | |||
| 15260 | ||||
| 15261 | CSA_DCHECK(&csa, csa.UintPtrLessThanOrEqual(csa.ChangeUint32ToWord(data),((void)0) | |||
| 15262 | max_value))((void)0); | |||
| 15263 | #endif | |||
| 15264 | ||||
| 15265 | csa.StoreToObject(mt.representation(), meta_table, offset, data, | |||
| 15266 | StoreToObjectWriteBarrier::kNone); | |||
| 15267 | } | |||
| 15268 | ||||
| 15269 | void Store(TNode<ByteArray> meta_table, int index, TNode<Uint32T> data) { | |||
| 15270 | Store(meta_table, csa.IntPtrConstant(index), data); | |||
| 15271 | } | |||
| 15272 | ||||
| 15273 | private: | |||
| 15274 | TNode<IntPtrT> OverallOffset(TNode<ByteArray> meta_table, | |||
| 15275 | TNode<IntPtrT> index) { | |||
| 15276 | // TODO(v8:11330): consider using ElementOffsetFromIndex(). | |||
| 15277 | ||||
| 15278 | int offset_to_data_minus_tag = ByteArray::kHeaderSize - kHeapObjectTag; | |||
| 15279 | ||||
| 15280 | TNode<IntPtrT> overall_offset; | |||
| 15281 | int size = mt.MemSize(); | |||
| 15282 | intptr_t constant; | |||
| 15283 | if (csa.TryToIntPtrConstant(index, &constant)) { | |||
| 15284 | intptr_t index_offset = constant * size; | |||
| 15285 | overall_offset = | |||
| 15286 | csa.IntPtrConstant(offset_to_data_minus_tag + index_offset); | |||
| 15287 | } else { | |||
| 15288 | TNode<IntPtrT> index_offset = | |||
| 15289 | csa.IntPtrMul(index, csa.IntPtrConstant(size)); | |||
| 15290 | overall_offset = csa.IntPtrAdd( | |||
| 15291 | csa.IntPtrConstant(offset_to_data_minus_tag), index_offset); | |||
| 15292 | } | |||
| 15293 | ||||
| 15294 | #ifdef DEBUG | |||
| 15295 | TNode<IntPtrT> byte_array_data_bytes = | |||
| 15296 | csa.SmiToIntPtr(csa.LoadFixedArrayBaseLength(meta_table)); | |||
| 15297 | TNode<IntPtrT> max_allowed_offset = csa.IntPtrAdd( | |||
| 15298 | byte_array_data_bytes, csa.IntPtrConstant(offset_to_data_minus_tag)); | |||
| 15299 | CSA_DCHECK(&csa, csa.UintPtrLessThan(overall_offset, max_allowed_offset))((void)0); | |||
| 15300 | #endif | |||
| 15301 | ||||
| 15302 | return overall_offset; | |||
| 15303 | } | |||
| 15304 | ||||
| 15305 | CodeStubAssembler& csa; | |||
| 15306 | MachineType mt; | |||
| 15307 | }; | |||
| 15308 | ||||
| 15309 | // Type of functions that given a MetaTableAccessor, use its load and store | |||
| 15310 | // functions to generate code for operating on the meta table. | |||
| 15311 | using MetaTableAccessFunction = std::function<void(MetaTableAccessor&)>; | |||
| 15312 | ||||
| 15313 | // Helper function for macros operating on the meta table of a | |||
| 15314 | // SwissNameDictionary. Given a MetaTableAccessFunction, generates branching | |||
| 15315 | // code and uses the builder to generate code for each of the three possible | |||
| 15316 | // sizes per entry a meta table can have. | |||
| 15317 | void GenerateMetaTableAccess(CodeStubAssembler* csa, TNode<IntPtrT> capacity, | |||
| 15318 | MetaTableAccessFunction builder) { | |||
| 15319 | MetaTableAccessor mta8 = MetaTableAccessor(*csa, MachineType::Uint8()); | |||
| 15320 | MetaTableAccessor mta16 = MetaTableAccessor(*csa, MachineType::Uint16()); | |||
| 15321 | MetaTableAccessor mta32 = MetaTableAccessor(*csa, MachineType::Uint32()); | |||
| 15322 | ||||
| 15323 | using Label = compiler::CodeAssemblerLabel; | |||
| 15324 | Label small(csa), medium(csa), done(csa); | |||
| 15325 | ||||
| 15326 | csa->GotoIf( | |||
| 15327 | csa->IntPtrLessThanOrEqual( | |||
| 15328 | capacity, | |||
| 15329 | csa->IntPtrConstant(SwissNameDictionary::kMax1ByteMetaTableCapacity)), | |||
| 15330 | &small); | |||
| 15331 | csa->GotoIf( | |||
| 15332 | csa->IntPtrLessThanOrEqual( | |||
| 15333 | capacity, | |||
| 15334 | csa->IntPtrConstant(SwissNameDictionary::kMax2ByteMetaTableCapacity)), | |||
| 15335 | &medium); | |||
| 15336 | ||||
| 15337 | builder(mta32); | |||
| 15338 | csa->Goto(&done); | |||
| 15339 | ||||
| 15340 | csa->Bind(&medium); | |||
| 15341 | builder(mta16); | |||
| 15342 | csa->Goto(&done); | |||
| 15343 | ||||
| 15344 | csa->Bind(&small); | |||
| 15345 | builder(mta8); | |||
| 15346 | csa->Goto(&done); | |||
| 15347 | csa->Bind(&done); | |||
| 15348 | } | |||
| 15349 | ||||
| 15350 | } // namespace | |||
| 15351 | ||||
| 15352 | TNode<IntPtrT> CodeStubAssembler::LoadSwissNameDictionaryNumberOfElements( | |||
| 15353 | TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity) { | |||
| 15354 | TNode<ByteArray> meta_table = LoadSwissNameDictionaryMetaTable(table); | |||
| 15355 | ||||
| 15356 | TVARIABLE(Uint32T, nof, Uint32Constant(0))TVariable<Uint32T> nof(Uint32Constant(0), this); | |||
| 15357 | MetaTableAccessFunction builder = [&](MetaTableAccessor& mta) { | |||
| 15358 | nof = mta.Load(meta_table, | |||
| 15359 | SwissNameDictionary::kMetaTableElementCountFieldIndex); | |||
| 15360 | }; | |||
| 15361 | ||||
| 15362 | GenerateMetaTableAccess(this, capacity, builder); | |||
| 15363 | return ChangeInt32ToIntPtr(nof.value()); | |||
| 15364 | } | |||
| 15365 | ||||
| 15366 | TNode<IntPtrT> | |||
| 15367 | CodeStubAssembler::LoadSwissNameDictionaryNumberOfDeletedElements( | |||
| 15368 | TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity) { | |||
| 15369 | TNode<ByteArray> meta_table = LoadSwissNameDictionaryMetaTable(table); | |||
| 15370 | ||||
| 15371 | TVARIABLE(Uint32T, nod, Uint32Constant(0))TVariable<Uint32T> nod(Uint32Constant(0), this); | |||
| 15372 | MetaTableAccessFunction builder = [&](MetaTableAccessor& mta) { | |||
| 15373 | nod = | |||
| 15374 | mta.Load(meta_table, | |||
| 15375 | SwissNameDictionary::kMetaTableDeletedElementCountFieldIndex); | |||
| 15376 | }; | |||
| 15377 | ||||
| 15378 | GenerateMetaTableAccess(this, capacity, builder); | |||
| 15379 | return ChangeInt32ToIntPtr(nod.value()); | |||
| 15380 | } | |||
| 15381 | ||||
| 15382 | void CodeStubAssembler::StoreSwissNameDictionaryEnumToEntryMapping( | |||
| 15383 | TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity, | |||
| 15384 | TNode<IntPtrT> enum_index, TNode<Int32T> entry) { | |||
| 15385 | TNode<ByteArray> meta_table = LoadSwissNameDictionaryMetaTable(table); | |||
| 15386 | TNode<IntPtrT> meta_table_index = IntPtrAdd( | |||
| 15387 | IntPtrConstant(SwissNameDictionary::kMetaTableEnumerationDataStartIndex), | |||
| 15388 | enum_index); | |||
| 15389 | ||||
| 15390 | MetaTableAccessFunction builder = [&](MetaTableAccessor& mta) { | |||
| 15391 | mta.Store(meta_table, meta_table_index, Unsigned(entry)); | |||
| 15392 | }; | |||
| 15393 | ||||
| 15394 | GenerateMetaTableAccess(this, capacity, builder); | |||
| 15395 | } | |||
| 15396 | ||||
| 15397 | TNode<Uint32T> | |||
| 15398 | CodeStubAssembler::SwissNameDictionaryIncreaseElementCountOrBailout( | |||
| 15399 | TNode<ByteArray> meta_table, TNode<IntPtrT> capacity, | |||
| 15400 | TNode<Uint32T> max_usable_capacity, Label* bailout) { | |||
| 15401 | TVARIABLE(Uint32T, used_var, Uint32Constant(0))TVariable<Uint32T> used_var(Uint32Constant(0), this); | |||
| 15402 | ||||
| 15403 | MetaTableAccessFunction builder = [&](MetaTableAccessor& mta) { | |||
| 15404 | TNode<Uint32T> nof = mta.Load( | |||
| 15405 | meta_table, SwissNameDictionary::kMetaTableElementCountFieldIndex); | |||
| 15406 | TNode<Uint32T> nod = | |||
| 15407 | mta.Load(meta_table, | |||
| 15408 | SwissNameDictionary::kMetaTableDeletedElementCountFieldIndex); | |||
| 15409 | TNode<Uint32T> used = Uint32Add(nof, nod); | |||
| 15410 | GotoIf(Uint32GreaterThanOrEqual(used, max_usable_capacity), bailout); | |||
| 15411 | TNode<Uint32T> inc_nof = Uint32Add(nof, Uint32Constant(1)); | |||
| 15412 | mta.Store(meta_table, SwissNameDictionary::kMetaTableElementCountFieldIndex, | |||
| 15413 | inc_nof); | |||
| 15414 | used_var = used; | |||
| 15415 | }; | |||
| 15416 | ||||
| 15417 | GenerateMetaTableAccess(this, capacity, builder); | |||
| 15418 | return used_var.value(); | |||
| 15419 | } | |||
| 15420 | ||||
| 15421 | TNode<Uint32T> CodeStubAssembler::SwissNameDictionaryUpdateCountsForDeletion( | |||
| 15422 | TNode<ByteArray> meta_table, TNode<IntPtrT> capacity) { | |||
| 15423 | TVARIABLE(Uint32T, new_nof_var, Uint32Constant(0))TVariable<Uint32T> new_nof_var(Uint32Constant(0), this); | |||
| 15424 | ||||
| 15425 | MetaTableAccessFunction builder = [&](MetaTableAccessor& mta) { | |||
| 15426 | TNode<Uint32T> nof = mta.Load( | |||
| 15427 | meta_table, SwissNameDictionary::kMetaTableElementCountFieldIndex); | |||
| 15428 | TNode<Uint32T> nod = | |||
| 15429 | mta.Load(meta_table, | |||
| 15430 | SwissNameDictionary::kMetaTableDeletedElementCountFieldIndex); | |||
| 15431 | ||||
| 15432 | TNode<Uint32T> new_nof = Uint32Sub(nof, Uint32Constant(1)); | |||
| 15433 | TNode<Uint32T> new_nod = Uint32Add(nod, Uint32Constant(1)); | |||
| 15434 | ||||
| 15435 | mta.Store(meta_table, SwissNameDictionary::kMetaTableElementCountFieldIndex, | |||
| 15436 | new_nof); | |||
| 15437 | mta.Store(meta_table, | |||
| 15438 | SwissNameDictionary::kMetaTableDeletedElementCountFieldIndex, | |||
| 15439 | new_nod); | |||
| 15440 | ||||
| 15441 | new_nof_var = new_nof; | |||
| 15442 | }; | |||
| 15443 | ||||
| 15444 | GenerateMetaTableAccess(this, capacity, builder); | |||
| 15445 | return new_nof_var.value(); | |||
| 15446 | } | |||
| 15447 | ||||
| 15448 | TNode<SwissNameDictionary> CodeStubAssembler::AllocateSwissNameDictionary( | |||
| 15449 | TNode<IntPtrT> at_least_space_for) { | |||
| 15450 | // Note that as AllocateNameDictionary, we return a table with initial | |||
| 15451 | // (non-zero) capacity even if |at_least_space_for| is 0. | |||
| 15452 | ||||
| 15453 | TNode<IntPtrT> capacity = | |||
| 15454 | IntPtrMax(IntPtrConstant(SwissNameDictionary::kInitialCapacity), | |||
| 15455 | SwissNameDictionaryCapacityFor(at_least_space_for)); | |||
| 15456 | ||||
| 15457 | return AllocateSwissNameDictionaryWithCapacity(capacity); | |||
| 15458 | } | |||
| 15459 | ||||
| 15460 | TNode<SwissNameDictionary> CodeStubAssembler::AllocateSwissNameDictionary( | |||
| 15461 | int at_least_space_for) { | |||
| 15462 | return AllocateSwissNameDictionary(IntPtrConstant(at_least_space_for)); | |||
| 15463 | } | |||
| 15464 | ||||
| 15465 | TNode<SwissNameDictionary> | |||
| 15466 | CodeStubAssembler::AllocateSwissNameDictionaryWithCapacity( | |||
| 15467 | TNode<IntPtrT> capacity) { | |||
| 15468 | Comment("[ AllocateSwissNameDictionaryWithCapacity"); | |||
| 15469 | CSA_DCHECK(this, WordIsPowerOfTwo(capacity))((void)0); | |||
| 15470 | CSA_DCHECK(this, UintPtrGreaterThanOrEqual(((void)0) | |||
| 15471 | capacity,((void)0) | |||
| 15472 | IntPtrConstant(SwissNameDictionary::kInitialCapacity)))((void)0); | |||
| 15473 | CSA_DCHECK(this,((void)0) | |||
| 15474 | UintPtrLessThanOrEqual(((void)0) | |||
| 15475 | capacity, IntPtrConstant(SwissNameDictionary::MaxCapacity())))((void)0); | |||
| 15476 | ||||
| 15477 | Comment("Size check."); | |||
| 15478 | intptr_t capacity_constant; | |||
| 15479 | if (ToParameterConstant(capacity, &capacity_constant)) { | |||
| 15480 | CHECK_LE(capacity_constant, SwissNameDictionary::MaxCapacity())do { bool _cmp = ::v8::base::CmpLEImpl< typename ::v8::base ::pass_value_or_ref<decltype(capacity_constant)>::type, typename ::v8::base::pass_value_or_ref<decltype(SwissNameDictionary ::MaxCapacity())>::type>((capacity_constant), (SwissNameDictionary ::MaxCapacity())); do { if ((__builtin_expect(!!(!(_cmp)), 0) )) { V8_Fatal("Check failed: %s.", "capacity_constant" " " "<=" " " "SwissNameDictionary::MaxCapacity()"); } } while (false) ; } while (false); | |||
| 15481 | } else { | |||
| 15482 | Label if_out_of_memory(this, Label::kDeferred), next(this); | |||
| 15483 | Branch(UintPtrGreaterThan( | |||
| 15484 | capacity, IntPtrConstant(SwissNameDictionary::MaxCapacity())), | |||
| 15485 | &if_out_of_memory, &next); | |||
| 15486 | ||||
| 15487 | BIND(&if_out_of_memory)Bind(&if_out_of_memory); | |||
| 15488 | CallRuntime(Runtime::kFatalProcessOutOfMemoryInAllocateRaw, | |||
| 15489 | NoContextConstant()); | |||
| 15490 | Unreachable(); | |||
| 15491 | ||||
| 15492 | BIND(&next)Bind(&next); | |||
| 15493 | } | |||
| 15494 | ||||
| 15495 | // TODO(v8:11330) Consider adding dedicated handling for constant capacties, | |||
| 15496 | // similar to AllocateOrderedHashTableWithCapacity. | |||
| 15497 | ||||
| 15498 | // We must allocate the ByteArray first. Otherwise, allocating the ByteArray | |||
| 15499 | // may trigger GC, which may try to verify the un-initialized | |||
| 15500 | // SwissNameDictionary. | |||
| 15501 | Comment("Meta table allocation."); | |||
| 15502 | TNode<IntPtrT> meta_table_payload_size = | |||
| 15503 | SwissNameDictionaryMetaTableSizeFor(capacity); | |||
| 15504 | ||||
| 15505 | TNode<ByteArray> meta_table = | |||
| 15506 | AllocateNonEmptyByteArray(Unsigned(meta_table_payload_size), | |||
| 15507 | AllocationFlag::kAllowLargeObjectAllocation); | |||
| 15508 | ||||
| 15509 | Comment("SwissNameDictionary allocation."); | |||
| 15510 | TNode<IntPtrT> total_size = SwissNameDictionarySizeFor(capacity); | |||
| 15511 | ||||
| 15512 | TNode<SwissNameDictionary> table = UncheckedCast<SwissNameDictionary>( | |||
| 15513 | Allocate(total_size, AllocationFlag::kAllowLargeObjectAllocation)); | |||
| 15514 | ||||
| 15515 | StoreMapNoWriteBarrier(table, RootIndex::kSwissNameDictionaryMap); | |||
| 15516 | ||||
| 15517 | Comment( | |||
| 15518 | "Initialize the hash, capacity, meta table pointer, and number of " | |||
| 15519 | "(deleted) elements."); | |||
| 15520 | ||||
| 15521 | StoreSwissNameDictionaryHash(table, | |||
| 15522 | Uint32Constant(PropertyArray::kNoHashSentinel)); | |||
| 15523 | StoreSwissNameDictionaryCapacity(table, TruncateIntPtrToInt32(capacity)); | |||
| 15524 | StoreSwissNameDictionaryMetaTable(table, meta_table); | |||
| 15525 | ||||
| 15526 | // Set present and deleted element count without doing branching needed for | |||
| 15527 | // meta table access twice. | |||
| 15528 | MetaTableAccessFunction builder = [&](MetaTableAccessor& mta) { | |||
| 15529 | mta.Store(meta_table, SwissNameDictionary::kMetaTableElementCountFieldIndex, | |||
| 15530 | Uint32Constant(0)); | |||
| 15531 | mta.Store(meta_table, | |||
| 15532 | SwissNameDictionary::kMetaTableDeletedElementCountFieldIndex, | |||
| 15533 | Uint32Constant(0)); | |||
| 15534 | }; | |||
| 15535 | GenerateMetaTableAccess(this, capacity, builder); | |||
| 15536 | ||||
| 15537 | Comment("Initialize the ctrl table."); | |||
| 15538 | ||||
| 15539 | TNode<IntPtrT> ctrl_table_start_offset_minus_tag = | |||
| 15540 | SwissNameDictionaryCtrlTableStartOffsetMT(capacity); | |||
| 15541 | ||||
| 15542 | TNode<IntPtrT> table_address_with_tag = BitcastTaggedToWord(table); | |||
| 15543 | TNode<IntPtrT> ctrl_table_size_bytes = | |||
| 15544 | IntPtrAdd(capacity, IntPtrConstant(SwissNameDictionary::kGroupWidth)); | |||
| 15545 | TNode<IntPtrT> ctrl_table_start_ptr = | |||
| 15546 | IntPtrAdd(table_address_with_tag, ctrl_table_start_offset_minus_tag); | |||
| 15547 | TNode<IntPtrT> ctrl_table_end_ptr = | |||
| 15548 | IntPtrAdd(ctrl_table_start_ptr, ctrl_table_size_bytes); | |||
| 15549 | ||||
| 15550 | // |ctrl_table_size_bytes| (= capacity + kGroupWidth) is divisble by four: | |||
| 15551 | STATIC_ASSERT(SwissNameDictionary::kGroupWidth % 4 == 0)static_assert(SwissNameDictionary::kGroupWidth % 4 == 0, "SwissNameDictionary::kGroupWidth % 4 == 0" ); | |||
| 15552 | STATIC_ASSERT(SwissNameDictionary::kInitialCapacity % 4 == 0)static_assert(SwissNameDictionary::kInitialCapacity % 4 == 0, "SwissNameDictionary::kInitialCapacity % 4 == 0"); | |||
| 15553 | ||||
| 15554 | // TODO(v8:11330) For all capacities except 4, we know that | |||
| 15555 | // |ctrl_table_size_bytes| is divisible by 8. Consider initializing the ctrl | |||
| 15556 | // table with WordTs in those cases. Alternatively, always initialize as many | |||
| 15557 | // bytes as possbible with WordT and then, if necessary, the remaining 4 bytes | |||
| 15558 | // with Word32T. | |||
| 15559 | ||||
| 15560 | constexpr uint8_t kEmpty = swiss_table::Ctrl::kEmpty; | |||
| 15561 | constexpr uint32_t kEmpty32 = | |||
| 15562 | (kEmpty << 24) | (kEmpty << 16) | (kEmpty << 8) | kEmpty; | |||
| 15563 | TNode<Int32T> empty32 = Int32Constant(kEmpty32); | |||
| 15564 | BuildFastLoop<IntPtrT>( | |||
| 15565 | ctrl_table_start_ptr, ctrl_table_end_ptr, | |||
| 15566 | [=](TNode<IntPtrT> current) { | |||
| 15567 | UnsafeStoreNoWriteBarrier(MachineRepresentation::kWord32, current, | |||
| 15568 | empty32); | |||
| 15569 | }, | |||
| 15570 | sizeof(uint32_t), IndexAdvanceMode::kPost); | |||
| 15571 | ||||
| 15572 | Comment("Initialize the data table."); | |||
| 15573 | ||||
| 15574 | TNode<IntPtrT> data_table_start_offset_minus_tag = | |||
| 15575 | SwissNameDictionaryDataTableStartOffsetMT(); | |||
| 15576 | TNode<IntPtrT> data_table_ptr = | |||
| 15577 | IntPtrAdd(table_address_with_tag, data_table_start_offset_minus_tag); | |||
| 15578 | TNode<IntPtrT> data_table_size = IntPtrMul( | |||
| 15579 | IntPtrConstant(SwissNameDictionary::kDataTableEntryCount * kTaggedSize), | |||
| 15580 | capacity); | |||
| 15581 | ||||
| 15582 | StoreFieldsNoWriteBarrier(data_table_ptr, | |||
| 15583 | IntPtrAdd(data_table_ptr, data_table_size), | |||
| 15584 | TheHoleConstant()); | |||
| 15585 | ||||
| 15586 | Comment("AllocateSwissNameDictionaryWithCapacity ]"); | |||
| 15587 | ||||
| 15588 | return table; | |||
| 15589 | } | |||
| 15590 | ||||
| 15591 | TNode<SwissNameDictionary> CodeStubAssembler::CopySwissNameDictionary( | |||
| 15592 | TNode<SwissNameDictionary> original) { | |||
| 15593 | Comment("[ CopySwissNameDictionary"); | |||
| 15594 | ||||
| 15595 | TNode<IntPtrT> capacity = | |||
| 15596 | Signed(ChangeUint32ToWord(LoadSwissNameDictionaryCapacity(original))); | |||
| 15597 | ||||
| 15598 | // We must allocate the ByteArray first. Otherwise, allocating the ByteArray | |||
| 15599 | // may trigger GC, which may try to verify the un-initialized | |||
| 15600 | // SwissNameDictionary. | |||
| 15601 | Comment("Meta table allocation."); | |||
| 15602 | TNode<IntPtrT> meta_table_payload_size = | |||
| 15603 | SwissNameDictionaryMetaTableSizeFor(capacity); | |||
| 15604 | ||||
| 15605 | TNode<ByteArray> meta_table = | |||
| 15606 | AllocateNonEmptyByteArray(Unsigned(meta_table_payload_size), | |||
| 15607 | AllocationFlag::kAllowLargeObjectAllocation); | |||
| 15608 | ||||
| 15609 | Comment("SwissNameDictionary allocation."); | |||
| 15610 | TNode<IntPtrT> total_size = SwissNameDictionarySizeFor(capacity); | |||
| 15611 | ||||
| 15612 | TNode<SwissNameDictionary> table = UncheckedCast<SwissNameDictionary>( | |||
| 15613 | Allocate(total_size, AllocationFlag::kAllowLargeObjectAllocation)); | |||
| 15614 | ||||
| 15615 | StoreMapNoWriteBarrier(table, RootIndex::kSwissNameDictionaryMap); | |||
| 15616 | ||||
| 15617 | Comment("Copy the hash and capacity."); | |||
| 15618 | ||||
| 15619 | StoreSwissNameDictionaryHash(table, LoadSwissNameDictionaryHash(original)); | |||
| 15620 | StoreSwissNameDictionaryCapacity(table, TruncateIntPtrToInt32(capacity)); | |||
| 15621 | StoreSwissNameDictionaryMetaTable(table, meta_table); | |||
| 15622 | // Not setting up number of (deleted elements), copying whole meta table | |||
| 15623 | // instead. | |||
| 15624 | ||||
| 15625 | TNode<ExternalReference> memcpy = | |||
| 15626 | ExternalConstant(ExternalReference::libc_memcpy_function()); | |||
| 15627 | ||||
| 15628 | TNode<IntPtrT> old_table_address_with_tag = BitcastTaggedToWord(original); | |||
| 15629 | TNode<IntPtrT> new_table_address_with_tag = BitcastTaggedToWord(table); | |||
| 15630 | ||||
| 15631 | TNode<IntPtrT> ctrl_table_start_offset_minus_tag = | |||
| 15632 | SwissNameDictionaryCtrlTableStartOffsetMT(capacity); | |||
| 15633 | ||||
| 15634 | TNode<IntPtrT> ctrl_table_size_bytes = | |||
| 15635 | IntPtrAdd(capacity, IntPtrConstant(SwissNameDictionary::kGroupWidth)); | |||
| 15636 | ||||
| 15637 | Comment("Copy the ctrl table."); | |||
| 15638 | { | |||
| 15639 | TNode<IntPtrT> old_ctrl_table_start_ptr = IntPtrAdd( | |||
| 15640 | old_table_address_with_tag, ctrl_table_start_offset_minus_tag); | |||
| 15641 | TNode<IntPtrT> new_ctrl_table_start_ptr = IntPtrAdd( | |||
| 15642 | new_table_address_with_tag, ctrl_table_start_offset_minus_tag); | |||
| 15643 | ||||
| 15644 | CallCFunction( | |||
| 15645 | memcpy, MachineType::Pointer(), | |||
| 15646 | std::make_pair(MachineType::Pointer(), new_ctrl_table_start_ptr), | |||
| 15647 | std::make_pair(MachineType::Pointer(), old_ctrl_table_start_ptr), | |||
| 15648 | std::make_pair(MachineType::UintPtr(), ctrl_table_size_bytes)); | |||
| 15649 | } | |||
| 15650 | ||||
| 15651 | Comment("Copy the data table."); | |||
| 15652 | { | |||
| 15653 | TNode<IntPtrT> start_offset = | |||
| 15654 | IntPtrConstant(SwissNameDictionary::DataTableStartOffset()); | |||
| 15655 | TNode<IntPtrT> data_table_size = IntPtrMul( | |||
| 15656 | IntPtrConstant(SwissNameDictionary::kDataTableEntryCount * kTaggedSize), | |||
| 15657 | capacity); | |||
| 15658 | ||||
| 15659 | BuildFastLoop<IntPtrT>( | |||
| 15660 | start_offset, IntPtrAdd(start_offset, data_table_size), | |||
| 15661 | [=](TNode<IntPtrT> offset) { | |||
| 15662 | TNode<Object> table_field = LoadObjectField(original, offset); | |||
| 15663 | StoreObjectField(table, offset, table_field); | |||
| 15664 | }, | |||
| 15665 | kTaggedSize, IndexAdvanceMode::kPost); | |||
| 15666 | } | |||
| 15667 | ||||
| 15668 | Comment("Copy the meta table"); | |||
| 15669 | { | |||
| 15670 | TNode<IntPtrT> old_meta_table_address_with_tag = | |||
| 15671 | BitcastTaggedToWord(LoadSwissNameDictionaryMetaTable(original)); | |||
| 15672 | TNode<IntPtrT> new_meta_table_address_with_tag = | |||
| 15673 | BitcastTaggedToWord(meta_table); | |||
| 15674 | ||||
| 15675 | TNode<IntPtrT> meta_table_size = | |||
| 15676 | SwissNameDictionaryMetaTableSizeFor(capacity); | |||
| 15677 | ||||
| 15678 | TNode<IntPtrT> old_data_start = | |||
| 15679 | IntPtrAdd(old_meta_table_address_with_tag, | |||
| 15680 | IntPtrConstant(ByteArray::kHeaderSize - kHeapObjectTag)); | |||
| 15681 | TNode<IntPtrT> new_data_start = | |||
| 15682 | IntPtrAdd(new_meta_table_address_with_tag, | |||
| 15683 | IntPtrConstant(ByteArray::kHeaderSize - kHeapObjectTag)); | |||
| 15684 | ||||
| 15685 | CallCFunction(memcpy, MachineType::Pointer(), | |||
| 15686 | std::make_pair(MachineType::Pointer(), new_data_start), | |||
| 15687 | std::make_pair(MachineType::Pointer(), old_data_start), | |||
| 15688 | std::make_pair(MachineType::UintPtr(), meta_table_size)); | |||
| 15689 | } | |||
| 15690 | ||||
| 15691 | Comment("Copy the PropertyDetails table"); | |||
| 15692 | { | |||
| 15693 | TNode<IntPtrT> property_details_start_offset_minus_tag = | |||
| 15694 | SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(table, capacity, | |||
| 15695 | IntPtrConstant(0)); | |||
| 15696 | ||||
| 15697 | // Offset to property details entry | |||
| 15698 | TVARIABLE(IntPtrT, details_table_offset_minus_tag,TVariable<IntPtrT> details_table_offset_minus_tag(property_details_start_offset_minus_tag , this) | |||
| 15699 | property_details_start_offset_minus_tag)TVariable<IntPtrT> details_table_offset_minus_tag(property_details_start_offset_minus_tag , this); | |||
| 15700 | ||||
| 15701 | TNode<IntPtrT> start = ctrl_table_start_offset_minus_tag; | |||
| 15702 | ||||
| 15703 | VariableList in_loop_variables({&details_table_offset_minus_tag}, zone()); | |||
| 15704 | BuildFastLoop<IntPtrT>( | |||
| 15705 | in_loop_variables, start, IntPtrAdd(start, ctrl_table_size_bytes), | |||
| 15706 | [&](TNode<IntPtrT> ctrl_table_offset) { | |||
| 15707 | TNode<Uint8T> ctrl = Load<Uint8T>(original, ctrl_table_offset); | |||
| 15708 | ||||
| 15709 | // TODO(v8:11330) Entries in the PropertyDetails table may be | |||
| 15710 | // uninitialized if the corresponding buckets in the data/ctrl table | |||
| 15711 | // are empty. Therefore, to avoid accessing un-initialized memory | |||
| 15712 | // here, we need to check the ctrl table to determine whether we | |||
| 15713 | // should copy a certain PropertyDetails entry or not. | |||
| 15714 | // TODO(v8:11330) If this function becomes performance-critical, we | |||
| 15715 | // may consider always initializing the PropertyDetails table entirely | |||
| 15716 | // during allocation, to avoid the branching during copying. | |||
| 15717 | Label done(this); | |||
| 15718 | // |kNotFullMask| catches kEmpty and kDeleted, both of which indicate | |||
| 15719 | // entries that we don't want to copy the PropertyDetails for. | |||
| 15720 | GotoIf(IsSetWord32(ctrl, swiss_table::kNotFullMask), &done); | |||
| 15721 | ||||
| 15722 | TNode<Uint8T> details = | |||
| 15723 | Load<Uint8T>(original, details_table_offset_minus_tag.value()); | |||
| 15724 | ||||
| 15725 | StoreToObject(MachineRepresentation::kWord8, table, | |||
| 15726 | details_table_offset_minus_tag.value(), details, | |||
| 15727 | StoreToObjectWriteBarrier::kNone); | |||
| 15728 | Goto(&done); | |||
| 15729 | BIND(&done)Bind(&done); | |||
| 15730 | ||||
| 15731 | details_table_offset_minus_tag = | |||
| 15732 | IntPtrAdd(details_table_offset_minus_tag.value(), | |||
| 15733 | IntPtrConstant(kOneByteSize)); | |||
| 15734 | }, | |||
| 15735 | kOneByteSize, IndexAdvanceMode::kPost); | |||
| 15736 | } | |||
| 15737 | ||||
| 15738 | Comment("CopySwissNameDictionary ]"); | |||
| 15739 | ||||
| 15740 | return table; | |||
| 15741 | } | |||
| 15742 | ||||
| 15743 | TNode<IntPtrT> CodeStubAssembler::SwissNameDictionaryOffsetIntoDataTableMT( | |||
| 15744 | TNode<SwissNameDictionary> dict, TNode<IntPtrT> index, int field_index) { | |||
| 15745 | TNode<IntPtrT> data_table_start = SwissNameDictionaryDataTableStartOffsetMT(); | |||
| 15746 | ||||
| 15747 | TNode<IntPtrT> offset_within_data_table = IntPtrMul( | |||
| 15748 | index, | |||
| 15749 | IntPtrConstant(SwissNameDictionary::kDataTableEntryCount * kTaggedSize)); | |||
| 15750 | ||||
| 15751 | if (field_index != 0) { | |||
| 15752 | offset_within_data_table = IntPtrAdd( | |||
| 15753 | offset_within_data_table, IntPtrConstant(field_index * kTaggedSize)); | |||
| 15754 | } | |||
| 15755 | ||||
| 15756 | return IntPtrAdd(data_table_start, offset_within_data_table); | |||
| 15757 | } | |||
| 15758 | ||||
| 15759 | TNode<IntPtrT> | |||
| 15760 | CodeStubAssembler::SwissNameDictionaryOffsetIntoPropertyDetailsTableMT( | |||
| 15761 | TNode<SwissNameDictionary> dict, TNode<IntPtrT> capacity, | |||
| 15762 | TNode<IntPtrT> index) { | |||
| 15763 | CSA_DCHECK(this,((void)0) | |||
| 15764 | WordEqual(capacity, ChangeUint32ToWord(((void)0) | |||
| 15765 | LoadSwissNameDictionaryCapacity(dict))))((void)0); | |||
| 15766 | ||||
| 15767 | TNode<IntPtrT> data_table_start = SwissNameDictionaryDataTableStartOffsetMT(); | |||
| 15768 | ||||
| 15769 | TNode<IntPtrT> gw = IntPtrConstant(SwissNameDictionary::kGroupWidth); | |||
| 15770 | TNode<IntPtrT> data_and_ctrl_table_size = IntPtrAdd( | |||
| 15771 | IntPtrMul(capacity, | |||
| 15772 | IntPtrConstant(kOneByteSize + | |||
| 15773 | SwissNameDictionary::kDataTableEntryCount * | |||
| 15774 | kTaggedSize)), | |||
| 15775 | gw); | |||
| 15776 | ||||
| 15777 | TNode<IntPtrT> property_details_table_start = | |||
| 15778 | IntPtrAdd(data_table_start, data_and_ctrl_table_size); | |||
| 15779 | ||||
| 15780 | CSA_DCHECK(((void)0) | |||
| 15781 | this,((void)0) | |||
| 15782 | WordEqual(FieldSliceSwissNameDictionaryPropertyDetailsTable(dict).offset,((void)0) | |||
| 15783 | // Our calculation subtracted the tag, Torque's offset didn't.((void)0) | |||
| 15784 | IntPtrAdd(property_details_table_start,((void)0) | |||
| 15785 | IntPtrConstant(kHeapObjectTag))))((void)0); | |||
| 15786 | ||||
| 15787 | TNode<IntPtrT> offset_within_details_table = index; | |||
| 15788 | return IntPtrAdd(property_details_table_start, offset_within_details_table); | |||
| 15789 | } | |||
| 15790 | ||||
| 15791 | void CodeStubAssembler::StoreSwissNameDictionaryCapacity( | |||
| 15792 | TNode<SwissNameDictionary> table, TNode<Int32T> capacity) { | |||
| 15793 | StoreObjectFieldNoWriteBarrier<Word32T>( | |||
| 15794 | table, SwissNameDictionary::CapacityOffset(), capacity); | |||
| 15795 | } | |||
| 15796 | ||||
| 15797 | TNode<Name> CodeStubAssembler::LoadSwissNameDictionaryKey( | |||
| 15798 | TNode<SwissNameDictionary> dict, TNode<IntPtrT> entry) { | |||
| 15799 | TNode<IntPtrT> offset_minus_tag = SwissNameDictionaryOffsetIntoDataTableMT( | |||
| 15800 | dict, entry, SwissNameDictionary::kDataTableKeyEntryIndex); | |||
| 15801 | ||||
| 15802 | // TODO(v8:11330) Consider using LoadObjectField here. | |||
| 15803 | return CAST(Load<Object>(dict, offset_minus_tag))Cast(Load<Object>(dict, offset_minus_tag)); | |||
| 15804 | } | |||
| 15805 | ||||
| 15806 | TNode<Uint8T> CodeStubAssembler::LoadSwissNameDictionaryPropertyDetails( | |||
| 15807 | TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity, | |||
| 15808 | TNode<IntPtrT> entry) { | |||
| 15809 | TNode<IntPtrT> offset_minus_tag = | |||
| 15810 | SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(table, capacity, | |||
| 15811 | entry); | |||
| 15812 | // TODO(v8:11330) Consider using LoadObjectField here. | |||
| 15813 | return Load<Uint8T>(table, offset_minus_tag); | |||
| 15814 | } | |||
| 15815 | ||||
| 15816 | void CodeStubAssembler::StoreSwissNameDictionaryPropertyDetails( | |||
| 15817 | TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity, | |||
| 15818 | TNode<IntPtrT> entry, TNode<Uint8T> details) { | |||
| 15819 | TNode<IntPtrT> offset_minus_tag = | |||
| 15820 | SwissNameDictionaryOffsetIntoPropertyDetailsTableMT(table, capacity, | |||
| 15821 | entry); | |||
| 15822 | ||||
| 15823 | // TODO(v8:11330) Consider using StoreObjectField here. | |||
| 15824 | StoreToObject(MachineRepresentation::kWord8, table, offset_minus_tag, details, | |||
| 15825 | StoreToObjectWriteBarrier::kNone); | |||
| 15826 | } | |||
| 15827 | ||||
| 15828 | void CodeStubAssembler::StoreSwissNameDictionaryKeyAndValue( | |||
| 15829 | TNode<SwissNameDictionary> dict, TNode<IntPtrT> entry, TNode<Object> key, | |||
| 15830 | TNode<Object> value) { | |||
| 15831 | STATIC_ASSERT(SwissNameDictionary::kDataTableKeyEntryIndex == 0)static_assert(SwissNameDictionary::kDataTableKeyEntryIndex == 0, "SwissNameDictionary::kDataTableKeyEntryIndex == 0"); | |||
| 15832 | STATIC_ASSERT(SwissNameDictionary::kDataTableValueEntryIndex == 1)static_assert(SwissNameDictionary::kDataTableValueEntryIndex == 1, "SwissNameDictionary::kDataTableValueEntryIndex == 1"); | |||
| 15833 | ||||
| 15834 | // TODO(v8:11330) Consider using StoreObjectField here. | |||
| 15835 | TNode<IntPtrT> key_offset_minus_tag = | |||
| 15836 | SwissNameDictionaryOffsetIntoDataTableMT( | |||
| 15837 | dict, entry, SwissNameDictionary::kDataTableKeyEntryIndex); | |||
| 15838 | StoreToObject(MachineRepresentation::kTagged, dict, key_offset_minus_tag, key, | |||
| 15839 | StoreToObjectWriteBarrier::kFull); | |||
| 15840 | ||||
| 15841 | TNode<IntPtrT> value_offset_minus_tag = | |||
| 15842 | IntPtrAdd(key_offset_minus_tag, IntPtrConstant(kTaggedSize)); | |||
| 15843 | StoreToObject(MachineRepresentation::kTagged, dict, value_offset_minus_tag, | |||
| 15844 | value, StoreToObjectWriteBarrier::kFull); | |||
| 15845 | } | |||
| 15846 | ||||
| 15847 | TNode<Uint64T> CodeStubAssembler::LoadSwissNameDictionaryCtrlTableGroup( | |||
| 15848 | TNode<IntPtrT> address) { | |||
| 15849 | TNode<RawPtrT> ptr = ReinterpretCast<RawPtrT>(address); | |||
| 15850 | TNode<Uint64T> data = UnalignedLoad<Uint64T>(ptr, IntPtrConstant(0)); | |||
| 15851 | ||||
| 15852 | #ifdef V8_TARGET_LITTLE_ENDIAN1 | |||
| 15853 | return data; | |||
| 15854 | #else | |||
| 15855 | // Reverse byte order. | |||
| 15856 | // TODO(v8:11330) Doing this without using dedicated instructions (which we | |||
| 15857 | // don't have access to here) will destroy any performance benefit Swiss | |||
| 15858 | // Tables have. So we just support this so that we don't have to disable the | |||
| 15859 | // test suite for SwissNameDictionary on big endian platforms. | |||
| 15860 | ||||
| 15861 | TNode<Uint64T> result = Uint64Constant(0); | |||
| 15862 | constexpr int count = sizeof(uint64_t); | |||
| 15863 | for (int i = 0; i < count; ++i) { | |||
| 15864 | int src_offset = i * 8; | |||
| 15865 | int dest_offset = (count - i - 1) * 8; | |||
| 15866 | ||||
| 15867 | TNode<Uint64T> mask = Uint64Constant(0xffULL << src_offset); | |||
| 15868 | TNode<Uint64T> src_data = Word64And(data, mask); | |||
| 15869 | ||||
| 15870 | TNode<Uint64T> shifted = | |||
| 15871 | src_offset < dest_offset | |||
| 15872 | ? Word64Shl(src_data, Uint64Constant(dest_offset - src_offset)) | |||
| 15873 | : Word64Shr(src_data, Uint64Constant(src_offset - dest_offset)); | |||
| 15874 | result = Unsigned(Word64Or(result, shifted)); | |||
| 15875 | } | |||
| 15876 | return result; | |||
| 15877 | #endif | |||
| 15878 | } | |||
| 15879 | ||||
| 15880 | void CodeStubAssembler::SwissNameDictionarySetCtrl( | |||
| 15881 | TNode<SwissNameDictionary> table, TNode<IntPtrT> capacity, | |||
| 15882 | TNode<IntPtrT> entry, TNode<Uint8T> ctrl) { | |||
| 15883 | CSA_DCHECK(this,((void)0) | |||
| 15884 | WordEqual(capacity, ChangeUint32ToWord(((void)0) | |||
| 15885 | LoadSwissNameDictionaryCapacity(table))))((void)0); | |||
| 15886 | CSA_DCHECK(this, UintPtrLessThan(entry, capacity))((void)0); | |||
| 15887 | ||||
| 15888 | TNode<IntPtrT> one = IntPtrConstant(1); | |||
| 15889 | TNode<IntPtrT> offset = SwissNameDictionaryCtrlTableStartOffsetMT(capacity); | |||
| 15890 | ||||
| 15891 | CSA_DCHECK(this,((void)0) | |||
| 15892 | WordEqual(FieldSliceSwissNameDictionaryCtrlTable(table).offset,((void)0) | |||
| 15893 | IntPtrAdd(offset, one)))((void)0); | |||
| 15894 | ||||
| 15895 | TNode<IntPtrT> offset_entry = IntPtrAdd(offset, entry); | |||
| 15896 | StoreToObject(MachineRepresentation::kWord8, table, offset_entry, ctrl, | |||
| 15897 | StoreToObjectWriteBarrier::kNone); | |||
| 15898 | ||||
| 15899 | TNode<IntPtrT> mask = IntPtrSub(capacity, one); | |||
| 15900 | TNode<IntPtrT> group_width = IntPtrConstant(SwissNameDictionary::kGroupWidth); | |||
| 15901 | ||||
| 15902 | // See SwissNameDictionary::SetCtrl for description of what's going on here. | |||
| 15903 | ||||
| 15904 | // ((entry - Group::kWidth) & mask) + 1 | |||
| 15905 | TNode<IntPtrT> copy_entry_lhs = | |||
| 15906 | IntPtrAdd(WordAnd(IntPtrSub(entry, group_width), mask), one); | |||
| 15907 | // ((Group::kWidth - 1) & mask) | |||
| 15908 | TNode<IntPtrT> copy_entry_rhs = WordAnd(IntPtrSub(group_width, one), mask); | |||
| 15909 | TNode<IntPtrT> copy_entry = IntPtrAdd(copy_entry_lhs, copy_entry_rhs); | |||
| 15910 | TNode<IntPtrT> offset_copy_entry = IntPtrAdd(offset, copy_entry); | |||
| 15911 | ||||
| 15912 | // |entry| < |kGroupWidth| implies |copy_entry| == |capacity| + |entry| | |||
| 15913 | CSA_DCHECK(this, Word32Or(UintPtrGreaterThanOrEqual(entry, group_width),((void)0) | |||
| 15914 | WordEqual(copy_entry, IntPtrAdd(capacity, entry))))((void)0); | |||
| 15915 | ||||
| 15916 | // |entry| >= |kGroupWidth| implies |copy_entry| == |entry| | |||
| 15917 | CSA_DCHECK(this, Word32Or(UintPtrLessThan(entry, group_width),((void)0) | |||
| 15918 | WordEqual(copy_entry, entry)))((void)0); | |||
| 15919 | ||||
| 15920 | // TODO(v8:11330): consider using StoreObjectFieldNoWriteBarrier here. | |||
| 15921 | StoreToObject(MachineRepresentation::kWord8, table, offset_copy_entry, ctrl, | |||
| 15922 | StoreToObjectWriteBarrier::kNone); | |||
| 15923 | } | |||
| 15924 | ||||
| 15925 | void CodeStubAssembler::SwissNameDictionaryFindEntry( | |||
| 15926 | TNode<SwissNameDictionary> table, TNode<Name> key, Label* found, | |||
| 15927 | TVariable<IntPtrT>* var_found_entry, Label* not_found) { | |||
| 15928 | if (SwissNameDictionary::kUseSIMD) { | |||
| 15929 | SwissNameDictionaryFindEntrySIMD(table, key, found, var_found_entry, | |||
| 15930 | not_found); | |||
| 15931 | } else { | |||
| 15932 | SwissNameDictionaryFindEntryPortable(table, key, found, var_found_entry, | |||
| 15933 | not_found); | |||
| 15934 | } | |||
| 15935 | } | |||
| 15936 | ||||
| 15937 | void CodeStubAssembler::SwissNameDictionaryAdd(TNode<SwissNameDictionary> table, | |||
| 15938 | TNode<Name> key, | |||
| 15939 | TNode<Object> value, | |||
| 15940 | TNode<Uint8T> property_details, | |||
| 15941 | Label* needs_resize) { | |||
| 15942 | if (SwissNameDictionary::kUseSIMD) { | |||
| 15943 | SwissNameDictionaryAddSIMD(table, key, value, property_details, | |||
| 15944 | needs_resize); | |||
| 15945 | } else { | |||
| 15946 | SwissNameDictionaryAddPortable(table, key, value, property_details, | |||
| 15947 | needs_resize); | |||
| 15948 | } | |||
| 15949 | } | |||
| 15950 | ||||
| 15951 | void CodeStubAssembler::SharedValueBarrier( | |||
| 15952 | TNode<Context> context, TVariable<Object>* var_shared_value) { | |||
| 15953 | // The barrier ensures that the value can be shared across Isolates. | |||
| 15954 | // The fast paths should be kept in sync with Object::Share. | |||
| 15955 | ||||
| 15956 | TNode<Object> value = var_shared_value->value(); | |||
| 15957 | Label check_in_shared_heap(this), slow(this), skip_barrier(this), done(this); | |||
| 15958 | ||||
| 15959 | // Fast path: Smis are trivially shared. | |||
| 15960 | GotoIf(TaggedIsSmi(value), &done); | |||
| 15961 | // Fast path: Shared memory features imply shared RO space, so RO objects are | |||
| 15962 | // trivially shared. | |||
| 15963 | CSA_DCHECK(this, BoolConstant(ReadOnlyHeap::IsReadOnlySpaceShared()))((void)0); | |||
| 15964 | TNode<IntPtrT> page_flags = LoadBasicMemoryChunkFlags(CAST(value)Cast(value)); | |||
| 15965 | GotoIf(WordNotEqual(WordAnd(page_flags, | |||
| 15966 | IntPtrConstant(BasicMemoryChunk::READ_ONLY_HEAP)), | |||
| 15967 | IntPtrConstant(0)), | |||
| 15968 | &skip_barrier); | |||
| 15969 | ||||
| 15970 | // Fast path: Check if the HeapObject is already shared. | |||
| 15971 | TNode<Uint16T> value_instance_type = | |||
| 15972 | LoadMapInstanceType(LoadMap(CAST(value)Cast(value))); | |||
| 15973 | GotoIf(IsSharedStringInstanceType(value_instance_type), &skip_barrier); | |||
| 15974 | GotoIf(IsJSSharedStructInstanceType(value_instance_type), &skip_barrier); | |||
| 15975 | GotoIf(IsHeapNumberInstanceType(value_instance_type), &check_in_shared_heap); | |||
| 15976 | Goto(&slow); | |||
| 15977 | ||||
| 15978 | BIND(&check_in_shared_heap)Bind(&check_in_shared_heap); | |||
| 15979 | { | |||
| 15980 | Branch( | |||
| 15981 | WordNotEqual(WordAnd(page_flags, | |||
| 15982 | IntPtrConstant(BasicMemoryChunk::IN_SHARED_HEAP)), | |||
| 15983 | IntPtrConstant(0)), | |||
| 15984 | &skip_barrier, &slow); | |||
| 15985 | } | |||
| 15986 | ||||
| 15987 | // Slow path: Call out to runtime to share primitives and to throw on | |||
| 15988 | // non-shared JS objects. | |||
| 15989 | BIND(&slow)Bind(&slow); | |||
| 15990 | { | |||
| 15991 | *var_shared_value = | |||
| 15992 | CallRuntime(Runtime::kSharedValueBarrierSlow, context, value); | |||
| 15993 | Goto(&skip_barrier); | |||
| 15994 | } | |||
| 15995 | ||||
| 15996 | BIND(&skip_barrier)Bind(&skip_barrier); | |||
| 15997 | { | |||
| 15998 | CSA_DCHECK(((void)0) | |||
| 15999 | this,((void)0) | |||
| 16000 | WordNotEqual(((void)0) | |||
| 16001 | WordAnd(LoadBasicMemoryChunkFlags(CAST(var_shared_value->value())),((void)0) | |||
| 16002 | IntPtrConstant(BasicMemoryChunk::READ_ONLY_HEAP |((void)0) | |||
| 16003 | BasicMemoryChunk::IN_SHARED_HEAP)),((void)0) | |||
| 16004 | IntPtrConstant(0)))((void)0); | |||
| 16005 | Goto(&done); | |||
| 16006 | } | |||
| 16007 | ||||
| 16008 | BIND(&done)Bind(&done); | |||
| 16009 | } | |||
| 16010 | ||||
| 16011 | } // namespace internal | |||
| 16012 | } // namespace v8 |
| 1 | // Copyright 2019 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_BASE_BIT_FIELD_H_ | |||
| 6 | #define V8_BASE_BIT_FIELD_H_ | |||
| 7 | ||||
| 8 | #include <stdint.h> | |||
| 9 | ||||
| 10 | #include "src/base/macros.h" | |||
| 11 | ||||
| 12 | namespace v8 { | |||
| 13 | namespace base { | |||
| 14 | ||||
| 15 | // ---------------------------------------------------------------------------- | |||
| 16 | // BitField is a help template for encoding and decode bitfield with | |||
| 17 | // unsigned content. | |||
| 18 | // Instantiate them via 'using', which is cheaper than deriving a new class: | |||
| 19 | // using MyBitField = base::BitField<MyEnum, 4, 2>; | |||
| 20 | // The BitField class is final to enforce this style over derivation. | |||
| 21 | ||||
| 22 | template <class T, int shift, int size, class U = uint32_t> | |||
| 23 | class BitField final { | |||
| 24 | public: | |||
| 25 | STATIC_ASSERT(std::is_unsigned<U>::value)static_assert(std::is_unsigned<U>::value, "std::is_unsigned<U>::value" ); | |||
| 26 | STATIC_ASSERT(shift < 8 * sizeof(U))static_assert(shift < 8 * sizeof(U), "shift < 8 * sizeof(U)" ); // Otherwise shifts by {shift} are UB. | |||
| 27 | STATIC_ASSERT(size < 8 * sizeof(U))static_assert(size < 8 * sizeof(U), "size < 8 * sizeof(U)" ); // Otherwise shifts by {size} are UB. | |||
| 28 | STATIC_ASSERT(shift + size <= 8 * sizeof(U))static_assert(shift + size <= 8 * sizeof(U), "shift + size <= 8 * sizeof(U)" ); | |||
| 29 | STATIC_ASSERT(size > 0)static_assert(size > 0, "size > 0"); | |||
| 30 | ||||
| 31 | using FieldType = T; | |||
| 32 | ||||
| 33 | // A type U mask of bit field. To use all bits of a type U of x bits | |||
| 34 | // in a bitfield without compiler warnings we have to compute 2^x | |||
| 35 | // without using a shift count of x in the computation. | |||
| 36 | static constexpr int kShift = shift; | |||
| 37 | static constexpr int kSize = size; | |||
| 38 | static constexpr U kMask = ((U{1} << kShift) << kSize) - (U{1} << kShift); | |||
| 39 | static constexpr int kLastUsedBit = kShift + kSize - 1; | |||
| 40 | static constexpr U kNumValues = U{1} << kSize; | |||
| 41 | ||||
| 42 | // Value for the field with all bits set. | |||
| 43 | static constexpr T kMax = static_cast<T>(kNumValues - 1); | |||
| 44 | ||||
| 45 | template <class T2, int size2> | |||
| 46 | using Next = BitField<T2, kShift + kSize, size2, U>; | |||
| 47 | ||||
| 48 | // Tells whether the provided value fits into the bit field. | |||
| 49 | static constexpr bool is_valid(T value) { | |||
| 50 | return (static_cast<U>(value) & ~static_cast<U>(kMax)) == 0; | |||
| 51 | } | |||
| 52 | ||||
| 53 | // Returns a type U with the bit field value encoded. | |||
| 54 | static constexpr U encode(T value) { | |||
| 55 | DCHECK(is_valid(value))((void) 0); | |||
| 56 | return static_cast<U>(value) << kShift; | |||
| ||||
| 57 | } | |||
| 58 | ||||
| 59 | // Returns a type U with the bit field value updated. | |||
| 60 | static constexpr U update(U previous, T value) { | |||
| 61 | return (previous & ~kMask) | encode(value); | |||
| 62 | } | |||
| 63 | ||||
| 64 | // Extracts the bit field from the value. | |||
| 65 | static constexpr T decode(U value) { | |||
| 66 | return static_cast<T>((value & kMask) >> kShift); | |||
| 67 | } | |||
| 68 | }; | |||
| 69 | ||||
| 70 | template <class T, int shift, int size> | |||
| 71 | using BitField8 = BitField<T, shift, size, uint8_t>; | |||
| 72 | ||||
| 73 | template <class T, int shift, int size> | |||
| 74 | using BitField16 = BitField<T, shift, size, uint16_t>; | |||
| 75 | ||||
| 76 | template <class T, int shift, int size> | |||
| 77 | using BitField64 = BitField<T, shift, size, uint64_t>; | |||
| 78 | ||||
| 79 | // Helper macros for defining a contiguous sequence of bit fields. Example: | |||
| 80 | // (backslashes at the ends of respective lines of this multi-line macro | |||
| 81 | // definition are omitted here to please the compiler) | |||
| 82 | // | |||
| 83 | // #define MAP_BIT_FIELD1(V, _) | |||
| 84 | // V(IsAbcBit, bool, 1, _) | |||
| 85 | // V(IsBcdBit, bool, 1, _) | |||
| 86 | // V(CdeBits, int, 5, _) | |||
| 87 | // V(DefBits, MutableMode, 1, _) | |||
| 88 | // | |||
| 89 | // DEFINE_BIT_FIELDS(MAP_BIT_FIELD1) | |||
| 90 | // or | |||
| 91 | // DEFINE_BIT_FIELDS_64(MAP_BIT_FIELD1) | |||
| 92 | // | |||
| 93 | #define DEFINE_BIT_FIELD_RANGE_TYPE(Name, Type, Size, _)kNameStart, kNameEnd = kNameStart + Size - 1, \ | |||
| 94 | k##Name##Start, k##Name##End = k##Name##Start + Size - 1, | |||
| 95 | ||||
| 96 | #define DEFINE_BIT_RANGES(LIST_MACRO)struct LIST_MACRO_Ranges { enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE , _) kBitsCount }; }; \ | |||
| 97 | struct LIST_MACRO##_Ranges { \ | |||
| 98 | enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE, _) kBitsCount }; \ | |||
| 99 | }; | |||
| 100 | ||||
| 101 | #define DEFINE_BIT_FIELD_TYPE(Name, Type, Size, RangesName)using Name = base::BitField<Type, RangesName::kNameStart, Size >; \ | |||
| 102 | using Name = base::BitField<Type, RangesName::k##Name##Start, Size>; | |||
| 103 | ||||
| 104 | #define DEFINE_BIT_FIELD_64_TYPE(Name, Type, Size, RangesName)using Name = base::BitField64<Type, RangesName::kNameStart , Size>; \ | |||
| 105 | using Name = base::BitField64<Type, RangesName::k##Name##Start, Size>; | |||
| 106 | ||||
| 107 | #define DEFINE_BIT_FIELDS(LIST_MACRO)struct LIST_MACRO_Ranges { enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE , _) kBitsCount }; }; LIST_MACRO(DEFINE_BIT_FIELD_TYPE, LIST_MACRO_Ranges ) \ | |||
| 108 | DEFINE_BIT_RANGES(LIST_MACRO)struct LIST_MACRO_Ranges { enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE , _) kBitsCount }; }; \ | |||
| 109 | LIST_MACRO(DEFINE_BIT_FIELD_TYPE, LIST_MACRO##_Ranges) | |||
| 110 | ||||
| 111 | #define DEFINE_BIT_FIELDS_64(LIST_MACRO)struct LIST_MACRO_Ranges { enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE , _) kBitsCount }; }; LIST_MACRO(DEFINE_BIT_FIELD_64_TYPE, LIST_MACRO_Ranges ) \ | |||
| 112 | DEFINE_BIT_RANGES(LIST_MACRO)struct LIST_MACRO_Ranges { enum { LIST_MACRO(DEFINE_BIT_FIELD_RANGE_TYPE , _) kBitsCount }; }; \ | |||
| 113 | LIST_MACRO(DEFINE_BIT_FIELD_64_TYPE, LIST_MACRO##_Ranges) | |||
| 114 | ||||
| 115 | // ---------------------------------------------------------------------------- | |||
| 116 | // BitSetComputer is a help template for encoding and decoding information for | |||
| 117 | // a variable number of items in an array. | |||
| 118 | // | |||
| 119 | // To encode boolean data in a smi array you would use: | |||
| 120 | // using BoolComputer = BitSetComputer<bool, 1, kSmiValueSize, uint32_t>; | |||
| 121 | // | |||
| 122 | template <class T, int kBitsPerItem, int kBitsPerWord, class U> | |||
| 123 | class BitSetComputer { | |||
| 124 | public: | |||
| 125 | static const int kItemsPerWord = kBitsPerWord / kBitsPerItem; | |||
| 126 | static const int kMask = (1 << kBitsPerItem) - 1; | |||
| 127 | ||||
| 128 | // The number of array elements required to embed T information for each item. | |||
| 129 | static int word_count(int items) { | |||
| 130 | if (items == 0) return 0; | |||
| 131 | return (items - 1) / kItemsPerWord + 1; | |||
| 132 | } | |||
| 133 | ||||
| 134 | // The array index to look at for item. | |||
| 135 | static int index(int base_index, int item) { | |||
| 136 | return base_index + item / kItemsPerWord; | |||
| 137 | } | |||
| 138 | ||||
| 139 | // Extract T data for a given item from data. | |||
| 140 | static T decode(U data, int item) { | |||
| 141 | return static_cast<T>((data >> shift(item)) & kMask); | |||
| 142 | } | |||
| 143 | ||||
| 144 | // Return the encoding for a store of value for item in previous. | |||
| 145 | static U encode(U previous, int item, T value) { | |||
| 146 | int shift_value = shift(item); | |||
| 147 | int set_bits = (static_cast<int>(value) << shift_value); | |||
| 148 | return (previous & ~(kMask << shift_value)) | set_bits; | |||
| 149 | } | |||
| 150 | ||||
| 151 | static int shift(int item) { return (item % kItemsPerWord) * kBitsPerItem; } | |||
| 152 | }; | |||
| 153 | ||||
| 154 | } // namespace base | |||
| 155 | } // namespace v8 | |||
| 156 | ||||
| 157 | #endif // V8_BASE_BIT_FIELD_H_ |