Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/v8.h" | 5 #include "src/v8.h" |
| 6 | 6 |
| 7 #if V8_TARGET_ARCH_IA32 | 7 #if V8_TARGET_ARCH_IA32 |
| 8 | 8 |
| 9 #include "src/code-factory.h" | 9 #include "src/code-factory.h" |
| 10 #include "src/codegen.h" | 10 #include "src/codegen.h" |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 132 __ jmp(count_incremented); | 132 __ jmp(count_incremented); |
| 133 } else { | 133 } else { |
| 134 __ jmp(allocated); | 134 __ jmp(allocated); |
| 135 } | 135 } |
| 136 } | 136 } |
| 137 | 137 |
| 138 | 138 |
| 139 static void Generate_JSConstructStubHelper(MacroAssembler* masm, | 139 static void Generate_JSConstructStubHelper(MacroAssembler* masm, |
| 140 bool is_api_function, | 140 bool is_api_function, |
| 141 bool use_new_target, | 141 bool use_new_target, |
| 142 bool is_derived_class, | |
| 142 bool create_memento) { | 143 bool create_memento) { |
| 143 // ----------- S t a t e ------------- | 144 // ----------- S t a t e ------------- |
| 144 // -- eax: number of arguments | 145 // -- eax: number of arguments |
| 145 // -- edi: constructor function | 146 // -- edi: constructor function |
| 146 // -- ebx: allocation site or undefined | 147 // -- ebx: allocation site or undefined |
| 147 // -- edx: original constructor | 148 // -- edx: original constructor |
| 148 // ----------------------------------- | 149 // ----------------------------------- |
| 149 | 150 |
| 150 // Should never create mementos for api functions. | 151 // Should never create mementos for api functions. |
| 151 DCHECK(!is_api_function || !create_memento); | 152 DCHECK(!is_api_function || !create_memento); |
| 152 | 153 |
| 154 // Should never create mementos for subclass constructors. | |
| 155 DCHECK(!is_derived_class || !create_memento); | |
| 156 | |
| 153 // Enter a construct frame. | 157 // Enter a construct frame. |
| 154 { | 158 { |
| 155 FrameScope scope(masm, StackFrame::CONSTRUCT); | 159 FrameScope scope(masm, StackFrame::CONSTRUCT); |
| 156 | 160 |
| 157 if (create_memento) { | 161 if (create_memento) { |
| 158 __ AssertUndefinedOrAllocationSite(ebx); | 162 __ AssertUndefinedOrAllocationSite(ebx); |
| 159 __ push(ebx); | 163 __ push(ebx); |
| 160 } | 164 } |
| 161 | 165 |
| 162 // Preserve the incoming parameters on the stack. | 166 // Preserve the incoming parameters on the stack. |
| 163 __ SmiTag(eax); | 167 __ SmiTag(eax); |
| 164 __ push(eax); | 168 __ push(eax); |
| 165 __ push(edi); | 169 if (!is_derived_class) { |
| 166 if (use_new_target) { | 170 __ push(edi); |
| 167 __ push(edx); | 171 if (use_new_target) { |
| 172 __ push(edx); | |
| 173 } | |
| 174 | |
| 175 __ cmp(edx, edi); | |
| 176 Label normal_new; | |
| 177 Label count_incremented; | |
| 178 Label allocated; | |
| 179 __ j(equal, &normal_new); | |
| 180 | |
| 181 // Original constructor and function are different. | |
| 182 Generate_Runtime_NewObject(masm, create_memento, edx, &count_incremented, | |
| 183 &allocated); | |
| 184 __ bind(&normal_new); | |
| 185 | |
| 186 // Try to allocate the object without transitioning into C code. If any of | |
| 187 // the preconditions is not met, the code bails out to the runtime call. | |
| 188 Label rt_call; | |
| 189 if (FLAG_inline_new) { | |
| 190 Label undo_allocation; | |
| 191 ExternalReference debug_step_in_fp = | |
| 192 ExternalReference::debug_step_in_fp_address(masm->isolate()); | |
| 193 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0)); | |
| 194 __ j(not_equal, &rt_call); | |
| 195 | |
| 196 // Verified that the constructor is a JSFunction. | |
| 197 // Load the initial map and verify that it is in fact a map. | |
| 198 // edi: constructor | |
| 199 __ mov(eax, | |
| 200 FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); | |
| 201 // Will both indicate a NULL and a Smi | |
| 202 __ JumpIfSmi(eax, &rt_call); | |
| 203 // edi: constructor | |
| 204 // eax: initial map (if proven valid below) | |
| 205 __ CmpObjectType(eax, MAP_TYPE, ebx); | |
| 206 __ j(not_equal, &rt_call); | |
| 207 | |
| 208 // Check that the constructor is not constructing a JSFunction (see | |
| 209 // comments in Runtime_NewObject in runtime.cc). In which case the | |
| 210 // initial map's instance type would be JS_FUNCTION_TYPE. | |
| 211 // edi: constructor | |
| 212 // eax: initial map | |
| 213 __ CmpInstanceType(eax, JS_FUNCTION_TYPE); | |
| 214 __ j(equal, &rt_call); | |
| 215 | |
| 216 if (!is_api_function) { | |
| 217 Label allocate; | |
| 218 // The code below relies on these assumptions. | |
| 219 STATIC_ASSERT(Map::Counter::kShift + Map::Counter::kSize == 32); | |
| 220 // Check if slack tracking is enabled. | |
| 221 __ mov(esi, FieldOperand(eax, Map::kBitField3Offset)); | |
| 222 __ shr(esi, Map::Counter::kShift); | |
| 223 __ cmp(esi, Map::kSlackTrackingCounterEnd); | |
| 224 __ j(less, &allocate); | |
| 225 // Decrease generous allocation count. | |
| 226 __ sub(FieldOperand(eax, Map::kBitField3Offset), | |
| 227 Immediate(1 << Map::Counter::kShift)); | |
| 228 | |
| 229 __ cmp(esi, Map::kSlackTrackingCounterEnd); | |
| 230 __ j(not_equal, &allocate); | |
| 231 | |
| 232 __ push(eax); | |
| 233 __ push(edi); | |
| 234 | |
| 235 __ push(edi); // constructor | |
| 236 __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); | |
| 237 | |
| 238 __ pop(edi); | |
| 239 __ pop(eax); | |
| 240 __ mov(esi, Map::kSlackTrackingCounterEnd - 1); | |
| 241 | |
| 242 __ bind(&allocate); | |
| 243 } | |
| 244 | |
| 245 // Now allocate the JSObject on the heap. | |
| 246 // edi: constructor | |
| 247 // eax: initial map | |
| 248 __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset)); | |
| 249 __ shl(edi, kPointerSizeLog2); | |
| 250 if (create_memento) { | |
| 251 __ add(edi, Immediate(AllocationMemento::kSize)); | |
| 252 } | |
| 253 | |
| 254 __ Allocate(edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS); | |
| 255 | |
| 256 Factory* factory = masm->isolate()->factory(); | |
| 257 | |
| 258 // Allocated the JSObject, now initialize the fields. | |
| 259 // eax: initial map | |
| 260 // ebx: JSObject | |
| 261 // edi: start of next object (including memento if create_memento) | |
| 262 __ mov(Operand(ebx, JSObject::kMapOffset), eax); | |
| 263 __ mov(ecx, factory->empty_fixed_array()); | |
| 264 __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx); | |
| 265 __ mov(Operand(ebx, JSObject::kElementsOffset), ecx); | |
| 266 // Set extra fields in the newly allocated object. | |
| 267 // eax: initial map | |
| 268 // ebx: JSObject | |
| 269 // edi: start of next object (including memento if create_memento) | |
| 270 // esi: slack tracking counter (non-API function case) | |
| 271 __ mov(edx, factory->undefined_value()); | |
| 272 __ lea(ecx, Operand(ebx, JSObject::kHeaderSize)); | |
| 273 if (!is_api_function) { | |
| 274 Label no_inobject_slack_tracking; | |
| 275 | |
| 276 // Check if slack tracking is enabled. | |
| 277 __ cmp(esi, Map::kSlackTrackingCounterEnd); | |
| 278 __ j(less, &no_inobject_slack_tracking); | |
| 279 | |
| 280 // Allocate object with a slack. | |
| 281 __ movzx_b(esi, | |
| 282 FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); | |
| 283 __ lea(esi, | |
| 284 Operand(ebx, esi, times_pointer_size, JSObject::kHeaderSize)); | |
| 285 // esi: offset of first field after pre-allocated fields | |
| 286 if (FLAG_debug_code) { | |
| 287 __ cmp(esi, edi); | |
| 288 __ Assert(less_equal, | |
| 289 kUnexpectedNumberOfPreAllocatedPropertyFields); | |
| 290 } | |
| 291 __ InitializeFieldsWithFiller(ecx, esi, edx); | |
| 292 __ mov(edx, factory->one_pointer_filler_map()); | |
| 293 // Fill the remaining fields with one pointer filler map. | |
| 294 | |
| 295 __ bind(&no_inobject_slack_tracking); | |
| 296 } | |
| 297 | |
| 298 if (create_memento) { | |
| 299 __ lea(esi, Operand(edi, -AllocationMemento::kSize)); | |
| 300 __ InitializeFieldsWithFiller(ecx, esi, edx); | |
| 301 | |
| 302 // Fill in memento fields if necessary. | |
| 303 // esi: points to the allocated but uninitialized memento. | |
| 304 __ mov(Operand(esi, AllocationMemento::kMapOffset), | |
| 305 factory->allocation_memento_map()); | |
| 306 // Get the cell or undefined. | |
| 307 __ mov(edx, Operand(esp, kPointerSize * 2)); | |
| 308 __ mov(Operand(esi, AllocationMemento::kAllocationSiteOffset), edx); | |
| 309 } else { | |
| 310 __ InitializeFieldsWithFiller(ecx, edi, edx); | |
| 311 } | |
| 312 | |
| 313 // Add the object tag to make the JSObject real, so that we can continue | |
| 314 // and jump into the continuation code at any time from now on. Any | |
| 315 // failures need to undo the allocation, so that the heap is in a | |
| 316 // consistent state and verifiable. | |
| 317 // eax: initial map | |
| 318 // ebx: JSObject | |
| 319 // edi: start of next object | |
| 320 __ or_(ebx, Immediate(kHeapObjectTag)); | |
| 321 | |
| 322 // Check if a non-empty properties array is needed. | |
| 323 // Allocate and initialize a FixedArray if it is. | |
| 324 // eax: initial map | |
| 325 // ebx: JSObject | |
| 326 // edi: start of next object | |
| 327 // Calculate the total number of properties described by the map. | |
| 328 __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset)); | |
| 329 __ movzx_b(ecx, | |
| 330 FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); | |
| 331 __ add(edx, ecx); | |
| 332 // Calculate unused properties past the end of the in-object properties. | |
| 333 __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset)); | |
| 334 __ sub(edx, ecx); | |
| 335 // Done if no extra properties are to be allocated. | |
| 336 __ j(zero, &allocated); | |
| 337 __ Assert(positive, kPropertyAllocationCountFailed); | |
| 338 | |
| 339 // Scale the number of elements by pointer size and add the header for | |
| 340 // FixedArrays to the start of the next object calculation from above. | |
| 341 // ebx: JSObject | |
| 342 // edi: start of next object (will be start of FixedArray) | |
| 343 // edx: number of elements in properties array | |
| 344 __ Allocate(FixedArray::kHeaderSize, times_pointer_size, edx, | |
| 345 REGISTER_VALUE_IS_INT32, edi, ecx, no_reg, &undo_allocation, | |
| 346 RESULT_CONTAINS_TOP); | |
| 347 | |
| 348 // Initialize the FixedArray. | |
| 349 // ebx: JSObject | |
| 350 // edi: FixedArray | |
| 351 // edx: number of elements | |
| 352 // ecx: start of next object | |
| 353 __ mov(eax, factory->fixed_array_map()); | |
| 354 __ mov(Operand(edi, FixedArray::kMapOffset), eax); // setup the map | |
| 355 __ SmiTag(edx); | |
| 356 __ mov(Operand(edi, FixedArray::kLengthOffset), edx); // and length | |
| 357 | |
| 358 // Initialize the fields to undefined. | |
| 359 // ebx: JSObject | |
| 360 // edi: FixedArray | |
| 361 // ecx: start of next object | |
| 362 __ mov(edx, factory->undefined_value()); | |
| 363 __ lea(eax, Operand(edi, FixedArray::kHeaderSize)); | |
| 364 __ InitializeFieldsWithFiller(eax, ecx, edx); | |
| 365 | |
| 366 // Store the initialized FixedArray into the properties field of | |
| 367 // the JSObject | |
| 368 // ebx: JSObject | |
| 369 // edi: FixedArray | |
| 370 __ or_(edi, Immediate(kHeapObjectTag)); // add the heap tag | |
| 371 __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi); | |
| 372 | |
| 373 | |
| 374 // Continue with JSObject being successfully allocated | |
| 375 // ebx: JSObject | |
| 376 __ jmp(&allocated); | |
| 377 | |
| 378 // Undo the setting of the new top so that the heap is verifiable. For | |
| 379 // example, the map's unused properties potentially do not match the | |
| 380 // allocated objects unused properties. | |
| 381 // ebx: JSObject (previous new top) | |
| 382 __ bind(&undo_allocation); | |
| 383 __ UndoAllocationInNewSpace(ebx); | |
| 384 } | |
| 385 | |
| 386 // Allocate the new receiver object using the runtime call. | |
| 387 __ bind(&rt_call); | |
| 388 Generate_Runtime_NewObject(masm, create_memento, edi, &count_incremented, | |
| 389 &allocated); | |
| 390 // New object allocated. | |
| 391 // ebx: newly allocated object | |
| 392 __ bind(&allocated); | |
| 393 | |
| 394 if (create_memento) { | |
| 395 int offset = (use_new_target ? 3 : 2) * kPointerSize; | |
| 396 __ mov(ecx, Operand(esp, offset)); | |
| 397 __ cmp(ecx, masm->isolate()->factory()->undefined_value()); | |
| 398 __ j(equal, &count_incremented); | |
| 399 // ecx is an AllocationSite. We are creating a memento from it, so we | |
| 400 // need to increment the memento create count. | |
| 401 __ add(FieldOperand(ecx, AllocationSite::kPretenureCreateCountOffset), | |
| 402 Immediate(Smi::FromInt(1))); | |
| 403 __ bind(&count_incremented); | |
| 404 } | |
| 405 | |
| 406 // Restore the parameters. | |
| 407 if (use_new_target) { | |
| 408 __ pop(edx); // new.target | |
| 409 } | |
| 410 __ pop(edi); // Constructor function. | |
| 411 | |
| 412 // Retrieve smi-tagged arguments count from the stack. | |
| 413 __ mov(eax, Operand(esp, 0)); | |
| 168 } | 414 } |
| 169 | |
| 170 __ cmp(edx, edi); | |
| 171 Label normal_new; | |
| 172 Label count_incremented; | |
| 173 Label allocated; | |
| 174 __ j(equal, &normal_new); | |
| 175 | |
| 176 // Original constructor and function are different. | |
| 177 Generate_Runtime_NewObject(masm, create_memento, edx, &count_incremented, | |
| 178 &allocated); | |
| 179 __ bind(&normal_new); | |
| 180 | |
| 181 // Try to allocate the object without transitioning into C code. If any of | |
| 182 // the preconditions is not met, the code bails out to the runtime call. | |
| 183 Label rt_call; | |
| 184 if (FLAG_inline_new) { | |
| 185 Label undo_allocation; | |
| 186 ExternalReference debug_step_in_fp = | |
| 187 ExternalReference::debug_step_in_fp_address(masm->isolate()); | |
| 188 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0)); | |
| 189 __ j(not_equal, &rt_call); | |
| 190 | |
| 191 // Verified that the constructor is a JSFunction. | |
| 192 // Load the initial map and verify that it is in fact a map. | |
| 193 // edi: constructor | |
| 194 __ mov(eax, FieldOperand(edi, JSFunction::kPrototypeOrInitialMapOffset)); | |
| 195 // Will both indicate a NULL and a Smi | |
| 196 __ JumpIfSmi(eax, &rt_call); | |
| 197 // edi: constructor | |
| 198 // eax: initial map (if proven valid below) | |
| 199 __ CmpObjectType(eax, MAP_TYPE, ebx); | |
| 200 __ j(not_equal, &rt_call); | |
| 201 | |
| 202 // Check that the constructor is not constructing a JSFunction (see | |
| 203 // comments in Runtime_NewObject in runtime.cc). In which case the | |
| 204 // initial map's instance type would be JS_FUNCTION_TYPE. | |
| 205 // edi: constructor | |
| 206 // eax: initial map | |
| 207 __ CmpInstanceType(eax, JS_FUNCTION_TYPE); | |
| 208 __ j(equal, &rt_call); | |
| 209 | |
| 210 if (!is_api_function) { | |
| 211 Label allocate; | |
| 212 // The code below relies on these assumptions. | |
| 213 STATIC_ASSERT(Map::Counter::kShift + Map::Counter::kSize == 32); | |
| 214 // Check if slack tracking is enabled. | |
| 215 __ mov(esi, FieldOperand(eax, Map::kBitField3Offset)); | |
| 216 __ shr(esi, Map::Counter::kShift); | |
| 217 __ cmp(esi, Map::kSlackTrackingCounterEnd); | |
| 218 __ j(less, &allocate); | |
| 219 // Decrease generous allocation count. | |
| 220 __ sub(FieldOperand(eax, Map::kBitField3Offset), | |
| 221 Immediate(1 << Map::Counter::kShift)); | |
| 222 | |
| 223 __ cmp(esi, Map::kSlackTrackingCounterEnd); | |
| 224 __ j(not_equal, &allocate); | |
| 225 | |
| 226 __ push(eax); | |
| 227 __ push(edi); | |
| 228 | |
| 229 __ push(edi); // constructor | |
| 230 __ CallRuntime(Runtime::kFinalizeInstanceSize, 1); | |
| 231 | |
| 232 __ pop(edi); | |
| 233 __ pop(eax); | |
| 234 __ mov(esi, Map::kSlackTrackingCounterEnd - 1); | |
| 235 | |
| 236 __ bind(&allocate); | |
| 237 } | |
| 238 | |
| 239 // Now allocate the JSObject on the heap. | |
| 240 // edi: constructor | |
| 241 // eax: initial map | |
| 242 __ movzx_b(edi, FieldOperand(eax, Map::kInstanceSizeOffset)); | |
| 243 __ shl(edi, kPointerSizeLog2); | |
| 244 if (create_memento) { | |
| 245 __ add(edi, Immediate(AllocationMemento::kSize)); | |
| 246 } | |
| 247 | |
| 248 __ Allocate(edi, ebx, edi, no_reg, &rt_call, NO_ALLOCATION_FLAGS); | |
| 249 | |
| 250 Factory* factory = masm->isolate()->factory(); | |
| 251 | |
| 252 // Allocated the JSObject, now initialize the fields. | |
| 253 // eax: initial map | |
| 254 // ebx: JSObject | |
| 255 // edi: start of next object (including memento if create_memento) | |
| 256 __ mov(Operand(ebx, JSObject::kMapOffset), eax); | |
| 257 __ mov(ecx, factory->empty_fixed_array()); | |
| 258 __ mov(Operand(ebx, JSObject::kPropertiesOffset), ecx); | |
| 259 __ mov(Operand(ebx, JSObject::kElementsOffset), ecx); | |
| 260 // Set extra fields in the newly allocated object. | |
| 261 // eax: initial map | |
| 262 // ebx: JSObject | |
| 263 // edi: start of next object (including memento if create_memento) | |
| 264 // esi: slack tracking counter (non-API function case) | |
| 265 __ mov(edx, factory->undefined_value()); | |
| 266 __ lea(ecx, Operand(ebx, JSObject::kHeaderSize)); | |
| 267 if (!is_api_function) { | |
| 268 Label no_inobject_slack_tracking; | |
| 269 | |
| 270 // Check if slack tracking is enabled. | |
| 271 __ cmp(esi, Map::kSlackTrackingCounterEnd); | |
| 272 __ j(less, &no_inobject_slack_tracking); | |
| 273 | |
| 274 // Allocate object with a slack. | |
| 275 __ movzx_b(esi, | |
| 276 FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); | |
| 277 __ lea(esi, | |
| 278 Operand(ebx, esi, times_pointer_size, JSObject::kHeaderSize)); | |
| 279 // esi: offset of first field after pre-allocated fields | |
| 280 if (FLAG_debug_code) { | |
| 281 __ cmp(esi, edi); | |
| 282 __ Assert(less_equal, | |
| 283 kUnexpectedNumberOfPreAllocatedPropertyFields); | |
| 284 } | |
| 285 __ InitializeFieldsWithFiller(ecx, esi, edx); | |
| 286 __ mov(edx, factory->one_pointer_filler_map()); | |
| 287 // Fill the remaining fields with one pointer filler map. | |
| 288 | |
| 289 __ bind(&no_inobject_slack_tracking); | |
| 290 } | |
| 291 | |
| 292 if (create_memento) { | |
| 293 __ lea(esi, Operand(edi, -AllocationMemento::kSize)); | |
| 294 __ InitializeFieldsWithFiller(ecx, esi, edx); | |
| 295 | |
| 296 // Fill in memento fields if necessary. | |
| 297 // esi: points to the allocated but uninitialized memento. | |
| 298 __ mov(Operand(esi, AllocationMemento::kMapOffset), | |
| 299 factory->allocation_memento_map()); | |
| 300 // Get the cell or undefined. | |
| 301 __ mov(edx, Operand(esp, kPointerSize*2)); | |
| 302 __ mov(Operand(esi, AllocationMemento::kAllocationSiteOffset), | |
| 303 edx); | |
| 304 } else { | |
| 305 __ InitializeFieldsWithFiller(ecx, edi, edx); | |
| 306 } | |
| 307 | |
| 308 // Add the object tag to make the JSObject real, so that we can continue | |
| 309 // and jump into the continuation code at any time from now on. Any | |
| 310 // failures need to undo the allocation, so that the heap is in a | |
| 311 // consistent state and verifiable. | |
| 312 // eax: initial map | |
| 313 // ebx: JSObject | |
| 314 // edi: start of next object | |
| 315 __ or_(ebx, Immediate(kHeapObjectTag)); | |
| 316 | |
| 317 // Check if a non-empty properties array is needed. | |
| 318 // Allocate and initialize a FixedArray if it is. | |
| 319 // eax: initial map | |
| 320 // ebx: JSObject | |
| 321 // edi: start of next object | |
| 322 // Calculate the total number of properties described by the map. | |
| 323 __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset)); | |
| 324 __ movzx_b(ecx, | |
| 325 FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); | |
| 326 __ add(edx, ecx); | |
| 327 // Calculate unused properties past the end of the in-object properties. | |
| 328 __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset)); | |
| 329 __ sub(edx, ecx); | |
| 330 // Done if no extra properties are to be allocated. | |
| 331 __ j(zero, &allocated); | |
| 332 __ Assert(positive, kPropertyAllocationCountFailed); | |
| 333 | |
| 334 // Scale the number of elements by pointer size and add the header for | |
| 335 // FixedArrays to the start of the next object calculation from above. | |
| 336 // ebx: JSObject | |
| 337 // edi: start of next object (will be start of FixedArray) | |
| 338 // edx: number of elements in properties array | |
| 339 __ Allocate(FixedArray::kHeaderSize, | |
| 340 times_pointer_size, | |
| 341 edx, | |
| 342 REGISTER_VALUE_IS_INT32, | |
| 343 edi, | |
| 344 ecx, | |
| 345 no_reg, | |
| 346 &undo_allocation, | |
| 347 RESULT_CONTAINS_TOP); | |
| 348 | |
| 349 // Initialize the FixedArray. | |
| 350 // ebx: JSObject | |
| 351 // edi: FixedArray | |
| 352 // edx: number of elements | |
| 353 // ecx: start of next object | |
| 354 __ mov(eax, factory->fixed_array_map()); | |
| 355 __ mov(Operand(edi, FixedArray::kMapOffset), eax); // setup the map | |
| 356 __ SmiTag(edx); | |
| 357 __ mov(Operand(edi, FixedArray::kLengthOffset), edx); // and length | |
| 358 | |
| 359 // Initialize the fields to undefined. | |
| 360 // ebx: JSObject | |
| 361 // edi: FixedArray | |
| 362 // ecx: start of next object | |
| 363 __ mov(edx, factory->undefined_value()); | |
| 364 __ lea(eax, Operand(edi, FixedArray::kHeaderSize)); | |
| 365 __ InitializeFieldsWithFiller(eax, ecx, edx); | |
| 366 | |
| 367 // Store the initialized FixedArray into the properties field of | |
| 368 // the JSObject | |
| 369 // ebx: JSObject | |
| 370 // edi: FixedArray | |
| 371 __ or_(edi, Immediate(kHeapObjectTag)); // add the heap tag | |
| 372 __ mov(FieldOperand(ebx, JSObject::kPropertiesOffset), edi); | |
| 373 | |
| 374 | |
| 375 // Continue with JSObject being successfully allocated | |
| 376 // ebx: JSObject | |
| 377 __ jmp(&allocated); | |
| 378 | |
| 379 // Undo the setting of the new top so that the heap is verifiable. For | |
| 380 // example, the map's unused properties potentially do not match the | |
| 381 // allocated objects unused properties. | |
| 382 // ebx: JSObject (previous new top) | |
| 383 __ bind(&undo_allocation); | |
| 384 __ UndoAllocationInNewSpace(ebx); | |
| 385 } | |
| 386 | |
| 387 // Allocate the new receiver object using the runtime call. | |
| 388 __ bind(&rt_call); | |
| 389 Generate_Runtime_NewObject(masm, create_memento, edi, &count_incremented, | |
| 390 &allocated); | |
| 391 // New object allocated. | |
| 392 // ebx: newly allocated object | |
| 393 __ bind(&allocated); | |
| 394 | |
| 395 if (create_memento) { | |
| 396 int offset = (use_new_target ? 3 : 2) * kPointerSize; | |
| 397 __ mov(ecx, Operand(esp, offset)); | |
| 398 __ cmp(ecx, masm->isolate()->factory()->undefined_value()); | |
| 399 __ j(equal, &count_incremented); | |
| 400 // ecx is an AllocationSite. We are creating a memento from it, so we | |
| 401 // need to increment the memento create count. | |
| 402 __ add(FieldOperand(ecx, AllocationSite::kPretenureCreateCountOffset), | |
| 403 Immediate(Smi::FromInt(1))); | |
| 404 __ bind(&count_incremented); | |
| 405 } | |
| 406 | |
| 407 // Restore the parameters. | |
| 408 if (use_new_target) { | |
| 409 __ pop(edx); // new.target | |
| 410 } | |
| 411 __ pop(edi); // Constructor function. | |
| 412 | |
| 413 // Retrieve smi-tagged arguments count from the stack. | |
| 414 __ mov(eax, Operand(esp, 0)); | |
| 415 __ SmiUntag(eax); | 415 __ SmiUntag(eax); |
| 416 | 416 |
| 417 // Push new.target onto the construct frame. This is stored just below the | 417 // Push new.target onto the construct frame. This is stored just below the |
| 418 // receiver on the stack. | 418 // receiver on the stack. |
| 419 if (use_new_target) { | 419 if (use_new_target) { |
| 420 __ push(edx); | 420 __ push(edx); |
| 421 } | 421 } |
| 422 | 422 |
| 423 // Push the allocated receiver to the stack. We need two copies | 423 // Push the allocated receiver to the stack. We need two copies |
| 424 // because we may have to return the original one and the calling | 424 // because we may have to return the original one and the calling |
| 425 // conventions dictate that the called function pops the receiver. | 425 // conventions dictate that the called function pops the receiver. |
| 426 __ push(ebx); | 426 if (!is_derived_class) { |
| 427 __ push(ebx); | 427 __ push(ebx); |
| 428 __ push(ebx); | |
| 429 } else { | |
| 430 // receiver is the hole. | |
| 431 __ push(Immediate(masm->isolate()->factory()->the_hole_value())); | |
| 432 } | |
| 428 | 433 |
| 429 // Set up pointer to last argument. | 434 // Set up pointer to last argument. |
| 430 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); | 435 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); |
| 431 | 436 |
| 432 // Copy arguments and receiver to the expression stack. | 437 // Copy arguments and receiver to the expression stack. |
| 433 Label loop, entry; | 438 Label loop, entry; |
| 434 __ mov(ecx, eax); | 439 __ mov(ecx, eax); |
| 435 __ jmp(&entry); | 440 __ jmp(&entry); |
| 436 __ bind(&loop); | 441 __ bind(&loop); |
| 437 __ push(Operand(ebx, ecx, times_4, 0)); | 442 __ push(Operand(ebx, ecx, times_4, 0)); |
| 438 __ bind(&entry); | 443 __ bind(&entry); |
| 439 __ dec(ecx); | 444 __ dec(ecx); |
| 440 __ j(greater_equal, &loop); | 445 __ j(greater_equal, &loop); |
| 441 | 446 |
| 447 // Handle step in. | |
| 448 if (is_derived_class) { | |
| 449 Label skip_step_in; | |
| 450 ExternalReference debug_step_in_fp = | |
| 451 ExternalReference::debug_step_in_fp_address(masm->isolate()); | |
| 452 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0)); | |
| 453 __ j(equal, &skip_step_in); | |
| 454 | |
| 455 __ push(eax); | |
| 456 __ push(edi); | |
| 457 __ push(edi); | |
| 458 __ CallRuntime(Runtime::kHandleStepInForDerivedConstructors, 1); | |
| 459 __ pop(edi); | |
| 460 __ pop(eax); | |
| 461 | |
| 462 __ bind(&skip_step_in); | |
| 463 } | |
| 464 | |
| 442 // Call the function. | 465 // Call the function. |
| 443 if (is_api_function) { | 466 if (is_api_function) { |
| 444 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); | 467 __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); |
| 445 Handle<Code> code = | 468 Handle<Code> code = |
| 446 masm->isolate()->builtins()->HandleApiCallConstruct(); | 469 masm->isolate()->builtins()->HandleApiCallConstruct(); |
| 447 __ call(code, RelocInfo::CODE_TARGET); | 470 __ call(code, RelocInfo::CODE_TARGET); |
| 448 } else { | 471 } else { |
| 449 ParameterCount actual(eax); | 472 ParameterCount actual(eax); |
| 450 __ InvokeFunction(edi, actual, CALL_FUNCTION, | 473 __ InvokeFunction(edi, actual, CALL_FUNCTION, |
| 451 NullCallWrapper()); | 474 NullCallWrapper()); |
| 452 } | 475 } |
| 453 | 476 |
| 454 // Store offset of return address for deoptimizer. | 477 // Store offset of return address for deoptimizer. |
| 455 // TODO(arv): Remove the "!use_new_target" before supporting optimization | 478 // TODO(arv): Remove the "!use_new_target" before supporting optimization |
| 456 // of functions that reference new.target | 479 // of functions that reference new.target |
| 457 if (!is_api_function && !use_new_target) { | 480 if (!is_api_function && !use_new_target && !is_derived_class) { |
| 458 masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); | 481 masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset()); |
| 459 } | 482 } |
| 460 | 483 |
| 461 // Restore context from the frame. | 484 // Restore context from the frame. |
| 462 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); | 485 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); |
| 463 | 486 |
| 464 // If the result is an object (in the ECMA sense), we should get rid | 487 // If the result is an object (in the ECMA sense), we should get rid |
| 465 // of the receiver and use the result; see ECMA-262 section 13.2.2-7 | 488 // of the receiver and use the result; see ECMA-262 section 13.2.2-7 |
| 466 // on page 74. | 489 // on page 74. |
| 467 Label use_receiver, exit; | 490 if (!is_derived_class) { |
| 491 Label use_receiver, exit; | |
| 468 | 492 |
| 469 // If the result is a smi, it is *not* an object in the ECMA sense. | 493 // If the result is a smi, it is *not* an object in the ECMA sense. |
| 470 __ JumpIfSmi(eax, &use_receiver); | 494 __ JumpIfSmi(eax, &use_receiver); |
| 471 | 495 |
| 472 // If the type of the result (stored in its map) is less than | 496 // If the type of the result (stored in its map) is less than |
| 473 // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. | 497 // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense. |
| 474 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); | 498 __ CmpObjectType(eax, FIRST_SPEC_OBJECT_TYPE, ecx); |
| 475 __ j(above_equal, &exit); | 499 __ j(above_equal, &exit); |
| 476 | 500 |
| 477 // Throw away the result of the constructor invocation and use the | 501 // Throw away the result of the constructor invocation and use the |
| 478 // on-stack receiver as the result. | 502 // on-stack receiver as the result. |
| 479 __ bind(&use_receiver); | 503 __ bind(&use_receiver); |
| 480 __ mov(eax, Operand(esp, 0)); | 504 __ mov(eax, Operand(esp, 0)); |
| 505 | |
| 506 __ bind(&exit); | |
| 507 } | |
| 481 | 508 |
| 482 // Restore the arguments count and leave the construct frame. The arguments | 509 // Restore the arguments count and leave the construct frame. The arguments |
| 483 // count is stored below the reciever and the new.target. | 510 // count is stored below the reciever and the new.target. |
| 484 __ bind(&exit); | 511 int offset = ((use_new_target && !is_derived_class) ? 2 : 1) * kPointerSize; |
| 485 int offset = (use_new_target ? 2 : 1) * kPointerSize; | |
| 486 __ mov(ebx, Operand(esp, offset)); | 512 __ mov(ebx, Operand(esp, offset)); |
| 487 | 513 |
| 488 // Leave construct frame. | 514 // Leave construct frame. |
| 489 } | 515 } |
| 490 | 516 |
| 491 // Remove caller arguments from the stack and return. | 517 // Remove caller arguments from the stack and return. |
| 492 STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); | 518 STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); |
| 493 __ pop(ecx); | 519 __ pop(ecx); |
| 494 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver | 520 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver |
| 495 __ push(ecx); | 521 __ push(ecx); |
| 496 __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1); | 522 __ IncrementCounter(masm->isolate()->counters()->constructed_objects(), 1); |
| 497 __ ret(0); | 523 __ ret(0); |
| 498 } | 524 } |
| 499 | 525 |
| 500 | 526 |
| 501 void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { | 527 void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { |
| 502 Generate_JSConstructStubHelper(masm, false, false, FLAG_pretenuring_call_new); | 528 bool create_memento = FLAG_pretenuring_call_new; |
| 529 Generate_JSConstructStubHelper(masm, false, false, false, create_memento); | |
| 503 } | 530 } |
| 504 | 531 |
| 505 | 532 |
| 506 void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { | 533 void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) { |
| 507 Generate_JSConstructStubHelper(masm, true, false, false); | 534 Generate_JSConstructStubHelper(masm, true, false, false, false); |
| 508 } | 535 } |
| 509 | 536 |
| 510 | 537 |
| 511 void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) { | 538 void Builtins::Generate_JSConstructStubNewTarget(MacroAssembler* masm) { |
|
arv (Not doing code reviews)
2015/07/07 16:32:26
I think we should get rid of this stub and have JS
Michael Starzinger
2015/07/07 16:46:59
Yes, oh god please yes, from the bottom of my hear
Michael Starzinger
2015/07/07 16:50:59
To rephrase this in a more professional way: Agree
| |
| 512 Generate_JSConstructStubHelper(masm, false, true, FLAG_pretenuring_call_new); | 539 bool create_memento = FLAG_pretenuring_call_new; |
| 540 Generate_JSConstructStubHelper(masm, false, true, false, create_memento); | |
| 513 } | 541 } |
| 514 | 542 |
| 515 | 543 |
| 516 void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) { | 544 void Builtins::Generate_JSConstructStubForDerived(MacroAssembler* masm) { |
| 517 // ----------- S t a t e ------------- | 545 Generate_JSConstructStubHelper(masm, false, true, true, false); |
| 518 // -- eax: number of arguments | |
| 519 // -- edi: constructor function | |
| 520 // -- ebx: allocation site or undefined | |
| 521 // -- edx: original constructor | |
| 522 // ----------------------------------- | |
| 523 | |
| 524 // TODO(dslomov): support pretenuring | |
| 525 CHECK(!FLAG_pretenuring_call_new); | |
| 526 | |
| 527 { | |
| 528 FrameScope frame_scope(masm, StackFrame::CONSTRUCT); | |
| 529 | |
| 530 // Preserve actual arguments count. | |
| 531 __ SmiTag(eax); | |
| 532 __ push(eax); | |
| 533 __ SmiUntag(eax); | |
| 534 | |
| 535 // Push new.target. | |
| 536 __ push(edx); | |
| 537 | |
| 538 // receiver is the hole. | |
| 539 __ push(Immediate(masm->isolate()->factory()->the_hole_value())); | |
| 540 | |
| 541 // Set up pointer to last argument. | |
| 542 __ lea(ebx, Operand(ebp, StandardFrameConstants::kCallerSPOffset)); | |
| 543 | |
| 544 // Copy arguments and receiver to the expression stack. | |
| 545 Label loop, entry; | |
| 546 __ mov(ecx, eax); | |
| 547 __ jmp(&entry); | |
| 548 __ bind(&loop); | |
| 549 __ push(Operand(ebx, ecx, times_4, 0)); | |
| 550 __ bind(&entry); | |
| 551 __ dec(ecx); | |
| 552 __ j(greater_equal, &loop); | |
| 553 | |
| 554 // Handle step in. | |
| 555 Label skip_step_in; | |
| 556 ExternalReference debug_step_in_fp = | |
| 557 ExternalReference::debug_step_in_fp_address(masm->isolate()); | |
| 558 __ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0)); | |
| 559 __ j(equal, &skip_step_in); | |
| 560 | |
| 561 __ push(eax); | |
| 562 __ push(edi); | |
| 563 __ push(edi); | |
| 564 __ CallRuntime(Runtime::kHandleStepInForDerivedConstructors, 1); | |
| 565 __ pop(edi); | |
| 566 __ pop(eax); | |
| 567 | |
| 568 __ bind(&skip_step_in); | |
| 569 | |
| 570 // Invoke function. | |
| 571 ParameterCount actual(eax); | |
| 572 __ InvokeFunction(edi, actual, CALL_FUNCTION, NullCallWrapper()); | |
| 573 | |
| 574 // Restore context from the frame. | |
| 575 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); | |
| 576 | |
| 577 // Get arguments count, skipping over new.target. | |
| 578 __ mov(ebx, Operand(esp, kPointerSize)); | |
| 579 } | |
| 580 | |
| 581 __ pop(ecx); // Return address. | |
| 582 __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); | |
| 583 __ push(ecx); | |
| 584 __ ret(0); | |
| 585 } | 546 } |
| 586 | 547 |
| 587 | 548 |
| 588 enum IsTagged { kEaxIsSmiTagged, kEaxIsUntaggedInt }; | 549 enum IsTagged { kEaxIsSmiTagged, kEaxIsUntaggedInt }; |
| 589 | 550 |
| 590 | 551 |
| 591 // Clobbers ecx, edx, edi; preserves all other registers. | 552 // Clobbers ecx, edx, edi; preserves all other registers. |
| 592 static void Generate_CheckStackOverflow(MacroAssembler* masm, | 553 static void Generate_CheckStackOverflow(MacroAssembler* masm, |
| 593 const int calleeOffset, | 554 const int calleeOffset, |
| 594 IsTagged eax_is_tagged) { | 555 IsTagged eax_is_tagged) { |
| (...skipping 1133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1728 | 1689 |
| 1729 __ bind(&ok); | 1690 __ bind(&ok); |
| 1730 __ ret(0); | 1691 __ ret(0); |
| 1731 } | 1692 } |
| 1732 | 1693 |
| 1733 #undef __ | 1694 #undef __ |
| 1734 } // namespace internal | 1695 } // namespace internal |
| 1735 } // namespace v8 | 1696 } // namespace v8 |
| 1736 | 1697 |
| 1737 #endif // V8_TARGET_ARCH_IA32 | 1698 #endif // V8_TARGET_ARCH_IA32 |
| OLD | NEW |