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 #ifndef V8_CODE_STUB_ASSEMBLER_H_ 6 #define V8_CODE_STUB_ASSEMBLER_H_ 7 8 #include "src/compiler/code-assembler.h" 9 #include "src/objects.h" 10 11 namespace v8 { 12 namespace internal { 13 14 class CallInterfaceDescriptor; 15 class StatsCounter; 16 class StubCache; 17 18 // Provides JavaScript-specific "macro-assembler" functionality on top of the 19 // CodeAssembler. By factoring the JavaScript-isms out of the CodeAssembler, 20 // it's possible to add JavaScript-specific useful CodeAssembler "macros" 21 // without modifying files in the compiler directory (and requiring a review 22 // from a compiler directory OWNER). 23 class CodeStubAssembler : public compiler::CodeAssembler { 24 public: 25 // Create with CallStub linkage. 26 // |result_size| specifies the number of results returned by the stub. 27 // TODO(rmcilroy): move result_size to the CallInterfaceDescriptor. 28 CodeStubAssembler(Isolate* isolate, Zone* zone, 29 const CallInterfaceDescriptor& descriptor, 30 Code::Flags flags, const char* name, 31 size_t result_size = 1); 32 33 // Create with JSCall linkage. 34 CodeStubAssembler(Isolate* isolate, Zone* zone, int parameter_count, 35 Code::Flags flags, const char* name); 36 37 enum ParameterMode { INTEGER_PARAMETERS, SMI_PARAMETERS }; 38 39 compiler::Node* BooleanMapConstant(); 40 compiler::Node* EmptyStringConstant(); 41 compiler::Node* HeapNumberMapConstant(); 42 compiler::Node* NoContextConstant(); 43 compiler::Node* NullConstant(); 44 compiler::Node* UndefinedConstant(); 45 compiler::Node* TheHoleConstant(); 46 compiler::Node* HashSeed(); 47 compiler::Node* StaleRegisterConstant(); 48 49 // Float64 operations. 50 compiler::Node* Float64Ceil(compiler::Node* x); 51 compiler::Node* Float64Floor(compiler::Node* x); 52 compiler::Node* Float64Round(compiler::Node* x); 53 compiler::Node* Float64Trunc(compiler::Node* x); 54 55 // Tag a Word as a Smi value. 56 compiler::Node* SmiTag(compiler::Node* value); 57 // Untag a Smi value as a Word. 58 compiler::Node* SmiUntag(compiler::Node* value); 59 60 // Smi conversions. 61 compiler::Node* SmiToFloat64(compiler::Node* value); SmiFromWord(compiler::Node * value)62 compiler::Node* SmiFromWord(compiler::Node* value) { return SmiTag(value); } 63 compiler::Node* SmiFromWord32(compiler::Node* value); SmiToWord(compiler::Node * value)64 compiler::Node* SmiToWord(compiler::Node* value) { return SmiUntag(value); } 65 compiler::Node* SmiToWord32(compiler::Node* value); 66 67 // Smi operations. 68 compiler::Node* SmiAdd(compiler::Node* a, compiler::Node* b); 69 compiler::Node* SmiAddWithOverflow(compiler::Node* a, compiler::Node* b); 70 compiler::Node* SmiSub(compiler::Node* a, compiler::Node* b); 71 compiler::Node* SmiSubWithOverflow(compiler::Node* a, compiler::Node* b); 72 compiler::Node* SmiEqual(compiler::Node* a, compiler::Node* b); 73 compiler::Node* SmiAboveOrEqual(compiler::Node* a, compiler::Node* b); 74 compiler::Node* SmiLessThan(compiler::Node* a, compiler::Node* b); 75 compiler::Node* SmiLessThanOrEqual(compiler::Node* a, compiler::Node* b); 76 compiler::Node* SmiMin(compiler::Node* a, compiler::Node* b); 77 78 // Allocate an object of the given size. 79 compiler::Node* Allocate(compiler::Node* size, AllocationFlags flags = kNone); 80 compiler::Node* Allocate(int size, AllocationFlags flags = kNone); 81 compiler::Node* InnerAllocate(compiler::Node* previous, int offset); 82 compiler::Node* InnerAllocate(compiler::Node* previous, 83 compiler::Node* offset); 84 85 void Assert(compiler::Node* condition); 86 87 // Check a value for smi-ness 88 compiler::Node* WordIsSmi(compiler::Node* a); 89 // Check that the value is a positive smi. 90 compiler::Node* WordIsPositiveSmi(compiler::Node* a); 91 BranchIfSmiLessThan(compiler::Node * a,compiler::Node * b,Label * if_true,Label * if_false)92 void BranchIfSmiLessThan(compiler::Node* a, compiler::Node* b, Label* if_true, 93 Label* if_false) { 94 BranchIf(SmiLessThan(a, b), if_true, if_false); 95 } 96 BranchIfSmiLessThanOrEqual(compiler::Node * a,compiler::Node * b,Label * if_true,Label * if_false)97 void BranchIfSmiLessThanOrEqual(compiler::Node* a, compiler::Node* b, 98 Label* if_true, Label* if_false) { 99 BranchIf(SmiLessThanOrEqual(a, b), if_true, if_false); 100 } 101 BranchIfFloat64IsNaN(compiler::Node * value,Label * if_true,Label * if_false)102 void BranchIfFloat64IsNaN(compiler::Node* value, Label* if_true, 103 Label* if_false) { 104 BranchIfFloat64Equal(value, value, if_false, if_true); 105 } 106 107 // Load value from current frame by given offset in bytes. 108 compiler::Node* LoadFromFrame(int offset, 109 MachineType rep = MachineType::AnyTagged()); 110 // Load value from current parent frame by given offset in bytes. 111 compiler::Node* LoadFromParentFrame( 112 int offset, MachineType rep = MachineType::AnyTagged()); 113 114 // Load an object pointer from a buffer that isn't in the heap. 115 compiler::Node* LoadBufferObject(compiler::Node* buffer, int offset, 116 MachineType rep = MachineType::AnyTagged()); 117 // Load a field from an object on the heap. 118 compiler::Node* LoadObjectField(compiler::Node* object, int offset, 119 MachineType rep = MachineType::AnyTagged()); 120 compiler::Node* LoadObjectField(compiler::Node* object, 121 compiler::Node* offset, 122 MachineType rep = MachineType::AnyTagged()); 123 124 // Load the floating point value of a HeapNumber. 125 compiler::Node* LoadHeapNumberValue(compiler::Node* object); 126 // Load the Map of an HeapObject. 127 compiler::Node* LoadMap(compiler::Node* object); 128 // Load the instance type of an HeapObject. 129 compiler::Node* LoadInstanceType(compiler::Node* object); 130 // Checks that given heap object has given instance type. 131 void AssertInstanceType(compiler::Node* object, InstanceType instance_type); 132 // Load the properties backing store of a JSObject. 133 compiler::Node* LoadProperties(compiler::Node* object); 134 // Load the elements backing store of a JSObject. 135 compiler::Node* LoadElements(compiler::Node* object); 136 // Load the length of a fixed array base instance. 137 compiler::Node* LoadFixedArrayBaseLength(compiler::Node* array); 138 // Load the bit field of a Map. 139 compiler::Node* LoadMapBitField(compiler::Node* map); 140 // Load bit field 2 of a map. 141 compiler::Node* LoadMapBitField2(compiler::Node* map); 142 // Load bit field 3 of a map. 143 compiler::Node* LoadMapBitField3(compiler::Node* map); 144 // Load the instance type of a map. 145 compiler::Node* LoadMapInstanceType(compiler::Node* map); 146 // Load the instance descriptors of a map. 147 compiler::Node* LoadMapDescriptors(compiler::Node* map); 148 // Load the prototype of a map. 149 compiler::Node* LoadMapPrototype(compiler::Node* map); 150 // Load the instance size of a Map. 151 compiler::Node* LoadMapInstanceSize(compiler::Node* map); 152 // Load the inobject properties count of a Map (valid only for JSObjects). 153 compiler::Node* LoadMapInobjectProperties(compiler::Node* map); 154 155 // Load the hash field of a name. 156 compiler::Node* LoadNameHashField(compiler::Node* name); 157 // Load the hash value of a name. If {if_hash_not_computed} label 158 // is specified then it also checks if hash is actually computed. 159 compiler::Node* LoadNameHash(compiler::Node* name, 160 Label* if_hash_not_computed = nullptr); 161 162 // Load length field of a String object. 163 compiler::Node* LoadStringLength(compiler::Node* object); 164 // Load value field of a JSValue object. 165 compiler::Node* LoadJSValueValue(compiler::Node* object); 166 // Load value field of a WeakCell object. 167 compiler::Node* LoadWeakCellValue(compiler::Node* weak_cell, 168 Label* if_cleared = nullptr); 169 170 compiler::Node* AllocateUninitializedFixedArray(compiler::Node* length); 171 172 // Load an array element from a FixedArray. 173 compiler::Node* LoadFixedArrayElement( 174 compiler::Node* object, compiler::Node* int32_index, 175 int additional_offset = 0, 176 ParameterMode parameter_mode = INTEGER_PARAMETERS); 177 // Load an array element from a FixedDoubleArray. 178 compiler::Node* LoadFixedDoubleArrayElement( 179 compiler::Node* object, compiler::Node* int32_index, 180 MachineType machine_type, int additional_offset = 0, 181 ParameterMode parameter_mode = INTEGER_PARAMETERS); 182 183 // Context manipulation 184 compiler::Node* LoadNativeContext(compiler::Node* context); 185 186 compiler::Node* LoadJSArrayElementsMap(ElementsKind kind, 187 compiler::Node* native_context); 188 189 // Store the floating point value of a HeapNumber. 190 compiler::Node* StoreHeapNumberValue(compiler::Node* object, 191 compiler::Node* value); 192 // Store a field to an object on the heap. 193 compiler::Node* StoreObjectField( 194 compiler::Node* object, int offset, compiler::Node* value); 195 compiler::Node* StoreObjectFieldNoWriteBarrier( 196 compiler::Node* object, int offset, compiler::Node* value, 197 MachineRepresentation rep = MachineRepresentation::kTagged); 198 // Store the Map of an HeapObject. 199 compiler::Node* StoreMapNoWriteBarrier(compiler::Node* object, 200 compiler::Node* map); 201 // Store an array element to a FixedArray. 202 compiler::Node* StoreFixedArrayElement( 203 compiler::Node* object, compiler::Node* index, compiler::Node* value, 204 WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER, 205 ParameterMode parameter_mode = INTEGER_PARAMETERS); 206 207 compiler::Node* StoreFixedDoubleArrayElement( 208 compiler::Node* object, compiler::Node* index, compiler::Node* value, 209 ParameterMode parameter_mode = INTEGER_PARAMETERS); 210 211 // Allocate a HeapNumber without initializing its value. 212 compiler::Node* AllocateHeapNumber(); 213 // Allocate a HeapNumber with a specific value. 214 compiler::Node* AllocateHeapNumberWithValue(compiler::Node* value); 215 // Allocate a SeqOneByteString with the given length. 216 compiler::Node* AllocateSeqOneByteString(int length); 217 compiler::Node* AllocateSeqOneByteString(compiler::Node* context, 218 compiler::Node* length); 219 // Allocate a SeqTwoByteString with the given length. 220 compiler::Node* AllocateSeqTwoByteString(int length); 221 compiler::Node* AllocateSeqTwoByteString(compiler::Node* context, 222 compiler::Node* length); 223 // Allocated an JSArray 224 compiler::Node* AllocateJSArray(ElementsKind kind, compiler::Node* array_map, 225 compiler::Node* capacity, 226 compiler::Node* length, 227 compiler::Node* allocation_site = nullptr, 228 ParameterMode mode = INTEGER_PARAMETERS); 229 230 // Allocation site manipulation 231 void InitializeAllocationMemento(compiler::Node* base_allocation, 232 int base_allocation_size, 233 compiler::Node* allocation_site); 234 235 compiler::Node* TruncateTaggedToFloat64(compiler::Node* context, 236 compiler::Node* value); 237 compiler::Node* TruncateTaggedToWord32(compiler::Node* context, 238 compiler::Node* value); 239 // Truncate the floating point value of a HeapNumber to an Int32. 240 compiler::Node* TruncateHeapNumberValueToWord32(compiler::Node* object); 241 242 // Conversions. 243 compiler::Node* ChangeFloat64ToTagged(compiler::Node* value); 244 compiler::Node* ChangeInt32ToTagged(compiler::Node* value); 245 compiler::Node* ChangeUint32ToTagged(compiler::Node* value); 246 247 // Type conversions. 248 // Throws a TypeError for {method_name} if {value} is not coercible to Object, 249 // or returns the {value} converted to a String otherwise. 250 compiler::Node* ToThisString(compiler::Node* context, compiler::Node* value, 251 char const* method_name); 252 253 // String helpers. 254 // Load a character from a String (might flatten a ConsString). 255 compiler::Node* StringCharCodeAt(compiler::Node* string, 256 compiler::Node* smi_index); 257 // Return the single character string with only {code}. 258 compiler::Node* StringFromCharCode(compiler::Node* code); 259 260 // Returns a node that is true if the given bit is set in |word32|. 261 template <typename T> BitFieldDecode(compiler::Node * word32)262 compiler::Node* BitFieldDecode(compiler::Node* word32) { 263 return BitFieldDecode(word32, T::kShift, T::kMask); 264 } 265 266 compiler::Node* BitFieldDecode(compiler::Node* word32, uint32_t shift, 267 uint32_t mask); 268 269 void SetCounter(StatsCounter* counter, int value); 270 void IncrementCounter(StatsCounter* counter, int delta); 271 void DecrementCounter(StatsCounter* counter, int delta); 272 273 // Various building blocks for stubs doing property lookups. 274 void TryToName(compiler::Node* key, Label* if_keyisindex, Variable* var_index, 275 Label* if_keyisunique, Label* if_bailout); 276 277 // Calculates array index for given dictionary entry and entry field. 278 // See Dictionary::EntryToIndex(). 279 template <typename Dictionary> 280 compiler::Node* EntryToIndex(compiler::Node* entry, int field_index); 281 template <typename Dictionary> EntryToIndex(compiler::Node * entry)282 compiler::Node* EntryToIndex(compiler::Node* entry) { 283 return EntryToIndex<Dictionary>(entry, Dictionary::kEntryKeyIndex); 284 } 285 286 // Looks up an entry in a NameDictionaryBase successor. If the entry is found 287 // control goes to {if_found} and {var_name_index} contains an index of the 288 // key field of the entry found. If the key is not found control goes to 289 // {if_not_found}. 290 static const int kInlinedDictionaryProbes = 4; 291 template <typename Dictionary> 292 void NameDictionaryLookup(compiler::Node* dictionary, 293 compiler::Node* unique_name, Label* if_found, 294 Variable* var_name_index, Label* if_not_found, 295 int inlined_probes = kInlinedDictionaryProbes); 296 297 compiler::Node* ComputeIntegerHash(compiler::Node* key, compiler::Node* seed); 298 299 template <typename Dictionary> 300 void NumberDictionaryLookup(compiler::Node* dictionary, compiler::Node* key, 301 Label* if_found, Variable* var_entry, 302 Label* if_not_found); 303 304 // Tries to check if {object} has own {unique_name} property. 305 void TryHasOwnProperty(compiler::Node* object, compiler::Node* map, 306 compiler::Node* instance_type, 307 compiler::Node* unique_name, Label* if_found, 308 Label* if_not_found, Label* if_bailout); 309 310 // Tries to get {object}'s own {unique_name} property value. If the property 311 // is an accessor then it also calls a getter. If the property is a double 312 // field it re-wraps value in an immutable heap number. 313 void TryGetOwnProperty(compiler::Node* context, compiler::Node* receiver, 314 compiler::Node* object, compiler::Node* map, 315 compiler::Node* instance_type, 316 compiler::Node* unique_name, Label* if_found, 317 Variable* var_value, Label* if_not_found, 318 Label* if_bailout); 319 320 void LoadPropertyFromFastObject(compiler::Node* object, compiler::Node* map, 321 compiler::Node* descriptors, 322 compiler::Node* name_index, 323 Variable* var_details, Variable* var_value); 324 325 void LoadPropertyFromNameDictionary(compiler::Node* dictionary, 326 compiler::Node* entry, 327 Variable* var_details, 328 Variable* var_value); 329 330 void LoadPropertyFromGlobalDictionary(compiler::Node* dictionary, 331 compiler::Node* entry, 332 Variable* var_details, 333 Variable* var_value, Label* if_deleted); 334 335 // Generic property lookup generator. If the {object} is fast and 336 // {unique_name} property is found then the control goes to {if_found_fast} 337 // label and {var_meta_storage} and {var_name_index} will contain 338 // DescriptorArray and an index of the descriptor's name respectively. 339 // If the {object} is slow or global then the control goes to {if_found_dict} 340 // or {if_found_global} and the {var_meta_storage} and {var_name_index} will 341 // contain a dictionary and an index of the key field of the found entry. 342 // If property is not found or given lookup is not supported then 343 // the control goes to {if_not_found} or {if_bailout} respectively. 344 // 345 // Note: this code does not check if the global dictionary points to deleted 346 // entry! This has to be done by the caller. 347 void TryLookupProperty(compiler::Node* object, compiler::Node* map, 348 compiler::Node* instance_type, 349 compiler::Node* unique_name, Label* if_found_fast, 350 Label* if_found_dict, Label* if_found_global, 351 Variable* var_meta_storage, Variable* var_name_index, 352 Label* if_not_found, Label* if_bailout); 353 354 void TryLookupElement(compiler::Node* object, compiler::Node* map, 355 compiler::Node* instance_type, compiler::Node* index, 356 Label* if_found, Label* if_not_found, 357 Label* if_bailout); 358 359 // Instanceof helpers. 360 // ES6 section 7.3.19 OrdinaryHasInstance (C, O) 361 compiler::Node* OrdinaryHasInstance(compiler::Node* context, 362 compiler::Node* callable, 363 compiler::Node* object); 364 365 // LoadIC helpers. 366 struct LoadICParameters { LoadICParametersLoadICParameters367 LoadICParameters(compiler::Node* context, compiler::Node* receiver, 368 compiler::Node* name, compiler::Node* slot, 369 compiler::Node* vector) 370 : context(context), 371 receiver(receiver), 372 name(name), 373 slot(slot), 374 vector(vector) {} 375 376 compiler::Node* context; 377 compiler::Node* receiver; 378 compiler::Node* name; 379 compiler::Node* slot; 380 compiler::Node* vector; 381 }; 382 383 // Load type feedback vector from the stub caller's frame. 384 compiler::Node* LoadTypeFeedbackVectorForStub(); 385 386 compiler::Node* LoadReceiverMap(compiler::Node* receiver); 387 388 // Checks monomorphic case. Returns {feedback} entry of the vector. 389 compiler::Node* TryMonomorphicCase(const LoadICParameters* p, 390 compiler::Node* receiver_map, 391 Label* if_handler, Variable* var_handler, 392 Label* if_miss); 393 void HandlePolymorphicCase(const LoadICParameters* p, 394 compiler::Node* receiver_map, 395 compiler::Node* feedback, Label* if_handler, 396 Variable* var_handler, Label* if_miss, 397 int unroll_count); 398 399 compiler::Node* StubCachePrimaryOffset(compiler::Node* name, 400 Code::Flags flags, 401 compiler::Node* map); 402 403 compiler::Node* StubCacheSecondaryOffset(compiler::Node* name, 404 Code::Flags flags, 405 compiler::Node* seed); 406 407 // This enum is used here as a replacement for StubCache::Table to avoid 408 // including stub cache header. 409 enum StubCacheTable : int; 410 411 void TryProbeStubCacheTable(StubCache* stub_cache, StubCacheTable table_id, 412 compiler::Node* entry_offset, 413 compiler::Node* name, Code::Flags flags, 414 compiler::Node* map, Label* if_handler, 415 Variable* var_handler, Label* if_miss); 416 417 void TryProbeStubCache(StubCache* stub_cache, Code::Flags flags, 418 compiler::Node* receiver, compiler::Node* name, 419 Label* if_handler, Variable* var_handler, 420 Label* if_miss); 421 422 void LoadIC(const LoadICParameters* p); 423 void LoadGlobalIC(const LoadICParameters* p); 424 425 private: 426 compiler::Node* ElementOffsetFromIndex(compiler::Node* index, 427 ElementsKind kind, ParameterMode mode, 428 int base_size = 0); 429 430 compiler::Node* AllocateRawAligned(compiler::Node* size_in_bytes, 431 AllocationFlags flags, 432 compiler::Node* top_address, 433 compiler::Node* limit_address); 434 compiler::Node* AllocateRawUnaligned(compiler::Node* size_in_bytes, 435 AllocationFlags flags, 436 compiler::Node* top_adddress, 437 compiler::Node* limit_address); 438 439 static const int kElementLoopUnrollThreshold = 8; 440 }; 441 442 } // namespace internal 443 } // namespace v8 444 #endif // V8_CODE_STUB_ASSEMBLER_H_ 445