The data contained in this repository can be downloaded to your computer using one of several clients.
Please see the documentation of your version control software client for more information.
Please select the desired protocol below to get the URL.
This URL has Read-Only access.
main_repo / deps / v8 / src / x64 / codegen-x64.cc @ f230a1cf
History | View | Annotate | Download (26.1 KB)
1 |
// Copyright 2012 the V8 project authors. All rights reserved.
|
---|---|
2 |
// Redistribution and use in source and binary forms, with or without
|
3 |
// modification, are permitted provided that the following conditions are
|
4 |
// met:
|
5 |
//
|
6 |
// * Redistributions of source code must retain the above copyright
|
7 |
// notice, this list of conditions and the following disclaimer.
|
8 |
// * Redistributions in binary form must reproduce the above
|
9 |
// copyright notice, this list of conditions and the following
|
10 |
// disclaimer in the documentation and/or other materials provided
|
11 |
// with the distribution.
|
12 |
// * Neither the name of Google Inc. nor the names of its
|
13 |
// contributors may be used to endorse or promote products derived
|
14 |
// from this software without specific prior written permission.
|
15 |
//
|
16 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
17 |
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
18 |
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
19 |
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
20 |
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
23 |
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
24 |
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
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.
|
27 |
|
28 |
#include "v8.h" |
29 |
|
30 |
#if V8_TARGET_ARCH_X64
|
31 |
|
32 |
#include "codegen.h" |
33 |
#include "macro-assembler.h" |
34 |
|
35 |
namespace v8 {
|
36 |
namespace internal {
|
37 |
|
38 |
// -------------------------------------------------------------------------
|
39 |
// Platform-specific RuntimeCallHelper functions.
|
40 |
|
41 |
void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { |
42 |
masm->EnterFrame(StackFrame::INTERNAL); |
43 |
ASSERT(!masm->has_frame()); |
44 |
masm->set_has_frame(true);
|
45 |
} |
46 |
|
47 |
|
48 |
void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { |
49 |
masm->LeaveFrame(StackFrame::INTERNAL); |
50 |
ASSERT(masm->has_frame()); |
51 |
masm->set_has_frame(false);
|
52 |
} |
53 |
|
54 |
|
55 |
#define __ masm.
|
56 |
|
57 |
|
58 |
UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) { |
59 |
size_t actual_size; |
60 |
// Allocate buffer in executable space.
|
61 |
byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, |
62 |
&actual_size, |
63 |
true));
|
64 |
if (buffer == NULL) { |
65 |
// Fallback to library function if function cannot be created.
|
66 |
switch (type) {
|
67 |
case TranscendentalCache::SIN: return &sin; |
68 |
case TranscendentalCache::COS: return &cos; |
69 |
case TranscendentalCache::TAN: return &tan; |
70 |
case TranscendentalCache::LOG: return &log; |
71 |
default: UNIMPLEMENTED();
|
72 |
} |
73 |
} |
74 |
|
75 |
MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size)); |
76 |
// xmm0: raw double input.
|
77 |
// Move double input into registers.
|
78 |
__ push(rbx); |
79 |
__ push(rdi); |
80 |
__ movq(rbx, xmm0); |
81 |
__ push(rbx); |
82 |
__ fld_d(Operand(rsp, 0));
|
83 |
TranscendentalCacheStub::GenerateOperation(&masm, type); |
84 |
// The return value is expected to be in xmm0.
|
85 |
__ fstp_d(Operand(rsp, 0));
|
86 |
__ pop(rbx); |
87 |
__ movq(xmm0, rbx); |
88 |
__ pop(rdi); |
89 |
__ pop(rbx); |
90 |
__ Ret(); |
91 |
|
92 |
CodeDesc desc; |
93 |
masm.GetCode(&desc); |
94 |
ASSERT(!RelocInfo::RequiresRelocation(desc)); |
95 |
|
96 |
CPU::FlushICache(buffer, actual_size); |
97 |
OS::ProtectCode(buffer, actual_size); |
98 |
return FUNCTION_CAST<UnaryMathFunction>(buffer);
|
99 |
} |
100 |
|
101 |
|
102 |
UnaryMathFunction CreateExpFunction() { |
103 |
if (!FLAG_fast_math) return &exp; |
104 |
size_t actual_size; |
105 |
byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true)); |
106 |
if (buffer == NULL) return &exp; |
107 |
ExternalReference::InitializeMathExpData(); |
108 |
|
109 |
MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size)); |
110 |
// xmm0: raw double input.
|
111 |
XMMRegister input = xmm0; |
112 |
XMMRegister result = xmm1; |
113 |
__ push(rax); |
114 |
__ push(rbx); |
115 |
|
116 |
MathExpGenerator::EmitMathExp(&masm, input, result, xmm2, rax, rbx); |
117 |
|
118 |
__ pop(rbx); |
119 |
__ pop(rax); |
120 |
__ movsd(xmm0, result); |
121 |
__ Ret(); |
122 |
|
123 |
CodeDesc desc; |
124 |
masm.GetCode(&desc); |
125 |
ASSERT(!RelocInfo::RequiresRelocation(desc)); |
126 |
|
127 |
CPU::FlushICache(buffer, actual_size); |
128 |
OS::ProtectCode(buffer, actual_size); |
129 |
return FUNCTION_CAST<UnaryMathFunction>(buffer);
|
130 |
} |
131 |
|
132 |
|
133 |
UnaryMathFunction CreateSqrtFunction() { |
134 |
size_t actual_size; |
135 |
// Allocate buffer in executable space.
|
136 |
byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, |
137 |
&actual_size, |
138 |
true));
|
139 |
if (buffer == NULL) return &sqrt; |
140 |
|
141 |
MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size)); |
142 |
// xmm0: raw double input.
|
143 |
// Move double input into registers.
|
144 |
__ sqrtsd(xmm0, xmm0); |
145 |
__ Ret(); |
146 |
|
147 |
CodeDesc desc; |
148 |
masm.GetCode(&desc); |
149 |
ASSERT(!RelocInfo::RequiresRelocation(desc)); |
150 |
|
151 |
CPU::FlushICache(buffer, actual_size); |
152 |
OS::ProtectCode(buffer, actual_size); |
153 |
return FUNCTION_CAST<UnaryMathFunction>(buffer);
|
154 |
} |
155 |
|
156 |
|
157 |
#ifdef _WIN64
|
158 |
typedef double (*ModuloFunction)(double, double); |
159 |
// Define custom fmod implementation.
|
160 |
ModuloFunction CreateModuloFunction() { |
161 |
size_t actual_size; |
162 |
byte* buffer = static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize,
|
163 |
&actual_size, |
164 |
true));
|
165 |
CHECK(buffer); |
166 |
Assembler masm(NULL, buffer, static_cast<int>(actual_size)); |
167 |
// Generated code is put into a fixed, unmovable, buffer, and not into
|
168 |
// the V8 heap. We can't, and don't, refer to any relocatable addresses
|
169 |
// (e.g. the JavaScript nan-object).
|
170 |
|
171 |
// Windows 64 ABI passes double arguments in xmm0, xmm1 and
|
172 |
// returns result in xmm0.
|
173 |
// Argument backing space is allocated on the stack above
|
174 |
// the return address.
|
175 |
|
176 |
// Compute x mod y.
|
177 |
// Load y and x (use argument backing store as temporary storage).
|
178 |
__ movsd(Operand(rsp, kPointerSize * 2), xmm1);
|
179 |
__ movsd(Operand(rsp, kPointerSize), xmm0); |
180 |
__ fld_d(Operand(rsp, kPointerSize * 2));
|
181 |
__ fld_d(Operand(rsp, kPointerSize)); |
182 |
|
183 |
// Clear exception flags before operation.
|
184 |
{ |
185 |
Label no_exceptions; |
186 |
__ fwait(); |
187 |
__ fnstsw_ax(); |
188 |
// Clear if Illegal Operand or Zero Division exceptions are set.
|
189 |
__ testb(rax, Immediate(5));
|
190 |
__ j(zero, &no_exceptions); |
191 |
__ fnclex(); |
192 |
__ bind(&no_exceptions); |
193 |
} |
194 |
|
195 |
// Compute st(0) % st(1)
|
196 |
{ |
197 |
Label partial_remainder_loop; |
198 |
__ bind(&partial_remainder_loop); |
199 |
__ fprem(); |
200 |
__ fwait(); |
201 |
__ fnstsw_ax(); |
202 |
__ testl(rax, Immediate(0x400 /* C2 */)); |
203 |
// If C2 is set, computation only has partial result. Loop to
|
204 |
// continue computation.
|
205 |
__ j(not_zero, &partial_remainder_loop); |
206 |
} |
207 |
|
208 |
Label valid_result; |
209 |
Label return_result; |
210 |
// If Invalid Operand or Zero Division exceptions are set,
|
211 |
// return NaN.
|
212 |
__ testb(rax, Immediate(5));
|
213 |
__ j(zero, &valid_result); |
214 |
__ fstp(0); // Drop result in st(0). |
215 |
int64_t kNaNValue = V8_INT64_C(0x7ff8000000000000);
|
216 |
__ movq(rcx, kNaNValue, RelocInfo::NONE64); |
217 |
__ movq(Operand(rsp, kPointerSize), rcx); |
218 |
__ movsd(xmm0, Operand(rsp, kPointerSize)); |
219 |
__ jmp(&return_result); |
220 |
|
221 |
// If result is valid, return that.
|
222 |
__ bind(&valid_result); |
223 |
__ fstp_d(Operand(rsp, kPointerSize)); |
224 |
__ movsd(xmm0, Operand(rsp, kPointerSize)); |
225 |
|
226 |
// Clean up FPU stack and exceptions and return xmm0
|
227 |
__ bind(&return_result); |
228 |
__ fstp(0); // Unload y. |
229 |
|
230 |
Label clear_exceptions; |
231 |
__ testb(rax, Immediate(0x3f /* Any Exception*/)); |
232 |
__ j(not_zero, &clear_exceptions); |
233 |
__ ret(0);
|
234 |
__ bind(&clear_exceptions); |
235 |
__ fnclex(); |
236 |
__ ret(0);
|
237 |
|
238 |
CodeDesc desc; |
239 |
masm.GetCode(&desc); |
240 |
OS::ProtectCode(buffer, actual_size); |
241 |
// Call the function from C++ through this pointer.
|
242 |
return FUNCTION_CAST<ModuloFunction>(buffer);
|
243 |
} |
244 |
|
245 |
#endif
|
246 |
|
247 |
#undef __
|
248 |
|
249 |
// -------------------------------------------------------------------------
|
250 |
// Code generators
|
251 |
|
252 |
#define __ ACCESS_MASM(masm)
|
253 |
|
254 |
void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
|
255 |
MacroAssembler* masm, AllocationSiteMode mode, |
256 |
Label* allocation_memento_found) { |
257 |
// ----------- S t a t e -------------
|
258 |
// -- rax : value
|
259 |
// -- rbx : target map
|
260 |
// -- rcx : key
|
261 |
// -- rdx : receiver
|
262 |
// -- rsp[0] : return address
|
263 |
// -----------------------------------
|
264 |
if (mode == TRACK_ALLOCATION_SITE) {
|
265 |
ASSERT(allocation_memento_found != NULL);
|
266 |
__ JumpIfJSArrayHasAllocationMemento(rdx, rdi, allocation_memento_found); |
267 |
} |
268 |
|
269 |
// Set transitioned map.
|
270 |
__ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx); |
271 |
__ RecordWriteField(rdx, |
272 |
HeapObject::kMapOffset, |
273 |
rbx, |
274 |
rdi, |
275 |
kDontSaveFPRegs, |
276 |
EMIT_REMEMBERED_SET, |
277 |
OMIT_SMI_CHECK); |
278 |
} |
279 |
|
280 |
|
281 |
void ElementsTransitionGenerator::GenerateSmiToDouble(
|
282 |
MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { |
283 |
// ----------- S t a t e -------------
|
284 |
// -- rax : value
|
285 |
// -- rbx : target map
|
286 |
// -- rcx : key
|
287 |
// -- rdx : receiver
|
288 |
// -- rsp[0] : return address
|
289 |
// -----------------------------------
|
290 |
// The fail label is not actually used since we do not allocate.
|
291 |
Label allocated, new_backing_store, only_change_map, done; |
292 |
|
293 |
if (mode == TRACK_ALLOCATION_SITE) {
|
294 |
__ JumpIfJSArrayHasAllocationMemento(rdx, rdi, fail); |
295 |
} |
296 |
|
297 |
// Check for empty arrays, which only require a map transition and no changes
|
298 |
// to the backing store.
|
299 |
__ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset)); |
300 |
__ CompareRoot(r8, Heap::kEmptyFixedArrayRootIndex); |
301 |
__ j(equal, &only_change_map); |
302 |
|
303 |
// Check backing store for COW-ness. For COW arrays we have to
|
304 |
// allocate a new backing store.
|
305 |
__ SmiToInteger32(r9, FieldOperand(r8, FixedDoubleArray::kLengthOffset)); |
306 |
__ CompareRoot(FieldOperand(r8, HeapObject::kMapOffset), |
307 |
Heap::kFixedCOWArrayMapRootIndex); |
308 |
__ j(equal, &new_backing_store); |
309 |
// Check if the backing store is in new-space. If not, we need to allocate
|
310 |
// a new one since the old one is in pointer-space.
|
311 |
// If in new space, we can reuse the old backing store because it is
|
312 |
// the same size.
|
313 |
__ JumpIfNotInNewSpace(r8, rdi, &new_backing_store); |
314 |
|
315 |
__ movq(r14, r8); // Destination array equals source array.
|
316 |
|
317 |
// r8 : source FixedArray
|
318 |
// r9 : elements array length
|
319 |
// r14: destination FixedDoubleArray
|
320 |
// Set backing store's map
|
321 |
__ LoadRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); |
322 |
__ movq(FieldOperand(r14, HeapObject::kMapOffset), rdi); |
323 |
|
324 |
__ bind(&allocated); |
325 |
// Set transitioned map.
|
326 |
__ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx); |
327 |
__ RecordWriteField(rdx, |
328 |
HeapObject::kMapOffset, |
329 |
rbx, |
330 |
rdi, |
331 |
kDontSaveFPRegs, |
332 |
EMIT_REMEMBERED_SET, |
333 |
OMIT_SMI_CHECK); |
334 |
|
335 |
// Convert smis to doubles and holes to hole NaNs. The Array's length
|
336 |
// remains unchanged.
|
337 |
STATIC_ASSERT(FixedDoubleArray::kLengthOffset == FixedArray::kLengthOffset); |
338 |
STATIC_ASSERT(FixedDoubleArray::kHeaderSize == FixedArray::kHeaderSize); |
339 |
|
340 |
Label loop, entry, convert_hole; |
341 |
__ movq(r15, BitCast<int64_t, uint64_t>(kHoleNanInt64), RelocInfo::NONE64); |
342 |
// r15: the-hole NaN
|
343 |
__ jmp(&entry); |
344 |
|
345 |
// Allocate new backing store.
|
346 |
__ bind(&new_backing_store); |
347 |
__ lea(rdi, Operand(r9, times_8, FixedArray::kHeaderSize)); |
348 |
__ Allocate(rdi, r14, r11, r15, fail, TAG_OBJECT); |
349 |
// Set backing store's map
|
350 |
__ LoadRoot(rdi, Heap::kFixedDoubleArrayMapRootIndex); |
351 |
__ movq(FieldOperand(r14, HeapObject::kMapOffset), rdi); |
352 |
// Set receiver's backing store.
|
353 |
__ movq(FieldOperand(rdx, JSObject::kElementsOffset), r14); |
354 |
__ movq(r11, r14); |
355 |
__ RecordWriteField(rdx, |
356 |
JSObject::kElementsOffset, |
357 |
r11, |
358 |
r15, |
359 |
kDontSaveFPRegs, |
360 |
EMIT_REMEMBERED_SET, |
361 |
OMIT_SMI_CHECK); |
362 |
// Set backing store's length.
|
363 |
__ Integer32ToSmi(r11, r9); |
364 |
__ movq(FieldOperand(r14, FixedDoubleArray::kLengthOffset), r11); |
365 |
__ jmp(&allocated); |
366 |
|
367 |
__ bind(&only_change_map); |
368 |
// Set transitioned map.
|
369 |
__ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx); |
370 |
__ RecordWriteField(rdx, |
371 |
HeapObject::kMapOffset, |
372 |
rbx, |
373 |
rdi, |
374 |
kDontSaveFPRegs, |
375 |
OMIT_REMEMBERED_SET, |
376 |
OMIT_SMI_CHECK); |
377 |
__ jmp(&done); |
378 |
|
379 |
// Conversion loop.
|
380 |
__ bind(&loop); |
381 |
__ movq(rbx, |
382 |
FieldOperand(r8, r9, times_pointer_size, FixedArray::kHeaderSize)); |
383 |
// r9 : current element's index
|
384 |
// rbx: current element (smi-tagged)
|
385 |
__ JumpIfNotSmi(rbx, &convert_hole); |
386 |
__ SmiToInteger32(rbx, rbx); |
387 |
__ Cvtlsi2sd(xmm0, rbx); |
388 |
__ movsd(FieldOperand(r14, r9, times_8, FixedDoubleArray::kHeaderSize), |
389 |
xmm0); |
390 |
__ jmp(&entry); |
391 |
__ bind(&convert_hole); |
392 |
|
393 |
if (FLAG_debug_code) {
|
394 |
__ CompareRoot(rbx, Heap::kTheHoleValueRootIndex); |
395 |
__ Assert(equal, kObjectFoundInSmiOnlyArray); |
396 |
} |
397 |
|
398 |
__ movq(FieldOperand(r14, r9, times_8, FixedDoubleArray::kHeaderSize), r15); |
399 |
__ bind(&entry); |
400 |
__ decq(r9); |
401 |
__ j(not_sign, &loop); |
402 |
|
403 |
__ bind(&done); |
404 |
} |
405 |
|
406 |
|
407 |
void ElementsTransitionGenerator::GenerateDoubleToObject(
|
408 |
MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { |
409 |
// ----------- S t a t e -------------
|
410 |
// -- rax : value
|
411 |
// -- rbx : target map
|
412 |
// -- rcx : key
|
413 |
// -- rdx : receiver
|
414 |
// -- rsp[0] : return address
|
415 |
// -----------------------------------
|
416 |
Label loop, entry, convert_hole, gc_required, only_change_map; |
417 |
|
418 |
if (mode == TRACK_ALLOCATION_SITE) {
|
419 |
__ JumpIfJSArrayHasAllocationMemento(rdx, rdi, fail); |
420 |
} |
421 |
|
422 |
// Check for empty arrays, which only require a map transition and no changes
|
423 |
// to the backing store.
|
424 |
__ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset)); |
425 |
__ CompareRoot(r8, Heap::kEmptyFixedArrayRootIndex); |
426 |
__ j(equal, &only_change_map); |
427 |
|
428 |
__ push(rax); |
429 |
|
430 |
__ movq(r8, FieldOperand(rdx, JSObject::kElementsOffset)); |
431 |
__ SmiToInteger32(r9, FieldOperand(r8, FixedDoubleArray::kLengthOffset)); |
432 |
// r8 : source FixedDoubleArray
|
433 |
// r9 : number of elements
|
434 |
__ lea(rdi, Operand(r9, times_pointer_size, FixedArray::kHeaderSize)); |
435 |
__ Allocate(rdi, r11, r14, r15, &gc_required, TAG_OBJECT); |
436 |
// r11: destination FixedArray
|
437 |
__ LoadRoot(rdi, Heap::kFixedArrayMapRootIndex); |
438 |
__ movq(FieldOperand(r11, HeapObject::kMapOffset), rdi); |
439 |
__ Integer32ToSmi(r14, r9); |
440 |
__ movq(FieldOperand(r11, FixedArray::kLengthOffset), r14); |
441 |
|
442 |
// Prepare for conversion loop.
|
443 |
__ movq(rsi, BitCast<int64_t, uint64_t>(kHoleNanInt64), RelocInfo::NONE64); |
444 |
__ LoadRoot(rdi, Heap::kTheHoleValueRootIndex); |
445 |
// rsi: the-hole NaN
|
446 |
// rdi: pointer to the-hole
|
447 |
__ jmp(&entry); |
448 |
|
449 |
// Call into runtime if GC is required.
|
450 |
__ bind(&gc_required); |
451 |
__ pop(rax); |
452 |
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
453 |
__ jmp(fail); |
454 |
|
455 |
// Box doubles into heap numbers.
|
456 |
__ bind(&loop); |
457 |
__ movq(r14, FieldOperand(r8, |
458 |
r9, |
459 |
times_8, |
460 |
FixedDoubleArray::kHeaderSize)); |
461 |
// r9 : current element's index
|
462 |
// r14: current element
|
463 |
__ cmpq(r14, rsi); |
464 |
__ j(equal, &convert_hole); |
465 |
|
466 |
// Non-hole double, copy value into a heap number.
|
467 |
__ AllocateHeapNumber(rax, r15, &gc_required); |
468 |
// rax: new heap number
|
469 |
__ MoveDouble(FieldOperand(rax, HeapNumber::kValueOffset), r14); |
470 |
__ movq(FieldOperand(r11, |
471 |
r9, |
472 |
times_pointer_size, |
473 |
FixedArray::kHeaderSize), |
474 |
rax); |
475 |
__ movq(r15, r9); |
476 |
__ RecordWriteArray(r11, |
477 |
rax, |
478 |
r15, |
479 |
kDontSaveFPRegs, |
480 |
EMIT_REMEMBERED_SET, |
481 |
OMIT_SMI_CHECK); |
482 |
__ jmp(&entry, Label::kNear); |
483 |
|
484 |
// Replace the-hole NaN with the-hole pointer.
|
485 |
__ bind(&convert_hole); |
486 |
__ movq(FieldOperand(r11, |
487 |
r9, |
488 |
times_pointer_size, |
489 |
FixedArray::kHeaderSize), |
490 |
rdi); |
491 |
|
492 |
__ bind(&entry); |
493 |
__ decq(r9); |
494 |
__ j(not_sign, &loop); |
495 |
|
496 |
// Replace receiver's backing store with newly created and filled FixedArray.
|
497 |
__ movq(FieldOperand(rdx, JSObject::kElementsOffset), r11); |
498 |
__ RecordWriteField(rdx, |
499 |
JSObject::kElementsOffset, |
500 |
r11, |
501 |
r15, |
502 |
kDontSaveFPRegs, |
503 |
EMIT_REMEMBERED_SET, |
504 |
OMIT_SMI_CHECK); |
505 |
__ pop(rax); |
506 |
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); |
507 |
|
508 |
__ bind(&only_change_map); |
509 |
// Set transitioned map.
|
510 |
__ movq(FieldOperand(rdx, HeapObject::kMapOffset), rbx); |
511 |
__ RecordWriteField(rdx, |
512 |
HeapObject::kMapOffset, |
513 |
rbx, |
514 |
rdi, |
515 |
kDontSaveFPRegs, |
516 |
OMIT_REMEMBERED_SET, |
517 |
OMIT_SMI_CHECK); |
518 |
} |
519 |
|
520 |
|
521 |
void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
522 |
Register string,
|
523 |
Register index, |
524 |
Register result, |
525 |
Label* call_runtime) { |
526 |
// Fetch the instance type of the receiver into result register.
|
527 |
__ movq(result, FieldOperand(string, HeapObject::kMapOffset));
|
528 |
__ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); |
529 |
|
530 |
// We need special handling for indirect strings.
|
531 |
Label check_sequential; |
532 |
__ testb(result, Immediate(kIsIndirectStringMask)); |
533 |
__ j(zero, &check_sequential, Label::kNear); |
534 |
|
535 |
// Dispatch on the indirect string shape: slice or cons.
|
536 |
Label cons_string; |
537 |
__ testb(result, Immediate(kSlicedNotConsMask)); |
538 |
__ j(zero, &cons_string, Label::kNear); |
539 |
|
540 |
// Handle slices.
|
541 |
Label indirect_string_loaded; |
542 |
__ SmiToInteger32(result, FieldOperand(string, SlicedString::kOffsetOffset));
|
543 |
__ addq(index, result); |
544 |
__ movq(string, FieldOperand(string, SlicedString::kParentOffset)); |
545 |
__ jmp(&indirect_string_loaded, Label::kNear); |
546 |
|
547 |
// Handle cons strings.
|
548 |
// Check whether the right hand side is the empty string (i.e. if
|
549 |
// this is really a flat string in a cons string). If that is not
|
550 |
// the case we would rather go to the runtime system now to flatten
|
551 |
// the string.
|
552 |
__ bind(&cons_string); |
553 |
__ CompareRoot(FieldOperand(string, ConsString::kSecondOffset),
|
554 |
Heap::kempty_stringRootIndex); |
555 |
__ j(not_equal, call_runtime); |
556 |
__ movq(string, FieldOperand(string, ConsString::kFirstOffset)); |
557 |
|
558 |
__ bind(&indirect_string_loaded); |
559 |
__ movq(result, FieldOperand(string, HeapObject::kMapOffset));
|
560 |
__ movzxbl(result, FieldOperand(result, Map::kInstanceTypeOffset)); |
561 |
|
562 |
// Distinguish sequential and external strings. Only these two string
|
563 |
// representations can reach here (slices and flat cons strings have been
|
564 |
// reduced to the underlying sequential or external string).
|
565 |
Label seq_string; |
566 |
__ bind(&check_sequential); |
567 |
STATIC_ASSERT(kSeqStringTag == 0);
|
568 |
__ testb(result, Immediate(kStringRepresentationMask)); |
569 |
__ j(zero, &seq_string, Label::kNear); |
570 |
|
571 |
// Handle external strings.
|
572 |
Label ascii_external, done; |
573 |
if (FLAG_debug_code) {
|
574 |
// Assert that we do not have a cons or slice (indirect strings) here.
|
575 |
// Sequential strings have already been ruled out.
|
576 |
__ testb(result, Immediate(kIsIndirectStringMask)); |
577 |
__ Assert(zero, kExternalStringExpectedButNotFound); |
578 |
} |
579 |
// Rule out short external strings.
|
580 |
STATIC_CHECK(kShortExternalStringTag != 0);
|
581 |
__ testb(result, Immediate(kShortExternalStringTag)); |
582 |
__ j(not_zero, call_runtime); |
583 |
// Check encoding.
|
584 |
STATIC_ASSERT(kTwoByteStringTag == 0);
|
585 |
__ testb(result, Immediate(kStringEncodingMask)); |
586 |
__ movq(result, FieldOperand(string, ExternalString::kResourceDataOffset));
|
587 |
__ j(not_equal, &ascii_external, Label::kNear); |
588 |
// Two-byte string.
|
589 |
__ movzxwl(result, Operand(result, index, times_2, 0));
|
590 |
__ jmp(&done, Label::kNear); |
591 |
__ bind(&ascii_external); |
592 |
// Ascii string.
|
593 |
__ movzxbl(result, Operand(result, index, times_1, 0));
|
594 |
__ jmp(&done, Label::kNear); |
595 |
|
596 |
// Dispatch on the encoding: ASCII or two-byte.
|
597 |
Label ascii; |
598 |
__ bind(&seq_string); |
599 |
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
600 |
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
601 |
__ testb(result, Immediate(kStringEncodingMask)); |
602 |
__ j(not_zero, &ascii, Label::kNear); |
603 |
|
604 |
// Two-byte string.
|
605 |
// Load the two-byte character code into the result register.
|
606 |
STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); |
607 |
__ movzxwl(result, FieldOperand(string,
|
608 |
index, |
609 |
times_2, |
610 |
SeqTwoByteString::kHeaderSize)); |
611 |
__ jmp(&done, Label::kNear); |
612 |
|
613 |
// ASCII string.
|
614 |
// Load the byte into the result register.
|
615 |
__ bind(&ascii); |
616 |
__ movzxbl(result, FieldOperand(string,
|
617 |
index, |
618 |
times_1, |
619 |
SeqOneByteString::kHeaderSize)); |
620 |
__ bind(&done); |
621 |
} |
622 |
|
623 |
|
624 |
void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
|
625 |
XMMRegister input, |
626 |
XMMRegister result, |
627 |
XMMRegister double_scratch, |
628 |
Register temp1, |
629 |
Register temp2) { |
630 |
ASSERT(!input.is(result)); |
631 |
ASSERT(!input.is(double_scratch)); |
632 |
ASSERT(!result.is(double_scratch)); |
633 |
ASSERT(!temp1.is(temp2)); |
634 |
ASSERT(ExternalReference::math_exp_constants(0).address() != NULL); |
635 |
|
636 |
Label done; |
637 |
|
638 |
__ movq(kScratchRegister, ExternalReference::math_exp_constants(0));
|
639 |
__ movsd(double_scratch, Operand(kScratchRegister, 0 * kDoubleSize));
|
640 |
__ xorpd(result, result); |
641 |
__ ucomisd(double_scratch, input); |
642 |
__ j(above_equal, &done); |
643 |
__ ucomisd(input, Operand(kScratchRegister, 1 * kDoubleSize));
|
644 |
__ movsd(result, Operand(kScratchRegister, 2 * kDoubleSize));
|
645 |
__ j(above_equal, &done); |
646 |
__ movsd(double_scratch, Operand(kScratchRegister, 3 * kDoubleSize));
|
647 |
__ movsd(result, Operand(kScratchRegister, 4 * kDoubleSize));
|
648 |
__ mulsd(double_scratch, input); |
649 |
__ addsd(double_scratch, result); |
650 |
__ movq(temp2, double_scratch); |
651 |
__ subsd(double_scratch, result); |
652 |
__ movsd(result, Operand(kScratchRegister, 6 * kDoubleSize));
|
653 |
__ lea(temp1, Operand(temp2, 0x1ff800));
|
654 |
__ and_(temp2, Immediate(0x7ff));
|
655 |
__ shr(temp1, Immediate(11));
|
656 |
__ mulsd(double_scratch, Operand(kScratchRegister, 5 * kDoubleSize));
|
657 |
__ movq(kScratchRegister, ExternalReference::math_exp_log_table()); |
658 |
__ shl(temp1, Immediate(52));
|
659 |
__ or_(temp1, Operand(kScratchRegister, temp2, times_8, 0));
|
660 |
__ movq(kScratchRegister, ExternalReference::math_exp_constants(0));
|
661 |
__ subsd(double_scratch, input); |
662 |
__ movsd(input, double_scratch); |
663 |
__ subsd(result, double_scratch); |
664 |
__ mulsd(input, double_scratch); |
665 |
__ mulsd(result, input); |
666 |
__ movq(input, temp1); |
667 |
__ mulsd(result, Operand(kScratchRegister, 7 * kDoubleSize));
|
668 |
__ subsd(result, double_scratch); |
669 |
__ addsd(result, Operand(kScratchRegister, 8 * kDoubleSize));
|
670 |
__ mulsd(result, input); |
671 |
|
672 |
__ bind(&done); |
673 |
} |
674 |
|
675 |
#undef __
|
676 |
|
677 |
|
678 |
static byte* GetNoCodeAgeSequence(uint32_t* length) {
|
679 |
static bool initialized = false; |
680 |
static byte sequence[kNoCodeAgeSequenceLength];
|
681 |
*length = kNoCodeAgeSequenceLength; |
682 |
if (!initialized) {
|
683 |
// The sequence of instructions that is patched out for aging code is the
|
684 |
// following boilerplate stack-building prologue that is found both in
|
685 |
// FUNCTION and OPTIMIZED_FUNCTION code:
|
686 |
CodePatcher patcher(sequence, kNoCodeAgeSequenceLength); |
687 |
patcher.masm()->push(rbp); |
688 |
patcher.masm()->movq(rbp, rsp); |
689 |
patcher.masm()->push(rsi); |
690 |
patcher.masm()->push(rdi); |
691 |
initialized = true;
|
692 |
} |
693 |
return sequence;
|
694 |
} |
695 |
|
696 |
|
697 |
bool Code::IsYoungSequence(byte* sequence) {
|
698 |
uint32_t young_length; |
699 |
byte* young_sequence = GetNoCodeAgeSequence(&young_length); |
700 |
bool result = (!memcmp(sequence, young_sequence, young_length));
|
701 |
ASSERT(result || *sequence == kCallOpcode); |
702 |
return result;
|
703 |
} |
704 |
|
705 |
|
706 |
void Code::GetCodeAgeAndParity(byte* sequence, Age* age,
|
707 |
MarkingParity* parity) { |
708 |
if (IsYoungSequence(sequence)) {
|
709 |
*age = kNoAgeCodeAge; |
710 |
*parity = NO_MARKING_PARITY; |
711 |
} else {
|
712 |
sequence++; // Skip the kCallOpcode byte
|
713 |
Address target_address = sequence + *reinterpret_cast<int*>(sequence) + |
714 |
Assembler::kCallTargetAddressOffset; |
715 |
Code* stub = GetCodeFromTargetAddress(target_address); |
716 |
GetCodeAgeAndParity(stub, age, parity); |
717 |
} |
718 |
} |
719 |
|
720 |
|
721 |
void Code::PatchPlatformCodeAge(Isolate* isolate,
|
722 |
byte* sequence, |
723 |
Code::Age age, |
724 |
MarkingParity parity) { |
725 |
uint32_t young_length; |
726 |
byte* young_sequence = GetNoCodeAgeSequence(&young_length); |
727 |
if (age == kNoAgeCodeAge) {
|
728 |
CopyBytes(sequence, young_sequence, young_length); |
729 |
CPU::FlushICache(sequence, young_length); |
730 |
} else {
|
731 |
Code* stub = GetCodeAgeStub(isolate, age, parity); |
732 |
CodePatcher patcher(sequence, young_length); |
733 |
patcher.masm()->call(stub->instruction_start()); |
734 |
patcher.masm()->Nop( |
735 |
kNoCodeAgeSequenceLength - Assembler::kShortCallInstructionLength); |
736 |
} |
737 |
} |
738 |
|
739 |
|
740 |
Operand StackArgumentsAccessor::GetArgumentOperand(int index) {
|
741 |
ASSERT(index >= 0);
|
742 |
int receiver = (receiver_mode_ == ARGUMENTS_CONTAIN_RECEIVER) ? 1 : 0; |
743 |
int displacement_to_last_argument = base_reg_.is(rsp) ?
|
744 |
kPCOnStackSize : kFPOnStackSize + kPCOnStackSize; |
745 |
displacement_to_last_argument += extra_displacement_to_last_argument_; |
746 |
if (argument_count_reg_.is(no_reg)) {
|
747 |
// argument[0] is at base_reg_ + displacement_to_last_argument +
|
748 |
// (argument_count_immediate_ + receiver - 1) * kPointerSize.
|
749 |
ASSERT(argument_count_immediate_ + receiver > 0);
|
750 |
return Operand(base_reg_, displacement_to_last_argument +
|
751 |
(argument_count_immediate_ + receiver - 1 - index) * kPointerSize);
|
752 |
} else {
|
753 |
// argument[0] is at base_reg_ + displacement_to_last_argument +
|
754 |
// argument_count_reg_ * times_pointer_size + (receiver - 1) * kPointerSize.
|
755 |
return Operand(base_reg_, argument_count_reg_, times_pointer_size,
|
756 |
displacement_to_last_argument + (receiver - 1 - index) * kPointerSize);
|
757 |
} |
758 |
} |
759 |
|
760 |
|
761 |
} } // namespace v8::internal
|
762 |
|
763 |
#endif // V8_TARGET_ARCH_X64 |