OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 12 matching lines...) Expand all Loading... |
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 #include "v8.h" | 28 #include "v8.h" |
29 | 29 |
30 #if defined(V8_TARGET_ARCH_X64) | 30 #if defined(V8_TARGET_ARCH_X64) |
31 | 31 |
32 #include "codegen-inl.h" | 32 #include "codegen-inl.h" |
33 #include "deoptimizer.h" | 33 #include "macro-assembler.h" |
34 #include "full-codegen.h" | |
35 | 34 |
36 namespace v8 { | 35 namespace v8 { |
37 namespace internal { | 36 namespace internal { |
38 | 37 |
39 | |
40 #define __ ACCESS_MASM(masm) | 38 #define __ ACCESS_MASM(masm) |
41 | 39 |
42 | 40 |
43 void Builtins::Generate_Adaptor(MacroAssembler* masm, | 41 void Builtins::Generate_Adaptor(MacroAssembler* masm, |
44 CFunctionId id, | 42 CFunctionId id, |
45 BuiltinExtraArguments extra_args) { | 43 BuiltinExtraArguments extra_args) { |
46 // ----------- S t a t e ------------- | 44 // ----------- S t a t e ------------- |
47 // -- rax : number of arguments excluding receiver | 45 // -- rax : number of arguments excluding receiver |
48 // -- rdi : called function (only guaranteed when | 46 // -- rdi : called function (only guaranteed when |
49 // extra_args requires it) | 47 // extra_args requires it) |
(...skipping 16 matching lines...) Expand all Loading... |
66 ASSERT(extra_args == NO_EXTRA_ARGUMENTS); | 64 ASSERT(extra_args == NO_EXTRA_ARGUMENTS); |
67 } | 65 } |
68 | 66 |
69 // JumpToExternalReference expects rax to contain the number of arguments | 67 // JumpToExternalReference expects rax to contain the number of arguments |
70 // including the receiver and the extra arguments. | 68 // including the receiver and the extra arguments. |
71 __ addq(rax, Immediate(num_extra_args + 1)); | 69 __ addq(rax, Immediate(num_extra_args + 1)); |
72 __ JumpToExternalReference(ExternalReference(id), 1); | 70 __ JumpToExternalReference(ExternalReference(id), 1); |
73 } | 71 } |
74 | 72 |
75 | 73 |
| 74 static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { |
| 75 __ push(rbp); |
| 76 __ movq(rbp, rsp); |
| 77 |
| 78 // Store the arguments adaptor context sentinel. |
| 79 __ Push(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); |
| 80 |
| 81 // Push the function on the stack. |
| 82 __ push(rdi); |
| 83 |
| 84 // Preserve the number of arguments on the stack. Must preserve both |
| 85 // rax and rbx because these registers are used when copying the |
| 86 // arguments and the receiver. |
| 87 __ Integer32ToSmi(rcx, rax); |
| 88 __ push(rcx); |
| 89 } |
| 90 |
| 91 |
| 92 static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { |
| 93 // Retrieve the number of arguments from the stack. Number is a Smi. |
| 94 __ movq(rbx, Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset)); |
| 95 |
| 96 // Leave the frame. |
| 97 __ movq(rsp, rbp); |
| 98 __ pop(rbp); |
| 99 |
| 100 // Remove caller arguments from the stack. |
| 101 __ pop(rcx); |
| 102 SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2); |
| 103 __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize)); |
| 104 __ push(rcx); |
| 105 } |
| 106 |
| 107 |
| 108 void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { |
| 109 // ----------- S t a t e ------------- |
| 110 // -- rax : actual number of arguments |
| 111 // -- rbx : expected number of arguments |
| 112 // -- rdx : code entry to call |
| 113 // ----------------------------------- |
| 114 |
| 115 Label invoke, dont_adapt_arguments; |
| 116 __ IncrementCounter(&Counters::arguments_adaptors, 1); |
| 117 |
| 118 Label enough, too_few; |
| 119 __ cmpq(rax, rbx); |
| 120 __ j(less, &too_few); |
| 121 __ cmpq(rbx, Immediate(SharedFunctionInfo::kDontAdaptArgumentsSentinel)); |
| 122 __ j(equal, &dont_adapt_arguments); |
| 123 |
| 124 { // Enough parameters: Actual >= expected. |
| 125 __ bind(&enough); |
| 126 EnterArgumentsAdaptorFrame(masm); |
| 127 |
| 128 // Copy receiver and all expected arguments. |
| 129 const int offset = StandardFrameConstants::kCallerSPOffset; |
| 130 __ lea(rax, Operand(rbp, rax, times_pointer_size, offset)); |
| 131 __ movq(rcx, Immediate(-1)); // account for receiver |
| 132 |
| 133 Label copy; |
| 134 __ bind(©); |
| 135 __ incq(rcx); |
| 136 __ push(Operand(rax, 0)); |
| 137 __ subq(rax, Immediate(kPointerSize)); |
| 138 __ cmpq(rcx, rbx); |
| 139 __ j(less, ©); |
| 140 __ jmp(&invoke); |
| 141 } |
| 142 |
| 143 { // Too few parameters: Actual < expected. |
| 144 __ bind(&too_few); |
| 145 EnterArgumentsAdaptorFrame(masm); |
| 146 |
| 147 // Copy receiver and all actual arguments. |
| 148 const int offset = StandardFrameConstants::kCallerSPOffset; |
| 149 __ lea(rdi, Operand(rbp, rax, times_pointer_size, offset)); |
| 150 __ movq(rcx, Immediate(-1)); // account for receiver |
| 151 |
| 152 Label copy; |
| 153 __ bind(©); |
| 154 __ incq(rcx); |
| 155 __ push(Operand(rdi, 0)); |
| 156 __ subq(rdi, Immediate(kPointerSize)); |
| 157 __ cmpq(rcx, rax); |
| 158 __ j(less, ©); |
| 159 |
| 160 // Fill remaining expected arguments with undefined values. |
| 161 Label fill; |
| 162 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); |
| 163 __ bind(&fill); |
| 164 __ incq(rcx); |
| 165 __ push(kScratchRegister); |
| 166 __ cmpq(rcx, rbx); |
| 167 __ j(less, &fill); |
| 168 |
| 169 // Restore function pointer. |
| 170 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); |
| 171 } |
| 172 |
| 173 // Call the entry point. |
| 174 __ bind(&invoke); |
| 175 __ call(rdx); |
| 176 |
| 177 // Leave frame and return. |
| 178 LeaveArgumentsAdaptorFrame(masm); |
| 179 __ ret(0); |
| 180 |
| 181 // ------------------------------------------- |
| 182 // Dont adapt arguments. |
| 183 // ------------------------------------------- |
| 184 __ bind(&dont_adapt_arguments); |
| 185 __ jmp(rdx); |
| 186 } |
| 187 |
| 188 |
| 189 void Builtins::Generate_FunctionCall(MacroAssembler* masm) { |
| 190 // Stack Layout: |
| 191 // rsp[0]: Return address |
| 192 // rsp[1]: Argument n |
| 193 // rsp[2]: Argument n-1 |
| 194 // ... |
| 195 // rsp[n]: Argument 1 |
| 196 // rsp[n+1]: Receiver (function to call) |
| 197 // |
| 198 // rax contains the number of arguments, n, not counting the receiver. |
| 199 // |
| 200 // 1. Make sure we have at least one argument. |
| 201 { Label done; |
| 202 __ testq(rax, rax); |
| 203 __ j(not_zero, &done); |
| 204 __ pop(rbx); |
| 205 __ Push(Factory::undefined_value()); |
| 206 __ push(rbx); |
| 207 __ incq(rax); |
| 208 __ bind(&done); |
| 209 } |
| 210 |
| 211 // 2. Get the function to call (passed as receiver) from the stack, check |
| 212 // if it is a function. |
| 213 Label non_function; |
| 214 // The function to call is at position n+1 on the stack. |
| 215 __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); |
| 216 __ JumpIfSmi(rdi, &non_function); |
| 217 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); |
| 218 __ j(not_equal, &non_function); |
| 219 |
| 220 // 3a. Patch the first argument if necessary when calling a function. |
| 221 Label shift_arguments; |
| 222 { Label convert_to_object, use_global_receiver, patch_receiver; |
| 223 // Change context eagerly in case we need the global receiver. |
| 224 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); |
| 225 |
| 226 __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0)); |
| 227 __ JumpIfSmi(rbx, &convert_to_object); |
| 228 |
| 229 __ CompareRoot(rbx, Heap::kNullValueRootIndex); |
| 230 __ j(equal, &use_global_receiver); |
| 231 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); |
| 232 __ j(equal, &use_global_receiver); |
| 233 |
| 234 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); |
| 235 __ j(below, &convert_to_object); |
| 236 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); |
| 237 __ j(below_equal, &shift_arguments); |
| 238 |
| 239 __ bind(&convert_to_object); |
| 240 __ EnterInternalFrame(); // In order to preserve argument count. |
| 241 __ Integer32ToSmi(rax, rax); |
| 242 __ push(rax); |
| 243 |
| 244 __ push(rbx); |
| 245 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); |
| 246 __ movq(rbx, rax); |
| 247 |
| 248 __ pop(rax); |
| 249 __ SmiToInteger32(rax, rax); |
| 250 __ LeaveInternalFrame(); |
| 251 // Restore the function to rdi. |
| 252 __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); |
| 253 __ jmp(&patch_receiver); |
| 254 |
| 255 // Use the global receiver object from the called function as the |
| 256 // receiver. |
| 257 __ bind(&use_global_receiver); |
| 258 const int kGlobalIndex = |
| 259 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; |
| 260 __ movq(rbx, FieldOperand(rsi, kGlobalIndex)); |
| 261 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); |
| 262 __ movq(rbx, FieldOperand(rbx, kGlobalIndex)); |
| 263 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); |
| 264 |
| 265 __ bind(&patch_receiver); |
| 266 __ movq(Operand(rsp, rax, times_pointer_size, 0), rbx); |
| 267 |
| 268 __ jmp(&shift_arguments); |
| 269 } |
| 270 |
| 271 |
| 272 // 3b. Patch the first argument when calling a non-function. The |
| 273 // CALL_NON_FUNCTION builtin expects the non-function callee as |
| 274 // receiver, so overwrite the first argument which will ultimately |
| 275 // become the receiver. |
| 276 __ bind(&non_function); |
| 277 __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi); |
| 278 __ xor_(rdi, rdi); |
| 279 |
| 280 // 4. Shift arguments and return address one slot down on the stack |
| 281 // (overwriting the original receiver). Adjust argument count to make |
| 282 // the original first argument the new receiver. |
| 283 __ bind(&shift_arguments); |
| 284 { Label loop; |
| 285 __ movq(rcx, rax); |
| 286 __ bind(&loop); |
| 287 __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0)); |
| 288 __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx); |
| 289 __ decq(rcx); |
| 290 __ j(not_sign, &loop); // While non-negative (to copy return address). |
| 291 __ pop(rbx); // Discard copy of return address. |
| 292 __ decq(rax); // One fewer argument (first argument is new receiver). |
| 293 } |
| 294 |
| 295 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin. |
| 296 { Label function; |
| 297 __ testq(rdi, rdi); |
| 298 __ j(not_zero, &function); |
| 299 __ xor_(rbx, rbx); |
| 300 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); |
| 301 __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), |
| 302 RelocInfo::CODE_TARGET); |
| 303 __ bind(&function); |
| 304 } |
| 305 |
| 306 // 5b. Get the code to call from the function and check that the number of |
| 307 // expected arguments matches what we're providing. If so, jump |
| 308 // (tail-call) to the code in register edx without checking arguments. |
| 309 __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); |
| 310 __ movsxlq(rbx, |
| 311 FieldOperand(rdx, |
| 312 SharedFunctionInfo::kFormalParameterCountOffset)); |
| 313 __ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); |
| 314 __ cmpq(rax, rbx); |
| 315 __ j(not_equal, |
| 316 Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), |
| 317 RelocInfo::CODE_TARGET); |
| 318 |
| 319 ParameterCount expected(0); |
| 320 __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION); |
| 321 } |
| 322 |
| 323 |
| 324 void Builtins::Generate_FunctionApply(MacroAssembler* masm) { |
| 325 // Stack at entry: |
| 326 // rsp: return address |
| 327 // rsp+8: arguments |
| 328 // rsp+16: receiver ("this") |
| 329 // rsp+24: function |
| 330 __ EnterInternalFrame(); |
| 331 // Stack frame: |
| 332 // rbp: Old base pointer |
| 333 // rbp[1]: return address |
| 334 // rbp[2]: function arguments |
| 335 // rbp[3]: receiver |
| 336 // rbp[4]: function |
| 337 static const int kArgumentsOffset = 2 * kPointerSize; |
| 338 static const int kReceiverOffset = 3 * kPointerSize; |
| 339 static const int kFunctionOffset = 4 * kPointerSize; |
| 340 __ push(Operand(rbp, kFunctionOffset)); |
| 341 __ push(Operand(rbp, kArgumentsOffset)); |
| 342 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); |
| 343 |
| 344 // Check the stack for overflow. We are not trying need to catch |
| 345 // interruptions (e.g. debug break and preemption) here, so the "real stack |
| 346 // limit" is checked. |
| 347 Label okay; |
| 348 __ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex); |
| 349 __ movq(rcx, rsp); |
| 350 // Make rcx the space we have left. The stack might already be overflowed |
| 351 // here which will cause rcx to become negative. |
| 352 __ subq(rcx, kScratchRegister); |
| 353 // Make rdx the space we need for the array when it is unrolled onto the |
| 354 // stack. |
| 355 __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2); |
| 356 // Check if the arguments will overflow the stack. |
| 357 __ cmpq(rcx, rdx); |
| 358 __ j(greater, &okay); // Signed comparison. |
| 359 |
| 360 // Out of stack space. |
| 361 __ push(Operand(rbp, kFunctionOffset)); |
| 362 __ push(rax); |
| 363 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); |
| 364 __ bind(&okay); |
| 365 // End of stack check. |
| 366 |
| 367 // Push current index and limit. |
| 368 const int kLimitOffset = |
| 369 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; |
| 370 const int kIndexOffset = kLimitOffset - 1 * kPointerSize; |
| 371 __ push(rax); // limit |
| 372 __ push(Immediate(0)); // index |
| 373 |
| 374 // Change context eagerly to get the right global object if |
| 375 // necessary. |
| 376 __ movq(rdi, Operand(rbp, kFunctionOffset)); |
| 377 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); |
| 378 |
| 379 // Compute the receiver. |
| 380 Label call_to_object, use_global_receiver, push_receiver; |
| 381 __ movq(rbx, Operand(rbp, kReceiverOffset)); |
| 382 __ JumpIfSmi(rbx, &call_to_object); |
| 383 __ CompareRoot(rbx, Heap::kNullValueRootIndex); |
| 384 __ j(equal, &use_global_receiver); |
| 385 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); |
| 386 __ j(equal, &use_global_receiver); |
| 387 |
| 388 // If given receiver is already a JavaScript object then there's no |
| 389 // reason for converting it. |
| 390 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); |
| 391 __ j(below, &call_to_object); |
| 392 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); |
| 393 __ j(below_equal, &push_receiver); |
| 394 |
| 395 // Convert the receiver to an object. |
| 396 __ bind(&call_to_object); |
| 397 __ push(rbx); |
| 398 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); |
| 399 __ movq(rbx, rax); |
| 400 __ jmp(&push_receiver); |
| 401 |
| 402 // Use the current global receiver object as the receiver. |
| 403 __ bind(&use_global_receiver); |
| 404 const int kGlobalOffset = |
| 405 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; |
| 406 __ movq(rbx, FieldOperand(rsi, kGlobalOffset)); |
| 407 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); |
| 408 __ movq(rbx, FieldOperand(rbx, kGlobalOffset)); |
| 409 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); |
| 410 |
| 411 // Push the receiver. |
| 412 __ bind(&push_receiver); |
| 413 __ push(rbx); |
| 414 |
| 415 // Copy all arguments from the array to the stack. |
| 416 Label entry, loop; |
| 417 __ movq(rax, Operand(rbp, kIndexOffset)); |
| 418 __ jmp(&entry); |
| 419 __ bind(&loop); |
| 420 __ movq(rdx, Operand(rbp, kArgumentsOffset)); // load arguments |
| 421 |
| 422 // Use inline caching to speed up access to arguments. |
| 423 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); |
| 424 __ Call(ic, RelocInfo::CODE_TARGET); |
| 425 // It is important that we do not have a test instruction after the |
| 426 // call. A test instruction after the call is used to indicate that |
| 427 // we have generated an inline version of the keyed load. In this |
| 428 // case, we know that we are not generating a test instruction next. |
| 429 |
| 430 // Push the nth argument. |
| 431 __ push(rax); |
| 432 |
| 433 // Update the index on the stack and in register rax. |
| 434 __ movq(rax, Operand(rbp, kIndexOffset)); |
| 435 __ SmiAddConstant(rax, rax, Smi::FromInt(1)); |
| 436 __ movq(Operand(rbp, kIndexOffset), rax); |
| 437 |
| 438 __ bind(&entry); |
| 439 __ cmpq(rax, Operand(rbp, kLimitOffset)); |
| 440 __ j(not_equal, &loop); |
| 441 |
| 442 // Invoke the function. |
| 443 ParameterCount actual(rax); |
| 444 __ SmiToInteger32(rax, rax); |
| 445 __ movq(rdi, Operand(rbp, kFunctionOffset)); |
| 446 __ InvokeFunction(rdi, actual, CALL_FUNCTION); |
| 447 |
| 448 __ LeaveInternalFrame(); |
| 449 __ ret(3 * kPointerSize); // remove function, receiver, and arguments |
| 450 } |
| 451 |
| 452 |
| 453 // Load the built-in Array function from the current context. |
| 454 static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) { |
| 455 // Load the global context. |
| 456 __ movq(result, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX))); |
| 457 __ movq(result, FieldOperand(result, GlobalObject::kGlobalContextOffset)); |
| 458 // Load the Array function from the global context. |
| 459 __ movq(result, |
| 460 Operand(result, Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX))); |
| 461 } |
| 462 |
| 463 |
| 464 // Number of empty elements to allocate for an empty array. |
| 465 static const int kPreallocatedArrayElements = 4; |
| 466 |
| 467 |
| 468 // Allocate an empty JSArray. The allocated array is put into the result |
| 469 // register. If the parameter initial_capacity is larger than zero an elements |
| 470 // backing store is allocated with this size and filled with the hole values. |
| 471 // Otherwise the elements backing store is set to the empty FixedArray. |
| 472 static void AllocateEmptyJSArray(MacroAssembler* masm, |
| 473 Register array_function, |
| 474 Register result, |
| 475 Register scratch1, |
| 476 Register scratch2, |
| 477 Register scratch3, |
| 478 int initial_capacity, |
| 479 Label* gc_required) { |
| 480 ASSERT(initial_capacity >= 0); |
| 481 |
| 482 // Load the initial map from the array function. |
| 483 __ movq(scratch1, FieldOperand(array_function, |
| 484 JSFunction::kPrototypeOrInitialMapOffset)); |
| 485 |
| 486 // Allocate the JSArray object together with space for a fixed array with the |
| 487 // requested elements. |
| 488 int size = JSArray::kSize; |
| 489 if (initial_capacity > 0) { |
| 490 size += FixedArray::SizeFor(initial_capacity); |
| 491 } |
| 492 __ AllocateInNewSpace(size, |
| 493 result, |
| 494 scratch2, |
| 495 scratch3, |
| 496 gc_required, |
| 497 TAG_OBJECT); |
| 498 |
| 499 // Allocated the JSArray. Now initialize the fields except for the elements |
| 500 // array. |
| 501 // result: JSObject |
| 502 // scratch1: initial map |
| 503 // scratch2: start of next object |
| 504 __ movq(FieldOperand(result, JSObject::kMapOffset), scratch1); |
| 505 __ Move(FieldOperand(result, JSArray::kPropertiesOffset), |
| 506 Factory::empty_fixed_array()); |
| 507 // Field JSArray::kElementsOffset is initialized later. |
| 508 __ Move(FieldOperand(result, JSArray::kLengthOffset), Smi::FromInt(0)); |
| 509 |
| 510 // If no storage is requested for the elements array just set the empty |
| 511 // fixed array. |
| 512 if (initial_capacity == 0) { |
| 513 __ Move(FieldOperand(result, JSArray::kElementsOffset), |
| 514 Factory::empty_fixed_array()); |
| 515 return; |
| 516 } |
| 517 |
| 518 // Calculate the location of the elements array and set elements array member |
| 519 // of the JSArray. |
| 520 // result: JSObject |
| 521 // scratch2: start of next object |
| 522 __ lea(scratch1, Operand(result, JSArray::kSize)); |
| 523 __ movq(FieldOperand(result, JSArray::kElementsOffset), scratch1); |
| 524 |
| 525 // Initialize the FixedArray and fill it with holes. FixedArray length is |
| 526 // stored as a smi. |
| 527 // result: JSObject |
| 528 // scratch1: elements array |
| 529 // scratch2: start of next object |
| 530 __ Move(FieldOperand(scratch1, HeapObject::kMapOffset), |
| 531 Factory::fixed_array_map()); |
| 532 __ Move(FieldOperand(scratch1, FixedArray::kLengthOffset), |
| 533 Smi::FromInt(initial_capacity)); |
| 534 |
| 535 // Fill the FixedArray with the hole value. Inline the code if short. |
| 536 // Reconsider loop unfolding if kPreallocatedArrayElements gets changed. |
| 537 static const int kLoopUnfoldLimit = 4; |
| 538 ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit); |
| 539 __ Move(scratch3, Factory::the_hole_value()); |
| 540 if (initial_capacity <= kLoopUnfoldLimit) { |
| 541 // Use a scratch register here to have only one reloc info when unfolding |
| 542 // the loop. |
| 543 for (int i = 0; i < initial_capacity; i++) { |
| 544 __ movq(FieldOperand(scratch1, |
| 545 FixedArray::kHeaderSize + i * kPointerSize), |
| 546 scratch3); |
| 547 } |
| 548 } else { |
| 549 Label loop, entry; |
| 550 __ jmp(&entry); |
| 551 __ bind(&loop); |
| 552 __ movq(Operand(scratch1, 0), scratch3); |
| 553 __ addq(scratch1, Immediate(kPointerSize)); |
| 554 __ bind(&entry); |
| 555 __ cmpq(scratch1, scratch2); |
| 556 __ j(below, &loop); |
| 557 } |
| 558 } |
| 559 |
| 560 |
| 561 // Allocate a JSArray with the number of elements stored in a register. The |
| 562 // register array_function holds the built-in Array function and the register |
| 563 // array_size holds the size of the array as a smi. The allocated array is put |
| 564 // into the result register and beginning and end of the FixedArray elements |
| 565 // storage is put into registers elements_array and elements_array_end (see |
| 566 // below for when that is not the case). If the parameter fill_with_holes is |
| 567 // true the allocated elements backing store is filled with the hole values |
| 568 // otherwise it is left uninitialized. When the backing store is filled the |
| 569 // register elements_array is scratched. |
| 570 static void AllocateJSArray(MacroAssembler* masm, |
| 571 Register array_function, // Array function. |
| 572 Register array_size, // As a smi. |
| 573 Register result, |
| 574 Register elements_array, |
| 575 Register elements_array_end, |
| 576 Register scratch, |
| 577 bool fill_with_hole, |
| 578 Label* gc_required) { |
| 579 Label not_empty, allocated; |
| 580 |
| 581 // Load the initial map from the array function. |
| 582 __ movq(elements_array, |
| 583 FieldOperand(array_function, |
| 584 JSFunction::kPrototypeOrInitialMapOffset)); |
| 585 |
| 586 // Check whether an empty sized array is requested. |
| 587 __ testq(array_size, array_size); |
| 588 __ j(not_zero, ¬_empty); |
| 589 |
| 590 // If an empty array is requested allocate a small elements array anyway. This |
| 591 // keeps the code below free of special casing for the empty array. |
| 592 int size = JSArray::kSize + FixedArray::SizeFor(kPreallocatedArrayElements); |
| 593 __ AllocateInNewSpace(size, |
| 594 result, |
| 595 elements_array_end, |
| 596 scratch, |
| 597 gc_required, |
| 598 TAG_OBJECT); |
| 599 __ jmp(&allocated); |
| 600 |
| 601 // Allocate the JSArray object together with space for a FixedArray with the |
| 602 // requested elements. |
| 603 __ bind(¬_empty); |
| 604 SmiIndex index = |
| 605 masm->SmiToIndex(kScratchRegister, array_size, kPointerSizeLog2); |
| 606 __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize, |
| 607 index.scale, |
| 608 index.reg, |
| 609 result, |
| 610 elements_array_end, |
| 611 scratch, |
| 612 gc_required, |
| 613 TAG_OBJECT); |
| 614 |
| 615 // Allocated the JSArray. Now initialize the fields except for the elements |
| 616 // array. |
| 617 // result: JSObject |
| 618 // elements_array: initial map |
| 619 // elements_array_end: start of next object |
| 620 // array_size: size of array (smi) |
| 621 __ bind(&allocated); |
| 622 __ movq(FieldOperand(result, JSObject::kMapOffset), elements_array); |
| 623 __ Move(elements_array, Factory::empty_fixed_array()); |
| 624 __ movq(FieldOperand(result, JSArray::kPropertiesOffset), elements_array); |
| 625 // Field JSArray::kElementsOffset is initialized later. |
| 626 __ movq(FieldOperand(result, JSArray::kLengthOffset), array_size); |
| 627 |
| 628 // Calculate the location of the elements array and set elements array member |
| 629 // of the JSArray. |
| 630 // result: JSObject |
| 631 // elements_array_end: start of next object |
| 632 // array_size: size of array (smi) |
| 633 __ lea(elements_array, Operand(result, JSArray::kSize)); |
| 634 __ movq(FieldOperand(result, JSArray::kElementsOffset), elements_array); |
| 635 |
| 636 // Initialize the fixed array. FixedArray length is stored as a smi. |
| 637 // result: JSObject |
| 638 // elements_array: elements array |
| 639 // elements_array_end: start of next object |
| 640 // array_size: size of array (smi) |
| 641 __ Move(FieldOperand(elements_array, JSObject::kMapOffset), |
| 642 Factory::fixed_array_map()); |
| 643 Label not_empty_2, fill_array; |
| 644 __ SmiTest(array_size); |
| 645 __ j(not_zero, ¬_empty_2); |
| 646 // Length of the FixedArray is the number of pre-allocated elements even |
| 647 // though the actual JSArray has length 0. |
| 648 __ Move(FieldOperand(elements_array, FixedArray::kLengthOffset), |
| 649 Smi::FromInt(kPreallocatedArrayElements)); |
| 650 __ jmp(&fill_array); |
| 651 __ bind(¬_empty_2); |
| 652 // For non-empty JSArrays the length of the FixedArray and the JSArray is the |
| 653 // same. |
| 654 __ movq(FieldOperand(elements_array, FixedArray::kLengthOffset), array_size); |
| 655 |
| 656 // Fill the allocated FixedArray with the hole value if requested. |
| 657 // result: JSObject |
| 658 // elements_array: elements array |
| 659 // elements_array_end: start of next object |
| 660 __ bind(&fill_array); |
| 661 if (fill_with_hole) { |
| 662 Label loop, entry; |
| 663 __ Move(scratch, Factory::the_hole_value()); |
| 664 __ lea(elements_array, Operand(elements_array, |
| 665 FixedArray::kHeaderSize - kHeapObjectTag)); |
| 666 __ jmp(&entry); |
| 667 __ bind(&loop); |
| 668 __ movq(Operand(elements_array, 0), scratch); |
| 669 __ addq(elements_array, Immediate(kPointerSize)); |
| 670 __ bind(&entry); |
| 671 __ cmpq(elements_array, elements_array_end); |
| 672 __ j(below, &loop); |
| 673 } |
| 674 } |
| 675 |
| 676 |
| 677 // Create a new array for the built-in Array function. This function allocates |
| 678 // the JSArray object and the FixedArray elements array and initializes these. |
| 679 // If the Array cannot be constructed in native code the runtime is called. This |
| 680 // function assumes the following state: |
| 681 // rdi: constructor (built-in Array function) |
| 682 // rax: argc |
| 683 // rsp[0]: return address |
| 684 // rsp[8]: last argument |
| 685 // This function is used for both construct and normal calls of Array. The only |
| 686 // difference between handling a construct call and a normal call is that for a |
| 687 // construct call the constructor function in rdi needs to be preserved for |
| 688 // entering the generic code. In both cases argc in rax needs to be preserved. |
| 689 // Both registers are preserved by this code so no need to differentiate between |
| 690 // a construct call and a normal call. |
| 691 static void ArrayNativeCode(MacroAssembler* masm, |
| 692 Label *call_generic_code) { |
| 693 Label argc_one_or_more, argc_two_or_more; |
| 694 |
| 695 // Check for array construction with zero arguments. |
| 696 __ testq(rax, rax); |
| 697 __ j(not_zero, &argc_one_or_more); |
| 698 |
| 699 // Handle construction of an empty array. |
| 700 AllocateEmptyJSArray(masm, |
| 701 rdi, |
| 702 rbx, |
| 703 rcx, |
| 704 rdx, |
| 705 r8, |
| 706 kPreallocatedArrayElements, |
| 707 call_generic_code); |
| 708 __ IncrementCounter(&Counters::array_function_native, 1); |
| 709 __ movq(rax, rbx); |
| 710 __ ret(kPointerSize); |
| 711 |
| 712 // Check for one argument. Bail out if argument is not smi or if it is |
| 713 // negative. |
| 714 __ bind(&argc_one_or_more); |
| 715 __ cmpq(rax, Immediate(1)); |
| 716 __ j(not_equal, &argc_two_or_more); |
| 717 __ movq(rdx, Operand(rsp, kPointerSize)); // Get the argument from the stack. |
| 718 __ JumpUnlessNonNegativeSmi(rdx, call_generic_code); |
| 719 |
| 720 // Handle construction of an empty array of a certain size. Bail out if size |
| 721 // is to large to actually allocate an elements array. |
| 722 __ SmiCompare(rdx, Smi::FromInt(JSObject::kInitialMaxFastElementArray)); |
| 723 __ j(greater_equal, call_generic_code); |
| 724 |
| 725 // rax: argc |
| 726 // rdx: array_size (smi) |
| 727 // rdi: constructor |
| 728 // esp[0]: return address |
| 729 // esp[8]: argument |
| 730 AllocateJSArray(masm, |
| 731 rdi, |
| 732 rdx, |
| 733 rbx, |
| 734 rcx, |
| 735 r8, |
| 736 r9, |
| 737 true, |
| 738 call_generic_code); |
| 739 __ IncrementCounter(&Counters::array_function_native, 1); |
| 740 __ movq(rax, rbx); |
| 741 __ ret(2 * kPointerSize); |
| 742 |
| 743 // Handle construction of an array from a list of arguments. |
| 744 __ bind(&argc_two_or_more); |
| 745 __ movq(rdx, rax); |
| 746 __ Integer32ToSmi(rdx, rdx); // Convet argc to a smi. |
| 747 // rax: argc |
| 748 // rdx: array_size (smi) |
| 749 // rdi: constructor |
| 750 // esp[0] : return address |
| 751 // esp[8] : last argument |
| 752 AllocateJSArray(masm, |
| 753 rdi, |
| 754 rdx, |
| 755 rbx, |
| 756 rcx, |
| 757 r8, |
| 758 r9, |
| 759 false, |
| 760 call_generic_code); |
| 761 __ IncrementCounter(&Counters::array_function_native, 1); |
| 762 |
| 763 // rax: argc |
| 764 // rbx: JSArray |
| 765 // rcx: elements_array |
| 766 // r8: elements_array_end (untagged) |
| 767 // esp[0]: return address |
| 768 // esp[8]: last argument |
| 769 |
| 770 // Location of the last argument |
| 771 __ lea(r9, Operand(rsp, kPointerSize)); |
| 772 |
| 773 // Location of the first array element (Parameter fill_with_holes to |
| 774 // AllocateJSArrayis false, so the FixedArray is returned in rcx). |
| 775 __ lea(rdx, Operand(rcx, FixedArray::kHeaderSize - kHeapObjectTag)); |
| 776 |
| 777 // rax: argc |
| 778 // rbx: JSArray |
| 779 // rdx: location of the first array element |
| 780 // r9: location of the last argument |
| 781 // esp[0]: return address |
| 782 // esp[8]: last argument |
| 783 Label loop, entry; |
| 784 __ movq(rcx, rax); |
| 785 __ jmp(&entry); |
| 786 __ bind(&loop); |
| 787 __ movq(kScratchRegister, Operand(r9, rcx, times_pointer_size, 0)); |
| 788 __ movq(Operand(rdx, 0), kScratchRegister); |
| 789 __ addq(rdx, Immediate(kPointerSize)); |
| 790 __ bind(&entry); |
| 791 __ decq(rcx); |
| 792 __ j(greater_equal, &loop); |
| 793 |
| 794 // Remove caller arguments from the stack and return. |
| 795 // rax: argc |
| 796 // rbx: JSArray |
| 797 // esp[0]: return address |
| 798 // esp[8]: last argument |
| 799 __ pop(rcx); |
| 800 __ lea(rsp, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); |
| 801 __ push(rcx); |
| 802 __ movq(rax, rbx); |
| 803 __ ret(0); |
| 804 } |
| 805 |
| 806 |
| 807 void Builtins::Generate_ArrayCode(MacroAssembler* masm) { |
| 808 // ----------- S t a t e ------------- |
| 809 // -- rax : argc |
| 810 // -- rsp[0] : return address |
| 811 // -- rsp[8] : last argument |
| 812 // ----------------------------------- |
| 813 Label generic_array_code; |
| 814 |
| 815 // Get the Array function. |
| 816 GenerateLoadArrayFunction(masm, rdi); |
| 817 |
| 818 if (FLAG_debug_code) { |
| 819 // Initial map for the builtin Array function shoud be a map. |
| 820 __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); |
| 821 // Will both indicate a NULL and a Smi. |
| 822 ASSERT(kSmiTag == 0); |
| 823 Condition not_smi = NegateCondition(masm->CheckSmi(rbx)); |
| 824 __ Check(not_smi, "Unexpected initial map for Array function"); |
| 825 __ CmpObjectType(rbx, MAP_TYPE, rcx); |
| 826 __ Check(equal, "Unexpected initial map for Array function"); |
| 827 } |
| 828 |
| 829 // Run the native code for the Array function called as a normal function. |
| 830 ArrayNativeCode(masm, &generic_array_code); |
| 831 |
| 832 // Jump to the generic array code in case the specialized code cannot handle |
| 833 // the construction. |
| 834 __ bind(&generic_array_code); |
| 835 Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric); |
| 836 Handle<Code> array_code(code); |
| 837 __ Jump(array_code, RelocInfo::CODE_TARGET); |
| 838 } |
| 839 |
| 840 |
| 841 void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { |
| 842 // ----------- S t a t e ------------- |
| 843 // -- rax : argc |
| 844 // -- rdi : constructor |
| 845 // -- rsp[0] : return address |
| 846 // -- rsp[8] : last argument |
| 847 // ----------------------------------- |
| 848 Label generic_constructor; |
| 849 |
| 850 if (FLAG_debug_code) { |
| 851 // The array construct code is only set for the builtin Array function which |
| 852 // does always have a map. |
| 853 GenerateLoadArrayFunction(masm, rbx); |
| 854 __ cmpq(rdi, rbx); |
| 855 __ Check(equal, "Unexpected Array function"); |
| 856 // Initial map for the builtin Array function should be a map. |
| 857 __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); |
| 858 // Will both indicate a NULL and a Smi. |
| 859 ASSERT(kSmiTag == 0); |
| 860 Condition not_smi = NegateCondition(masm->CheckSmi(rbx)); |
| 861 __ Check(not_smi, "Unexpected initial map for Array function"); |
| 862 __ CmpObjectType(rbx, MAP_TYPE, rcx); |
| 863 __ Check(equal, "Unexpected initial map for Array function"); |
| 864 } |
| 865 |
| 866 // Run the native code for the Array function called as constructor. |
| 867 ArrayNativeCode(masm, &generic_constructor); |
| 868 |
| 869 // Jump to the generic construct code in case the specialized code cannot |
| 870 // handle the construction. |
| 871 __ bind(&generic_constructor); |
| 872 Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric); |
| 873 Handle<Code> generic_construct_stub(code); |
| 874 __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET); |
| 875 } |
| 876 |
| 877 |
| 878 void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { |
| 879 // TODO(849): implement custom construct stub. |
| 880 // Generate a copy of the generic stub for now. |
| 881 Generate_JSConstructStubGeneric(masm); |
| 882 } |
| 883 |
| 884 |
76 void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { | 885 void Builtins::Generate_JSConstructCall(MacroAssembler* masm) { |
77 // ----------- S t a t e ------------- | 886 // ----------- S t a t e ------------- |
78 // -- rax: number of arguments | 887 // -- rax: number of arguments |
79 // -- rdi: constructor function | 888 // -- rdi: constructor function |
80 // ----------------------------------- | 889 // ----------------------------------- |
81 | 890 |
82 Label non_function_call; | 891 Label non_function_call; |
83 // Check that function is not a smi. | 892 // Check that function is not a smi. |
84 __ JumpIfSmi(rdi, &non_function_call); | 893 __ JumpIfSmi(rdi, &non_function_call); |
85 // Check that function is a JSFunction. | 894 // Check that function is a JSFunction. |
(...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
552 // Restore function and tear down temporary frame. | 1361 // Restore function and tear down temporary frame. |
553 __ pop(rdi); | 1362 __ pop(rdi); |
554 __ LeaveInternalFrame(); | 1363 __ LeaveInternalFrame(); |
555 | 1364 |
556 // Do a tail-call of the compiled function. | 1365 // Do a tail-call of the compiled function. |
557 __ lea(rcx, FieldOperand(rax, Code::kHeaderSize)); | 1366 __ lea(rcx, FieldOperand(rax, Code::kHeaderSize)); |
558 __ jmp(rcx); | 1367 __ jmp(rcx); |
559 } | 1368 } |
560 | 1369 |
561 | 1370 |
562 static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm, | 1371 void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { |
563 Deoptimizer::BailoutType type) { | |
564 __ int3(); | 1372 __ int3(); |
565 } | 1373 } |
566 | 1374 |
567 void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) { | |
568 Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); | |
569 } | |
570 | |
571 | 1375 |
572 void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) { | 1376 void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) { |
573 Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER); | 1377 __ int3(); |
574 } | 1378 } |
575 | 1379 |
576 | 1380 |
577 void Builtins::Generate_NotifyOSR(MacroAssembler* masm) { | 1381 void Builtins::Generate_NotifyOSR(MacroAssembler* masm) { |
578 __ int3(); | 1382 __ int3(); |
579 } | 1383 } |
580 | 1384 |
581 | 1385 |
582 void Builtins::Generate_FunctionCall(MacroAssembler* masm) { | |
583 // Stack Layout: | |
584 // rsp[0]: Return address | |
585 // rsp[1]: Argument n | |
586 // rsp[2]: Argument n-1 | |
587 // ... | |
588 // rsp[n]: Argument 1 | |
589 // rsp[n+1]: Receiver (function to call) | |
590 // | |
591 // rax contains the number of arguments, n, not counting the receiver. | |
592 // | |
593 // 1. Make sure we have at least one argument. | |
594 { Label done; | |
595 __ testq(rax, rax); | |
596 __ j(not_zero, &done); | |
597 __ pop(rbx); | |
598 __ Push(Factory::undefined_value()); | |
599 __ push(rbx); | |
600 __ incq(rax); | |
601 __ bind(&done); | |
602 } | |
603 | |
604 // 2. Get the function to call (passed as receiver) from the stack, check | |
605 // if it is a function. | |
606 Label non_function; | |
607 // The function to call is at position n+1 on the stack. | |
608 __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); | |
609 __ JumpIfSmi(rdi, &non_function); | |
610 __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); | |
611 __ j(not_equal, &non_function); | |
612 | |
613 // 3a. Patch the first argument if necessary when calling a function. | |
614 Label shift_arguments; | |
615 { Label convert_to_object, use_global_receiver, patch_receiver; | |
616 // Change context eagerly in case we need the global receiver. | |
617 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); | |
618 | |
619 __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0)); | |
620 __ JumpIfSmi(rbx, &convert_to_object); | |
621 | |
622 __ CompareRoot(rbx, Heap::kNullValueRootIndex); | |
623 __ j(equal, &use_global_receiver); | |
624 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); | |
625 __ j(equal, &use_global_receiver); | |
626 | |
627 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); | |
628 __ j(below, &convert_to_object); | |
629 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); | |
630 __ j(below_equal, &shift_arguments); | |
631 | |
632 __ bind(&convert_to_object); | |
633 __ EnterInternalFrame(); // In order to preserve argument count. | |
634 __ Integer32ToSmi(rax, rax); | |
635 __ push(rax); | |
636 | |
637 __ push(rbx); | |
638 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); | |
639 __ movq(rbx, rax); | |
640 | |
641 __ pop(rax); | |
642 __ SmiToInteger32(rax, rax); | |
643 __ LeaveInternalFrame(); | |
644 // Restore the function to rdi. | |
645 __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); | |
646 __ jmp(&patch_receiver); | |
647 | |
648 // Use the global receiver object from the called function as the | |
649 // receiver. | |
650 __ bind(&use_global_receiver); | |
651 const int kGlobalIndex = | |
652 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; | |
653 __ movq(rbx, FieldOperand(rsi, kGlobalIndex)); | |
654 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); | |
655 __ movq(rbx, FieldOperand(rbx, kGlobalIndex)); | |
656 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); | |
657 | |
658 __ bind(&patch_receiver); | |
659 __ movq(Operand(rsp, rax, times_pointer_size, 0), rbx); | |
660 | |
661 __ jmp(&shift_arguments); | |
662 } | |
663 | |
664 | |
665 // 3b. Patch the first argument when calling a non-function. The | |
666 // CALL_NON_FUNCTION builtin expects the non-function callee as | |
667 // receiver, so overwrite the first argument which will ultimately | |
668 // become the receiver. | |
669 __ bind(&non_function); | |
670 __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi); | |
671 __ xor_(rdi, rdi); | |
672 | |
673 // 4. Shift arguments and return address one slot down on the stack | |
674 // (overwriting the original receiver). Adjust argument count to make | |
675 // the original first argument the new receiver. | |
676 __ bind(&shift_arguments); | |
677 { Label loop; | |
678 __ movq(rcx, rax); | |
679 __ bind(&loop); | |
680 __ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0)); | |
681 __ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx); | |
682 __ decq(rcx); | |
683 __ j(not_sign, &loop); // While non-negative (to copy return address). | |
684 __ pop(rbx); // Discard copy of return address. | |
685 __ decq(rax); // One fewer argument (first argument is new receiver). | |
686 } | |
687 | |
688 // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin. | |
689 { Label function; | |
690 __ testq(rdi, rdi); | |
691 __ j(not_zero, &function); | |
692 __ xor_(rbx, rbx); | |
693 __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION); | |
694 __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), | |
695 RelocInfo::CODE_TARGET); | |
696 __ bind(&function); | |
697 } | |
698 | |
699 // 5b. Get the code to call from the function and check that the number of | |
700 // expected arguments matches what we're providing. If so, jump | |
701 // (tail-call) to the code in register edx without checking arguments. | |
702 __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset)); | |
703 __ movsxlq(rbx, | |
704 FieldOperand(rdx, | |
705 SharedFunctionInfo::kFormalParameterCountOffset)); | |
706 __ movq(rdx, FieldOperand(rdi, JSFunction::kCodeEntryOffset)); | |
707 __ cmpq(rax, rbx); | |
708 __ j(not_equal, | |
709 Handle<Code>(builtin(ArgumentsAdaptorTrampoline)), | |
710 RelocInfo::CODE_TARGET); | |
711 | |
712 ParameterCount expected(0); | |
713 __ InvokeCode(rdx, expected, expected, JUMP_FUNCTION); | |
714 } | |
715 | |
716 | |
717 void Builtins::Generate_FunctionApply(MacroAssembler* masm) { | |
718 // Stack at entry: | |
719 // rsp: return address | |
720 // rsp+8: arguments | |
721 // rsp+16: receiver ("this") | |
722 // rsp+24: function | |
723 __ EnterInternalFrame(); | |
724 // Stack frame: | |
725 // rbp: Old base pointer | |
726 // rbp[1]: return address | |
727 // rbp[2]: function arguments | |
728 // rbp[3]: receiver | |
729 // rbp[4]: function | |
730 static const int kArgumentsOffset = 2 * kPointerSize; | |
731 static const int kReceiverOffset = 3 * kPointerSize; | |
732 static const int kFunctionOffset = 4 * kPointerSize; | |
733 __ push(Operand(rbp, kFunctionOffset)); | |
734 __ push(Operand(rbp, kArgumentsOffset)); | |
735 __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION); | |
736 | |
737 // Check the stack for overflow. We are not trying need to catch | |
738 // interruptions (e.g. debug break and preemption) here, so the "real stack | |
739 // limit" is checked. | |
740 Label okay; | |
741 __ LoadRoot(kScratchRegister, Heap::kRealStackLimitRootIndex); | |
742 __ movq(rcx, rsp); | |
743 // Make rcx the space we have left. The stack might already be overflowed | |
744 // here which will cause rcx to become negative. | |
745 __ subq(rcx, kScratchRegister); | |
746 // Make rdx the space we need for the array when it is unrolled onto the | |
747 // stack. | |
748 __ PositiveSmiTimesPowerOfTwoToInteger64(rdx, rax, kPointerSizeLog2); | |
749 // Check if the arguments will overflow the stack. | |
750 __ cmpq(rcx, rdx); | |
751 __ j(greater, &okay); // Signed comparison. | |
752 | |
753 // Out of stack space. | |
754 __ push(Operand(rbp, kFunctionOffset)); | |
755 __ push(rax); | |
756 __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION); | |
757 __ bind(&okay); | |
758 // End of stack check. | |
759 | |
760 // Push current index and limit. | |
761 const int kLimitOffset = | |
762 StandardFrameConstants::kExpressionsOffset - 1 * kPointerSize; | |
763 const int kIndexOffset = kLimitOffset - 1 * kPointerSize; | |
764 __ push(rax); // limit | |
765 __ push(Immediate(0)); // index | |
766 | |
767 // Change context eagerly to get the right global object if | |
768 // necessary. | |
769 __ movq(rdi, Operand(rbp, kFunctionOffset)); | |
770 __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); | |
771 | |
772 // Compute the receiver. | |
773 Label call_to_object, use_global_receiver, push_receiver; | |
774 __ movq(rbx, Operand(rbp, kReceiverOffset)); | |
775 __ JumpIfSmi(rbx, &call_to_object); | |
776 __ CompareRoot(rbx, Heap::kNullValueRootIndex); | |
777 __ j(equal, &use_global_receiver); | |
778 __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); | |
779 __ j(equal, &use_global_receiver); | |
780 | |
781 // If given receiver is already a JavaScript object then there's no | |
782 // reason for converting it. | |
783 __ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx); | |
784 __ j(below, &call_to_object); | |
785 __ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE); | |
786 __ j(below_equal, &push_receiver); | |
787 | |
788 // Convert the receiver to an object. | |
789 __ bind(&call_to_object); | |
790 __ push(rbx); | |
791 __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); | |
792 __ movq(rbx, rax); | |
793 __ jmp(&push_receiver); | |
794 | |
795 // Use the current global receiver object as the receiver. | |
796 __ bind(&use_global_receiver); | |
797 const int kGlobalOffset = | |
798 Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize; | |
799 __ movq(rbx, FieldOperand(rsi, kGlobalOffset)); | |
800 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalContextOffset)); | |
801 __ movq(rbx, FieldOperand(rbx, kGlobalOffset)); | |
802 __ movq(rbx, FieldOperand(rbx, GlobalObject::kGlobalReceiverOffset)); | |
803 | |
804 // Push the receiver. | |
805 __ bind(&push_receiver); | |
806 __ push(rbx); | |
807 | |
808 // Copy all arguments from the array to the stack. | |
809 Label entry, loop; | |
810 __ movq(rax, Operand(rbp, kIndexOffset)); | |
811 __ jmp(&entry); | |
812 __ bind(&loop); | |
813 __ movq(rdx, Operand(rbp, kArgumentsOffset)); // load arguments | |
814 | |
815 // Use inline caching to speed up access to arguments. | |
816 Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize)); | |
817 __ Call(ic, RelocInfo::CODE_TARGET); | |
818 // It is important that we do not have a test instruction after the | |
819 // call. A test instruction after the call is used to indicate that | |
820 // we have generated an inline version of the keyed load. In this | |
821 // case, we know that we are not generating a test instruction next. | |
822 | |
823 // Push the nth argument. | |
824 __ push(rax); | |
825 | |
826 // Update the index on the stack and in register rax. | |
827 __ movq(rax, Operand(rbp, kIndexOffset)); | |
828 __ SmiAddConstant(rax, rax, Smi::FromInt(1)); | |
829 __ movq(Operand(rbp, kIndexOffset), rax); | |
830 | |
831 __ bind(&entry); | |
832 __ cmpq(rax, Operand(rbp, kLimitOffset)); | |
833 __ j(not_equal, &loop); | |
834 | |
835 // Invoke the function. | |
836 ParameterCount actual(rax); | |
837 __ SmiToInteger32(rax, rax); | |
838 __ movq(rdi, Operand(rbp, kFunctionOffset)); | |
839 __ InvokeFunction(rdi, actual, CALL_FUNCTION); | |
840 | |
841 __ LeaveInternalFrame(); | |
842 __ ret(3 * kPointerSize); // remove function, receiver, and arguments | |
843 } | |
844 | |
845 | |
846 // Number of empty elements to allocate for an empty array. | |
847 static const int kPreallocatedArrayElements = 4; | |
848 | |
849 | |
850 // Allocate an empty JSArray. The allocated array is put into the result | |
851 // register. If the parameter initial_capacity is larger than zero an elements | |
852 // backing store is allocated with this size and filled with the hole values. | |
853 // Otherwise the elements backing store is set to the empty FixedArray. | |
854 static void AllocateEmptyJSArray(MacroAssembler* masm, | |
855 Register array_function, | |
856 Register result, | |
857 Register scratch1, | |
858 Register scratch2, | |
859 Register scratch3, | |
860 int initial_capacity, | |
861 Label* gc_required) { | |
862 ASSERT(initial_capacity >= 0); | |
863 | |
864 // Load the initial map from the array function. | |
865 __ movq(scratch1, FieldOperand(array_function, | |
866 JSFunction::kPrototypeOrInitialMapOffset)); | |
867 | |
868 // Allocate the JSArray object together with space for a fixed array with the | |
869 // requested elements. | |
870 int size = JSArray::kSize; | |
871 if (initial_capacity > 0) { | |
872 size += FixedArray::SizeFor(initial_capacity); | |
873 } | |
874 __ AllocateInNewSpace(size, | |
875 result, | |
876 scratch2, | |
877 scratch3, | |
878 gc_required, | |
879 TAG_OBJECT); | |
880 | |
881 // Allocated the JSArray. Now initialize the fields except for the elements | |
882 // array. | |
883 // result: JSObject | |
884 // scratch1: initial map | |
885 // scratch2: start of next object | |
886 __ movq(FieldOperand(result, JSObject::kMapOffset), scratch1); | |
887 __ Move(FieldOperand(result, JSArray::kPropertiesOffset), | |
888 Factory::empty_fixed_array()); | |
889 // Field JSArray::kElementsOffset is initialized later. | |
890 __ Move(FieldOperand(result, JSArray::kLengthOffset), Smi::FromInt(0)); | |
891 | |
892 // If no storage is requested for the elements array just set the empty | |
893 // fixed array. | |
894 if (initial_capacity == 0) { | |
895 __ Move(FieldOperand(result, JSArray::kElementsOffset), | |
896 Factory::empty_fixed_array()); | |
897 return; | |
898 } | |
899 | |
900 // Calculate the location of the elements array and set elements array member | |
901 // of the JSArray. | |
902 // result: JSObject | |
903 // scratch2: start of next object | |
904 __ lea(scratch1, Operand(result, JSArray::kSize)); | |
905 __ movq(FieldOperand(result, JSArray::kElementsOffset), scratch1); | |
906 | |
907 // Initialize the FixedArray and fill it with holes. FixedArray length is | |
908 // stored as a smi. | |
909 // result: JSObject | |
910 // scratch1: elements array | |
911 // scratch2: start of next object | |
912 __ Move(FieldOperand(scratch1, HeapObject::kMapOffset), | |
913 Factory::fixed_array_map()); | |
914 __ Move(FieldOperand(scratch1, FixedArray::kLengthOffset), | |
915 Smi::FromInt(initial_capacity)); | |
916 | |
917 // Fill the FixedArray with the hole value. Inline the code if short. | |
918 // Reconsider loop unfolding if kPreallocatedArrayElements gets changed. | |
919 static const int kLoopUnfoldLimit = 4; | |
920 ASSERT(kPreallocatedArrayElements <= kLoopUnfoldLimit); | |
921 __ Move(scratch3, Factory::the_hole_value()); | |
922 if (initial_capacity <= kLoopUnfoldLimit) { | |
923 // Use a scratch register here to have only one reloc info when unfolding | |
924 // the loop. | |
925 for (int i = 0; i < initial_capacity; i++) { | |
926 __ movq(FieldOperand(scratch1, | |
927 FixedArray::kHeaderSize + i * kPointerSize), | |
928 scratch3); | |
929 } | |
930 } else { | |
931 Label loop, entry; | |
932 __ jmp(&entry); | |
933 __ bind(&loop); | |
934 __ movq(Operand(scratch1, 0), scratch3); | |
935 __ addq(scratch1, Immediate(kPointerSize)); | |
936 __ bind(&entry); | |
937 __ cmpq(scratch1, scratch2); | |
938 __ j(below, &loop); | |
939 } | |
940 } | |
941 | |
942 | |
943 // Allocate a JSArray with the number of elements stored in a register. The | |
944 // register array_function holds the built-in Array function and the register | |
945 // array_size holds the size of the array as a smi. The allocated array is put | |
946 // into the result register and beginning and end of the FixedArray elements | |
947 // storage is put into registers elements_array and elements_array_end (see | |
948 // below for when that is not the case). If the parameter fill_with_holes is | |
949 // true the allocated elements backing store is filled with the hole values | |
950 // otherwise it is left uninitialized. When the backing store is filled the | |
951 // register elements_array is scratched. | |
952 static void AllocateJSArray(MacroAssembler* masm, | |
953 Register array_function, // Array function. | |
954 Register array_size, // As a smi. | |
955 Register result, | |
956 Register elements_array, | |
957 Register elements_array_end, | |
958 Register scratch, | |
959 bool fill_with_hole, | |
960 Label* gc_required) { | |
961 Label not_empty, allocated; | |
962 | |
963 // Load the initial map from the array function. | |
964 __ movq(elements_array, | |
965 FieldOperand(array_function, | |
966 JSFunction::kPrototypeOrInitialMapOffset)); | |
967 | |
968 // Check whether an empty sized array is requested. | |
969 __ testq(array_size, array_size); | |
970 __ j(not_zero, ¬_empty); | |
971 | |
972 // If an empty array is requested allocate a small elements array anyway. This | |
973 // keeps the code below free of special casing for the empty array. | |
974 int size = JSArray::kSize + FixedArray::SizeFor(kPreallocatedArrayElements); | |
975 __ AllocateInNewSpace(size, | |
976 result, | |
977 elements_array_end, | |
978 scratch, | |
979 gc_required, | |
980 TAG_OBJECT); | |
981 __ jmp(&allocated); | |
982 | |
983 // Allocate the JSArray object together with space for a FixedArray with the | |
984 // requested elements. | |
985 __ bind(¬_empty); | |
986 SmiIndex index = | |
987 masm->SmiToIndex(kScratchRegister, array_size, kPointerSizeLog2); | |
988 __ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize, | |
989 index.scale, | |
990 index.reg, | |
991 result, | |
992 elements_array_end, | |
993 scratch, | |
994 gc_required, | |
995 TAG_OBJECT); | |
996 | |
997 // Allocated the JSArray. Now initialize the fields except for the elements | |
998 // array. | |
999 // result: JSObject | |
1000 // elements_array: initial map | |
1001 // elements_array_end: start of next object | |
1002 // array_size: size of array (smi) | |
1003 __ bind(&allocated); | |
1004 __ movq(FieldOperand(result, JSObject::kMapOffset), elements_array); | |
1005 __ Move(elements_array, Factory::empty_fixed_array()); | |
1006 __ movq(FieldOperand(result, JSArray::kPropertiesOffset), elements_array); | |
1007 // Field JSArray::kElementsOffset is initialized later. | |
1008 __ movq(FieldOperand(result, JSArray::kLengthOffset), array_size); | |
1009 | |
1010 // Calculate the location of the elements array and set elements array member | |
1011 // of the JSArray. | |
1012 // result: JSObject | |
1013 // elements_array_end: start of next object | |
1014 // array_size: size of array (smi) | |
1015 __ lea(elements_array, Operand(result, JSArray::kSize)); | |
1016 __ movq(FieldOperand(result, JSArray::kElementsOffset), elements_array); | |
1017 | |
1018 // Initialize the fixed array. FixedArray length is stored as a smi. | |
1019 // result: JSObject | |
1020 // elements_array: elements array | |
1021 // elements_array_end: start of next object | |
1022 // array_size: size of array (smi) | |
1023 __ Move(FieldOperand(elements_array, JSObject::kMapOffset), | |
1024 Factory::fixed_array_map()); | |
1025 Label not_empty_2, fill_array; | |
1026 __ SmiTest(array_size); | |
1027 __ j(not_zero, ¬_empty_2); | |
1028 // Length of the FixedArray is the number of pre-allocated elements even | |
1029 // though the actual JSArray has length 0. | |
1030 __ Move(FieldOperand(elements_array, FixedArray::kLengthOffset), | |
1031 Smi::FromInt(kPreallocatedArrayElements)); | |
1032 __ jmp(&fill_array); | |
1033 __ bind(¬_empty_2); | |
1034 // For non-empty JSArrays the length of the FixedArray and the JSArray is the | |
1035 // same. | |
1036 __ movq(FieldOperand(elements_array, FixedArray::kLengthOffset), array_size); | |
1037 | |
1038 // Fill the allocated FixedArray with the hole value if requested. | |
1039 // result: JSObject | |
1040 // elements_array: elements array | |
1041 // elements_array_end: start of next object | |
1042 __ bind(&fill_array); | |
1043 if (fill_with_hole) { | |
1044 Label loop, entry; | |
1045 __ Move(scratch, Factory::the_hole_value()); | |
1046 __ lea(elements_array, Operand(elements_array, | |
1047 FixedArray::kHeaderSize - kHeapObjectTag)); | |
1048 __ jmp(&entry); | |
1049 __ bind(&loop); | |
1050 __ movq(Operand(elements_array, 0), scratch); | |
1051 __ addq(elements_array, Immediate(kPointerSize)); | |
1052 __ bind(&entry); | |
1053 __ cmpq(elements_array, elements_array_end); | |
1054 __ j(below, &loop); | |
1055 } | |
1056 } | |
1057 | |
1058 | |
1059 // Create a new array for the built-in Array function. This function allocates | |
1060 // the JSArray object and the FixedArray elements array and initializes these. | |
1061 // If the Array cannot be constructed in native code the runtime is called. This | |
1062 // function assumes the following state: | |
1063 // rdi: constructor (built-in Array function) | |
1064 // rax: argc | |
1065 // rsp[0]: return address | |
1066 // rsp[8]: last argument | |
1067 // This function is used for both construct and normal calls of Array. The only | |
1068 // difference between handling a construct call and a normal call is that for a | |
1069 // construct call the constructor function in rdi needs to be preserved for | |
1070 // entering the generic code. In both cases argc in rax needs to be preserved. | |
1071 // Both registers are preserved by this code so no need to differentiate between | |
1072 // a construct call and a normal call. | |
1073 static void ArrayNativeCode(MacroAssembler* masm, | |
1074 Label *call_generic_code) { | |
1075 Label argc_one_or_more, argc_two_or_more; | |
1076 | |
1077 // Check for array construction with zero arguments. | |
1078 __ testq(rax, rax); | |
1079 __ j(not_zero, &argc_one_or_more); | |
1080 | |
1081 // Handle construction of an empty array. | |
1082 AllocateEmptyJSArray(masm, | |
1083 rdi, | |
1084 rbx, | |
1085 rcx, | |
1086 rdx, | |
1087 r8, | |
1088 kPreallocatedArrayElements, | |
1089 call_generic_code); | |
1090 __ IncrementCounter(&Counters::array_function_native, 1); | |
1091 __ movq(rax, rbx); | |
1092 __ ret(kPointerSize); | |
1093 | |
1094 // Check for one argument. Bail out if argument is not smi or if it is | |
1095 // negative. | |
1096 __ bind(&argc_one_or_more); | |
1097 __ cmpq(rax, Immediate(1)); | |
1098 __ j(not_equal, &argc_two_or_more); | |
1099 __ movq(rdx, Operand(rsp, kPointerSize)); // Get the argument from the stack. | |
1100 __ JumpUnlessNonNegativeSmi(rdx, call_generic_code); | |
1101 | |
1102 // Handle construction of an empty array of a certain size. Bail out if size | |
1103 // is to large to actually allocate an elements array. | |
1104 __ SmiCompare(rdx, Smi::FromInt(JSObject::kInitialMaxFastElementArray)); | |
1105 __ j(greater_equal, call_generic_code); | |
1106 | |
1107 // rax: argc | |
1108 // rdx: array_size (smi) | |
1109 // rdi: constructor | |
1110 // esp[0]: return address | |
1111 // esp[8]: argument | |
1112 AllocateJSArray(masm, | |
1113 rdi, | |
1114 rdx, | |
1115 rbx, | |
1116 rcx, | |
1117 r8, | |
1118 r9, | |
1119 true, | |
1120 call_generic_code); | |
1121 __ IncrementCounter(&Counters::array_function_native, 1); | |
1122 __ movq(rax, rbx); | |
1123 __ ret(2 * kPointerSize); | |
1124 | |
1125 // Handle construction of an array from a list of arguments. | |
1126 __ bind(&argc_two_or_more); | |
1127 __ movq(rdx, rax); | |
1128 __ Integer32ToSmi(rdx, rdx); // Convet argc to a smi. | |
1129 // rax: argc | |
1130 // rdx: array_size (smi) | |
1131 // rdi: constructor | |
1132 // esp[0] : return address | |
1133 // esp[8] : last argument | |
1134 AllocateJSArray(masm, | |
1135 rdi, | |
1136 rdx, | |
1137 rbx, | |
1138 rcx, | |
1139 r8, | |
1140 r9, | |
1141 false, | |
1142 call_generic_code); | |
1143 __ IncrementCounter(&Counters::array_function_native, 1); | |
1144 | |
1145 // rax: argc | |
1146 // rbx: JSArray | |
1147 // rcx: elements_array | |
1148 // r8: elements_array_end (untagged) | |
1149 // esp[0]: return address | |
1150 // esp[8]: last argument | |
1151 | |
1152 // Location of the last argument | |
1153 __ lea(r9, Operand(rsp, kPointerSize)); | |
1154 | |
1155 // Location of the first array element (Parameter fill_with_holes to | |
1156 // AllocateJSArrayis false, so the FixedArray is returned in rcx). | |
1157 __ lea(rdx, Operand(rcx, FixedArray::kHeaderSize - kHeapObjectTag)); | |
1158 | |
1159 // rax: argc | |
1160 // rbx: JSArray | |
1161 // rdx: location of the first array element | |
1162 // r9: location of the last argument | |
1163 // esp[0]: return address | |
1164 // esp[8]: last argument | |
1165 Label loop, entry; | |
1166 __ movq(rcx, rax); | |
1167 __ jmp(&entry); | |
1168 __ bind(&loop); | |
1169 __ movq(kScratchRegister, Operand(r9, rcx, times_pointer_size, 0)); | |
1170 __ movq(Operand(rdx, 0), kScratchRegister); | |
1171 __ addq(rdx, Immediate(kPointerSize)); | |
1172 __ bind(&entry); | |
1173 __ decq(rcx); | |
1174 __ j(greater_equal, &loop); | |
1175 | |
1176 // Remove caller arguments from the stack and return. | |
1177 // rax: argc | |
1178 // rbx: JSArray | |
1179 // esp[0]: return address | |
1180 // esp[8]: last argument | |
1181 __ pop(rcx); | |
1182 __ lea(rsp, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize)); | |
1183 __ push(rcx); | |
1184 __ movq(rax, rbx); | |
1185 __ ret(0); | |
1186 } | |
1187 | |
1188 | |
1189 void Builtins::Generate_ArrayCode(MacroAssembler* masm) { | |
1190 // ----------- S t a t e ------------- | |
1191 // -- rax : argc | |
1192 // -- rsp[0] : return address | |
1193 // -- rsp[8] : last argument | |
1194 // ----------------------------------- | |
1195 Label generic_array_code; | |
1196 | |
1197 // Get the Array function. | |
1198 __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, rdi); | |
1199 | |
1200 if (FLAG_debug_code) { | |
1201 // Initial map for the builtin Array function shoud be a map. | |
1202 __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); | |
1203 // Will both indicate a NULL and a Smi. | |
1204 ASSERT(kSmiTag == 0); | |
1205 Condition not_smi = NegateCondition(masm->CheckSmi(rbx)); | |
1206 __ Check(not_smi, "Unexpected initial map for Array function"); | |
1207 __ CmpObjectType(rbx, MAP_TYPE, rcx); | |
1208 __ Check(equal, "Unexpected initial map for Array function"); | |
1209 } | |
1210 | |
1211 // Run the native code for the Array function called as a normal function. | |
1212 ArrayNativeCode(masm, &generic_array_code); | |
1213 | |
1214 // Jump to the generic array code in case the specialized code cannot handle | |
1215 // the construction. | |
1216 __ bind(&generic_array_code); | |
1217 Code* code = Builtins::builtin(Builtins::ArrayCodeGeneric); | |
1218 Handle<Code> array_code(code); | |
1219 __ Jump(array_code, RelocInfo::CODE_TARGET); | |
1220 } | |
1221 | |
1222 | |
1223 void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) { | |
1224 // ----------- S t a t e ------------- | |
1225 // -- rax : argc | |
1226 // -- rdi : constructor | |
1227 // -- rsp[0] : return address | |
1228 // -- rsp[8] : last argument | |
1229 // ----------------------------------- | |
1230 Label generic_constructor; | |
1231 | |
1232 if (FLAG_debug_code) { | |
1233 // The array construct code is only set for the builtin Array function which | |
1234 // does always have a map. | |
1235 __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, rbx); | |
1236 __ cmpq(rdi, rbx); | |
1237 __ Check(equal, "Unexpected Array function"); | |
1238 // Initial map for the builtin Array function should be a map. | |
1239 __ movq(rbx, FieldOperand(rdi, JSFunction::kPrototypeOrInitialMapOffset)); | |
1240 // Will both indicate a NULL and a Smi. | |
1241 ASSERT(kSmiTag == 0); | |
1242 Condition not_smi = NegateCondition(masm->CheckSmi(rbx)); | |
1243 __ Check(not_smi, "Unexpected initial map for Array function"); | |
1244 __ CmpObjectType(rbx, MAP_TYPE, rcx); | |
1245 __ Check(equal, "Unexpected initial map for Array function"); | |
1246 } | |
1247 | |
1248 // Run the native code for the Array function called as constructor. | |
1249 ArrayNativeCode(masm, &generic_constructor); | |
1250 | |
1251 // Jump to the generic construct code in case the specialized code cannot | |
1252 // handle the construction. | |
1253 __ bind(&generic_constructor); | |
1254 Code* code = Builtins::builtin(Builtins::JSConstructStubGeneric); | |
1255 Handle<Code> generic_construct_stub(code); | |
1256 __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET); | |
1257 } | |
1258 | |
1259 | |
1260 void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { | |
1261 // TODO(849): implement custom construct stub. | |
1262 // Generate a copy of the generic stub for now. | |
1263 Generate_JSConstructStubGeneric(masm); | |
1264 } | |
1265 | |
1266 | |
1267 static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) { | |
1268 __ push(rbp); | |
1269 __ movq(rbp, rsp); | |
1270 | |
1271 // Store the arguments adaptor context sentinel. | |
1272 __ Push(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)); | |
1273 | |
1274 // Push the function on the stack. | |
1275 __ push(rdi); | |
1276 | |
1277 // Preserve the number of arguments on the stack. Must preserve both | |
1278 // rax and rbx because these registers are used when copying the | |
1279 // arguments and the receiver. | |
1280 __ Integer32ToSmi(rcx, rax); | |
1281 __ push(rcx); | |
1282 } | |
1283 | |
1284 | |
1285 static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) { | |
1286 // Retrieve the number of arguments from the stack. Number is a Smi. | |
1287 __ movq(rbx, Operand(rbp, ArgumentsAdaptorFrameConstants::kLengthOffset)); | |
1288 | |
1289 // Leave the frame. | |
1290 __ movq(rsp, rbp); | |
1291 __ pop(rbp); | |
1292 | |
1293 // Remove caller arguments from the stack. | |
1294 __ pop(rcx); | |
1295 SmiIndex index = masm->SmiToIndex(rbx, rbx, kPointerSizeLog2); | |
1296 __ lea(rsp, Operand(rsp, index.reg, index.scale, 1 * kPointerSize)); | |
1297 __ push(rcx); | |
1298 } | |
1299 | |
1300 | |
1301 void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) { | |
1302 // ----------- S t a t e ------------- | |
1303 // -- rax : actual number of arguments | |
1304 // -- rbx : expected number of arguments | |
1305 // -- rdx : code entry to call | |
1306 // ----------------------------------- | |
1307 | |
1308 Label invoke, dont_adapt_arguments; | |
1309 __ IncrementCounter(&Counters::arguments_adaptors, 1); | |
1310 | |
1311 Label enough, too_few; | |
1312 __ cmpq(rax, rbx); | |
1313 __ j(less, &too_few); | |
1314 __ cmpq(rbx, Immediate(SharedFunctionInfo::kDontAdaptArgumentsSentinel)); | |
1315 __ j(equal, &dont_adapt_arguments); | |
1316 | |
1317 { // Enough parameters: Actual >= expected. | |
1318 __ bind(&enough); | |
1319 EnterArgumentsAdaptorFrame(masm); | |
1320 | |
1321 // Copy receiver and all expected arguments. | |
1322 const int offset = StandardFrameConstants::kCallerSPOffset; | |
1323 __ lea(rax, Operand(rbp, rax, times_pointer_size, offset)); | |
1324 __ movq(rcx, Immediate(-1)); // account for receiver | |
1325 | |
1326 Label copy; | |
1327 __ bind(©); | |
1328 __ incq(rcx); | |
1329 __ push(Operand(rax, 0)); | |
1330 __ subq(rax, Immediate(kPointerSize)); | |
1331 __ cmpq(rcx, rbx); | |
1332 __ j(less, ©); | |
1333 __ jmp(&invoke); | |
1334 } | |
1335 | |
1336 { // Too few parameters: Actual < expected. | |
1337 __ bind(&too_few); | |
1338 EnterArgumentsAdaptorFrame(masm); | |
1339 | |
1340 // Copy receiver and all actual arguments. | |
1341 const int offset = StandardFrameConstants::kCallerSPOffset; | |
1342 __ lea(rdi, Operand(rbp, rax, times_pointer_size, offset)); | |
1343 __ movq(rcx, Immediate(-1)); // account for receiver | |
1344 | |
1345 Label copy; | |
1346 __ bind(©); | |
1347 __ incq(rcx); | |
1348 __ push(Operand(rdi, 0)); | |
1349 __ subq(rdi, Immediate(kPointerSize)); | |
1350 __ cmpq(rcx, rax); | |
1351 __ j(less, ©); | |
1352 | |
1353 // Fill remaining expected arguments with undefined values. | |
1354 Label fill; | |
1355 __ LoadRoot(kScratchRegister, Heap::kUndefinedValueRootIndex); | |
1356 __ bind(&fill); | |
1357 __ incq(rcx); | |
1358 __ push(kScratchRegister); | |
1359 __ cmpq(rcx, rbx); | |
1360 __ j(less, &fill); | |
1361 | |
1362 // Restore function pointer. | |
1363 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); | |
1364 } | |
1365 | |
1366 // Call the entry point. | |
1367 __ bind(&invoke); | |
1368 __ call(rdx); | |
1369 | |
1370 // Leave frame and return. | |
1371 LeaveArgumentsAdaptorFrame(masm); | |
1372 __ ret(0); | |
1373 | |
1374 // ------------------------------------------- | |
1375 // Dont adapt arguments. | |
1376 // ------------------------------------------- | |
1377 __ bind(&dont_adapt_arguments); | |
1378 __ jmp(rdx); | |
1379 } | |
1380 | |
1381 | |
1382 void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { | 1386 void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) { |
1383 __ int3(); | 1387 __ int3(); |
1384 } | 1388 } |
1385 | 1389 |
1386 | 1390 |
1387 #undef __ | |
1388 | |
1389 } } // namespace v8::internal | 1391 } } // namespace v8::internal |
1390 | 1392 |
1391 #endif // V8_TARGET_ARCH_X64 | 1393 #endif // V8_TARGET_ARCH_X64 |
OLD | NEW |