blob: e33959e65b4ce3891cb81fa4c86fb2b3efef4053 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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_X87
8
9#include "src/codegen.h"
10#include "src/heap/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 DCHECK(!masm->has_frame());
23 masm->set_has_frame(true);
24}
25
26
27void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
28 masm->LeaveFrame(StackFrame::INTERNAL);
29 DCHECK(masm->has_frame());
30 masm->set_has_frame(false);
31}
32
33
34#define __ masm.
35
36
37UnaryMathFunction CreateExpFunction() {
38 // No SSE2 support
39 return &std::exp;
40}
41
42
43UnaryMathFunction CreateSqrtFunction() {
44 // No SSE2 support
45 return &std::sqrt;
46}
47
48
49// Helper functions for CreateMemMoveFunction.
50#undef __
51#define __ ACCESS_MASM(masm)
52
53enum Direction { FORWARD, BACKWARD };
54enum Alignment { MOVE_ALIGNED, MOVE_UNALIGNED };
55
56
57void MemMoveEmitPopAndReturn(MacroAssembler* masm) {
58 __ pop(esi);
59 __ pop(edi);
60 __ ret(0);
61}
62
63
64#undef __
65#define __ masm.
66
67
68class LabelConverter {
69 public:
70 explicit LabelConverter(byte* buffer) : buffer_(buffer) {}
71 int32_t address(Label* l) const {
72 return reinterpret_cast<int32_t>(buffer_) + l->pos();
73 }
74 private:
75 byte* buffer_;
76};
77
78
79MemMoveFunction CreateMemMoveFunction() {
80 size_t actual_size;
81 // Allocate buffer in executable space.
82 byte* buffer =
83 static_cast<byte*>(base::OS::Allocate(1 * KB, &actual_size, true));
84 if (buffer == NULL) return NULL;
85 MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size));
86 LabelConverter conv(buffer);
87
88 // Generated code is put into a fixed, unmovable buffer, and not into
89 // the V8 heap. We can't, and don't, refer to any relocatable addresses
90 // (e.g. the JavaScript nan-object).
91
92 // 32-bit C declaration function calls pass arguments on stack.
93
94 // Stack layout:
95 // esp[12]: Third argument, size.
96 // esp[8]: Second argument, source pointer.
97 // esp[4]: First argument, destination pointer.
98 // esp[0]: return address
99
100 const int kDestinationOffset = 1 * kPointerSize;
101 const int kSourceOffset = 2 * kPointerSize;
102 const int kSizeOffset = 3 * kPointerSize;
103
104 int stack_offset = 0; // Update if we change the stack height.
105
106 Label backward, backward_much_overlap;
107 Label forward_much_overlap, small_size, medium_size, pop_and_return;
108 __ push(edi);
109 __ push(esi);
110 stack_offset += 2 * kPointerSize;
111 Register dst = edi;
112 Register src = esi;
113 Register count = ecx;
114 __ mov(dst, Operand(esp, stack_offset + kDestinationOffset));
115 __ mov(src, Operand(esp, stack_offset + kSourceOffset));
116 __ mov(count, Operand(esp, stack_offset + kSizeOffset));
117
118 __ cmp(dst, src);
119 __ j(equal, &pop_and_return);
120
121 // No SSE2.
122 Label forward;
123 __ cmp(count, 0);
124 __ j(equal, &pop_and_return);
125 __ cmp(dst, src);
126 __ j(above, &backward);
127 __ jmp(&forward);
128 {
129 // Simple forward copier.
130 Label forward_loop_1byte, forward_loop_4byte;
131 __ bind(&forward_loop_4byte);
132 __ mov(eax, Operand(src, 0));
133 __ sub(count, Immediate(4));
134 __ add(src, Immediate(4));
135 __ mov(Operand(dst, 0), eax);
136 __ add(dst, Immediate(4));
137 __ bind(&forward); // Entry point.
138 __ cmp(count, 3);
139 __ j(above, &forward_loop_4byte);
140 __ bind(&forward_loop_1byte);
141 __ cmp(count, 0);
142 __ j(below_equal, &pop_and_return);
143 __ mov_b(eax, Operand(src, 0));
144 __ dec(count);
145 __ inc(src);
146 __ mov_b(Operand(dst, 0), eax);
147 __ inc(dst);
148 __ jmp(&forward_loop_1byte);
149 }
150 {
151 // Simple backward copier.
152 Label backward_loop_1byte, backward_loop_4byte, entry_shortcut;
153 __ bind(&backward);
154 __ add(src, count);
155 __ add(dst, count);
156 __ cmp(count, 3);
157 __ j(below_equal, &entry_shortcut);
158
159 __ bind(&backward_loop_4byte);
160 __ sub(src, Immediate(4));
161 __ sub(count, Immediate(4));
162 __ mov(eax, Operand(src, 0));
163 __ sub(dst, Immediate(4));
164 __ mov(Operand(dst, 0), eax);
165 __ cmp(count, 3);
166 __ j(above, &backward_loop_4byte);
167 __ bind(&backward_loop_1byte);
168 __ cmp(count, 0);
169 __ j(below_equal, &pop_and_return);
170 __ bind(&entry_shortcut);
171 __ dec(src);
172 __ dec(count);
173 __ mov_b(eax, Operand(src, 0));
174 __ dec(dst);
175 __ mov_b(Operand(dst, 0), eax);
176 __ jmp(&backward_loop_1byte);
177 }
178
179 __ bind(&pop_and_return);
180 MemMoveEmitPopAndReturn(&masm);
181
182 CodeDesc desc;
183 masm.GetCode(&desc);
184 DCHECK(!RelocInfo::RequiresRelocation(desc));
185 CpuFeatures::FlushICache(buffer, actual_size);
186 base::OS::ProtectCode(buffer, actual_size);
187 // TODO(jkummerow): It would be nice to register this code creation event
188 // with the PROFILE / GDBJIT system.
189 return FUNCTION_CAST<MemMoveFunction>(buffer);
190}
191
192
193#undef __
194
195// -------------------------------------------------------------------------
196// Code generators
197
198#define __ ACCESS_MASM(masm)
199
200
201void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
202 MacroAssembler* masm,
203 Register receiver,
204 Register key,
205 Register value,
206 Register target_map,
207 AllocationSiteMode mode,
208 Label* allocation_memento_found) {
209 Register scratch = edi;
210 DCHECK(!AreAliased(receiver, key, value, target_map, scratch));
211
212 if (mode == TRACK_ALLOCATION_SITE) {
213 DCHECK(allocation_memento_found != NULL);
214 __ JumpIfJSArrayHasAllocationMemento(
215 receiver, scratch, allocation_memento_found);
216 }
217
218 // Set transitioned map.
219 __ mov(FieldOperand(receiver, HeapObject::kMapOffset), target_map);
220 __ RecordWriteField(receiver, HeapObject::kMapOffset, target_map, scratch,
221 kDontSaveFPRegs, EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
222}
223
224
225void ElementsTransitionGenerator::GenerateSmiToDouble(
226 MacroAssembler* masm,
227 Register receiver,
228 Register key,
229 Register value,
230 Register target_map,
231 AllocationSiteMode mode,
232 Label* fail) {
233 // Return address is on the stack.
234 DCHECK(receiver.is(edx));
235 DCHECK(key.is(ecx));
236 DCHECK(value.is(eax));
237 DCHECK(target_map.is(ebx));
238
239 Label loop, entry, convert_hole, gc_required, only_change_map;
240
241 if (mode == TRACK_ALLOCATION_SITE) {
242 __ JumpIfJSArrayHasAllocationMemento(edx, edi, fail);
243 }
244
245 // Check for empty arrays, which only require a map transition and no changes
246 // to the backing store.
247 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
248 __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
249 __ j(equal, &only_change_map);
250
251 __ push(eax);
252 __ push(ebx);
253
254 __ mov(edi, FieldOperand(edi, FixedArray::kLengthOffset));
255
256 // Allocate new FixedDoubleArray.
257 // edx: receiver
258 // edi: length of source FixedArray (smi-tagged)
259 AllocationFlags flags =
260 static_cast<AllocationFlags>(TAG_OBJECT | DOUBLE_ALIGNMENT);
261 __ Allocate(FixedDoubleArray::kHeaderSize, times_8, edi,
262 REGISTER_VALUE_IS_SMI, eax, ebx, no_reg, &gc_required, flags);
263
264 // eax: destination FixedDoubleArray
265 // edi: number of elements
266 // edx: receiver
267 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
268 Immediate(masm->isolate()->factory()->fixed_double_array_map()));
269 __ mov(FieldOperand(eax, FixedDoubleArray::kLengthOffset), edi);
270 __ mov(esi, FieldOperand(edx, JSObject::kElementsOffset));
271 // Replace receiver's backing store with newly created FixedDoubleArray.
272 __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
273 __ mov(ebx, eax);
274 __ RecordWriteField(edx, JSObject::kElementsOffset, ebx, edi, kDontSaveFPRegs,
275 EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
276
277 __ mov(edi, FieldOperand(esi, FixedArray::kLengthOffset));
278
279 // Prepare for conversion loop.
280 ExternalReference canonical_the_hole_nan_reference =
281 ExternalReference::address_of_the_hole_nan();
282 __ jmp(&entry);
283
284 // Call into runtime if GC is required.
285 __ bind(&gc_required);
286 // Restore registers before jumping into runtime.
287 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
288 __ pop(ebx);
289 __ pop(eax);
290 __ jmp(fail);
291
292 // Convert and copy elements
293 // esi: source FixedArray
294 __ bind(&loop);
295 __ mov(ebx, FieldOperand(esi, edi, times_2, FixedArray::kHeaderSize));
296 // ebx: current element from source
297 // edi: index of current element
298 __ JumpIfNotSmi(ebx, &convert_hole);
299
300 // Normal smi, convert it to double and store.
301 __ SmiUntag(ebx);
302 __ push(ebx);
303 __ fild_s(Operand(esp, 0));
304 __ pop(ebx);
305 __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize));
306 __ jmp(&entry);
307
308 // Found hole, store hole_nan_as_double instead.
309 __ bind(&convert_hole);
310
311 if (FLAG_debug_code) {
312 __ cmp(ebx, masm->isolate()->factory()->the_hole_value());
313 __ Assert(equal, kObjectFoundInSmiOnlyArray);
314 }
315
316 __ fld_d(Operand::StaticVariable(canonical_the_hole_nan_reference));
317 __ fstp_d(FieldOperand(eax, edi, times_4, FixedDoubleArray::kHeaderSize));
318
319 __ bind(&entry);
320 __ sub(edi, Immediate(Smi::FromInt(1)));
321 __ j(not_sign, &loop);
322
323 __ pop(ebx);
324 __ pop(eax);
325
326 // Restore esi.
327 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
328
329 __ bind(&only_change_map);
330 // eax: value
331 // ebx: target map
332 // Set transitioned map.
333 __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
334 __ RecordWriteField(edx, HeapObject::kMapOffset, ebx, edi, kDontSaveFPRegs,
335 OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
336}
337
338
339void ElementsTransitionGenerator::GenerateDoubleToObject(
340 MacroAssembler* masm,
341 Register receiver,
342 Register key,
343 Register value,
344 Register target_map,
345 AllocationSiteMode mode,
346 Label* fail) {
347 // Return address is on the stack.
348 DCHECK(receiver.is(edx));
349 DCHECK(key.is(ecx));
350 DCHECK(value.is(eax));
351 DCHECK(target_map.is(ebx));
352
353 Label loop, entry, convert_hole, gc_required, only_change_map, success;
354
355 if (mode == TRACK_ALLOCATION_SITE) {
356 __ JumpIfJSArrayHasAllocationMemento(edx, edi, fail);
357 }
358
359 // Check for empty arrays, which only require a map transition and no changes
360 // to the backing store.
361 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
362 __ cmp(edi, Immediate(masm->isolate()->factory()->empty_fixed_array()));
363 __ j(equal, &only_change_map);
364
365 __ push(eax);
366 __ push(edx);
367 __ push(ebx);
368
369 __ mov(ebx, FieldOperand(edi, FixedDoubleArray::kLengthOffset));
370
371 // Allocate new FixedArray.
372 // ebx: length of source FixedDoubleArray (smi-tagged)
373 __ lea(edi, Operand(ebx, times_2, FixedArray::kHeaderSize));
374 __ Allocate(edi, eax, esi, no_reg, &gc_required, TAG_OBJECT);
375
376 // eax: destination FixedArray
377 // ebx: number of elements
378 __ mov(FieldOperand(eax, HeapObject::kMapOffset),
379 Immediate(masm->isolate()->factory()->fixed_array_map()));
380 __ mov(FieldOperand(eax, FixedArray::kLengthOffset), ebx);
381 __ mov(edi, FieldOperand(edx, JSObject::kElementsOffset));
382
383 __ jmp(&entry);
384
385 // ebx: target map
386 // edx: receiver
387 // Set transitioned map.
388 __ bind(&only_change_map);
389 __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
390 __ RecordWriteField(edx, HeapObject::kMapOffset, ebx, edi, kDontSaveFPRegs,
391 OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
392 __ jmp(&success);
393
394 // Call into runtime if GC is required.
395 __ bind(&gc_required);
396 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
397 __ pop(ebx);
398 __ pop(edx);
399 __ pop(eax);
400 __ jmp(fail);
401
402 // Box doubles into heap numbers.
403 // edi: source FixedDoubleArray
404 // eax: destination FixedArray
405 __ bind(&loop);
406 // ebx: index of current element (smi-tagged)
407 uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
408 __ cmp(FieldOperand(edi, ebx, times_4, offset), Immediate(kHoleNanUpper32));
409 __ j(equal, &convert_hole);
410
411 // Non-hole double, copy value into a heap number.
412 __ AllocateHeapNumber(edx, esi, no_reg, &gc_required);
413 // edx: new heap number
414 __ mov(esi, FieldOperand(edi, ebx, times_4, FixedDoubleArray::kHeaderSize));
415 __ mov(FieldOperand(edx, HeapNumber::kValueOffset), esi);
416 __ mov(esi, FieldOperand(edi, ebx, times_4, offset));
417 __ mov(FieldOperand(edx, HeapNumber::kValueOffset + kPointerSize), esi);
418 __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize), edx);
419 __ mov(esi, ebx);
420 __ RecordWriteArray(eax, edx, esi, kDontSaveFPRegs, EMIT_REMEMBERED_SET,
421 OMIT_SMI_CHECK);
422 __ jmp(&entry, Label::kNear);
423
424 // Replace the-hole NaN with the-hole pointer.
425 __ bind(&convert_hole);
426 __ mov(FieldOperand(eax, ebx, times_2, FixedArray::kHeaderSize),
427 masm->isolate()->factory()->the_hole_value());
428
429 __ bind(&entry);
430 __ sub(ebx, Immediate(Smi::FromInt(1)));
431 __ j(not_sign, &loop);
432
433 __ pop(ebx);
434 __ pop(edx);
435 // ebx: target map
436 // edx: receiver
437 // Set transitioned map.
438 __ mov(FieldOperand(edx, HeapObject::kMapOffset), ebx);
439 __ RecordWriteField(edx, HeapObject::kMapOffset, ebx, edi, kDontSaveFPRegs,
440 OMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
441 // Replace receiver's backing store with newly created and filled FixedArray.
442 __ mov(FieldOperand(edx, JSObject::kElementsOffset), eax);
443 __ RecordWriteField(edx, JSObject::kElementsOffset, eax, edi, kDontSaveFPRegs,
444 EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
445
446 // Restore registers.
447 __ pop(eax);
448 __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
449
450 __ bind(&success);
451}
452
453
454void StringCharLoadGenerator::Generate(MacroAssembler* masm,
455 Factory* factory,
456 Register string,
457 Register index,
458 Register result,
459 Label* call_runtime) {
460 // Fetch the instance type of the receiver into result register.
461 __ mov(result, FieldOperand(string, HeapObject::kMapOffset));
462 __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
463
464 // We need special handling for indirect strings.
465 Label check_sequential;
466 __ test(result, Immediate(kIsIndirectStringMask));
467 __ j(zero, &check_sequential, Label::kNear);
468
469 // Dispatch on the indirect string shape: slice or cons.
470 Label cons_string;
471 __ test(result, Immediate(kSlicedNotConsMask));
472 __ j(zero, &cons_string, Label::kNear);
473
474 // Handle slices.
475 Label indirect_string_loaded;
476 __ mov(result, FieldOperand(string, SlicedString::kOffsetOffset));
477 __ SmiUntag(result);
478 __ add(index, result);
479 __ mov(string, FieldOperand(string, SlicedString::kParentOffset));
480 __ jmp(&indirect_string_loaded, Label::kNear);
481
482 // Handle cons strings.
483 // Check whether the right hand side is the empty string (i.e. if
484 // this is really a flat string in a cons string). If that is not
485 // the case we would rather go to the runtime system now to flatten
486 // the string.
487 __ bind(&cons_string);
488 __ cmp(FieldOperand(string, ConsString::kSecondOffset),
489 Immediate(factory->empty_string()));
490 __ j(not_equal, call_runtime);
491 __ mov(string, FieldOperand(string, ConsString::kFirstOffset));
492
493 __ bind(&indirect_string_loaded);
494 __ mov(result, FieldOperand(string, HeapObject::kMapOffset));
495 __ movzx_b(result, FieldOperand(result, Map::kInstanceTypeOffset));
496
497 // Distinguish sequential and external strings. Only these two string
498 // representations can reach here (slices and flat cons strings have been
499 // reduced to the underlying sequential or external string).
500 Label seq_string;
501 __ bind(&check_sequential);
502 STATIC_ASSERT(kSeqStringTag == 0);
503 __ test(result, Immediate(kStringRepresentationMask));
504 __ j(zero, &seq_string, Label::kNear);
505
506 // Handle external strings.
507 Label one_byte_external, done;
508 if (FLAG_debug_code) {
509 // Assert that we do not have a cons or slice (indirect strings) here.
510 // Sequential strings have already been ruled out.
511 __ test(result, Immediate(kIsIndirectStringMask));
512 __ Assert(zero, kExternalStringExpectedButNotFound);
513 }
514 // Rule out short external strings.
515 STATIC_ASSERT(kShortExternalStringTag != 0);
516 __ test_b(result, kShortExternalStringMask);
517 __ j(not_zero, call_runtime);
518 // Check encoding.
519 STATIC_ASSERT(kTwoByteStringTag == 0);
520 __ test_b(result, kStringEncodingMask);
521 __ mov(result, FieldOperand(string, ExternalString::kResourceDataOffset));
522 __ j(not_equal, &one_byte_external, Label::kNear);
523 // Two-byte string.
524 __ movzx_w(result, Operand(result, index, times_2, 0));
525 __ jmp(&done, Label::kNear);
526 __ bind(&one_byte_external);
527 // One-byte string.
528 __ movzx_b(result, Operand(result, index, times_1, 0));
529 __ jmp(&done, Label::kNear);
530
531 // Dispatch on the encoding: one-byte or two-byte.
532 Label one_byte;
533 __ bind(&seq_string);
534 STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
535 STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
536 __ test(result, Immediate(kStringEncodingMask));
537 __ j(not_zero, &one_byte, Label::kNear);
538
539 // Two-byte string.
540 // Load the two-byte character code into the result register.
541 __ movzx_w(result, FieldOperand(string,
542 index,
543 times_2,
544 SeqTwoByteString::kHeaderSize));
545 __ jmp(&done, Label::kNear);
546
547 // One-byte string.
548 // Load the byte into the result register.
549 __ bind(&one_byte);
550 __ movzx_b(result, FieldOperand(string,
551 index,
552 times_1,
553 SeqOneByteString::kHeaderSize));
554 __ bind(&done);
555}
556
557
558#undef __
559
560
561CodeAgingHelper::CodeAgingHelper() {
562 DCHECK(young_sequence_.length() == kNoCodeAgeSequenceLength);
563 CodePatcher patcher(young_sequence_.start(), young_sequence_.length());
564 patcher.masm()->push(ebp);
565 patcher.masm()->mov(ebp, esp);
566 patcher.masm()->push(esi);
567 patcher.masm()->push(edi);
568}
569
570
571#ifdef DEBUG
572bool CodeAgingHelper::IsOld(byte* candidate) const {
573 return *candidate == kCallOpcode;
574}
575#endif
576
577
578bool Code::IsYoungSequence(Isolate* isolate, byte* sequence) {
579 bool result = isolate->code_aging_helper()->IsYoung(sequence);
580 DCHECK(result || isolate->code_aging_helper()->IsOld(sequence));
581 return result;
582}
583
584
585void Code::GetCodeAgeAndParity(Isolate* isolate, byte* sequence, Age* age,
586 MarkingParity* parity) {
587 if (IsYoungSequence(isolate, sequence)) {
588 *age = kNoAgeCodeAge;
589 *parity = NO_MARKING_PARITY;
590 } else {
591 sequence++; // Skip the kCallOpcode byte
592 Address target_address = sequence + *reinterpret_cast<int*>(sequence) +
593 Assembler::kCallTargetAddressOffset;
594 Code* stub = GetCodeFromTargetAddress(target_address);
595 GetCodeAgeAndParity(stub, age, parity);
596 }
597}
598
599
600void Code::PatchPlatformCodeAge(Isolate* isolate,
601 byte* sequence,
602 Code::Age age,
603 MarkingParity parity) {
604 uint32_t young_length = isolate->code_aging_helper()->young_sequence_length();
605 if (age == kNoAgeCodeAge) {
606 isolate->code_aging_helper()->CopyYoungSequenceTo(sequence);
607 CpuFeatures::FlushICache(sequence, young_length);
608 } else {
609 Code* stub = GetCodeAgeStub(isolate, age, parity);
610 CodePatcher patcher(sequence, young_length);
611 patcher.masm()->call(stub->instruction_start(), RelocInfo::NONE32);
612 }
613}
614
615
616} } // namespace v8::internal
617
618#endif // V8_TARGET_ARCH_X87