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 / mips / codegen-mips.cc @ f230a1cf
History | View | Annotate | Download (23 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_MIPS
|
31 |
|
32 |
#include "codegen.h" |
33 |
#include "macro-assembler.h" |
34 |
#include "simulator-mips.h" |
35 |
|
36 |
namespace v8 {
|
37 |
namespace internal {
|
38 |
|
39 |
|
40 |
UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) { |
41 |
switch (type) {
|
42 |
case TranscendentalCache::SIN: return &sin; |
43 |
case TranscendentalCache::COS: return &cos; |
44 |
case TranscendentalCache::TAN: return &tan; |
45 |
case TranscendentalCache::LOG: return &log; |
46 |
default: UNIMPLEMENTED();
|
47 |
} |
48 |
return NULL; |
49 |
} |
50 |
|
51 |
|
52 |
#define __ masm.
|
53 |
|
54 |
|
55 |
#if defined(USE_SIMULATOR)
|
56 |
byte* fast_exp_mips_machine_code = NULL;
|
57 |
double fast_exp_simulator(double x) { |
58 |
return Simulator::current(Isolate::Current())->CallFP(
|
59 |
fast_exp_mips_machine_code, x, 0);
|
60 |
} |
61 |
#endif
|
62 |
|
63 |
|
64 |
UnaryMathFunction CreateExpFunction() { |
65 |
if (!FLAG_fast_math) return &exp; |
66 |
size_t actual_size; |
67 |
byte* buffer = static_cast<byte*>(OS::Allocate(1 * KB, &actual_size, true)); |
68 |
if (buffer == NULL) return &exp; |
69 |
ExternalReference::InitializeMathExpData(); |
70 |
|
71 |
MacroAssembler masm(NULL, buffer, static_cast<int>(actual_size)); |
72 |
|
73 |
{ |
74 |
DoubleRegister input = f12; |
75 |
DoubleRegister result = f0; |
76 |
DoubleRegister double_scratch1 = f4; |
77 |
DoubleRegister double_scratch2 = f6; |
78 |
Register temp1 = t0; |
79 |
Register temp2 = t1; |
80 |
Register temp3 = t2; |
81 |
|
82 |
if (!IsMipsSoftFloatABI) {
|
83 |
// Input value is in f12 anyway, nothing to do.
|
84 |
} else {
|
85 |
__ Move(input, a0, a1); |
86 |
} |
87 |
__ Push(temp3, temp2, temp1); |
88 |
MathExpGenerator::EmitMathExp( |
89 |
&masm, input, result, double_scratch1, double_scratch2, |
90 |
temp1, temp2, temp3); |
91 |
__ Pop(temp3, temp2, temp1); |
92 |
if (!IsMipsSoftFloatABI) {
|
93 |
// Result is already in f0, nothing to do.
|
94 |
} else {
|
95 |
__ Move(v0, v1, result); |
96 |
} |
97 |
__ Ret(); |
98 |
} |
99 |
|
100 |
CodeDesc desc; |
101 |
masm.GetCode(&desc); |
102 |
ASSERT(!RelocInfo::RequiresRelocation(desc)); |
103 |
|
104 |
CPU::FlushICache(buffer, actual_size); |
105 |
OS::ProtectCode(buffer, actual_size); |
106 |
|
107 |
#if !defined(USE_SIMULATOR)
|
108 |
return FUNCTION_CAST<UnaryMathFunction>(buffer);
|
109 |
#else
|
110 |
fast_exp_mips_machine_code = buffer; |
111 |
return &fast_exp_simulator;
|
112 |
#endif
|
113 |
} |
114 |
|
115 |
|
116 |
#undef __
|
117 |
|
118 |
|
119 |
UnaryMathFunction CreateSqrtFunction() { |
120 |
return &sqrt;
|
121 |
} |
122 |
|
123 |
|
124 |
// -------------------------------------------------------------------------
|
125 |
// Platform-specific RuntimeCallHelper functions.
|
126 |
|
127 |
void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const { |
128 |
masm->EnterFrame(StackFrame::INTERNAL); |
129 |
ASSERT(!masm->has_frame()); |
130 |
masm->set_has_frame(true);
|
131 |
} |
132 |
|
133 |
|
134 |
void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const { |
135 |
masm->LeaveFrame(StackFrame::INTERNAL); |
136 |
ASSERT(masm->has_frame()); |
137 |
masm->set_has_frame(false);
|
138 |
} |
139 |
|
140 |
|
141 |
// -------------------------------------------------------------------------
|
142 |
// Code generators
|
143 |
|
144 |
#define __ ACCESS_MASM(masm)
|
145 |
|
146 |
void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
|
147 |
MacroAssembler* masm, AllocationSiteMode mode, |
148 |
Label* allocation_memento_found) { |
149 |
// ----------- S t a t e -------------
|
150 |
// -- a0 : value
|
151 |
// -- a1 : key
|
152 |
// -- a2 : receiver
|
153 |
// -- ra : return address
|
154 |
// -- a3 : target map, scratch for subsequent call
|
155 |
// -- t0 : scratch (elements)
|
156 |
// -----------------------------------
|
157 |
if (mode == TRACK_ALLOCATION_SITE) {
|
158 |
ASSERT(allocation_memento_found != NULL);
|
159 |
__ JumpIfJSArrayHasAllocationMemento(a2, t0, allocation_memento_found); |
160 |
} |
161 |
|
162 |
// Set transitioned map.
|
163 |
__ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); |
164 |
__ RecordWriteField(a2, |
165 |
HeapObject::kMapOffset, |
166 |
a3, |
167 |
t5, |
168 |
kRAHasNotBeenSaved, |
169 |
kDontSaveFPRegs, |
170 |
EMIT_REMEMBERED_SET, |
171 |
OMIT_SMI_CHECK); |
172 |
} |
173 |
|
174 |
|
175 |
void ElementsTransitionGenerator::GenerateSmiToDouble(
|
176 |
MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { |
177 |
// ----------- S t a t e -------------
|
178 |
// -- a0 : value
|
179 |
// -- a1 : key
|
180 |
// -- a2 : receiver
|
181 |
// -- ra : return address
|
182 |
// -- a3 : target map, scratch for subsequent call
|
183 |
// -- t0 : scratch (elements)
|
184 |
// -----------------------------------
|
185 |
Label loop, entry, convert_hole, gc_required, only_change_map, done; |
186 |
|
187 |
Register scratch = t6; |
188 |
|
189 |
if (mode == TRACK_ALLOCATION_SITE) {
|
190 |
__ JumpIfJSArrayHasAllocationMemento(a2, t0, fail); |
191 |
} |
192 |
|
193 |
// Check for empty arrays, which only require a map transition and no changes
|
194 |
// to the backing store.
|
195 |
__ lw(t0, FieldMemOperand(a2, JSObject::kElementsOffset)); |
196 |
__ LoadRoot(at, Heap::kEmptyFixedArrayRootIndex); |
197 |
__ Branch(&only_change_map, eq, at, Operand(t0)); |
198 |
|
199 |
__ push(ra); |
200 |
__ lw(t1, FieldMemOperand(t0, FixedArray::kLengthOffset)); |
201 |
// t0: source FixedArray
|
202 |
// t1: number of elements (smi-tagged)
|
203 |
|
204 |
// Allocate new FixedDoubleArray.
|
205 |
__ sll(scratch, t1, 2);
|
206 |
__ Addu(scratch, scratch, FixedDoubleArray::kHeaderSize); |
207 |
__ Allocate(scratch, t2, t3, t5, &gc_required, DOUBLE_ALIGNMENT); |
208 |
// t2: destination FixedDoubleArray, not tagged as heap object
|
209 |
|
210 |
// Set destination FixedDoubleArray's length and map.
|
211 |
__ LoadRoot(t5, Heap::kFixedDoubleArrayMapRootIndex); |
212 |
__ sw(t1, MemOperand(t2, FixedDoubleArray::kLengthOffset)); |
213 |
__ sw(t5, MemOperand(t2, HeapObject::kMapOffset)); |
214 |
// Update receiver's map.
|
215 |
|
216 |
__ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); |
217 |
__ RecordWriteField(a2, |
218 |
HeapObject::kMapOffset, |
219 |
a3, |
220 |
t5, |
221 |
kRAHasBeenSaved, |
222 |
kDontSaveFPRegs, |
223 |
OMIT_REMEMBERED_SET, |
224 |
OMIT_SMI_CHECK); |
225 |
// Replace receiver's backing store with newly created FixedDoubleArray.
|
226 |
__ Addu(a3, t2, Operand(kHeapObjectTag)); |
227 |
__ sw(a3, FieldMemOperand(a2, JSObject::kElementsOffset)); |
228 |
__ RecordWriteField(a2, |
229 |
JSObject::kElementsOffset, |
230 |
a3, |
231 |
t5, |
232 |
kRAHasBeenSaved, |
233 |
kDontSaveFPRegs, |
234 |
EMIT_REMEMBERED_SET, |
235 |
OMIT_SMI_CHECK); |
236 |
|
237 |
|
238 |
// Prepare for conversion loop.
|
239 |
__ Addu(a3, t0, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); |
240 |
__ Addu(t3, t2, Operand(FixedDoubleArray::kHeaderSize)); |
241 |
__ sll(t2, t1, 2);
|
242 |
__ Addu(t2, t2, t3); |
243 |
__ li(t0, Operand(kHoleNanLower32)); |
244 |
__ li(t1, Operand(kHoleNanUpper32)); |
245 |
// t0: kHoleNanLower32
|
246 |
// t1: kHoleNanUpper32
|
247 |
// t2: end of destination FixedDoubleArray, not tagged
|
248 |
// t3: begin of FixedDoubleArray element fields, not tagged
|
249 |
|
250 |
__ Branch(&entry); |
251 |
|
252 |
__ bind(&only_change_map); |
253 |
__ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); |
254 |
__ RecordWriteField(a2, |
255 |
HeapObject::kMapOffset, |
256 |
a3, |
257 |
t5, |
258 |
kRAHasNotBeenSaved, |
259 |
kDontSaveFPRegs, |
260 |
OMIT_REMEMBERED_SET, |
261 |
OMIT_SMI_CHECK); |
262 |
__ Branch(&done); |
263 |
|
264 |
// Call into runtime if GC is required.
|
265 |
__ bind(&gc_required); |
266 |
__ pop(ra); |
267 |
__ Branch(fail); |
268 |
|
269 |
// Convert and copy elements.
|
270 |
__ bind(&loop); |
271 |
__ lw(t5, MemOperand(a3)); |
272 |
__ Addu(a3, a3, kIntSize); |
273 |
// t5: current element
|
274 |
__ UntagAndJumpIfNotSmi(t5, t5, &convert_hole); |
275 |
|
276 |
// Normal smi, convert to double and store.
|
277 |
__ mtc1(t5, f0); |
278 |
__ cvt_d_w(f0, f0); |
279 |
__ sdc1(f0, MemOperand(t3)); |
280 |
__ Addu(t3, t3, kDoubleSize); |
281 |
|
282 |
__ Branch(&entry); |
283 |
|
284 |
// Hole found, store the-hole NaN.
|
285 |
__ bind(&convert_hole); |
286 |
if (FLAG_debug_code) {
|
287 |
// Restore a "smi-untagged" heap object.
|
288 |
__ SmiTag(t5); |
289 |
__ Or(t5, t5, Operand(1));
|
290 |
__ LoadRoot(at, Heap::kTheHoleValueRootIndex); |
291 |
__ Assert(eq, kObjectFoundInSmiOnlyArray, at, Operand(t5)); |
292 |
} |
293 |
__ sw(t0, MemOperand(t3)); // mantissa
|
294 |
__ sw(t1, MemOperand(t3, kIntSize)); // exponent
|
295 |
__ Addu(t3, t3, kDoubleSize); |
296 |
|
297 |
__ bind(&entry); |
298 |
__ Branch(&loop, lt, t3, Operand(t2)); |
299 |
|
300 |
__ pop(ra); |
301 |
__ bind(&done); |
302 |
} |
303 |
|
304 |
|
305 |
void ElementsTransitionGenerator::GenerateDoubleToObject(
|
306 |
MacroAssembler* masm, AllocationSiteMode mode, Label* fail) { |
307 |
// ----------- S t a t e -------------
|
308 |
// -- a0 : value
|
309 |
// -- a1 : key
|
310 |
// -- a2 : receiver
|
311 |
// -- ra : return address
|
312 |
// -- a3 : target map, scratch for subsequent call
|
313 |
// -- t0 : scratch (elements)
|
314 |
// -----------------------------------
|
315 |
Label entry, loop, convert_hole, gc_required, only_change_map; |
316 |
|
317 |
if (mode == TRACK_ALLOCATION_SITE) {
|
318 |
__ JumpIfJSArrayHasAllocationMemento(a2, t0, fail); |
319 |
} |
320 |
|
321 |
// Check for empty arrays, which only require a map transition and no changes
|
322 |
// to the backing store.
|
323 |
__ lw(t0, FieldMemOperand(a2, JSObject::kElementsOffset)); |
324 |
__ LoadRoot(at, Heap::kEmptyFixedArrayRootIndex); |
325 |
__ Branch(&only_change_map, eq, at, Operand(t0)); |
326 |
|
327 |
__ MultiPush(a0.bit() | a1.bit() | a2.bit() | a3.bit() | ra.bit()); |
328 |
|
329 |
__ lw(t1, FieldMemOperand(t0, FixedArray::kLengthOffset)); |
330 |
// t0: source FixedArray
|
331 |
// t1: number of elements (smi-tagged)
|
332 |
|
333 |
// Allocate new FixedArray.
|
334 |
__ sll(a0, t1, 1);
|
335 |
__ Addu(a0, a0, FixedDoubleArray::kHeaderSize); |
336 |
__ Allocate(a0, t2, t3, t5, &gc_required, NO_ALLOCATION_FLAGS); |
337 |
// t2: destination FixedArray, not tagged as heap object
|
338 |
// Set destination FixedDoubleArray's length and map.
|
339 |
__ LoadRoot(t5, Heap::kFixedArrayMapRootIndex); |
340 |
__ sw(t1, MemOperand(t2, FixedDoubleArray::kLengthOffset)); |
341 |
__ sw(t5, MemOperand(t2, HeapObject::kMapOffset)); |
342 |
|
343 |
// Prepare for conversion loop.
|
344 |
__ Addu(t0, t0, Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag + 4));
|
345 |
__ Addu(a3, t2, Operand(FixedArray::kHeaderSize)); |
346 |
__ Addu(t2, t2, Operand(kHeapObjectTag)); |
347 |
__ sll(t1, t1, 1);
|
348 |
__ Addu(t1, a3, t1); |
349 |
__ LoadRoot(t3, Heap::kTheHoleValueRootIndex); |
350 |
__ LoadRoot(t5, Heap::kHeapNumberMapRootIndex); |
351 |
// Using offsetted addresses.
|
352 |
// a3: begin of destination FixedArray element fields, not tagged
|
353 |
// t0: begin of source FixedDoubleArray element fields, not tagged, +4
|
354 |
// t1: end of destination FixedArray, not tagged
|
355 |
// t2: destination FixedArray
|
356 |
// t3: the-hole pointer
|
357 |
// t5: heap number map
|
358 |
__ Branch(&entry); |
359 |
|
360 |
// Call into runtime if GC is required.
|
361 |
__ bind(&gc_required); |
362 |
__ MultiPop(a0.bit() | a1.bit() | a2.bit() | a3.bit() | ra.bit()); |
363 |
|
364 |
__ Branch(fail); |
365 |
|
366 |
__ bind(&loop); |
367 |
__ lw(a1, MemOperand(t0)); |
368 |
__ Addu(t0, t0, kDoubleSize); |
369 |
// a1: current element's upper 32 bit
|
370 |
// t0: address of next element's upper 32 bit
|
371 |
__ Branch(&convert_hole, eq, a1, Operand(kHoleNanUpper32)); |
372 |
|
373 |
// Non-hole double, copy value into a heap number.
|
374 |
__ AllocateHeapNumber(a2, a0, t6, t5, &gc_required); |
375 |
// a2: new heap number
|
376 |
__ lw(a0, MemOperand(t0, -12));
|
377 |
__ sw(a0, FieldMemOperand(a2, HeapNumber::kMantissaOffset)); |
378 |
__ sw(a1, FieldMemOperand(a2, HeapNumber::kExponentOffset)); |
379 |
__ mov(a0, a3); |
380 |
__ sw(a2, MemOperand(a3)); |
381 |
__ Addu(a3, a3, kIntSize); |
382 |
__ RecordWrite(t2, |
383 |
a0, |
384 |
a2, |
385 |
kRAHasBeenSaved, |
386 |
kDontSaveFPRegs, |
387 |
EMIT_REMEMBERED_SET, |
388 |
OMIT_SMI_CHECK); |
389 |
__ Branch(&entry); |
390 |
|
391 |
// Replace the-hole NaN with the-hole pointer.
|
392 |
__ bind(&convert_hole); |
393 |
__ sw(t3, MemOperand(a3)); |
394 |
__ Addu(a3, a3, kIntSize); |
395 |
|
396 |
__ bind(&entry); |
397 |
__ Branch(&loop, lt, a3, Operand(t1)); |
398 |
|
399 |
__ MultiPop(a2.bit() | a3.bit() | a0.bit() | a1.bit()); |
400 |
// Replace receiver's backing store with newly created and filled FixedArray.
|
401 |
__ sw(t2, FieldMemOperand(a2, JSObject::kElementsOffset)); |
402 |
__ RecordWriteField(a2, |
403 |
JSObject::kElementsOffset, |
404 |
t2, |
405 |
t5, |
406 |
kRAHasBeenSaved, |
407 |
kDontSaveFPRegs, |
408 |
EMIT_REMEMBERED_SET, |
409 |
OMIT_SMI_CHECK); |
410 |
__ pop(ra); |
411 |
|
412 |
__ bind(&only_change_map); |
413 |
// Update receiver's map.
|
414 |
__ sw(a3, FieldMemOperand(a2, HeapObject::kMapOffset)); |
415 |
__ RecordWriteField(a2, |
416 |
HeapObject::kMapOffset, |
417 |
a3, |
418 |
t5, |
419 |
kRAHasNotBeenSaved, |
420 |
kDontSaveFPRegs, |
421 |
OMIT_REMEMBERED_SET, |
422 |
OMIT_SMI_CHECK); |
423 |
} |
424 |
|
425 |
|
426 |
void StringCharLoadGenerator::Generate(MacroAssembler* masm,
|
427 |
Register string,
|
428 |
Register index, |
429 |
Register result, |
430 |
Label* call_runtime) { |
431 |
// Fetch the instance type of the receiver into result register.
|
432 |
__ lw(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
433 |
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); |
434 |
|
435 |
// We need special handling for indirect strings.
|
436 |
Label check_sequential; |
437 |
__ And(at, result, Operand(kIsIndirectStringMask)); |
438 |
__ Branch(&check_sequential, eq, at, Operand(zero_reg)); |
439 |
|
440 |
// Dispatch on the indirect string shape: slice or cons.
|
441 |
Label cons_string; |
442 |
__ And(at, result, Operand(kSlicedNotConsMask)); |
443 |
__ Branch(&cons_string, eq, at, Operand(zero_reg)); |
444 |
|
445 |
// Handle slices.
|
446 |
Label indirect_string_loaded; |
447 |
__ lw(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
|
448 |
__ lw(string, FieldMemOperand(string, SlicedString::kParentOffset)); |
449 |
__ sra(at, result, kSmiTagSize); |
450 |
__ Addu(index, index, at); |
451 |
__ jmp(&indirect_string_loaded); |
452 |
|
453 |
// Handle cons strings.
|
454 |
// Check whether the right hand side is the empty string (i.e. if
|
455 |
// this is really a flat string in a cons string). If that is not
|
456 |
// the case we would rather go to the runtime system now to flatten
|
457 |
// the string.
|
458 |
__ bind(&cons_string); |
459 |
__ lw(result, FieldMemOperand(string, ConsString::kSecondOffset));
|
460 |
__ LoadRoot(at, Heap::kempty_stringRootIndex); |
461 |
__ Branch(call_runtime, ne, result, Operand(at)); |
462 |
// Get the first of the two strings and load its instance type.
|
463 |
__ lw(string, FieldMemOperand(string, ConsString::kFirstOffset)); |
464 |
|
465 |
__ bind(&indirect_string_loaded); |
466 |
__ lw(result, FieldMemOperand(string, HeapObject::kMapOffset));
|
467 |
__ lbu(result, FieldMemOperand(result, Map::kInstanceTypeOffset)); |
468 |
|
469 |
// Distinguish sequential and external strings. Only these two string
|
470 |
// representations can reach here (slices and flat cons strings have been
|
471 |
// reduced to the underlying sequential or external string).
|
472 |
Label external_string, check_encoding; |
473 |
__ bind(&check_sequential); |
474 |
STATIC_ASSERT(kSeqStringTag == 0);
|
475 |
__ And(at, result, Operand(kStringRepresentationMask)); |
476 |
__ Branch(&external_string, ne, at, Operand(zero_reg)); |
477 |
|
478 |
// Prepare sequential strings
|
479 |
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize); |
480 |
__ Addu(string,
|
481 |
string,
|
482 |
SeqTwoByteString::kHeaderSize - kHeapObjectTag); |
483 |
__ jmp(&check_encoding); |
484 |
|
485 |
// Handle external strings.
|
486 |
__ bind(&external_string); |
487 |
if (FLAG_debug_code) {
|
488 |
// Assert that we do not have a cons or slice (indirect strings) here.
|
489 |
// Sequential strings have already been ruled out.
|
490 |
__ And(at, result, Operand(kIsIndirectStringMask)); |
491 |
__ Assert(eq, kExternalStringExpectedButNotFound, |
492 |
at, Operand(zero_reg)); |
493 |
} |
494 |
// Rule out short external strings.
|
495 |
STATIC_CHECK(kShortExternalStringTag != 0);
|
496 |
__ And(at, result, Operand(kShortExternalStringMask)); |
497 |
__ Branch(call_runtime, ne, at, Operand(zero_reg)); |
498 |
__ lw(string, FieldMemOperand(string, ExternalString::kResourceDataOffset)); |
499 |
|
500 |
Label ascii, done; |
501 |
__ bind(&check_encoding); |
502 |
STATIC_ASSERT(kTwoByteStringTag == 0);
|
503 |
__ And(at, result, Operand(kStringEncodingMask)); |
504 |
__ Branch(&ascii, ne, at, Operand(zero_reg)); |
505 |
// Two-byte string.
|
506 |
__ sll(at, index, 1);
|
507 |
__ Addu(at, string, at);
|
508 |
__ lhu(result, MemOperand(at)); |
509 |
__ jmp(&done); |
510 |
__ bind(&ascii); |
511 |
// Ascii string.
|
512 |
__ Addu(at, string, index);
|
513 |
__ lbu(result, MemOperand(at)); |
514 |
__ bind(&done); |
515 |
} |
516 |
|
517 |
|
518 |
static MemOperand ExpConstant(int index, Register base) { |
519 |
return MemOperand(base, index * kDoubleSize);
|
520 |
} |
521 |
|
522 |
|
523 |
void MathExpGenerator::EmitMathExp(MacroAssembler* masm,
|
524 |
DoubleRegister input, |
525 |
DoubleRegister result, |
526 |
DoubleRegister double_scratch1, |
527 |
DoubleRegister double_scratch2, |
528 |
Register temp1, |
529 |
Register temp2, |
530 |
Register temp3) { |
531 |
ASSERT(!input.is(result)); |
532 |
ASSERT(!input.is(double_scratch1)); |
533 |
ASSERT(!input.is(double_scratch2)); |
534 |
ASSERT(!result.is(double_scratch1)); |
535 |
ASSERT(!result.is(double_scratch2)); |
536 |
ASSERT(!double_scratch1.is(double_scratch2)); |
537 |
ASSERT(!temp1.is(temp2)); |
538 |
ASSERT(!temp1.is(temp3)); |
539 |
ASSERT(!temp2.is(temp3)); |
540 |
ASSERT(ExternalReference::math_exp_constants(0).address() != NULL); |
541 |
|
542 |
Label zero, infinity, done; |
543 |
|
544 |
__ li(temp3, Operand(ExternalReference::math_exp_constants(0)));
|
545 |
|
546 |
__ ldc1(double_scratch1, ExpConstant(0, temp3));
|
547 |
__ BranchF(&zero, NULL, ge, double_scratch1, input);
|
548 |
|
549 |
__ ldc1(double_scratch2, ExpConstant(1, temp3));
|
550 |
__ BranchF(&infinity, NULL, ge, input, double_scratch2);
|
551 |
|
552 |
__ ldc1(double_scratch1, ExpConstant(3, temp3));
|
553 |
__ ldc1(result, ExpConstant(4, temp3));
|
554 |
__ mul_d(double_scratch1, double_scratch1, input); |
555 |
__ add_d(double_scratch1, double_scratch1, result); |
556 |
__ FmoveLow(temp2, double_scratch1); |
557 |
__ sub_d(double_scratch1, double_scratch1, result); |
558 |
__ ldc1(result, ExpConstant(6, temp3));
|
559 |
__ ldc1(double_scratch2, ExpConstant(5, temp3));
|
560 |
__ mul_d(double_scratch1, double_scratch1, double_scratch2); |
561 |
__ sub_d(double_scratch1, double_scratch1, input); |
562 |
__ sub_d(result, result, double_scratch1); |
563 |
__ mul_d(double_scratch2, double_scratch1, double_scratch1); |
564 |
__ mul_d(result, result, double_scratch2); |
565 |
__ ldc1(double_scratch2, ExpConstant(7, temp3));
|
566 |
__ mul_d(result, result, double_scratch2); |
567 |
__ sub_d(result, result, double_scratch1); |
568 |
// Mov 1 in double_scratch2 as math_exp_constants_array[8] == 1.
|
569 |
ASSERT(*reinterpret_cast<double*> |
570 |
(ExternalReference::math_exp_constants(8).address()) == 1); |
571 |
__ Move(double_scratch2, 1);
|
572 |
__ add_d(result, result, double_scratch2); |
573 |
__ srl(temp1, temp2, 11);
|
574 |
__ Ext(temp2, temp2, 0, 11); |
575 |
__ Addu(temp1, temp1, Operand(0x3ff));
|
576 |
|
577 |
// Must not call ExpConstant() after overwriting temp3!
|
578 |
__ li(temp3, Operand(ExternalReference::math_exp_log_table())); |
579 |
__ sll(at, temp2, 3);
|
580 |
__ Addu(temp3, temp3, Operand(at)); |
581 |
__ lw(temp2, MemOperand(temp3, 0));
|
582 |
__ lw(temp3, MemOperand(temp3, kPointerSize)); |
583 |
// The first word is loaded is the lower number register.
|
584 |
if (temp2.code() < temp3.code()) {
|
585 |
__ sll(at, temp1, 20);
|
586 |
__ Or(temp1, temp3, at); |
587 |
__ Move(double_scratch1, temp2, temp1); |
588 |
} else {
|
589 |
__ sll(at, temp1, 20);
|
590 |
__ Or(temp1, temp2, at); |
591 |
__ Move(double_scratch1, temp3, temp1); |
592 |
} |
593 |
__ mul_d(result, result, double_scratch1); |
594 |
__ Branch(&done); |
595 |
|
596 |
__ bind(&zero); |
597 |
__ Move(result, kDoubleRegZero); |
598 |
__ Branch(&done); |
599 |
|
600 |
__ bind(&infinity); |
601 |
__ ldc1(result, ExpConstant(2, temp3));
|
602 |
|
603 |
__ bind(&done); |
604 |
} |
605 |
|
606 |
|
607 |
// nop(CODE_AGE_MARKER_NOP)
|
608 |
static const uint32_t kCodeAgePatchFirstInstruction = 0x00010180; |
609 |
|
610 |
static byte* GetNoCodeAgeSequence(uint32_t* length) {
|
611 |
// The sequence of instructions that is patched out for aging code is the
|
612 |
// following boilerplate stack-building prologue that is found in FUNCTIONS
|
613 |
static bool initialized = false; |
614 |
static uint32_t sequence[kNoCodeAgeSequenceLength];
|
615 |
byte* byte_sequence = reinterpret_cast<byte*>(sequence);
|
616 |
*length = kNoCodeAgeSequenceLength * Assembler::kInstrSize; |
617 |
if (!initialized) {
|
618 |
CodePatcher patcher(byte_sequence, kNoCodeAgeSequenceLength); |
619 |
patcher.masm()->Push(ra, fp, cp, a1); |
620 |
patcher.masm()->nop(Assembler::CODE_AGE_SEQUENCE_NOP); |
621 |
patcher.masm()->Addu(fp, sp, Operand(2 * kPointerSize));
|
622 |
initialized = true;
|
623 |
} |
624 |
return byte_sequence;
|
625 |
} |
626 |
|
627 |
|
628 |
bool Code::IsYoungSequence(byte* sequence) {
|
629 |
uint32_t young_length; |
630 |
byte* young_sequence = GetNoCodeAgeSequence(&young_length); |
631 |
bool result = !memcmp(sequence, young_sequence, young_length);
|
632 |
ASSERT(result || |
633 |
Memory::uint32_at(sequence) == kCodeAgePatchFirstInstruction); |
634 |
return result;
|
635 |
} |
636 |
|
637 |
|
638 |
void Code::GetCodeAgeAndParity(byte* sequence, Age* age,
|
639 |
MarkingParity* parity) { |
640 |
if (IsYoungSequence(sequence)) {
|
641 |
*age = kNoAgeCodeAge; |
642 |
*parity = NO_MARKING_PARITY; |
643 |
} else {
|
644 |
Address target_address = Memory::Address_at( |
645 |
sequence + Assembler::kInstrSize * (kNoCodeAgeSequenceLength - 1));
|
646 |
Code* stub = GetCodeFromTargetAddress(target_address); |
647 |
GetCodeAgeAndParity(stub, age, parity); |
648 |
} |
649 |
} |
650 |
|
651 |
|
652 |
void Code::PatchPlatformCodeAge(Isolate* isolate,
|
653 |
byte* sequence, |
654 |
Code::Age age, |
655 |
MarkingParity parity) { |
656 |
uint32_t young_length; |
657 |
byte* young_sequence = GetNoCodeAgeSequence(&young_length); |
658 |
if (age == kNoAgeCodeAge) {
|
659 |
CopyBytes(sequence, young_sequence, young_length); |
660 |
CPU::FlushICache(sequence, young_length); |
661 |
} else {
|
662 |
Code* stub = GetCodeAgeStub(isolate, age, parity); |
663 |
CodePatcher patcher(sequence, young_length / Assembler::kInstrSize); |
664 |
// Mark this code sequence for FindPlatformCodeAgeSequence()
|
665 |
patcher.masm()->nop(Assembler::CODE_AGE_MARKER_NOP); |
666 |
// Save the function's original return address
|
667 |
// (it will be clobbered by Call(t9))
|
668 |
patcher.masm()->mov(at, ra); |
669 |
// Load the stub address to t9 and call it
|
670 |
patcher.masm()->li(t9, |
671 |
Operand(reinterpret_cast<uint32_t>(stub->instruction_start())));
|
672 |
patcher.masm()->Call(t9); |
673 |
// Record the stub address in the empty space for GetCodeAgeAndParity()
|
674 |
patcher.masm()->dd(reinterpret_cast<uint32_t>(stub->instruction_start()));
|
675 |
} |
676 |
} |
677 |
|
678 |
|
679 |
#undef __
|
680 |
|
681 |
} } // namespace v8::internal
|
682 |
|
683 |
#endif // V8_TARGET_ARCH_MIPS |