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 3// found in the LICENSE file. 4 5#include "src/v8.h" 6 7#if V8_TARGET_ARCH_IA32 8 9#include "src/codegen.h" 10#include "src/heap.h" 11#include "src/macro-assembler.h" 12 13namespace v8 { 14namespace internal { 15 16 17// ------------------------------------------------------------------------- 18// Platform-specific RuntimeCallHelper functions. 19 20void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { 21 masm->EnterFrame(StackFrame::INTERNAL); 22 ASSERT(!masm->has_frame()); 23 masm->set_has_frame(true); 24} 25 26 27void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { 28 masm->LeaveFrame(StackFrame::INTERNAL); 29 ASSERT(masm->has_frame()); 30 masm->set_has_frame(false); 31} 32 33 34#define __ masm. 35 36 37UnaryMathFunction CreateExpFunction() { 38 if (!FLAG_fast_math) return &std::exp; 39 size_t actual_size; 40 byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true)); 41 if (buffer == NULL) return &std::exp; 42 ExternalReference::InitializeMathExpData(); 43 44 MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size)); 45 // esp[1 * kPointerSize]: raw double input 46 // esp[0 * kPointerSize]: return address 47 { 48 XMMRegister input = xmm1; 49 XMMRegister result = xmm2; 50 __ movsd(input, Operand(esp, 1 * kPointerSize)); 51 __ push(eax); 52 __ push(ebx); 53 54 MathExpGenerator::EmitMathExp(&masm, input, result, xmm0, eax, ebx); 55 56 __ pop(ebx); 57 __ pop(eax); 58 __ movsd(Operand(esp, 1 * kPointerSize), result); 59 __ fld_d(Operand(esp, 1 * kPointerSize)); 60 __ Ret(); 61 } 62 63 CodeDesc desc; 64 masm.GetCode(&desc); 65 ASSERT(!RelocInfo::RequiresRelocation(desc)); 66 67 CPU::FlushICache(buffer, actual_size); 68 OS::ProtectCode(buffer, actual_size); 69 return FUNCTION_CAST<UnaryMathFunction>(buffer); 70} 71 72 73UnaryMathFunction CreateSqrtFunction() { 74 size_t actual_size; 75 // Allocate buffer in executable space. 76 byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, 77 &actual_size, 78 true)); 79 if (buffer == NULL) return &std::sqrt; 80 MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size)); 81 // esp[1 * kPointerSize]: raw double input 82 // esp[0 * kPointerSize]: return address 83 // Move double input into registers. 84 { 85 __ movsd(xmm0, Operand(esp, 1 * kPointerSize)); 86 __ sqrtsd(xmm0, xmm0); 87 __ movsd(Operand(esp, 1 * kPointerSize), xmm0); 88 // Load result into floating point register as return value. 89 __ fld_d(Operand(esp, 1 * kPointerSize)); 90 __ Ret(); 91 } 92 93 CodeDesc desc; 94 masm.GetCode(&desc); 95 ASSERT(!RelocInfo::RequiresRelocation(desc)); 96 97 CPU::FlushICache(buffer, actual_size); 98 OS::ProtectCode(buffer, actual_size); 99 return FUNCTION_CAST<UnaryMathFunction>(buffer); 100} 101 102 103// Helper functions for CreateMemMoveFunction. 104#undef __ 105#define __ ACCESS_MASM(masm) 106 107enum Direction { FORWARD, BACKWARD }; 108enum Alignment { MOVE_ALIGNED, MOVE_UNALIGNED }; 109 110// Expects registers: 111// esi - source, aligned if alignment == ALIGNED 112// edi - destination, always aligned 113// ecx - count (copy size in bytes) 114// edx - loop count (number of 64 byte chunks) 115void MemMoveEmitMainLoop(MacroAssembler* masm, 116 Label* move_last_15, 117 Direction direction, 118 Alignment alignment) { 119 Register src = esi; 120 Register dst = edi; 121 Register count = ecx; 122 Register loop_count = edx; 123 Label loop, move_last_31, move_last_63; 124 __ cmp(loop_count, 0); 125 __ j(equal, &move_last_63); 126 __ bind(&loop); 127 // Main loop. Copy in 64 byte chunks. 128 if (direction == BACKWARD) __ sub(src, Immediate(0x40)); 129 __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00)); 130 __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10)); 131 __ movdq(alignment == MOVE_ALIGNED, xmm2, Operand(src, 0x20)); 132 __ movdq(alignment == MOVE_ALIGNED, xmm3, Operand(src, 0x30)); 133 if (direction == FORWARD) __ add(src, Immediate(0x40)); 134 if (direction == BACKWARD) __ sub(dst, Immediate(0x40)); 135 __ movdqa(Operand(dst, 0x00), xmm0); 136 __ movdqa(Operand(dst, 0x10), xmm1); 137 __ movdqa(Operand(dst, 0x20), xmm2); 138 __ movdqa(Operand(dst, 0x30), xmm3); 139 if (direction == FORWARD) __ add(dst, Immediate(0x40)); 140 __ dec(loop_count); 141 __ j(not_zero, &loop); 142 // At most 63 bytes left to copy. 143 __ bind(&move_last_63); 144 __ test(count, Immediate(0x20)); 145 __ j(zero, &move_last_31); 146 if (direction == BACKWARD) __ sub(src, Immediate(0x20)); 147 __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0x00)); 148 __ movdq(alignment == MOVE_ALIGNED, xmm1, Operand(src, 0x10)); 149 if (direction == FORWARD) __ add(src, Immediate(0x20)); 150 if (direction == BACKWARD) __ sub(dst, Immediate(0x20)); 151 __ movdqa(Operand(dst, 0x00), xmm0); 152 __ movdqa(Operand(dst, 0x10), xmm1); 153 if (direction == FORWARD) __ add(dst, Immediate(0x20)); 154 // At most 31 bytes left to copy. 155 __ bind(&move_last_31); 156 __ test(count, Immediate(0x10)); 157 __ j(zero, move_last_15); 158 if (direction == BACKWARD) __ sub(src, Immediate(0x10)); 159 __ movdq(alignment == MOVE_ALIGNED, xmm0, Operand(src, 0)); 160 if (direction == FORWARD) __ add(src, Immediate(0x10)); 161 if (direction == BACKWARD) __ sub(dst, Immediate(0x10)); 162 __ movdqa(Operand(dst, 0), xmm0); 163 if (direction == FORWARD) __ add(dst, Immediate(0x10)); 164} 165 166 167void MemMoveEmitPopAndReturn(MacroAssembler* masm) { 168 __ pop(esi); 169 __ pop(edi); 170 __ ret(0); 171} 172 173 174#undef __ 175#define __ masm. 176 177 178class LabelConverter { 179 public: 180 explicit LabelConverter(byte* buffer) : buffer_(buffer) {} 181 int32_t address(Label* l) const { 182 return reinterpret_cast<int32_t>(buffer_) + l->pos(); 183 } 184 private: 185 byte* buffer_; 186}; 187 188 189MemMoveFunction CreateMemMoveFunction() { 190 size_t actual_size; 191 // Allocate buffer in executable space. 192 byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true)); 193 if (buffer == NULL) return NULL; 194 MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size)); 195 LabelConverter conv(buffer); 196 197 // Generated code is put into a fixed, unmovable buffer, and not into 198 // the V8 heap. We can't, and don't, refer to any relocatable addresses 199 // (e.g. the JavaScript nan-object). 200 201 // 32-bit C declaration function calls pass arguments on stack. 202 203 // Stack layout: 204 // esp[12]: Third argument, size. 205 // esp[8]: Second argument, source pointer. 206 // esp[4]: First argument, destination pointer. 207 // esp[0]: return address 208 209 const int kDestinationOffset = 1 * kPointerSize; 210 const int kSourceOffset = 2 * kPointerSize; 211 const int kSizeOffset = 3 * kPointerSize; 212 213 // When copying up to this many bytes, use special "small" handlers. 214 const size_t kSmallCopySize = 8; 215 // When copying up to this many bytes, use special "medium" handlers. 216 const size_t kMediumCopySize = 63; 217 // When non-overlapping region of src and dst is less than this, 218 // use a more careful implementation (slightly slower). 219 const size_t kMinMoveDistance = 16; 220 // Note that these values are dictated by the implementation below, 221 // do not just change them and hope things will work! 222 223 int stack_offset = 0; // Update if we change the stack height. 224 225 Label backward, backward_much_overlap; 226 Label forward_much_overlap, small_size, medium_size, pop_and_return; 227 __ push(edi); 228 __ push(esi); 229 stack_offset += 2 * kPointerSize; 230 Register dst = edi; 231 Register src = esi; 232 Register count = ecx; 233 Register loop_count = edx; 234 __ mov(dst, Operand(esp, stack_offset + kDestinationOffset)); 235 __ mov(src, Operand(esp, stack_offset + kSourceOffset)); 236 __ mov(count, Operand(esp, stack_offset + kSizeOffset)); 237 238 __ cmp(dst, src); 239 __ j(equal, &pop_and_return); 240 241 __ prefetch(Operand(src, 0), 1); 242 __ cmp(count, kSmallCopySize); 243 __ j(below_equal, &small_size); 244 __ cmp(count, kMediumCopySize); 245 __ j(below_equal, &medium_size); 246 __ cmp(dst, src); 247 __ j(above, &backward); 248 249 { 250 // |dst| is a lower address than |src|. Copy front-to-back. 251 Label unaligned_source, move_last_15, skip_last_move; 252 __ mov(eax, src); 253 __ sub(eax, dst); 254 __ cmp(eax, kMinMoveDistance); 255 __ j(below, &forward_much_overlap); 256 // Copy first 16 bytes. 257 __ movdqu(xmm0, Operand(src, 0)); 258 __ movdqu(Operand(dst, 0), xmm0); 259 // Determine distance to alignment: 16 - (dst & 0xF). 260 __ mov(edx, dst); 261 __ and_(edx, 0xF); 262 __ neg(edx); 263 __ add(edx, Immediate(16)); 264 __ add(dst, edx); 265 __ add(src, edx); 266 __ sub(count, edx); 267 // dst is now aligned. Main copy loop. 268 __ mov(loop_count, count); 269 __ shr(loop_count, 6); 270 // Check if src is also aligned. 271 __ test(src, Immediate(0xF)); 272 __ j(not_zero, &unaligned_source); 273 // Copy loop for aligned source and destination. 274 MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_ALIGNED); 275 // At most 15 bytes to copy. Copy 16 bytes at end of string. 276 __ bind(&move_last_15); 277 __ and_(count, 0xF); 278 __ j(zero, &skip_last_move, Label::kNear); 279 __ movdqu(xmm0, Operand(src, count, times_1, -0x10)); 280 __ movdqu(Operand(dst, count, times_1, -0x10), xmm0); 281 __ bind(&skip_last_move); 282 MemMoveEmitPopAndReturn(&masm); 283 284 // Copy loop for unaligned source and aligned destination. 285 __ bind(&unaligned_source); 286 MemMoveEmitMainLoop(&masm, &move_last_15, FORWARD, MOVE_UNALIGNED); 287 __ jmp(&move_last_15); 288 289 // Less than kMinMoveDistance offset between dst and src. 290 Label loop_until_aligned, last_15_much_overlap; 291 __ bind(&loop_until_aligned); 292 __ mov_b(eax, Operand(src, 0)); 293 __ inc(src); 294 __ mov_b(Operand(dst, 0), eax); 295 __ inc(dst); 296 __ dec(count); 297 __ bind(&forward_much_overlap); // Entry point into this block. 298 __ test(dst, Immediate(0xF)); 299 __ j(not_zero, &loop_until_aligned); 300 // dst is now aligned, src can't be. Main copy loop. 301 __ mov(loop_count, count); 302 __ shr(loop_count, 6); 303 MemMoveEmitMainLoop(&masm, &last_15_much_overlap, 304 FORWARD, MOVE_UNALIGNED); 305 __ bind(&last_15_much_overlap); 306 __ and_(count, 0xF); 307 __ j(zero, &pop_and_return); 308 __ cmp(count, kSmallCopySize); 309 __ j(below_equal, &small_size); 310 __ jmp(&medium_size); 311 } 312 313 { 314 // |dst| is a higher address than |src|. Copy backwards. 315 Label unaligned_source, move_first_15, skip_last_move; 316 __ bind(&backward); 317 // |dst| and |src| always point to the end of what's left to copy. 318 __ add(dst, count); 319 __ add(src, count); 320 __ mov(eax, dst); 321 __ sub(eax, src); 322 __ cmp(eax, kMinMoveDistance); 323 __ j(below, &backward_much_overlap); 324 // Copy last 16 bytes. 325 __ movdqu(xmm0, Operand(src, -0x10)); 326 __ movdqu(Operand(dst, -0x10), xmm0); 327 // Find distance to alignment: dst & 0xF 328 __ mov(edx, dst); 329 __ and_(edx, 0xF); 330 __ sub(dst, edx); 331 __ sub(src, edx); 332 __ sub(count, edx); 333 // dst is now aligned. Main copy loop. 334 __ mov(loop_count, count); 335 __ shr(loop_count, 6); 336 // Check if src is also aligned. 337 __ test(src, Immediate(0xF)); 338 __ j(not_zero, &unaligned_source); 339 // Copy loop for aligned source and destination. 340 MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_ALIGNED); 341 // At most 15 bytes to copy. Copy 16 bytes at beginning of string. 342 __ bind(&move_first_15); 343 __ and_(count, 0xF); 344 __ j(zero, &skip_last_move, Label::kNear); 345 __ sub(src, count); 346 __ sub(dst, count); 347 __ movdqu(xmm0, Operand(src, 0)); 348 __ movdqu(Operand(dst, 0), xmm0); 349 __ bind(&skip_last_move); 350 MemMoveEmitPopAndReturn(&masm); 351 352 // Copy loop for unaligned source and aligned destination. 353 __ bind(&unaligned_source); 354 MemMoveEmitMainLoop(&masm, &move_first_15, BACKWARD, MOVE_UNALIGNED); 355 __ jmp(&move_first_15); 356 357 // Less than kMinMoveDistance offset between dst and src. 358 Label loop_until_aligned, first_15_much_overlap; 359 __ bind(&loop_until_aligned); 360 __ dec(src); 361 __ dec(dst); 362 __ mov_b(eax, Operand(src, 0)); 363 __ mov_b(Operand(dst, 0), eax); 364 __ dec(count); 365 __ bind(&backward_much_overlap); // Entry point into this block. 366 __ test(dst, Immediate(0xF)); 367 __ j(not_zero, &loop_until_aligned); 368 // dst is now aligned, src can't be. Main copy loop. 369 __ mov(loop_count, count); 370 __ shr(loop_count, 6); 371 MemMoveEmitMainLoop(&masm, &first_15_much_overlap, 372 BACKWARD, MOVE_UNALIGNED); 373 __ bind(&first_15_much_overlap); 374 __ and_(count, 0xF); 375 __ j(zero, &pop_and_return); 376 // Small/medium handlers expect dst/src to point to the beginning. 377 __ sub(dst, count); 378 __ sub(src, count); 379 __ cmp(count, kSmallCopySize); 380 __ j(below_equal, &small_size); 381 __ jmp(&medium_size); 382 } 383 { 384 // Special handlers for 9 <= copy_size < 64. No assumptions about 385 // alignment or move distance, so all reads must be unaligned and 386 // must happen before any writes. 387 Label medium_handlers, f9_16, f17_32, f33_48, f49_63; 388 389 __ bind(&f9_16); 390 __ movsd(xmm0, Operand(src, 0)); 391 __ movsd(xmm1, Operand(src, count, times_1, -8)); 392 __ movsd(Operand(dst, 0), xmm0); 393 __ movsd(Operand(dst, count, times_1, -8), xmm1); 394 MemMoveEmitPopAndReturn(&masm); 395 396 __ bind(&f17_32); 397 __ movdqu(xmm0, Operand(src, 0)); 398 __ movdqu(xmm1, Operand(src, count, times_1, -0x10)); 399 __ movdqu(Operand(dst, 0x00), xmm0); 400 __ movdqu(Operand(dst, count, times_1, -0x10), xmm1); 401 MemMoveEmitPopAndReturn(&masm); 402 403 __ bind(&f33_48); 404 __ movdqu(xmm0, Operand(src, 0x00)); 405 __ movdqu(xmm1, Operand(src, 0x10)); 406 __ movdqu(xmm2, Operand(src, count, times_1, -0x10)); 407 __ movdqu(Operand(dst, 0x00), xmm0); 408 __ movdqu(Operand(dst, 0x10), xmm1); 409 __ movdqu(Operand(dst, count, times_1, -0x10), xmm2); 410 MemMoveEmitPopAndReturn(&masm); 411 412 __ bind(&f49_63); 413 __ movdqu(xmm0, Operand(src, 0x00)); 414 __ movdqu(xmm1, Operand(src, 0x10)); 415 __ movdqu(xmm2, Operand(src, 0x20)); 416 __ movdqu(xmm3, Operand(src, count, times_1, -0x10)); 417 __ movdqu(Operand(dst, 0x00), xmm0); 418 __ movdqu(Operand(dst, 0x10), xmm1); 419 __ movdqu(Operand(dst, 0x20), xmm2); 420 __ movdqu(Operand(dst, count, times_1, -0x10), xmm3); 421 MemMoveEmitPopAndReturn(&masm); 422 423 __ bind(&medium_handlers); 424 __ dd(conv.address(&f9_16)); 425 __ dd(conv.address(&f17_32)); 426 __ dd(conv.address(&f33_48)); 427 __ dd(conv.address(&f49_63)); 428 429 __ bind(&medium_size); // Entry point into this block. 430 __ mov(eax, count); 431 __ dec(eax); 432 __ shr(eax, 4); 433 if (FLAG_debug_code) { 434 Label ok; 435 __ cmp(eax, 3); 436 __ j(below_equal, &ok); 437 __ int3(); 438 __ bind(&ok); 439 } 440 __ mov(eax, Operand(eax, times_4, conv.address(&medium_handlers))); 441 __ jmp(eax); 442 } 443 { 444 // Specialized copiers for copy_size <= 8 bytes. 445 Label small_handlers, f0, f1, f2, f3, f4, f5_8; 446 __ bind(&f0); 447 MemMoveEmitPopAndReturn(&masm); 448 449 __ bind(&f1); 450 __ mov_b(eax, Operand(src, 0)); 451 __ mov_b(Operand(dst, 0), eax); 452 MemMoveEmitPopAndReturn(&masm); 453 454 __ bind(&f2); 455 __ mov_w(eax, Operand(src, 0)); 456 __ mov_w(Operand(dst, 0), eax); 457 MemMoveEmitPopAndReturn(&masm); 458 459 __ bind(&f3); 460 __ mov_w(eax, Operand(src, 0)); 461 __ mov_b(edx, Operand(src, 2)); 462 __ mov_w(Operand(dst, 0), eax); 463 __ mov_b(Operand(dst, 2), edx); 464 MemMoveEmitPopAndReturn(&masm); 465 466 __ bind(&f4); 467 __ mov(eax, Operand(src, 0)); 468 __ mov(Operand(dst, 0), eax); 469 MemMoveEmitPopAndReturn(&masm); 470 471 __ bind(&f5_8); 472 __ mov(eax, Operand(src, 0)); 473 __ mov(edx, Operand(src, count, times_1, -4)); 474 __ mov(Operand(dst, 0), eax); 475 __ mov(Operand(dst, count, times_1, -4), edx); 476 MemMoveEmitPopAndReturn(&masm); 477 478 __ bind(&small_handlers); 479 __ dd(conv.address(&f0)); 480 __ dd(conv.address(&f1)); 481 __ dd(conv.address(&f2)); 482 __ dd(conv.address(&f3)); 483 __ dd(conv.address(&f4)); 484 __ dd(conv.address(&f5_8)); 485 __ dd(conv.address(&f5_8)); 486 __ dd(conv.address(&f5_8)); 487 __ dd(conv.address(&f5_8)); 488 489 __ bind(&small_size); // Entry point into this block. 490 if (FLAG_debug_code) { 491 Label ok; 492 __ cmp(count, 8); 493 __ j(below_equal, &ok); 494 __ int3(); 495 __ bind(&ok); 496 } 497 __ mov(eax, Operand(count, times_4, conv.address(&small_handlers))); 498 __ jmp(eax); 499 } 500 501 __ bind(&pop_and_return); 502 MemMoveEmitPopAndReturn(&masm); 503 504 CodeDesc desc; 505 masm.GetCode(&desc); 506 ASSERT(!RelocInfo::RequiresRelocation(desc)); 507 CPU::FlushICache(buffer, actual_size); 508 OS::ProtectCode(buffer, actual_size); 509 // TODO(jkummerow): It would be nice to register this code creation event 510 // with the PROFILE / GDBJIT system. 511 return FUNCTION_CAST<MemMoveFunction>(buffer); 512} 513 514 515#undef __ 516 517// ------------------------------------------------------------------------- 518// Code generators 519 520#define __ ACCESS_MASM(masm) 521 522 523void ElementsTransitionGenerator::GenerateMapChangeElementsTransition( 524 MacroAssembler* masm, AllocationSiteMode mode, 525 Label* allocation_memento_found) { 526 // ----------- S t a t e ------------- 527 // -- eax : value 528 // -- ebx : target map 529 // -- ecx : key 530 // -- edx : receiver 531 // -- esp[0] : return address 532 // ----------------------------------- 533 if (mode == TRACK_ALLOCATION_SITE) { 534 ASSERT(allocation_memento_found != NULL); 535 __ JumpIfJSArrayHasAllocationMemento(edx, edi, allocation_memento_found); 536 } 537 538 // Set transitioned map. 539 __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx); 540 __ RecordWriteField(edx, 541 HeapObject::kMapOffset, 542 ebx, 543 edi, 544 kDontSaveFPRegs, 545 EMIT_REMEMBERED_SET, 546 OMIT_SMI_CHECK); 547} 548 549 550void ElementsTransitionGenerator::GenerateSmiToDouble( 551 MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { 552 // ----------- S t a t e ------------- 553 // -- eax : value 554 // -- ebx : target map 555 // -- ecx : key 556 // -- edx : receiver 557 // -- esp[0] : return address 558 // ----------------------------------- 559 Label loop, entry, convert_hole, gc_required, only_change_map; 560 561 if (mode == TRACK_ALLOCATION_SITE) { 562 __ JumpIfJSArrayHasAllocationMemento(edx, edi, fail); 563 } 564 565 // Check for empty arrays, which only require a map transition and no changes 566 // to the backing store. 567 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); 568 __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array())); 569 __ j(equal, &only_change_map); 570 571 __ push(eax); 572 __ push(ebx); 573 574 __ mov(edi, FieldOperand(edi, FixedArray::kLengthOffset)); 575 576 // Allocate new FixedDoubleArray. 577 // edx: receiver 578 // edi: length of source FixedArray (smi-tagged) 579 AllocationFlags flags = 580 static_cast<AllocationFlags>(TAG_OBJECT | DOUBLE_ALIGNMENT); 581 __ Allocate(FixedDoubleArray::kHeaderSize, times_8, edi, 582 REGISTER_VALUE_IS_SMI, eax, ebx, no_reg, &gc_required, flags); 583 584 // eax: destination FixedDoubleArray 585 // edi: number of elements 586 // edx: receiver 587 __ mov(FieldOperand(eax, HeapObject::kMapOffset), 588 Immediate(masm->isolate()->factory()->fixed_double_array_map())); 589 __ mov(FieldOperand(eax, FixedDoubleArray::kLengthOffset), edi); 590 __ mov(esi, FieldOperand(edx, JSObject::kElementsOffset)); 591 // Replace receiver's backing store with newly created FixedDoubleArray. 592 __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax); 593 __ mov(ebx, eax); 594 __ RecordWriteField(edx, 595 JSObject::kElementsOffset, 596 ebx, 597 edi, 598 kDontSaveFPRegs, 599 EMIT_REMEMBERED_SET, 600 OMIT_SMI_CHECK); 601 602 __ mov(edi, FieldOperand(esi, FixedArray::kLengthOffset)); 603 604 // Prepare for conversion loop. 605 ExternalReference canonical_the_hole_nan_reference = 606 ExternalReference::address_of_the_hole_nan(); 607 XMMRegister the_hole_nan = xmm1; 608 __ movsd(the_hole_nan, 609 Operand::StaticVariable(canonical_the_hole_nan_reference)); 610 __ jmp(&entry); 611 612 // Call into runtime if GC is required. 613 __ bind(&gc_required); 614 // Restore registers before jumping into runtime. 615 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 616 __ pop(ebx); 617 __ pop(eax); 618 __ jmp(fail); 619 620 // Convert and copy elements 621 // esi: source FixedArray 622 __ bind(&loop); 623 __ mov(ebx, FieldOperand(esi, edi, times_2, FixedArray::kHeaderSize)); 624 // ebx: current element from source 625 // edi: index of current element 626 __ JumpIfNotSmi(ebx, &convert_hole); 627 628 // Normal smi, convert it to double and store. 629 __ SmiUntag(ebx); 630 __ Cvtsi2sd(xmm0, ebx); 631 __ movsd(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize), 632 xmm0); 633 __ jmp(&entry); 634 635 // Found hole, store hole_nan_as_double instead. 636 __ bind(&convert_hole); 637 638 if (FLAG_debug_code) { 639 __ cmp(ebx, masm->isolate()->factory()->the_hole_value()); 640 __ Assert(equal, kObjectFoundInSmiOnlyArray); 641 } 642 643 __ movsd(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize), 644 the_hole_nan); 645 646 __ bind(&entry); 647 __ sub(edi, Immediate(Smi::FromInt(1))); 648 __ j(not_sign, &loop); 649 650 __ pop(ebx); 651 __ pop(eax); 652 653 // Restore esi. 654 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 655 656 __ bind(&only_change_map); 657 // eax: value 658 // ebx: target map 659 // Set transitioned map. 660 __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx); 661 __ RecordWriteField(edx, 662 HeapObject::kMapOffset, 663 ebx, 664 edi, 665 kDontSaveFPRegs, 666 OMIT_REMEMBERED_SET, 667 OMIT_SMI_CHECK); 668} 669 670 671void ElementsTransitionGenerator::GenerateDoubleToObject( 672 MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { 673 // ----------- S t a t e ------------- 674 // -- eax : value 675 // -- ebx : target map 676 // -- ecx : key 677 // -- edx : receiver 678 // -- esp[0] : return address 679 // ----------------------------------- 680 Label loop, entry, convert_hole, gc_required, only_change_map, success; 681 682 if (mode == TRACK_ALLOCATION_SITE) { 683 __ JumpIfJSArrayHasAllocationMemento(edx, edi, fail); 684 } 685 686 // Check for empty arrays, which only require a map transition and no changes 687 // to the backing store. 688 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); 689 __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array())); 690 __ j(equal, &only_change_map); 691 692 __ push(eax); 693 __ push(edx); 694 __ push(ebx); 695 696 __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset)); 697 698 // Allocate new FixedArray. 699 // ebx: length of source FixedDoubleArray (smi-tagged) 700 __ lea(edi, Operand(ebx, times_2, FixedArray::kHeaderSize)); 701 __ Allocate(edi, eax, esi, no_reg, &gc_required, TAG_OBJECT); 702 703 // eax: destination FixedArray 704 // ebx: number of elements 705 __ mov(FieldOperand(eax, HeapObject::kMapOffset), 706 Immediate(masm->isolate()->factory()->fixed_array_map())); 707 __ mov(FieldOperand(eax, FixedArray::kLengthOffset), ebx); 708 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset)); 709 710 __ jmp(&entry); 711 712 // ebx: target map 713 // edx: receiver 714 // Set transitioned map. 715 __ bind(&only_change_map); 716 __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx); 717 __ RecordWriteField(edx, 718 HeapObject::kMapOffset, 719 ebx, 720 edi, 721 kDontSaveFPRegs, 722 OMIT_REMEMBERED_SET, 723 OMIT_SMI_CHECK); 724 __ jmp(&success); 725 726 // Call into runtime if GC is required. 727 __ bind(&gc_required); 728 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 729 __ pop(ebx); 730 __ pop(edx); 731 __ pop(eax); 732 __ jmp(fail); 733 734 // Box doubles into heap numbers. 735 // edi: source FixedDoubleArray 736 // eax: destination FixedArray 737 __ bind(&loop); 738 // ebx: index of current element (smi-tagged) 739 uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32); 740 __ cmp(FieldOperand(edi, ebx, times_4, offset), Immediate(kHoleNanUpper32)); 741 __ j(equal, &convert_hole); 742 743 // Non-hole double, copy value into a heap number. 744 __ AllocateHeapNumber(edx, esi, no_reg, &gc_required); 745 // edx: new heap number 746 __ movsd(xmm0, 747 FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize)); 748 __ movsd(FieldOperand(edx, HeapNumber::kValueOffset), xmm0); 749 __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), edx); 750 __ mov(esi, ebx); 751 __ RecordWriteArray(eax, 752 edx, 753 esi, 754 kDontSaveFPRegs, 755 EMIT_REMEMBERED_SET, 756 OMIT_SMI_CHECK); 757 __ jmp(&entry, Label::kNear); 758 759 // Replace the-hole NaN with the-hole pointer. 760 __ bind(&convert_hole); 761 __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), 762 masm->isolate()->factory()->the_hole_value()); 763 764 __ bind(&entry); 765 __ sub(ebx, Immediate(Smi::FromInt(1))); 766 __ j(not_sign, &loop); 767 768 __ pop(ebx); 769 __ pop(edx); 770 // ebx: target map 771 // edx: receiver 772 // Set transitioned map. 773 __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx); 774 __ RecordWriteField(edx, 775 HeapObject::kMapOffset, 776 ebx, 777 edi, 778 kDontSaveFPRegs, 779 OMIT_REMEMBERED_SET, 780 OMIT_SMI_CHECK); 781 // Replace receiver's backing store with newly created and filled FixedArray. 782 __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax); 783 __ RecordWriteField(edx, 784 JSObject::kElementsOffset, 785 eax, 786 edi, 787 kDontSaveFPRegs, 788 EMIT_REMEMBERED_SET, 789 OMIT_SMI_CHECK); 790 791 // Restore registers. 792 __ pop(eax); 793 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); 794 795 __ bind(&success); 796} 797 798 799void StringCharLoadGenerator::Generate(MacroAssembler* masm, 800 Factory* factory, 801 Register string, 802 Register index, 803 Register result, 804 Label* call_runtime) { 805 // Fetch the instance type of the receiver into result register. 806 __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); 807 __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); 808 809 // We need special handling for indirect strings. 810 Label check_sequential; 811 __ test(result, Immediate(kIsIndirectStringMask)); 812 __ j(zero, &check_sequential, Label::kNear); 813 814 // Dispatch on the indirect string shape: slice or cons. 815 Label cons_string; 816 __ test(result, Immediate(kSlicedNotConsMask)); 817 __ j(zero, &cons_string, Label::kNear); 818 819 // Handle slices. 820 Label indirect_string_loaded; 821 __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset)); 822 __ SmiUntag(result); 823 __ add(index, result); 824 __ mov(string, FieldOperand(string, SlicedString::kParentOffset)); 825 __ jmp(&indirect_string_loaded, Label::kNear); 826 827 // Handle cons strings. 828 // Check whether the right hand side is the empty string (i.e. if 829 // this is really a flat string in a cons string). If that is not 830 // the case we would rather go to the runtime system now to flatten 831 // the string. 832 __ bind(&cons_string); 833 __ cmp(FieldOperand(string, ConsString::kSecondOffset), 834 Immediate(factory->empty_string())); 835 __ j(not_equal, call_runtime); 836 __ mov(string, FieldOperand(string, ConsString::kFirstOffset)); 837 838 __ bind(&indirect_string_loaded); 839 __ mov(result, FieldOperand(string, HeapObject::kMapOffset)); 840 __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset)); 841 842 // Distinguish sequential and external strings. Only these two string 843 // representations can reach here (slices and flat cons strings have been 844 // reduced to the underlying sequential or external string). 845 Label seq_string; 846 __ bind(&check_sequential); 847 STATIC_ASSERT(kSeqStringTag == 0); 848 __ test(result, Immediate(kStringRepresentationMask)); 849 __ j(zero, &seq_string, Label::kNear); 850 851 // Handle external strings. 852 Label ascii_external, done; 853 if (FLAG_debug_code) { 854 // Assert that we do not have a cons or slice (indirect strings) here. 855 // Sequential strings have already been ruled out. 856 __ test(result, Immediate(kIsIndirectStringMask)); 857 __ Assert(zero, kExternalStringExpectedButNotFound); 858 } 859 // Rule out short external strings. 860 STATIC_ASSERT(kShortExternalStringTag != 0); 861 __ test_b(result, kShortExternalStringMask); 862 __ j(not_zero, call_runtime); 863 // Check encoding. 864 STATIC_ASSERT(kTwoByteStringTag == 0); 865 __ test_b(result, kStringEncodingMask); 866 __ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset)); 867 __ j(not_equal, &ascii_external, Label::kNear); 868 // Two-byte string. 869 __ movzx_w(result, Operand(result, index, times_2, 0)); 870 __ jmp(&done, Label::kNear); 871 __ bind(&ascii_external); 872 // Ascii string. 873 __ movzx_b(result, Operand(result, index, times_1, 0)); 874 __ jmp(&done, Label::kNear); 875 876 // Dispatch on the encoding: ASCII or two-byte. 877 Label ascii; 878 __ bind(&seq_string); 879 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0); 880 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); 881 __ test(result, Immediate(kStringEncodingMask)); 882 __ j(not_zero, &ascii, Label::kNear); 883 884 // Two-byte string. 885 // Load the two-byte character code into the result register. 886 __ movzx_w(result, FieldOperand(string, 887 index, 888 times_2, 889 SeqTwoByteString::kHeaderSize)); 890 __ jmp(&done, Label::kNear); 891 892 // Ascii string. 893 // Load the byte into the result register. 894 __ bind(&ascii); 895 __ movzx_b(result, FieldOperand(string, 896 index, 897 times_1, 898 SeqOneByteString::kHeaderSize)); 899 __ bind(&done); 900} 901 902 903static Operand ExpConstant(int index) { 904 return Operand::StaticVariable(ExternalReference::math_exp_constants(index)); 905} 906 907 908void MathExpGenerator::EmitMathExp(MacroAssembler* masm, 909 XMMRegister input, 910 XMMRegister result, 911 XMMRegister double_scratch, 912 Register temp1, 913 Register temp2) { 914 ASSERT(!input.is(double_scratch)); 915 ASSERT(!input.is(result)); 916 ASSERT(!result.is(double_scratch)); 917 ASSERT(!temp1.is(temp2)); 918 ASSERT(ExternalReference::math_exp_constants(0).address() != NULL); 919 920 Label done; 921 922 __ movsd(double_scratch, ExpConstant(0)); 923 __ xorpd(result, result); 924 __ ucomisd(double_scratch, input); 925 __ j(above_equal, &done); 926 __ ucomisd(input, ExpConstant(1)); 927 __ movsd(result, ExpConstant(2)); 928 __ j(above_equal, &done); 929 __ movsd(double_scratch, ExpConstant(3)); 930 __ movsd(result, ExpConstant(4)); 931 __ mulsd(double_scratch, input); 932 __ addsd(double_scratch, result); 933 __ movd(temp2, double_scratch); 934 __ subsd(double_scratch, result); 935 __ movsd(result, ExpConstant(6)); 936 __ mulsd(double_scratch, ExpConstant(5)); 937 __ subsd(double_scratch, input); 938 __ subsd(result, double_scratch); 939 __ movsd(input, double_scratch); 940 __ mulsd(input, double_scratch); 941 __ mulsd(result, input); 942 __ mov(temp1, temp2); 943 __ mulsd(result, ExpConstant(7)); 944 __ subsd(result, double_scratch); 945 __ add(temp1, Immediate(0x1ff800)); 946 __ addsd(result, ExpConstant(8)); 947 __ and_(temp2, Immediate(0x7ff)); 948 __ shr(temp1, 11); 949 __ shl(temp1, 20); 950 __ movd(input, temp1); 951 __ pshufd(input, input, static_cast<uint8_t>(0xe1)); // Order: 11 10 00 01 952 __ movsd(double_scratch, Operand::StaticArray( 953 temp2, times_8, ExternalReference::math_exp_log_table())); 954 __ orps(input, double_scratch); 955 __ mulsd(result, input); 956 __ bind(&done); 957} 958 959#undef __ 960 961 962CodeAgingHelper::CodeAgingHelper() { 963 ASSERT(young_sequence_.length() == kNoCodeAgeSequenceLength); 964 CodePatcher patcher(young_sequence_.start(), young_sequence_.length()); 965 patcher.masm()->push(ebp); 966 patcher.masm()->mov(ebp, esp); 967 patcher.masm()->push(esi); 968 patcher.masm()->push(edi); 969} 970 971 972#ifdef DEBUG 973bool CodeAgingHelper::IsOld(byte* candidate) const { 974 return *candidate == kCallOpcode; 975} 976#endif 977 978 979bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) { 980 bool result = isolate->code_aging_helper()->IsYoung(sequence); 981 ASSERT(result || isolate->code_aging_helper()->IsOld(sequence)); 982 return result; 983} 984 985 986void Code::GetCodeAgeAndParity(Isolate* isolate, byte* sequence, Age* age, 987 MarkingParity* parity) { 988 if (IsYoungSequence(isolate, sequence)) { 989 *age = kNoAgeCodeAge; 990 *parity = NO_MARKING_PARITY; 991 } else { 992 sequence++; // Skip the kCallOpcode byte 993 Address target_address = sequence + *reinterpret_cast<int*>(sequence) + 994 Assembler::kCallTargetAddressOffset; 995 Code* stub = GetCodeFromTargetAddress(target_address); 996 GetCodeAgeAndParity(stub, age, parity); 997 } 998} 999 1000 1001void Code::PatchPlatformCodeAge(Isolate* isolate, 1002 byte* sequence, 1003 Code::Age age, 1004 MarkingParity parity) { 1005 uint32_t young_length = isolate->code_aging_helper()->young_sequence_length(); 1006 if (age == kNoAgeCodeAge) { 1007 isolate->code_aging_helper()->CopyYoungSequenceTo(sequence); 1008 CPU::FlushICache(sequence, young_length); 1009 } else { 1010 Code* stub = GetCodeAgeStub(isolate, age, parity); 1011 CodePatcher patcher(sequence, young_length); 1012 patcher.masm()->call(stub->instruction_start(), RelocInfo::NONE32); 1013 } 1014} 1015 1016 1017} } // namespace v8::internal 1018 1019#endif // V8_TARGET_ARCH_IA32 1020