The package rpms/v8-314.git has added or updated architecture specific content in its
spec file (ExclusiveArch/ExcludeArch or %ifarch/%ifnarch) in commit(s):
https://src.fedoraproject.org/cgit/rpms/v8-314.git/commit/?id=7131f4d5b63....
Change:
+%ifarch ppc
Thanks.
Full change:
============
commit 7131f4d5b63c6b794ec1b440d48cc05fb361975d
Author: Mohan Boddu <mboddu(a)bhujji.com>
Date: Tue Mar 26 15:33:29 2019 -0400
Revert "old, broken, effectively useless"
Unretirement for
https://pagure.io/releng/issue/8235
This reverts commit b7a8187978a0d5d458fa498a88de2eb1d269bbd1.
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..031ba26
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/v8-3.14.5.10.tar.bz2
diff --git a/0002_mips.patch b/0002_mips.patch
new file mode 100644
index 0000000..c477717
--- /dev/null
+++ b/0002_mips.patch
@@ -0,0 +1,935 @@
+Description: mips arch support backported to v8 3.14 branch
+Origin:
https://github.com/paul99/v8m-rb/tree/dm-mipsbe-3.14
+Last-Update: 2014-04-09
+Acked-by: Jrmy Lal <kapouer(a)melix.org>
+
+--- a/Makefile
++++ b/Makefile
+@@ -133,7 +133,7 @@
+
+ # Architectures and modes to be compiled. Consider these to be internal
+ # variables, don't override them (use the targets instead).
+-ARCHES = ia32 x64 arm mipsel
++ARCHES = ia32 x64 arm mipsel mips
+ DEFAULT_ARCHES = ia32 x64 arm
+ MODES = release debug
+ ANDROID_ARCHES = android_ia32 android_arm
+@@ -168,10 +168,6 @@
+ $(MAKE) -C "$(OUTDIR)" BUILDTYPE=$(BUILDTYPE) \
+ builddir="$(abspath $(OUTDIR))/$(BUILDTYPE)"
+
+-mips mips.release mips.debug:
+- @echo "V8 does not support big-endian MIPS builds at the moment," \
+- "please use little-endian builds (mipsel)."
+-
+ # Compile targets. MODES and ARCHES are convenience targets.
+ .SECONDEXPANSION:
+ $(MODES): $(addsuffix .$$@,$(DEFAULT_ARCHES))
+--- a/build/common.gypi
++++ b/build/common.gypi
+@@ -176,7 +176,7 @@
+ 'V8_TARGET_ARCH_IA32',
+ ],
+ }], # v8_target_arch=="ia32"
+- ['v8_target_arch=="mipsel"', {
++ ['v8_target_arch=="mipsel" or v8_target_arch=="mips"',
{
+ 'defines': [
+ 'V8_TARGET_ARCH_MIPS',
+ ],
+@@ -187,12 +187,17 @@
+ ['mipscompiler=="yes"', {
+ 'target_conditions': [
+ ['_toolset=="target"', {
+- 'cflags': ['-EL'],
+- 'ldflags': ['-EL'],
+ 'conditions': [
++ ['v8_target_arch=="mipsel"', {
++ 'cflags': ['-EL'],
++ 'ldflags': ['-EL'],
++ }],
++ ['v8_target_arch=="mips"', {
++ 'cflags': ['-EB'],
++ 'ldflags': ['-EB'],
++ }],
+ [ 'v8_use_mips_abi_hardfloat=="true"', {
+ 'cflags': ['-mhard-float'],
+- 'ldflags': ['-mhard-float'],
+ }, {
+ 'cflags': ['-msoft-float'],
+ 'ldflags': ['-msoft-float'],
+@@ -202,7 +207,8 @@
+ }],
+ ['mips_arch_variant=="loongson"', {
+ 'cflags': ['-mips3', '-Wa,-mips3'],
+- }, {
++ }],
++ ['mips_arch_variant=="mips32r1"', {
+ 'cflags': ['-mips32', '-Wa,-mips32'],
+ }],
+ ],
+@@ -290,7 +296,7 @@
+ ['(OS=="linux" or OS=="freebsd" or OS=="openbsd"
or OS=="solaris" \
+ or OS=="netbsd" or OS=="mac" or OS=="android")
and \
+ (v8_target_arch=="arm" or v8_target_arch=="ia32" or \
+- v8_target_arch=="mipsel")', {
++ v8_target_arch=="mipsel" or v8_target_arch=="mips")',
{
+ # Check whether the host compiler and target compiler support the
+ # '-m32' option and set it if so.
+ 'target_conditions': [
+--- a/build/standalone.gypi
++++ b/build/standalone.gypi
+@@ -68,6 +68,7 @@
+ 'conditions': [
+ ['(v8_target_arch=="arm" and host_arch!="arm") or \
+ (v8_target_arch=="mipsel" and host_arch!="mipsel") or \
++ (v8_target_arch=="mips" and host_arch!="mips") or \
+ (v8_target_arch=="x64" and host_arch!="x64") or \
+ (OS=="android")', {
+ 'want_separate_host_toolset': 1,
+--- a/src/conversions-inl.h
++++ b/src/conversions-inl.h
+@@ -75,7 +75,11 @@
+ if (x < k2Pow52) {
+ x += k2Pow52;
+ uint32_t result;
++#ifndef BIG_ENDIAN_FLOATING_POINT
+ Address mantissa_ptr = reinterpret_cast<Address>(&x);
++#else
++ Address mantissa_ptr = reinterpret_cast<Address>(&x) + 4;
++#endif
+ // Copy least significant 32 bits of mantissa.
+ memcpy(&result, mantissa_ptr, sizeof(result));
+ return negative ? ~result + 1 : result;
+--- a/src/globals.h
++++ b/src/globals.h
+@@ -83,7 +83,7 @@
+ #if CAN_USE_UNALIGNED_ACCESSES
+ #define V8_HOST_CAN_READ_UNALIGNED 1
+ #endif
+-#elif defined(__MIPSEL__)
++#elif defined(__MIPSEL__) || defined(__MIPSEB__)
+ #define V8_HOST_ARCH_MIPS 1
+ #define V8_HOST_ARCH_32_BIT 1
+ #else
+@@ -101,13 +101,17 @@
+ #define V8_TARGET_ARCH_IA32 1
+ #elif defined(__ARMEL__)
+ #define V8_TARGET_ARCH_ARM 1
+-#elif defined(__MIPSEL__)
++#elif defined(__MIPSEL__) || defined(__MIPSEB__)
+ #define V8_TARGET_ARCH_MIPS 1
+ #else
+ #error Target architecture was not detected as supported by v8
+ #endif
+ #endif
+
++#if defined(__MIPSEB__)
++#define BIG_ENDIAN_FLOATING_POINT 1
++#endif
++
+ // Check for supported combinations of host and target architectures.
+ #if defined(V8_TARGET_ARCH_IA32) && !defined(V8_HOST_ARCH_IA32)
+ #error Target architecture ia32 is only supported on ia32 host
+--- a/src/mips/assembler-mips.cc
++++ b/src/mips/assembler-mips.cc
+@@ -1631,10 +1631,17 @@
+ void Assembler::ldc1(FPURegister fd, const MemOperand& src) {
+ // Workaround for non-8-byte alignment of HeapNumber, convert 64-bit
+ // load to two 32-bit loads.
++#ifndef BIG_ENDIAN_FLOATING_POINT
+ GenInstrImmediate(LWC1, src.rm(), fd, src.offset_);
+ FPURegister nextfpreg;
+ nextfpreg.setcode(fd.code() + 1);
+ GenInstrImmediate(LWC1, src.rm(), nextfpreg, src.offset_ + 4);
++#else
++ GenInstrImmediate(LWC1, src.rm(), fd, src.offset_ + 4);
++ FPURegister nextfpreg;
++ nextfpreg.setcode(fd.code() + 1);
++ GenInstrImmediate(LWC1, src.rm(), nextfpreg, src.offset_);
++#endif
+ }
+
+
+@@ -1646,10 +1653,17 @@
+ void Assembler::sdc1(FPURegister fd, const MemOperand& src) {
+ // Workaround for non-8-byte alignment of HeapNumber, convert 64-bit
+ // store to two 32-bit stores.
++#ifndef BIG_ENDIAN_FLOATING_POINT
+ GenInstrImmediate(SWC1, src.rm(), fd, src.offset_);
+ FPURegister nextfpreg;
+ nextfpreg.setcode(fd.code() + 1);
+ GenInstrImmediate(SWC1, src.rm(), nextfpreg, src.offset_ + 4);
++#else
++ GenInstrImmediate(SWC1, src.rm(), fd, src.offset_ + 4);
++ FPURegister nextfpreg;
++ nextfpreg.setcode(fd.code() + 1);
++ GenInstrImmediate(SWC1, src.rm(), nextfpreg, src.offset_ );
++#endif
+ }
+
+
+--- a/src/mips/assembler-mips.h
++++ b/src/mips/assembler-mips.h
+@@ -74,6 +74,13 @@
+ static const int kNumRegisters = v8::internal::kNumRegisters;
+ static const int kNumAllocatableRegisters = 14; // v0 through t7.
+ static const int kSizeInBytes = 4;
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ static const int kMantissaOffset = 0;
++ static const int kExponentOffset = 4;
++#else
++ static const int kMantissaOffset = 4;
++ static const int kExponentOffset = 0;
++#endif
+
+ static int ToAllocationIndex(Register reg) {
+ return reg.code() - 2; // zero_reg and 'at' are skipped.
+--- a/src/mips/builtins-mips.cc
++++ b/src/mips/builtins-mips.cc
+@@ -869,9 +869,7 @@
+ ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize);
+ __ LoadRoot(t7, Heap::kUndefinedValueRootIndex);
+ if (count_constructions) {
+- __ lw(a0, FieldMemOperand(a2, Map::kInstanceSizesOffset));
+- __ Ext(a0, a0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte,
+- kBitsPerByte);
++ __ lbu(a0, FieldMemOperand(a2, Map::kPreAllocatedPropertyFieldsOffset));
+ __ sll(t0, a0, kPointerSizeLog2);
+ __ addu(a0, t5, t0);
+ // a0: offset of first field after pre-allocated fields
+@@ -899,14 +897,12 @@
+ __ lbu(a3, FieldMemOperand(a2, Map::kUnusedPropertyFieldsOffset));
+ // The field instance sizes contains both pre-allocated property fields
+ // and in-object properties.
+- __ lw(a0, FieldMemOperand(a2, Map::kInstanceSizesOffset));
+- __ Ext(t6, a0, Map::kPreAllocatedPropertyFieldsByte * kBitsPerByte,
+- kBitsPerByte);
++ __ lbu(t6, FieldMemOperand(a2, Map::kPreAllocatedPropertyFieldsOffset));
+ __ Addu(a3, a3, Operand(t6));
+- __ Ext(t6, a0, Map::kInObjectPropertiesByte * kBitsPerByte,
+- kBitsPerByte);
++ __ lbu(t6, FieldMemOperand(a2, Map::kInObjectPropertiesOffset));
+ __ subu(a3, a3, t6);
+
++
+ // Done if no extra properties are to be allocated.
+ __ Branch(&allocated, eq, a3, Operand(zero_reg));
+ __ Assert(greater_equal, "Property allocation count failed.",
+--- a/src/mips/code-stubs-mips.cc
++++ b/src/mips/code-stubs-mips.cc
+@@ -536,13 +536,8 @@
+
+
+ void ConvertToDoubleStub::Generate(MacroAssembler* masm) {
+-#ifndef BIG_ENDIAN_FLOATING_POINT
+ Register exponent = result1_;
+ Register mantissa = result2_;
+-#else
+- Register exponent = result2_;
+- Register mantissa = result1_;
+-#endif
+ Label not_special;
+ // Convert from Smi to integer.
+ __ sra(source_, source_, kSmiTagSize);
+@@ -679,9 +674,8 @@
+ } else {
+ ASSERT(destination == kCoreRegisters);
+ // Load the double from heap number to dst1 and dst2 in double format.
+- __ lw(dst1, FieldMemOperand(object, HeapNumber::kValueOffset));
+- __ lw(dst2, FieldMemOperand(object,
+- HeapNumber::kValueOffset + kPointerSize));
++ __ lw(dst1, FieldMemOperand(object, HeapNumber::kMantissaOffset));
++ __ lw(dst2, FieldMemOperand(object, HeapNumber::kExponentOffset));
+ }
+ __ Branch(&done);
+
+@@ -1075,6 +1069,11 @@
+ // a0-a3 registers to f12/f14 register pairs.
+ __ Move(f12, a0, a1);
+ __ Move(f14, a2, a3);
++ } else {
++#ifdef BIG_ENDIAN_FLOATING_POINT
++ __ Swap(a0, a1);
++ __ Swap(a2, a3);
++#endif
+ }
+ {
+ AllowExternalCallThatCantCauseGC scope(masm);
+@@ -1088,8 +1087,13 @@
+ __ sdc1(f0, FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
+ } else {
+ // Double returned in registers v0 and v1.
++#ifndef BIG_ENDIAN_FLOATING_POINT
+ __ sw(v1, FieldMemOperand(heap_number_result, HeapNumber::kExponentOffset));
+ __ sw(v0, FieldMemOperand(heap_number_result, HeapNumber::kMantissaOffset));
++#else
++ __ sw(v0, FieldMemOperand(heap_number_result, HeapNumber::kExponentOffset));
++ __ sw(v1, FieldMemOperand(heap_number_result, HeapNumber::kMantissaOffset));
++#endif
+ }
+ // Place heap_number_result in v0 and return to the pushed return address.
+ __ pop(ra);
+@@ -1320,8 +1324,8 @@
+ __ ldc1(f12, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+ } else {
+ // Load lhs to a double in a2, a3.
+- __ lw(a3, FieldMemOperand(lhs, HeapNumber::kValueOffset + 4));
+- __ lw(a2, FieldMemOperand(lhs, HeapNumber::kValueOffset));
++ __ lw(a3, FieldMemOperand(lhs, HeapNumber::kExponentOffset));
++ __ lw(a2, FieldMemOperand(lhs, HeapNumber::kMantissaOffset));
+
+ // Write Smi from rhs to a1 and a0 in double format. t5 is scratch.
+ __ mov(t6, rhs);
+@@ -1366,11 +1370,11 @@
+ __ pop(ra);
+ // Load rhs to a double in a1, a0.
+ if (rhs.is(a0)) {
+- __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
+- __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
++ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kExponentOffset));
++ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kMantissaOffset));
+ } else {
+- __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+- __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
++ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kMantissaOffset));
++ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kExponentOffset));
+ }
+ }
+ // Fall through to both_loaded_as_doubles.
+@@ -1378,7 +1382,6 @@
+
+
+ void EmitNanCheck(MacroAssembler* masm, Condition cc) {
+- bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Lhs and rhs are already loaded to f12 and f14 register pairs.
+@@ -1391,10 +1394,10 @@
+ __ mov(t2, a2); // a2 has LS 32 bits of lhs.
+ __ mov(t3, a3); // a3 has MS 32 bits of lhs.
+ }
+- Register rhs_exponent = exp_first ? t0 : t1;
+- Register lhs_exponent = exp_first ? t2 : t3;
+- Register rhs_mantissa = exp_first ? t1 : t0;
+- Register lhs_mantissa = exp_first ? t3 : t2;
++ Register rhs_exponent = t1;
++ Register lhs_exponent = t3;
++ Register rhs_mantissa = t0;
++ Register lhs_mantissa = t2;
+ Label one_is_nan, neither_is_nan;
+ Label lhs_not_nan_exp_mask_is_loaded;
+
+@@ -1445,7 +1448,6 @@
+ if (cc == eq) {
+ // Doubles are not equal unless they have the same bit pattern.
+ // Exception: 0 and -0.
+- bool exp_first = (HeapNumber::kExponentOffset == HeapNumber::kValueOffset);
+ if (CpuFeatures::IsSupported(FPU)) {
+ CpuFeatures::Scope scope(FPU);
+ // Lhs and rhs are already loaded to f12 and f14 register pairs.
+@@ -1458,10 +1460,10 @@
+ __ mov(t2, a2); // a2 has LS 32 bits of lhs.
+ __ mov(t3, a3); // a3 has MS 32 bits of lhs.
+ }
+- Register rhs_exponent = exp_first ? t0 : t1;
+- Register lhs_exponent = exp_first ? t2 : t3;
+- Register rhs_mantissa = exp_first ? t1 : t0;
+- Register lhs_mantissa = exp_first ? t3 : t2;
++ Register rhs_exponent = t1;
++ Register lhs_exponent = t3;
++ Register rhs_mantissa = t0;
++ Register lhs_mantissa = t2;
+
+ __ xor_(v0, rhs_mantissa, lhs_mantissa);
+ __ Branch(&return_result_not_equal, ne, v0, Operand(zero_reg));
+@@ -1495,6 +1497,11 @@
+ // a0-a3 registers to f12/f14 register pairs.
+ __ Move(f12, a0, a1);
+ __ Move(f14, a2, a3);
++ } else {
++#ifdef BIG_ENDIAN_FLOATING_POINT
++ __ Swap(a0, a1);
++ __ Swap(a2, a3);
++#endif
+ }
+
+ AllowExternalCallThatCantCauseGC scope(masm);
+@@ -1582,14 +1589,14 @@
+ __ ldc1(f12, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+ __ ldc1(f14, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+ } else {
+- __ lw(a2, FieldMemOperand(lhs, HeapNumber::kValueOffset));
+- __ lw(a3, FieldMemOperand(lhs, HeapNumber::kValueOffset + 4));
++ __ lw(a2, FieldMemOperand(lhs, HeapNumber::kMantissaOffset));
++ __ lw(a3, FieldMemOperand(lhs, HeapNumber::kExponentOffset));
+ if (rhs.is(a0)) {
+- __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
+- __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
++ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kExponentOffset));
++ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kMantissaOffset));
+ } else {
+- __ lw(a0, FieldMemOperand(rhs, HeapNumber::kValueOffset));
+- __ lw(a1, FieldMemOperand(rhs, HeapNumber::kValueOffset + 4));
++ __ lw(a0, FieldMemOperand(rhs, HeapNumber::kMantissaOffset));
++ __ lw(a1, FieldMemOperand(rhs, HeapNumber::kExponentOffset));
+ }
+ }
+ __ jmp(both_loaded_as_doubles);
+@@ -5902,14 +5909,18 @@
+ __ Branch(&simple_loop, eq, scratch4, Operand(zero_reg));
+
+ // Loop for src/dst that are not aligned the same way.
+- // This loop uses lwl and lwr instructions. These instructions
+- // depend on the endianness, and the implementation assumes little-endian.
+ {
+ Label loop;
+ __ bind(&loop);
++#if __BYTE_ORDER == __BIG_ENDIAN
++ __ lwl(scratch1, MemOperand(src));
++ __ Addu(src, src, Operand(kReadAlignment));
++ __ lwr(scratch1, MemOperand(src, -1));
++#else
+ __ lwr(scratch1, MemOperand(src));
+ __ Addu(src, src, Operand(kReadAlignment));
+ __ lwl(scratch1, MemOperand(src, -1));
++#endif
+ __ sw(scratch1, MemOperand(dest));
+ __ Addu(dest, dest, Operand(kReadAlignment));
+ __ Subu(scratch2, limit, dest);
+@@ -6616,6 +6627,11 @@
+ // in a little endian mode).
+ __ li(t2, Operand(2));
+ __ AllocateAsciiString(v0, t2, t0, t1, t5, &call_runtime);
++#if __BYTE_ORDER == __BIG_ENDIAN
++ __ sll(t0, a2, 8);
++ __ srl(t1, a2, 8);
++ __ or_(a2, t0, t1);
++#endif
+ __ sh(a2, FieldMemOperand(v0, SeqAsciiString::kHeaderSize));
+ __ IncrementCounter(counters->string_add_native(), 1, a2, a3);
+ __ DropAndRet(2);
+--- a/src/mips/codegen-mips.cc
++++ b/src/mips/codegen-mips.cc
+@@ -210,8 +210,8 @@
+ a1,
+ t7,
+ f0);
+- __ sw(a0, MemOperand(t3)); // mantissa
+- __ sw(a1, MemOperand(t3, kIntSize)); // exponent
++ __ sw(a0, MemOperand(t3, Register::kMantissaOffset)); // mantissa
++ __ sw(a1, MemOperand(t3, Register::kExponentOffset)); // exponent
+ __ Addu(t3, t3, kDoubleSize);
+ }
+ __ Branch(&entry);
+@@ -225,8 +225,8 @@
+ __ LoadRoot(at, Heap::kTheHoleValueRootIndex);
+ __ Assert(eq, "object found in smi-only array", at, Operand(t5));
+ }
+- __ sw(t0, MemOperand(t3)); // mantissa
+- __ sw(t1, MemOperand(t3, kIntSize)); // exponent
++ __ sw(t0, MemOperand(t3, Register::kMantissaOffset)); // mantissa
++ __ sw(t1, MemOperand(t3, Register::kExponentOffset)); // exponent
+ __ Addu(t3, t3, kDoubleSize);
+
+ __ bind(&entry);
+@@ -273,7 +273,7 @@
+ __ sw(t5, MemOperand(t2, HeapObject::kMapOffset));
+
+ // Prepare for conversion loop.
+- __ Addu(t0, t0, Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag + 4));
++ __ Addu(t0, t0, Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag +
Register::kExponentOffset));
+ __ Addu(a3, t2, Operand(FixedArray::kHeaderSize));
+ __ Addu(t2, t2, Operand(kHeapObjectTag));
+ __ sll(t1, t1, 1);
+@@ -282,7 +282,7 @@
+ __ LoadRoot(t5, Heap::kHeapNumberMapRootIndex);
+ // Using offsetted addresses.
+ // a3: begin of destination FixedArray element fields, not tagged
+- // t0: begin of source FixedDoubleArray element fields, not tagged, +4
++ // t0: begin of source FixedDoubleArray element fields, not tagged, points to the
exponent
+ // t1: end of destination FixedArray, not tagged
+ // t2: destination FixedArray
+ // t3: the-hole pointer
+@@ -296,7 +296,7 @@
+ __ Branch(fail);
+
+ __ bind(&loop);
+- __ lw(a1, MemOperand(t0));
++ __ lw(a1, MemOperand(t0, 0)); // exponent
+ __ Addu(t0, t0, kDoubleSize);
+ // a1: current element's upper 32 bit
+ // t0: address of next element's upper 32 bit
+@@ -305,7 +305,8 @@
+ // Non-hole double, copy value into a heap number.
+ __ AllocateHeapNumber(a2, a0, t6, t5, &gc_required);
+ // a2: new heap number
+- __ lw(a0, MemOperand(t0, -12));
++ // Load mantissa of current element, t0 point to exponent of next element.
++ __ lw(a0, MemOperand(t0, (Register::kMantissaOffset - Register::kExponentOffset -
kDoubleSize)));
+ __ sw(a0, FieldMemOperand(a2, HeapNumber::kMantissaOffset));
+ __ sw(a1, FieldMemOperand(a2, HeapNumber::kExponentOffset));
+ __ mov(a0, a3);
+--- a/src/mips/constants-mips.h
++++ b/src/mips/constants-mips.h
+@@ -69,6 +69,15 @@
+ #endif
+
+
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++const uint32_t kHoleNanUpper32Offset = 4;
++const uint32_t kHoleNanLower32Offset = 0;
++#else
++const uint32_t kHoleNanUpper32Offset = 0;
++const uint32_t kHoleNanLower32Offset = 4;
++#endif
++
++
+ // Defines constants and accessor classes to assemble, disassemble and
+ // simulate MIPS32 instructions.
+ //
+--- a/src/mips/lithium-codegen-mips.cc
++++ b/src/mips/lithium-codegen-mips.cc
+@@ -2699,7 +2699,7 @@
+ }
+
+ if (instr->hydrogen()->RequiresHoleCheck()) {
+- __ lw(scratch, MemOperand(elements, sizeof(kHoleNanLower32)));
++ __ lw(scratch, MemOperand(elements, kHoleNanUpper32Offset));
+ DeoptimizeIf(eq, instr->environment(), scratch, Operand(kHoleNanUpper32));
+ }
+
+@@ -4869,15 +4869,14 @@
+ Handle<FixedDoubleArray>::cast(elements);
+ for (int i = 0; i < elements_length; i++) {
+ int64_t value = double_array->get_representation(i);
+- // We only support little endian mode...
+ int32_t value_low = static_cast<int32_t>(value & 0xFFFFFFFF);
+ int32_t value_high = static_cast<int32_t>(value >> 32);
+ int total_offset =
+ elements_offset + FixedDoubleArray::OffsetOfElementAt(i);
+ __ li(a2, Operand(value_low));
+- __ sw(a2, FieldMemOperand(result, total_offset));
++ __ sw(a2, FieldMemOperand(result, total_offset + Register::kMantissaOffset));
+ __ li(a2, Operand(value_high));
+- __ sw(a2, FieldMemOperand(result, total_offset + 4));
++ __ sw(a2, FieldMemOperand(result, total_offset + Register::kExponentOffset));
+ }
+ } else if (elements->IsFixedArray()) {
+ Handle<FixedArray> fast_elements =
Handle<FixedArray>::cast(elements);
+--- a/src/mips/macro-assembler-mips.cc
++++ b/src/mips/macro-assembler-mips.cc
+@@ -3300,6 +3300,7 @@
+
+ // TODO(kalmard) check if this can be optimized to use sw in most cases.
+ // Can't use unaligned access - copy byte by byte.
++#if __BYTE_ORDER == __LITTLE_ENDIAN
+ sb(scratch, MemOperand(dst, 0));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 1));
+@@ -3307,6 +3308,16 @@
+ sb(scratch, MemOperand(dst, 2));
+ srl(scratch, scratch, 8);
+ sb(scratch, MemOperand(dst, 3));
++#else
++ sb(scratch, MemOperand(dst, 3));
++ srl(scratch, scratch, 8);
++ sb(scratch, MemOperand(dst, 2));
++ srl(scratch, scratch, 8);
++ sb(scratch, MemOperand(dst, 1));
++ srl(scratch, scratch, 8);
++ sb(scratch, MemOperand(dst, 0));
++#endif
++
+ Addu(dst, dst, 4);
+
+ Subu(length, length, Operand(kPointerSize));
+@@ -3412,9 +3423,8 @@
+ bind(&have_double_value);
+ sll(scratch1, key_reg, kDoubleSizeLog2 - kSmiTagSize);
+ Addu(scratch1, scratch1, elements_reg);
+- sw(mantissa_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize));
+- uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
+- sw(exponent_reg, FieldMemOperand(scratch1, offset));
++ sw(mantissa_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize +
kHoleNanLower32Offset));
++ sw(exponent_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize +
kHoleNanUpper32Offset));
+ jmp(&done);
+
+ bind(&maybe_nan);
+@@ -3459,8 +3469,8 @@
+ CpuFeatures::Scope scope(FPU);
+ sdc1(f0, MemOperand(scratch1, 0));
+ } else {
+- sw(mantissa_reg, MemOperand(scratch1, 0));
+- sw(exponent_reg, MemOperand(scratch1, Register::kSizeInBytes));
++ sw(mantissa_reg, MemOperand(scratch1, Register::kMantissaOffset));
++ sw(exponent_reg, MemOperand(scratch1, Register::kExponentOffset));
+ }
+ bind(&done);
+ }
+--- a/src/mips/stub-cache-mips.cc
++++ b/src/mips/stub-cache-mips.cc
+@@ -2195,7 +2195,7 @@
+
+ // Start checking for special cases.
+ // Get the argument exponent and clear the sign bit.
+- __ lw(t1, FieldMemOperand(v0, HeapNumber::kValueOffset + kPointerSize));
++ __ lw(t1, FieldMemOperand(v0, HeapNumber::kExponentOffset));
+ __ And(t2, t1, Operand(~HeapNumber::kSignMask));
+ __ srl(t2, t2, HeapNumber::kMantissaBitsInTopWord);
+
+@@ -3768,8 +3768,8 @@
+ __ ldc1(f0, MemOperand(t3, 0));
+ } else {
+ // t3: pointer to the beginning of the double we want to load.
+- __ lw(a2, MemOperand(t3, 0));
+- __ lw(a3, MemOperand(t3, Register::kSizeInBytes));
++ __ lw(a2, MemOperand(t3, Register::kMantissaOffset));
++ __ lw(a3, MemOperand(t3, Register::kExponentOffset));
+ }
+ break;
+ case FAST_ELEMENTS:
+@@ -4132,8 +4132,8 @@
+ CpuFeatures::Scope scope(FPU);
+ __ sdc1(f0, MemOperand(a3, 0));
+ } else {
+- __ sw(t2, MemOperand(a3, 0));
+- __ sw(t3, MemOperand(a3, Register::kSizeInBytes));
++ __ sw(t2, MemOperand(a3, Register::kMantissaOffset));
++ __ sw(t3, MemOperand(a3, Register::kExponentOffset));
+ }
+ break;
+ case FAST_ELEMENTS:
+@@ -4296,8 +4296,8 @@
+ __ sll(t8, key, 2);
+ __ addu(t8, a3, t8);
+ // t8: effective address of destination element.
+- __ sw(t4, MemOperand(t8, 0));
+- __ sw(t3, MemOperand(t8, Register::kSizeInBytes));
++ __ sw(t4, MemOperand(t8, Register::kMantissaOffset));
++ __ sw(t3, MemOperand(t8, Register::kExponentOffset));
+ __ mov(v0, a0);
+ __ Ret();
+ } else {
+@@ -4497,11 +4497,11 @@
+ __ lw(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
+ __ Branch(&miss_force_generic, hs, key_reg, Operand(scratch));
+
+- // Load the upper word of the double in the fixed array and test for NaN.
++ // Load the exponent in the fixed array and test for NaN.
+ __ sll(scratch2, key_reg, kDoubleSizeLog2 - kSmiTagSize);
+ __ Addu(indexed_double_offset, elements_reg, Operand(scratch2));
+- uint32_t upper_32_offset = FixedArray::kHeaderSize + sizeof(kHoleNanLower32);
+- __ lw(scratch, FieldMemOperand(indexed_double_offset, upper_32_offset));
++ __ lw(scratch, FieldMemOperand(indexed_double_offset,
++ FixedArray::kHeaderSize + kHoleNanUpper32Offset));
+ __ Branch(&miss_force_generic, eq, scratch, Operand(kHoleNanUpper32));
+
+ // Non-NaN. Allocate a new heap number and copy the double value into it.
+@@ -4509,12 +4509,12 @@
+ __ AllocateHeapNumber(heap_number_reg, scratch2, scratch3,
+ heap_number_map, &slow_allocate_heapnumber);
+
+- // Don't need to reload the upper 32 bits of the double, it's already in
++ // Don't need to reload the exponent (the upper 32 bits of the double), it's
already in
+ // scratch.
+ __ sw(scratch, FieldMemOperand(heap_number_reg,
+ HeapNumber::kExponentOffset));
+ __ lw(scratch, FieldMemOperand(indexed_double_offset,
+- FixedArray::kHeaderSize));
++ FixedArray::kHeaderSize + kHoleNanLower32Offset));
+ __ sw(scratch, FieldMemOperand(heap_number_reg,
+ HeapNumber::kMantissaOffset));
+
+--- a/src/objects.h
++++ b/src/objects.h
+@@ -1344,8 +1344,13 @@
+ // is a mixture of sign, exponent and mantissa. Our current platforms are all
+ // little endian apart from non-EABI arm which is little endian with big
+ // endian floating point word ordering!
++#ifndef BIG_ENDIAN_FLOATING_POINT
+ static const int kMantissaOffset = kValueOffset;
+ static const int kExponentOffset = kValueOffset + 4;
++#else
++ static const int kMantissaOffset = kValueOffset + 4;
++ static const int kExponentOffset = kValueOffset;
++#endif
+
+ static const int kSize = kValueOffset + kDoubleSize;
+ static const uint32_t kSignMask = 0x80000000u;
+--- a/src/profile-generator.cc
++++ b/src/profile-generator.cc
+@@ -1819,7 +1819,9 @@
+ Address field = obj->address() + offset;
+ ASSERT(!Memory::Object_at(field)->IsFailure());
+ ASSERT(Memory::Object_at(field)->IsHeapObject());
+- *field |= kFailureTag;
++ Object* untagged = *reinterpret_cast<Object**>(field);
++ intptr_t tagged = reinterpret_cast<intptr_t>(untagged) | kFailureTag;
++ *reinterpret_cast<Object**>(field) = reinterpret_cast<Object*>(tagged);
+ }
+
+ private:
+--- a/src/runtime.cc
++++ b/src/runtime.cc
+@@ -8553,8 +8553,15 @@
+ #else
+ typedef uint64_t ObjectPair;
+ static inline ObjectPair MakePair(MaybeObject* x, MaybeObject* y) {
++#if __BYTE_ORDER == __LITTLE_ENDIAN
+ return reinterpret_cast<uint32_t>(x) |
+ (reinterpret_cast<ObjectPair>(y) << 32);
++#elif __BYTE_ORDER == __BIG_ENDIAN
++ return reinterpret_cast<uint32_t>(y) |
++ (reinterpret_cast<ObjectPair>(x) << 32);
++#else
++#error Unknown endianess
++#endif
+ }
+ #endif
+
+--- a/test/cctest/cctest.gyp
++++ b/test/cctest/cctest.gyp
+@@ -118,7 +118,7 @@
+ 'test-disasm-arm.cc'
+ ],
+ }],
+- ['v8_target_arch=="mipsel"', {
++ ['v8_target_arch=="mipsel" or
v8_target_arch=="mips"', {
+ 'sources': [
+ 'test-assembler-mips.cc',
+ 'test-disasm-mips.cc',
+--- a/test/cctest/test-assembler-mips.cc
++++ b/test/cctest/test-assembler-mips.cc
+@@ -537,11 +537,21 @@
+ USE(dummy);
+
+ CHECK_EQ(0x11223344, t.r1);
++#if __BYTE_ORDER == __LITTLE_ENDIAN
+ CHECK_EQ(0x3344, t.r2);
+ CHECK_EQ(0xffffbbcc, t.r3);
+ CHECK_EQ(0x0000bbcc, t.r4);
+ CHECK_EQ(0xffffffcc, t.r5);
+ CHECK_EQ(0x3333bbcc, t.r6);
++#elif __BYTE_ORDER == __BIG_ENDIAN
++ CHECK_EQ(0x1122, t.r2);
++ CHECK_EQ(0xffff99aa, t.r3);
++ CHECK_EQ(0x000099aa, t.r4);
++ CHECK_EQ(0xffffff99, t.r5);
++ CHECK_EQ(0x99aa3333, t.r6);
++#else
++#error Unknown endianess
++#endif
+ }
+
+
+@@ -955,6 +965,7 @@
+ Object* dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0);
+ USE(dummy);
+
++#if __BYTE_ORDER == __LITTLE_ENDIAN
+ CHECK_EQ(0x44bbccdd, t.lwl_0);
+ CHECK_EQ(0x3344ccdd, t.lwl_1);
+ CHECK_EQ(0x223344dd, t.lwl_2);
+@@ -974,6 +985,29 @@
+ CHECK_EQ(0xbbccdd44, t.swr_1);
+ CHECK_EQ(0xccdd3344, t.swr_2);
+ CHECK_EQ(0xdd223344, t.swr_3);
++#elif __BYTE_ORDER == __BIG_ENDIAN
++ CHECK_EQ(0x11223344, t.lwl_0);
++ CHECK_EQ(0x223344dd, t.lwl_1);
++ CHECK_EQ(0x3344ccdd, t.lwl_2);
++ CHECK_EQ(0x44bbccdd, t.lwl_3);
++
++ CHECK_EQ(0xaabbcc11, t.lwr_0);
++ CHECK_EQ(0xaabb1122, t.lwr_1);
++ CHECK_EQ(0xaa112233, t.lwr_2);
++ CHECK_EQ(0x11223344, t.lwr_3);
++
++ CHECK_EQ(0xaabbccdd, t.swl_0);
++ CHECK_EQ(0x11aabbcc, t.swl_1);
++ CHECK_EQ(0x1122aabb, t.swl_2);
++ CHECK_EQ(0x112233aa, t.swl_3);
++
++ CHECK_EQ(0xdd223344, t.swr_0);
++ CHECK_EQ(0xccdd3344, t.swr_1);
++ CHECK_EQ(0xbbccdd44, t.swr_2);
++ CHECK_EQ(0xaabbccdd, t.swr_3);
++#else
++#error Unknown endianess
++#endif
+ }
+
+
+--- a/test/mjsunit/mjsunit.status
++++ b/test/mjsunit/mjsunit.status
+@@ -49,7 +49,7 @@
+ ##############################################################################
+ # These use a built-in that's only present in debug mode. They take
+ # too long to run in debug mode on ARM and MIPS.
+-fuzz-natives-part*: PASS, SKIP if ($mode == release || $arch == arm || $arch ==
android_arm || $arch == mipsel)
++fuzz-natives-part*: PASS, SKIP if ($mode == release || $arch == arm || $arch ==
android_arm || $arch == mipsel || $arch == mips)
+
+ big-object-literal: PASS, SKIP if ($arch == arm || $arch == android_arm)
+
+@@ -57,7 +57,7 @@
+ array-constructor: PASS || TIMEOUT
+
+ # Very slow on ARM and MIPS, contains no architecture dependent code.
+-unicode-case-overoptimization: PASS, TIMEOUT if ($arch == arm || $arch == android_arm ||
$arch == mipsel)
++unicode-case-overoptimization: PASS, TIMEOUT if ($arch == arm || $arch == android_arm ||
$arch == mipsel || $arch == mips)
+
+ ##############################################################################
+ # This test sets the umask on a per-process basis and hence cannot be
+@@ -127,7 +127,7 @@
+ math-floor-of-div-minus-zero: SKIP
+
+ ##############################################################################
+-[ $arch == mipsel ]
++[ $arch == mipsel || $arch == mips ]
+
+ # Slow tests which times out in debug mode.
+ try: PASS, SKIP if $mode == debug
+--- a/test/mozilla/mozilla.status
++++ b/test/mozilla/mozilla.status
+@@ -126,13 +126,13 @@
+ ecma/Date/15.9.2.2-6: PASS || FAIL
+
+ # 1026139: These date tests fail on arm and mips
+-ecma/Date/15.9.5.29-1: PASS || FAIL if ($arch == arm || $arch == mipsel)
+-ecma/Date/15.9.5.34-1: PASS || FAIL if ($arch == arm || $arch == mipsel)
+-ecma/Date/15.9.5.28-1: PASS || FAIL if ($arch == arm || $arch == mipsel)
++ecma/Date/15.9.5.29-1: PASS || FAIL if ($arch == arm || $arch == mipsel || $arch ==
mips)
++ecma/Date/15.9.5.34-1: PASS || FAIL if ($arch == arm || $arch == mipsel || $arch ==
mips)
++ecma/Date/15.9.5.28-1: PASS || FAIL if ($arch == arm || $arch == mipsel || $arch ==
mips)
+
+ # 1050186: Arm/MIPS vm is broken; probably unrelated to dates
+-ecma/Array/15.4.4.5-3: PASS || FAIL if ($arch == arm || $arch == mipsel)
+-ecma/Date/15.9.5.22-2: PASS || FAIL if ($arch == arm || $arch == mipsel)
++ecma/Array/15.4.4.5-3: PASS || FAIL if ($arch == arm || $arch == mipsel || $arch ==
mips)
++ecma/Date/15.9.5.22-2: PASS || FAIL if ($arch == arm || $arch == mipsel || $arch ==
mips)
+
+ # Flaky test that fails due to what appears to be a bug in the test.
+ # Occurs depending on current time
+@@ -854,6 +854,28 @@
+
+ # Times out and print so much output that we need to skip it to not
+ # hang the builder.
++js1_5/extensions/regress-342960: SKIP
++
++# BUG(3251229): Times out when running new crankshaft test script.
++ecma_3/RegExp/regress-311414: SKIP
++ecma/Date/15.9.5.8: SKIP
++ecma/Date/15.9.5.10-2: SKIP
++ecma/Date/15.9.5.11-2: SKIP
++ecma/Date/15.9.5.12-2: SKIP
++js1_5/Array/regress-99120-02: SKIP
++js1_5/extensions/regress-371636: SKIP
++js1_5/Regress/regress-203278-1: SKIP
++js1_5/Regress/regress-404755: SKIP
++js1_5/Regress/regress-451322: SKIP
++
++
++# BUG(1040): Allow this test to timeout.
++js1_5/GC/regress-203278-2: PASS || TIMEOUT
++
++[ $arch == mips ]
++
++# Times out and print so much output that we need to skip it to not
++# hang the builder.
+ js1_5/extensions/regress-342960: SKIP
+
+ # BUG(3251229): Times out when running new crankshaft test script.
+--- a/test/sputnik/sputnik.status
++++ b/test/sputnik/sputnik.status
+@@ -229,3 +229,17 @@
+ S15.1.3.4_A2.3_T1: SKIP
+ S15.1.3.1_A2.5_T1: SKIP
+ S15.1.3.2_A2.5_T1: SKIP
++
++[ $arch == mips ]
++
++# BUG(3251225): Tests that timeout with --nocrankshaft.
++S15.1.3.1_A2.5_T1: SKIP
++S15.1.3.2_A2.5_T1: SKIP
++S15.1.3.1_A2.4_T1: SKIP
++S15.1.3.1_A2.5_T1: SKIP
++S15.1.3.2_A2.4_T1: SKIP
++S15.1.3.2_A2.5_T1: SKIP
++S15.1.3.3_A2.3_T1: SKIP
++S15.1.3.4_A2.3_T1: SKIP
++S15.1.3.1_A2.5_T1: SKIP
++S15.1.3.2_A2.5_T1: SKIP
+--- a/test/test262/test262.status
++++ b/test/test262/test262.status
+@@ -74,7 +74,7 @@
+ S15.1.3.1_A2.5_T1: PASS, SKIP if $mode == debug
+ S15.1.3.2_A2.5_T1: PASS, SKIP if $mode == debug
+
+-[ $arch == arm || $arch == mipsel ]
++[ $arch == arm || $arch == mipsel || $arch == mips ]
+
+ # TODO(mstarzinger): Causes stack overflow on simulators due to eager
+ # compilation of parenthesized function literals. Needs investigation.
+--- a/tools/gyp/v8.gyp
++++ b/tools/gyp/v8.gyp
+@@ -564,7 +564,7 @@
+ '../../src/ia32/stub-cache-ia32.cc',
+ ],
+ }],
+- ['v8_target_arch=="mipsel"', {
++ ['v8_target_arch=="mipsel" or
v8_target_arch=="mips"', {
+ 'sources': [
+ '../../src/mips/assembler-mips.cc',
+ '../../src/mips/assembler-mips.h',
+--- a/tools/run-tests.py
++++ b/tools/run-tests.py
+@@ -65,6 +65,7 @@
+ "arm",
+ "ia32",
+ "mipsel",
++ "mips",
+ "x64"]
+
+
+@@ -268,7 +269,7 @@
+ timeout = options.timeout
+ if timeout == -1:
+ # Simulators are slow, therefore allow a longer default timeout.
+- if arch in ["android", "arm", "mipsel"]:
++ if arch in ["android", "arm", "mipsel",
"mips"]:
+ timeout = 2 * TIMEOUT_DEFAULT;
+ else:
+ timeout = TIMEOUT_DEFAULT;
+--- a/tools/test-wrapper-gypbuild.py
++++ b/tools/test-wrapper-gypbuild.py
+@@ -151,7 +151,7 @@
+ print "Unknown mode %s" % mode
+ return False
+ for arch in options.arch:
+- if not arch in ['ia32', 'x64', 'arm', 'mipsel',
'android_arm',
++ if not arch in ['ia32', 'x64', 'arm', 'mipsel',
'mips', 'android_arm',
+ 'android_ia32']:
+ print "Unknown architecture %s" % arch
+ return False
+--- a/tools/test.py
++++ b/tools/test.py
+@@ -1282,7 +1282,7 @@
+ options.scons_flags.append("arch=" + options.arch)
+ # Simulators are slow, therefore allow a longer default timeout.
+ if options.timeout == -1:
+- if options.arch in ['android', 'arm', 'mipsel']:
++ if options.arch in ['android', 'arm', 'mipsel',
'mips']:
+ options.timeout = 2 * TIMEOUT_DEFAULT;
+ else:
+ options.timeout = TIMEOUT_DEFAULT;
+--- a/tools/testrunner/local/statusfile.py
++++ b/tools/testrunner/local/statusfile.py
+@@ -59,7 +59,7 @@
+ # Support arches, modes to be written as keywords instead of strings.
+ VARIABLES = {ALWAYS: True}
+ for var in ["debug", "release", "android_arm",
"android_ia32", "arm", "ia32",
+- "mipsel", "x64"]:
++ "mipsel", "mips", "x64"]:
+ VARIABLES[var] = var
+
+
diff --git a/0002_mips_r15102_backport.patch b/0002_mips_r15102_backport.patch
new file mode 100644
index 0000000..23014d3
--- /dev/null
+++ b/0002_mips_r15102_backport.patch
@@ -0,0 +1,17 @@
+Description: upstream fix needed by mips arch
+Origin:
https://code.google.com/p/v8/source/detail?r=15102
+
+--- a/test/cctest/test-mark-compact.cc
++++ b/test/cctest/test-mark-compact.cc
+@@ -545,9 +545,9 @@
+ }
+ } else {
+ if (v8::internal::Snapshot::IsEnabled()) {
+- CHECK_LE(delta, 2500 * 1024); // 2400.
++ CHECK_LE(delta, 2942 * 1024); // 2400.
+ } else {
+- CHECK_LE(delta, 2860 * 1024); // 2760.
++ CHECK_LE(delta, 3400 * 1024); // 2760.
+ }
+ }
+ }
diff --git a/0002_mips_r19121_backport.patch b/0002_mips_r19121_backport.patch
new file mode 100644
index 0000000..7d08790
--- /dev/null
+++ b/0002_mips_r19121_backport.patch
@@ -0,0 +1,71 @@
+Description: upstream fix needed by mips arch
+Origin:
https://code.google.com/p/v8/source/detail?r=19121
+
+--- a/src/mips/code-stubs-mips.cc
++++ b/src/mips/code-stubs-mips.cc
+@@ -7808,9 +7808,16 @@
+ const int32_t kReturnAddressDistanceFromFunctionStart =
+ Assembler::kCallTargetAddressOffset + (2 * Assembler::kInstrSize);
+
+- // Save live volatile registers.
+- __ Push(ra, t1, a1);
+- const int32_t kNumSavedRegs = 3;
++ // This should contain all kJSCallerSaved registers.
++ const RegList kSavedRegs =
++ kJSCallerSaved | // Caller saved registers.
++ s5.bit(); // Saved stack pointer.
++
++ // We also save ra, so the count here is one higher than the mask indicates.
++ const int32_t kNumSavedRegs = kNumJSCallerSaved + 2;
++
++ // Save all caller-save registers as this may be called from anywhere.
++ __ MultiPush(kSavedRegs | ra.bit());
+
+ // Compute the function's address for the first argument.
+ __ Subu(a0, ra, Operand(kReturnAddressDistanceFromFunctionStart));
+@@ -7822,32 +7829,36 @@
+ // Align the stack if necessary.
+ int frame_alignment = masm->ActivationFrameAlignment();
+ if (frame_alignment > kPointerSize) {
+- __ mov(t1, sp);
+ ASSERT(IsPowerOf2(frame_alignment));
++ __ mov(s5, sp);
+ __ And(sp, sp, Operand(-frame_alignment));
+ }
+-
++ // Allocate space for arg slots.
++ __ Subu(sp, sp, kCArgsSlotsSize);
+ #if defined(V8_HOST_ARCH_MIPS)
+- __ li(at, Operand(reinterpret_cast<int32_t>(&entry_hook_)));
+- __ lw(at, MemOperand(at));
++ __ li(t9, Operand(reinterpret_cast<int32_t>(&entry_hook_)));
++ __ lw(t9, MemOperand(t9));
+ #else
+ // Under the simulator we need to indirect the entry hook through a
+ // trampoline function at a known address.
+ Address trampoline_address = reinterpret_cast<Address>(
+ reinterpret_cast<intptr_t>(EntryHookTrampoline));
+ ApiFunction dispatcher(trampoline_address);
+- __ li(at, Operand(ExternalReference(&dispatcher,
++ __ li(t9, Operand(ExternalReference(&dispatcher,
+ ExternalReference::BUILTIN_CALL,
+ masm->isolate())));
+ #endif
+- __ Call(at);
+-
++ // Call C function through t9 to conform ABI for PIC.
++ __ Call(t9);
+ // Restore the stack pointer if needed.
+ if (frame_alignment > kPointerSize) {
+- __ mov(sp, t1);
++ __ mov(sp, s5);
++ } else {
++ __ Addu(sp, sp, kCArgsSlotsSize);
+ }
+
+- __ Pop(ra, t1, a1);
++ // Also pop ra to get Ret(0).
++ __ MultiPop(kSavedRegs | ra.bit());
+ __ Ret();
+ }
+
diff --git a/0012_loongson_force_cache_flush.patch
b/0012_loongson_force_cache_flush.patch
new file mode 100644
index 0000000..6211e6c
--- /dev/null
+++ b/0012_loongson_force_cache_flush.patch
@@ -0,0 +1,22 @@
+Description: Forced whole instruction cache flushing on Loongson.
+ Workaround for instruction cache flushing malfunction on Loongson systems
+ that occasionally cause failures under stress test conditions.
+Author: Dusan Milosavljevic <dusan.milosavljevic(a)rt-rk.com>
+Origin:upstream,https://github.com/paul99/v8m-rb/commit/ded6c2c2.patch
+Last-Update: 2012-06-13
+--- a/src/mips/cpu-mips.cc
++++ b/src/mips/cpu-mips.cc
+@@ -72,6 +72,13 @@
+ #else // ANDROID
+ int res;
+ // See
http://www.linux-mips.org/wiki/Cacheflush_Syscall.
++ if (kArchVariant==kLoongson) {
++ // Force flushing of whole instruction cache on Loongson. This is a
++ // workaround for problem when under stress tests cache lines are not
++ // flushed through syscall for some reasons.
++ size_t iCacheSize = 64 * KB;
++ size = iCacheSize + 1;
++ }
+ res = syscall(__NR_cacheflush, start, size, ICACHE);
+ if (res) {
+ V8_Fatal(__FILE__, __LINE__, "Failed to flush the instruction cache");
diff --git a/dead.package b/dead.package
deleted file mode 100644
index 028c167..0000000
--- a/dead.package
+++ /dev/null
@@ -1 +0,0 @@
-old, broken, effectively useless
diff --git a/sources b/sources
new file mode 100644
index 0000000..0ed3ff1
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+d4e3a038ad387fd7b75d40a89e231ef7 v8-3.14.5.10.tar.bz2
diff --git a/v8-3.14.5.10-CVE-2013-2882.patch b/v8-3.14.5.10-CVE-2013-2882.patch
new file mode 100644
index 0000000..b14dbb5
--- /dev/null
+++ b/v8-3.14.5.10-CVE-2013-2882.patch
@@ -0,0 +1,55 @@
+From 18e43f925d5d502b7531f40e4a1becba56089303 Mon Sep 17 00:00:00 2001
+From: "mstarzinger(a)chromium.org" <mstarzinger(a)chromium.org>
+Date: Mon, 15 Jul 2013 11:41:41 +0000
+Subject: [PATCH] Use internal array as API function cache.
+
+R=yangguo(a)chromium.org
+BUG=chromium:260106
+TEST=cctest/test-api/Regress260106
+
+Review URL:
https://codereview.chromium.org/19159003
+
+git-svn-id:
https://v8.googlecode.com/svn/branches/bleeding_edge@15665
ce2b1a6d-e550-0410-aec6-3dcde31c8c00
+---
+ src/apinatives.js | 2 +-
+ test/cctest/test-api.cc | 11 +++++++++++
+ 2 files changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/src/apinatives.js b/src/apinatives.js
+index 79b41dd..adefab6 100644
+--- a/src/apinatives.js
++++ b/src/apinatives.js
+@@ -37,7 +37,7 @@ function CreateDate(time) {
+ }
+
+
+-var kApiFunctionCache = {};
++var kApiFunctionCache = new InternalArray();
+ var functionCache = kApiFunctionCache;
+
+
+diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
+index 728a8f7..bcd28bd 100644
+--- a/test/cctest/test-api.cc
++++ b/test/cctest/test-api.cc
+@@ -17707,6 +17707,17 @@ THREADED_TEST(Regress157124) {
+ }
+
+
++THREADED_TEST(Regress260106) {
++ LocalContext context;
++ v8::HandleScope scope(context->GetIsolate());
++ Local<FunctionTemplate> templ = FunctionTemplate::New(DummyCallHandler);
++ CompileRun("for (var i = 0; i < 128; i++) Object.prototype[i] = 0;");
++ Local<Function> function = templ->GetFunction();
++ CHECK(!function.IsEmpty());
++ CHECK(function->IsFunction());
++}
++
++
+ #ifndef WIN32
+ class ThreadInterruptTest {
+ public:
+--
+1.8.3.1
+
diff --git a/v8-3.14.5.10-CVE-2013-6640.patch b/v8-3.14.5.10-CVE-2013-6640.patch
new file mode 100644
index 0000000..1a2a420
--- /dev/null
+++ b/v8-3.14.5.10-CVE-2013-6640.patch
@@ -0,0 +1,316 @@
+From 520f94a1da96b0f1fd3d0e0c0b82e4d7c1e7d58f Mon Sep 17 00:00:00 2001
+From: "T.C. Hollingsworth" <tchollingsworth(a)gmail.com>
+Date: Fri, 13 Dec 2013 00:45:27 -0700
+Subject: [PATCH] Limit size of dehoistable array indices
+
+backported from upstream r17801
+---
+ src/elements-kind.cc | 30 ++++++++++++++++
+ src/elements-kind.h | 2 ++
+ src/hydrogen-instructions.h | 9 +++++
+ src/hydrogen.cc | 2 +-
+ src/lithium.cc | 30 ----------------
+ src/lithium.h | 3 --
+ test/mjsunit/regress/regress-crbug-319835.js | 51 ++++++++++++++++++++++++++++
+ test/mjsunit/regress/regress-crbug-319860.js | 47 +++++++++++++++++++++++++
+ 8 files changed, 140 insertions(+), 34 deletions(-)
+ create mode 100644 test/mjsunit/regress/regress-crbug-319835.js
+ create mode 100644 test/mjsunit/regress/regress-crbug-319860.js
+
+diff --git a/src/elements-kind.cc b/src/elements-kind.cc
+index 655a23b..c93a602 100644
+--- a/src/elements-kind.cc
++++ b/src/elements-kind.cc
+@@ -35,6 +35,36 @@ namespace v8 {
+ namespace internal {
+
+
++int ElementsKindToShiftSize(ElementsKind elements_kind) {
++ switch (elements_kind) {
++ case EXTERNAL_BYTE_ELEMENTS:
++ case EXTERNAL_PIXEL_ELEMENTS:
++ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
++ return 0;
++ case EXTERNAL_SHORT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
++ return 1;
++ case EXTERNAL_INT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
++ case EXTERNAL_FLOAT_ELEMENTS:
++ return 2;
++ case EXTERNAL_DOUBLE_ELEMENTS:
++ case FAST_DOUBLE_ELEMENTS:
++ case FAST_HOLEY_DOUBLE_ELEMENTS:
++ return 3;
++ case FAST_SMI_ELEMENTS:
++ case FAST_ELEMENTS:
++ case FAST_HOLEY_SMI_ELEMENTS:
++ case FAST_HOLEY_ELEMENTS:
++ case DICTIONARY_ELEMENTS:
++ case NON_STRICT_ARGUMENTS_ELEMENTS:
++ return kPointerSizeLog2;
++ }
++ UNREACHABLE();
++ return 0;
++}
++
++
+ void PrintElementsKind(FILE* out, ElementsKind kind) {
+ ElementsAccessor* accessor = ElementsAccessor::ForKind(kind);
+ PrintF(out, "%s", accessor->name());
+diff --git a/src/elements-kind.h b/src/elements-kind.h
+index 3be7711..c5d9df8 100644
+--- a/src/elements-kind.h
++++ b/src/elements-kind.h
+@@ -77,6 +77,8 @@ const int kElementsKindCount = LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND
+ 1;
+ const int kFastElementsKindCount = LAST_FAST_ELEMENTS_KIND -
+ FIRST_FAST_ELEMENTS_KIND + 1;
+
++int ElementsKindToShiftSize(ElementsKind elements_kind);
++
+ void PrintElementsKind(FILE* out, ElementsKind kind);
+
+ ElementsKind GetInitialFastElementsKind();
+diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h
+index 015212d..a1e5b97 100644
+--- a/src/hydrogen-instructions.h
++++ b/src/hydrogen-instructions.h
+@@ -4240,6 +4240,7 @@ class ArrayInstructionInterface {
+ virtual HValue* GetKey() = 0;
+ virtual void SetKey(HValue* key) = 0;
+ virtual void SetIndexOffset(uint32_t index_offset) = 0;
++ virtual int MaxIndexOffsetBits() = 0;
+ virtual bool IsDehoisted() = 0;
+ virtual void SetDehoisted(bool is_dehoisted) = 0;
+ virtual ~ArrayInstructionInterface() { };
+@@ -4274,6 +4275,7 @@ class HLoadKeyedFastElement
+ void SetIndexOffset(uint32_t index_offset) {
+ bit_field_ = IndexOffsetField::update(bit_field_, index_offset);
+ }
++ int MaxIndexOffsetBits() { return 25; }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return IsDehoistedField::decode(bit_field_); }
+@@ -4343,6 +4345,7 @@ class HLoadKeyedFastDoubleElement
+ HValue* dependency() { return OperandAt(2); }
+ uint32_t index_offset() { return index_offset_; }
+ void SetIndexOffset(uint32_t index_offset) { index_offset_ = index_offset; }
++ int MaxIndexOffsetBits() { return 25; }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return is_dehoisted_; }
+@@ -4420,6 +4423,7 @@ class HLoadKeyedSpecializedArrayElement
+ ElementsKind elements_kind() const { return elements_kind_; }
+ uint32_t index_offset() { return index_offset_; }
+ void SetIndexOffset(uint32_t index_offset) { index_offset_ = index_offset; }
++ int MaxIndexOffsetBits() { return 25; }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return is_dehoisted_; }
+@@ -4595,6 +4599,7 @@ class HStoreKeyedFastElement
+ }
+ uint32_t index_offset() { return index_offset_; }
+ void SetIndexOffset(uint32_t index_offset) { index_offset_ = index_offset; }
++ int MaxIndexOffsetBits() { return 25; }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return is_dehoisted_; }
+@@ -4648,6 +4653,7 @@ class HStoreKeyedFastDoubleElement
+ HValue* value() { return OperandAt(2); }
+ uint32_t index_offset() { return index_offset_; }
+ void SetIndexOffset(uint32_t index_offset) { index_offset_ = index_offset; }
++ int MaxIndexOffsetBits() { return 25; }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return is_dehoisted_; }
+@@ -4706,6 +4712,9 @@ class HStoreKeyedSpecializedArrayElement
+ ElementsKind elements_kind() const { return elements_kind_; }
+ uint32_t index_offset() { return index_offset_; }
+ void SetIndexOffset(uint32_t index_offset) { index_offset_ = index_offset; }
++ int MaxIndexOffsetBits() {
++ return 31 - ElementsKindToShiftSize(elements_kind_);
++ }
+ HValue* GetKey() { return key(); }
+ void SetKey(HValue* key) { SetOperandAt(1, key); }
+ bool IsDehoisted() { return is_dehoisted_; }
+diff --git a/src/hydrogen.cc b/src/hydrogen.cc
+index 8393e51..e3f79ee 100644
+--- a/src/hydrogen.cc
++++ b/src/hydrogen.cc
+@@ -3737,7 +3737,7 @@ static void DehoistArrayIndex(ArrayInstructionInterface*
array_operation) {
+ int32_t value = constant->Integer32Value() * sign;
+ // We limit offset values to 30 bits because we want to avoid the risk of
+ // overflows when the offset is added to the object header size.
+- if (value >= 1 << 30 || value < 0) return;
++ if (value >= 1 << array_operation->MaxIndexOffsetBits() || value < 0)
return;
+ array_operation->SetKey(subexpression);
+ if (index->HasNoUses()) {
+ index->DeleteAndReplaceWith(NULL);
+diff --git a/src/lithium.cc b/src/lithium.cc
+index eb2198d..1232bf1 100644
+--- a/src/lithium.cc
++++ b/src/lithium.cc
+@@ -227,36 +227,6 @@ void LPointerMap::PrintTo(StringStream* stream) {
+ }
+
+
+-int ElementsKindToShiftSize(ElementsKind elements_kind) {
+- switch (elements_kind) {
+- case EXTERNAL_BYTE_ELEMENTS:
+- case EXTERNAL_PIXEL_ELEMENTS:
+- case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
+- return 0;
+- case EXTERNAL_SHORT_ELEMENTS:
+- case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
+- return 1;
+- case EXTERNAL_INT_ELEMENTS:
+- case EXTERNAL_UNSIGNED_INT_ELEMENTS:
+- case EXTERNAL_FLOAT_ELEMENTS:
+- return 2;
+- case EXTERNAL_DOUBLE_ELEMENTS:
+- case FAST_DOUBLE_ELEMENTS:
+- case FAST_HOLEY_DOUBLE_ELEMENTS:
+- return 3;
+- case FAST_SMI_ELEMENTS:
+- case FAST_ELEMENTS:
+- case FAST_HOLEY_SMI_ELEMENTS:
+- case FAST_HOLEY_ELEMENTS:
+- case DICTIONARY_ELEMENTS:
+- case NON_STRICT_ARGUMENTS_ELEMENTS:
+- return kPointerSizeLog2;
+- }
+- UNREACHABLE();
+- return 0;
+-}
+-
+-
+ LLabel* LChunk::GetLabel(int block_id) const {
+ HBasicBlock* block = graph_->blocks()->at(block_id);
+ int first_instruction = block->first_instruction_index();
+diff --git a/src/lithium.h b/src/lithium.h
+index 089926e..a29d9d0 100644
+--- a/src/lithium.h
++++ b/src/lithium.h
+@@ -704,9 +704,6 @@ class LChunk: public ZoneObject {
+ };
+
+
+-int ElementsKindToShiftSize(ElementsKind elements_kind);
+-
+-
+ } } // namespace v8::internal
+
+ #endif // V8_LITHIUM_H_
+diff --git a/test/mjsunit/regress/regress-crbug-319835.js
b/test/mjsunit/regress/regress-crbug-319835.js
+new file mode 100644
+index 0000000..48f871f
+--- /dev/null
++++ b/test/mjsunit/regress/regress-crbug-319835.js
+@@ -0,0 +1,51 @@
++// Copyright 2013 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// Flags: --allow-natives-syntax
++
++try {} catch(e) {} // No need to optimize the top level.
++
++var size = 0x20000;
++var a = new Float64Array(size);
++var training = new Float64Array(10);
++function store(a, index) {
++ var offset = 0x20000000;
++ for (var i = 0; i < 1; i++) {
++ a[index + offset] = 0xcc;
++ }
++}
++
++store(training, -0x20000000);
++store(training, -0x20000000 + 1);
++store(training, -0x20000000);
++store(training, -0x20000000 + 1);
++%OptimizeFunctionOnNextCall(store);
++
++// Segfault maybe?
++for (var i = -0x20000000; i < -0x20000000 + size; i++) {
++ store(a, i);
++}
+diff --git a/test/mjsunit/regress/regress-crbug-319860.js
b/test/mjsunit/regress/regress-crbug-319860.js
+new file mode 100644
+index 0000000..b81fb85
+--- /dev/null
++++ b/test/mjsunit/regress/regress-crbug-319860.js
+@@ -0,0 +1,47 @@
++// Copyright 2013 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// Flags: --allow-natives-syntax
++
++function read(a, index) {
++ var offset = 0x2000000;
++ var result;
++ for (var i = 0; i < 1; i++) {
++ result = a[index + offset];
++ }
++ return result;
++}
++
++var a = new Int8Array(0x2000001);
++read(a, 0);
++read(a, 0);
++%OptimizeFunctionOnNextCall(read);
++
++// Segfault maybe?
++for (var i = 0; i > -1000000; --i) {
++ read(a, i);
++}
+--
+1.8.4.2
+
diff --git a/v8-3.14.5.10-CVE-2013-6650.patch b/v8-3.14.5.10-CVE-2013-6650.patch
new file mode 100644
index 0000000..d44811f
--- /dev/null
+++ b/v8-3.14.5.10-CVE-2013-6650.patch
@@ -0,0 +1,80 @@
+From 3928813f014d3cdaed83fefc3a454078272f114b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Hr=C4=8Dka?= <thrcka(a)redhat.com>
+Date: Tue, 18 Feb 2014 00:23:04 +0100
+Subject: [PATCH] Backport Fix for CVE-2013-6650 Original patch
+
https://code.google.com/p/v8/source/detail?r=18483
+
+Resolve: rhbz#1059070
+---
+ src/store-buffer.cc | 2 +-
+ test/mjsunit/regress/regress-331444.js | 45 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 46 insertions(+), 1 deletion(-)
+ create mode 100644 test/mjsunit/regress/regress-331444.js
+
+diff --git a/src/store-buffer.cc b/src/store-buffer.cc
+index 66488ae..b9055f8 100644
+--- a/src/store-buffer.cc
++++ b/src/store-buffer.cc
+@@ -242,7 +242,7 @@ void StoreBuffer::ExemptPopularPages(int prime_sample_step, int
threshold) {
+ containing_chunk = MemoryChunk::FromAnyPointerAddress(addr);
+ }
+ int old_counter = containing_chunk->store_buffer_counter();
+- if (old_counter == threshold) {
++ if (old_counter >= threshold) {
+ containing_chunk->set_scan_on_scavenge(true);
+ created_new_scan_on_scavenge_pages = true;
+ }
+diff --git a/test/mjsunit/regress/regress-331444.js
b/test/mjsunit/regress/regress-331444.js
+new file mode 100644
+index 0000000..3df0a08
+--- /dev/null
++++ b/test/mjsunit/regress/regress-331444.js
+@@ -0,0 +1,45 @@
++// Copyright 2014 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// Flags: --expose-gc
++
++
++function boom() {
++ var args = [];
++ for (var i = 0; i < 125000; i++)
++ args.push(i);
++ return Array.apply(Array, args);
++}
++var array = boom();
++function fib(n) {
++ var f0 = 0, f1 = 1;
++ for (; n > 0; n = n - 1) {
++ f0 + f1;
++ f0 = array;
++ }
++}
++fib(12);
+--
+1.8.3.1
+
diff --git a/v8-3.14.5.10-CVE-2013-6668-segfault.patch
b/v8-3.14.5.10-CVE-2013-6668-segfault.patch
new file mode 100644
index 0000000..8e3d600
--- /dev/null
+++ b/v8-3.14.5.10-CVE-2013-6668-segfault.patch
@@ -0,0 +1,60 @@
+From 3122e0eae64c5ab494b29d0a9cadef902d93f1f9 Mon Sep 17 00:00:00 2001
+From: Fedor Indutny <fedor(a)indutny.com>
+Date: Fri, 22 Aug 2014 03:59:35 +0400
+Subject: [PATCH] deps: fix up v8 after fd80a3
+
+fd80a31e0697d6317ce8c2d289575399f4e06d21 has introduced a segfault
+during redundant boundary check elimination (#8208).
+
+The problem consists of two parts:
+
+ 1. Abscense of instruction iterator in
+ `EliminateRedundantBoundsChecks`. It was present in recent v8, but
+ wasn't considered important at the time of backport. However, since
+ the function is changing instructions order in block, it is
+ important to not rely at `i->next()` at the end of the loop.
+ 2. Too strict ASSERT in `MoveIndexIfNecessary`. It is essentially a
+ backport of a45c96ab from v8's upstream. See
+
https://github.com/v8/v8/commit/a45c96ab for details.
+
+fix #8208
+---
+ src/hydrogen.cc | 11 ++++++++---
+ 1 file changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/src/hydrogen.cc b/src/hydrogen.cc
+index 50d8e49..18a6b60 100644
+--- a/src/hydrogen.cc
++++ b/src/hydrogen.cc
+@@ -3546,7 +3546,11 @@ class BoundsCheckBbData: public ZoneObject {
+ void MoveIndexIfNecessary(HValue* index_raw,
+ HBoundsCheck* insert_before,
+ HInstruction* end_of_scan_range) {
+- ASSERT(index_raw->IsAdd() || index_raw->IsSub());
++ if (!index_raw->IsAdd() && !index_raw->IsSub()) {
++ // index_raw can be HAdd(index_base, offset), HSub(index_base, offset),
++ // or index_base directly. In the latter case, no need to move anything.
++ return;
++ }
+ HBinaryOperation* index =
+ HArithmeticBinaryOperation::cast(index_raw);
+ HValue* left_input = index->left();
+@@ -3581,7 +3585,6 @@ class BoundsCheckBbData: public ZoneObject {
+ HBoundsCheck* tighter_check) {
+ ASSERT(original_check->length() == tighter_check->length());
+ MoveIndexIfNecessary(tighter_check->index(), original_check, tighter_check);
+- original_check->ReplaceAllUsesWith(original_check->index());
+ original_check->SetOperandAt(0, tighter_check->index());
+ }
+ };
+@@ -3624,7 +3627,9 @@ void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb,
+ BoundsCheckTable* table) {
+ BoundsCheckBbData* bb_data_list = NULL;
+
+- for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
++ HInstruction* next;
++ for (HInstruction* i = bb->first(); i != NULL; i = next) {
++ next = i->next();
+ if (!i->IsBoundsCheck()) continue;
+
+ HBoundsCheck* check = HBoundsCheck::cast(i);
diff --git a/v8-3.14.5.10-CVE-2013-6668.patch b/v8-3.14.5.10-CVE-2013-6668.patch
new file mode 100644
index 0000000..fe5cb6d
--- /dev/null
+++ b/v8-3.14.5.10-CVE-2013-6668.patch
@@ -0,0 +1,186 @@
+From fd80a31e0697d6317ce8c2d289575399f4e06d21 Mon Sep 17 00:00:00 2001
+From: Fedor Indutny <fedor(a)indutny.com>
+Date: Thu, 14 Aug 2014 19:29:28 +0400
+Subject: [PATCH] deps: backport 5f836c from v8 upstream
+
+Original commit message:
+
+ Fix Hydrogen bounds check elimination
+
+ When combining bounds checks, they must all be moved before the first load/store
+ that they are guarding.
+
+ BUG=chromium:344186
+ LOG=y
+ R=svenpanne(a)chromium.org
+
+ Review URL:
https://codereview.chromium.org/172093002
+
+ git-svn-id:
https://v8.googlecode.com/svn/branches/bleeding_edge@19475
ce2b1a6d-e550-0410-aec6-3dcde31c8c00
+
+fix #8070
+---
+ src/hydrogen.cc | 106 +++++++++++++++++++++++-------------------------
+ 1 file changed, 50 insertions(+), 56 deletions(-)
+
+diff --git a/src/hydrogen.cc b/src/hydrogen.cc
+index e3f79ee..50d8e49 100644
+--- a/src/hydrogen.cc
++++ b/src/hydrogen.cc
+@@ -3487,13 +3487,7 @@ class BoundsCheckBbData: public ZoneObject {
+ keep_new_check = true;
+ upper_check_ = new_check;
+ } else {
+- BuildOffsetAdd(upper_check_,
+- &added_upper_index_,
+- &added_upper_offset_,
+- Key()->IndexBase(),
+- new_check->index()->representation(),
+- new_offset);
+- upper_check_->SetOperandAt(0, added_upper_index_);
++ TightenCheck(upper_check_, new_check);
+ }
+ } else if (new_offset < lower_offset_) {
+ lower_offset_ = new_offset;
+@@ -3501,28 +3495,27 @@ class BoundsCheckBbData: public ZoneObject {
+ keep_new_check = true;
+ lower_check_ = new_check;
+ } else {
+- BuildOffsetAdd(lower_check_,
+- &added_lower_index_,
+- &added_lower_offset_,
+- Key()->IndexBase(),
+- new_check->index()->representation(),
+- new_offset);
+- lower_check_->SetOperandAt(0, added_lower_index_);
++ TightenCheck(lower_check_, new_check);
+ }
+ } else {
+- ASSERT(false);
++ // Should never have called CoverCheck() in this case.
++ UNREACHABLE();
+ }
+
+ if (!keep_new_check) {
+ new_check->DeleteAndReplaceWith(NULL);
++ } else {
++ HBoundsCheck* first_check = new_check == lower_check_ ? upper_check_
++ : lower_check_;
++ // The length is guaranteed to be live at first_check.
++ ASSERT(new_check->length() == first_check->length());
++ HInstruction* old_position = new_check->next();
++ new_check->Unlink();
++ new_check->InsertAfter(first_check);
++ MoveIndexIfNecessary(new_check->index(), new_check, old_position);
+ }
+ }
+
+- void RemoveZeroOperations() {
+- RemoveZeroAdd(&added_lower_index_, &added_lower_offset_);
+- RemoveZeroAdd(&added_upper_index_, &added_upper_offset_);
+- }
+-
+ BoundsCheckBbData(BoundsCheckKey* key,
+ int32_t lower_offset,
+ int32_t upper_offset,
+@@ -3537,10 +3530,6 @@ class BoundsCheckBbData: public ZoneObject {
+ basic_block_(bb),
+ lower_check_(lower_check),
+ upper_check_(upper_check),
+- added_lower_index_(NULL),
+- added_lower_offset_(NULL),
+- added_upper_index_(NULL),
+- added_upper_offset_(NULL),
+ next_in_bb_(next_in_bb),
+ father_in_dt_(father_in_dt) { }
+
+@@ -3551,44 +3540,50 @@ class BoundsCheckBbData: public ZoneObject {
+ HBasicBlock* basic_block_;
+ HBoundsCheck* lower_check_;
+ HBoundsCheck* upper_check_;
+- HAdd* added_lower_index_;
+- HConstant* added_lower_offset_;
+- HAdd* added_upper_index_;
+- HConstant* added_upper_offset_;
+ BoundsCheckBbData* next_in_bb_;
+ BoundsCheckBbData* father_in_dt_;
+
+- void BuildOffsetAdd(HBoundsCheck* check,
+- HAdd** add,
+- HConstant** constant,
+- HValue* original_value,
+- Representation representation,
+- int32_t new_offset) {
+- HConstant* new_constant = new(BasicBlock()->zone())
+- HConstant(new_offset, Representation::Integer32());
+- if (*add == NULL) {
+- new_constant->InsertBefore(check);
+- // Because of the bounds checks elimination algorithm, the index is always
+- // an HAdd or an HSub here, so we can safely cast to an HBinaryOperation.
+- HValue* context = HBinaryOperation::cast(check->index())->context();
+- *add = new(BasicBlock()->zone()) HAdd(context,
+- original_value,
+- new_constant);
+- (*add)->AssumeRepresentation(representation);
+- (*add)->InsertBefore(check);
+- } else {
+- new_constant->InsertBefore(*add);
+- (*constant)->DeleteAndReplaceWith(new_constant);
++ void MoveIndexIfNecessary(HValue* index_raw,
++ HBoundsCheck* insert_before,
++ HInstruction* end_of_scan_range) {
++ ASSERT(index_raw->IsAdd() || index_raw->IsSub());
++ HBinaryOperation* index =
++ HArithmeticBinaryOperation::cast(index_raw);
++ HValue* left_input = index->left();
++ HValue* right_input = index->right();
++ bool must_move_index = false;
++ bool must_move_left_input = false;
++ bool must_move_right_input = false;
++ for (HInstruction* cursor = end_of_scan_range; cursor != insert_before;) {
++ if (cursor == left_input) must_move_left_input = true;
++ if (cursor == right_input) must_move_right_input = true;
++ if (cursor == index) must_move_index = true;
++ if (cursor->previous() == NULL) {
++ cursor = cursor->block()->dominator()->end();
++ } else {
++ cursor = cursor->previous();
++ }
+ }
+- *constant = new_constant;
+- }
+
+- void RemoveZeroAdd(HAdd** add, HConstant** constant) {
+- if (*add != NULL && (*constant)->Integer32Value() == 0) {
+- (*add)->DeleteAndReplaceWith((*add)->left());
+- (*constant)->DeleteAndReplaceWith(NULL);
++ // The BCE algorithm only selects mergeable bounds checks that share
++ // the same "index_base", so we'll only ever have to move constants.
++ if (must_move_left_input) {
++ HConstant::cast(left_input)->Unlink();
++ HConstant::cast(left_input)->InsertBefore(index);
++ }
++ if (must_move_right_input) {
++ HConstant::cast(right_input)->Unlink();
++ HConstant::cast(right_input)->InsertBefore(index);
+ }
+ }
++
++ void TightenCheck(HBoundsCheck* original_check,
++ HBoundsCheck* tighter_check) {
++ ASSERT(original_check->length() == tighter_check->length());
++ MoveIndexIfNecessary(tighter_check->index(), original_check, tighter_check);
++ original_check->ReplaceAllUsesWith(original_check->index());
++ original_check->SetOperandAt(0, tighter_check->index());
++ }
+ };
+
+
+@@ -3683,7 +3678,6 @@ void HGraph::EliminateRedundantBoundsChecks(HBasicBlock* bb,
+ for (BoundsCheckBbData* data = bb_data_list;
+ data != NULL;
+ data = data->NextInBasicBlock()) {
+- data->RemoveZeroOperations();
+ if (data->FatherInDominatorTree()) {
+ table->Insert(data->Key(), data->FatherInDominatorTree(), zone());
+ } else {
diff --git a/v8-3.14.5.10-CVE-2014-1704-1.patch b/v8-3.14.5.10-CVE-2014-1704-1.patch
new file mode 100644
index 0000000..2df5230
--- /dev/null
+++ b/v8-3.14.5.10-CVE-2014-1704-1.patch
@@ -0,0 +1,77 @@
+From bf973073d98660edf35e01e6984029e46eb85368 Mon Sep 17 00:00:00 2001
+From: "dslomov(a)chromium.org"
+ <dslomov@chromium.org(a)ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
+Date: Mon, 13 Jan 2014 13:00:09 +0000
+Subject: [PATCH] Use unsigned integer arithmetic in Zone::NewExpand.
+
+ BUG=328202
+ R=jkummerow(a)chromium.org
+ LOG=N
+
+ Review URL:
https://codereview.chromium.org/108783005
+
+ git-svn-id:
https://v8.googlecode.com/svn/branches/bleeding_edge@18564
ce2b1a6d-e550-0410-aec6-3dcde31c8c00
+---
+ src/zone.cc | 29 +++++++++++++++++++----------
+ 1 file changed, 19 insertions(+), 10 deletions(-)
+
+diff --git a/src/zone.cc b/src/zone.cc
+index 51b8113..c12978f 100644
+--- a/src/zone.cc
++++ b/src/zone.cc
+@@ -175,25 +175,31 @@ Address Zone::NewExpand(int size) {
+ // except that we employ a maximum segment size when we delete. This
+ // is to avoid excessive malloc() and free() overhead.
+ Segment* head = segment_head_;
+- int old_size = (head == NULL) ? 0 : head->size();
+- static const int kSegmentOverhead = sizeof(Segment) + kAlignment;
+- int new_size_no_overhead = size + (old_size << 1);
+- int new_size = kSegmentOverhead + new_size_no_overhead;
++ const size_t old_size = (head == NULL) ? 0 : head->size();
++ static const size_t kSegmentOverhead = sizeof(Segment) + kAlignment;
++ const size_t new_size_no_overhead = size + (old_size << 1);
++ size_t new_size = kSegmentOverhead + new_size_no_overhead;
++ const size_t min_new_size = kSegmentOverhead + static_cast<size_t>(size);
+ // Guard against integer overflow.
+- if (new_size_no_overhead < size || new_size < kSegmentOverhead) {
++ if (new_size_no_overhead < static_cast<size_t>(size) ||
++ new_size < static_cast<size_t>(kSegmentOverhead)) {
+ V8::FatalProcessOutOfMemory("Zone");
+ return NULL;
+ }
+- if (new_size < kMinimumSegmentSize) {
++ if (new_size < static_cast<size_t>(kMinimumSegmentSize)) {
+ new_size = kMinimumSegmentSize;
+- } else if (new_size > kMaximumSegmentSize) {
++ } else if (new_size > static_cast<size_t>(kMaximumSegmentSize)) {
+ // Limit the size of new segments to avoid growing the segment size
+ // exponentially, thus putting pressure on contiguous virtual address space.
+ // All the while making sure to allocate a segment large enough to hold the
+ // requested size.
+- new_size = Max(kSegmentOverhead + size, kMaximumSegmentSize);
++ new_size = Max(min_new_size, static_cast<size_t>(kMaximumSegmentSize));
+ }
+- Segment* segment = NewSegment(new_size);
++ if (new_size > INT_MAX) {
++ V8::FatalProcessOutOfMemory("Zone");
++ return NULL;
++ }
++ Segment* segment = NewSegment(static_cast<int>(new_size));
+ if (segment == NULL) {
+ V8::FatalProcessOutOfMemory("Zone");
+ return NULL;
+@@ -203,7 +209,10 @@ Address Zone::NewExpand(int size) {
+ Address result = RoundUp(segment->start(), kAlignment);
+ position_ = result + size;
+ // Check for address overflow.
+- if (position_ < result) {
++ // (Should not happen since the segment is guaranteed to accomodate
++ // size bytes + header and alignment padding)
++ if (reinterpret_cast<uintptr_t>(position_)
++ < reinterpret_cast<uintptr_t>(result)) {
+ V8::FatalProcessOutOfMemory("Zone");
+ return NULL;
+ }
+--
+1.8.5.3
+
diff --git a/v8-3.14.5.10-CVE-2016-1669.patch b/v8-3.14.5.10-CVE-2016-1669.patch
new file mode 100644
index 0000000..1a37129
--- /dev/null
+++ b/v8-3.14.5.10-CVE-2016-1669.patch
@@ -0,0 +1,30 @@
+diff -up v8-3.14.5.10/src/zone.cc.3a9bfec v8-3.14.5.10/src/zone.cc
+--- v8-3.14.5.10/src/zone.cc.3a9bfec 2016-07-06 11:21:45.362891427 -0400
++++ v8-3.14.5.10/src/zone.cc 2016-07-06 11:22:08.538764825 -0400
+@@ -168,7 +168,10 @@ Address Zone::NewExpand(int size) {
+ // Make sure the requested size is already properly aligned and that
+ // there isn't enough room in the Zone to satisfy the request.
+ ASSERT(size == RoundDown(size, kAlignment));
+- ASSERT(size > limit_ - position_);
++ ASSERT(limit_ < position_ ||
++ reinterpret_cast<uintptr_t>(limit_) -
++ reinterpret_cast<uintptr_t>(position_) <
++ size);
+
+ // Compute the new segment size. We use a 'high water mark'
+ // strategy, where we increase the segment size every time we expand
+diff -up v8-3.14.5.10/src/zone-inl.h.3a9bfec v8-3.14.5.10/src/zone-inl.h
+--- v8-3.14.5.10/src/zone-inl.h.3a9bfec 2016-07-06 11:21:00.075136898 -0400
++++ v8-3.14.5.10/src/zone-inl.h 2016-07-06 11:21:31.546966899 -0400
+@@ -55,7 +55,10 @@ inline void* Zone::New(int size) {
+ // Check if the requested size is available without expanding.
+ Address result = position_;
+
+- if (size > limit_ - position_) {
++ const uintptr_t limit = reinterpret_cast<uintptr_t>(limit_);
++ const uintptr_t position = reinterpret_cast<uintptr_t>(position_);
++ // position_ > limit_ can be true after the alignment correction above.
++ if (limit < position || (size_t) size > limit - position) {
+ result = NewExpand(size);
+ } else {
+ position_ += size;
diff --git a/v8-3.14.5.10-REPLACE_INVALID_UTF8.patch
b/v8-3.14.5.10-REPLACE_INVALID_UTF8.patch
new file mode 100644
index 0000000..28db0dd
--- /dev/null
+++ b/v8-3.14.5.10-REPLACE_INVALID_UTF8.patch
@@ -0,0 +1,195 @@
+diff -up v8-3.14.5.10/include/v8.h.riu v8-3.14.5.10/include/v8.h
+--- v8-3.14.5.10/include/v8.h.riu 2015-09-21 13:31:44.871068685 -0400
++++ v8-3.14.5.10/include/v8.h 2015-09-21 13:32:08.884868178 -0400
+@@ -1076,7 +1076,11 @@ class String : public Primitive {
+ NO_OPTIONS = 0,
+ HINT_MANY_WRITES_EXPECTED = 1,
+ NO_NULL_TERMINATION = 2,
+- PRESERVE_ASCII_NULL = 4
++ PRESERVE_ASCII_NULL = 4,
++ // Used by WriteUtf8 to replace orphan surrogate code units with the
++ // unicode replacement character. Needs to be set to guarantee valid UTF-8
++ // output.
++ REPLACE_INVALID_UTF8 = 8
+ };
+
+ // 16-bit character codes.
+diff -up v8-3.14.5.10/src/api.cc.riu v8-3.14.5.10/src/api.cc
+--- v8-3.14.5.10/src/api.cc.riu 2015-09-21 13:33:24.604235945 -0400
++++ v8-3.14.5.10/src/api.cc 2015-09-21 13:37:21.428258540 -0400
+@@ -3759,7 +3759,8 @@ static int RecursivelySerializeToUtf8(i:
+ int end,
+ int recursion_budget,
+ int32_t previous_character,
+- int32_t* last_character) {
++ int32_t* last_character,
++ bool replace_invalid_utf8) {
+ int utf8_bytes = 0;
+ while (true) {
+ if (string->IsAsciiRepresentation()) {
+@@ -3775,7 +3776,10 @@ static int RecursivelySerializeToUtf8(i:
+ for (int i = start; i < end; i++) {
+ uint16_t character = data[i];
+ current +=
+- unibrow::Utf8::Encode(current, character, previous_character);
++ unibrow::Utf8::Encode(current,
++ character,
++ previous_character,
++ replace_invalid_utf8);
+ previous_character = character;
+ }
+ *last_character = previous_character;
+@@ -3788,7 +3792,10 @@ static int RecursivelySerializeToUtf8(i:
+ for (int i = start; i < end; i++) {
+ uint16_t character = data[i];
+ current +=
+- unibrow::Utf8::Encode(current, character, previous_character);
++ unibrow::Utf8::Encode(current,
++ character,
++ previous_character,
++ replace_invalid_utf8);
+ previous_character = character;
+ }
+ *last_character = previous_character;
+@@ -3824,7 +3831,8 @@ static int RecursivelySerializeToUtf8(i:
+ boundary,
+ recursion_budget - 1,
+ previous_character,
+- &previous_character);
++ &previous_character,
++ replace_invalid_utf8);
+ if (extra_utf8_bytes < 0) return extra_utf8_bytes;
+ buffer += extra_utf8_bytes;
+ utf8_bytes += extra_utf8_bytes;
+@@ -3879,7 +3887,10 @@ int String::WriteUtf8(char* buffer,
+ return len;
+ }
+
+- if (capacity == -1 || capacity / 3 >= string_length) {
++ bool replace_invalid_utf8 = (options & REPLACE_INVALID_UTF8);
++ int max16BitCodeUnitSize = unibrow::Utf8::kMax16BitCodeUnitSize;
++
++ if (capacity == -1 || capacity / max16BitCodeUnitSize >= string_length) {
+ int32_t previous = unibrow::Utf16::kNoPreviousCharacter;
+ const int kMaxRecursion = 100;
+ int utf8_bytes =
+@@ -3889,7 +3900,8 @@ int String::WriteUtf8(char* buffer,
+ string_length,
+ kMaxRecursion,
+ previous,
+- &previous);
++ &previous,
++ replace_invalid_utf8);
+ if (utf8_bytes >= 0) {
+ // Success serializing with recursion.
+ if ((options & NO_NULL_TERMINATION) == 0 &&
+@@ -3942,14 +3954,16 @@ int String::WriteUtf8(char* buffer,
+ char intermediate[unibrow::Utf8::kMaxEncodedSize];
+ for (; i < len && pos < capacity; i++) {
+ i::uc32 c = write_input_buffer.GetNext();
+- if (unibrow::Utf16::IsTrailSurrogate(c) &&
+- unibrow::Utf16::IsLeadSurrogate(previous)) {
++ if (unibrow::Utf16::IsSurrogatePair(previous, c)) {
+ // We can't use the intermediate buffer here because the encoding
+ // of surrogate pairs is done under assumption that you can step
+ // back and fix the UTF8 stream. Luckily we only need space for one
+ // more byte, so there is always space.
+ ASSERT(pos < capacity);
+- int written = unibrow::Utf8::Encode(buffer + pos, c, previous);
++ int written = unibrow::Utf8::Encode(buffer + pos,
++ c,
++ previous,
++ replace_invalid_utf8);
+ ASSERT(written == 1);
+ pos += written;
+ nchars++;
+@@ -3957,7 +3971,8 @@ int String::WriteUtf8(char* buffer,
+ int written =
+ unibrow::Utf8::Encode(intermediate,
+ c,
+- unibrow::Utf16::kNoPreviousCharacter);
++ unibrow::Utf16::kNoPreviousCharacter,
++ replace_invalid_utf8);
+ if (pos + written <= capacity) {
+ for (int j = 0; j < written; j++) {
+ buffer[pos + j] = intermediate[j];
+diff -up v8-3.14.5.10/src/unicode.h.riu v8-3.14.5.10/src/unicode.h
+--- v8-3.14.5.10/src/unicode.h.riu 2015-09-21 13:38:50.617513839 -0400
++++ v8-3.14.5.10/src/unicode.h 2015-09-21 13:40:32.019667163 -0400
+@@ -117,6 +117,9 @@ class Buffer {
+
+ class Utf16 {
+ public:
++ static inline bool IsSurrogatePair(int lead, int trail) {
++ return IsLeadSurrogate(lead) && IsTrailSurrogate(trail);
++ }
+ static inline bool IsLeadSurrogate(int code) {
+ if (code == kNoPreviousCharacter) return false;
+ return (code & 0xfc00) == 0xd800;
+@@ -152,13 +155,19 @@ class Utf16 {
+ class Utf8 {
+ public:
+ static inline uchar Length(uchar chr, int previous);
+- static inline unsigned Encode(
+- char* out, uchar c, int previous);
++ static inline unsigned Encode(char* out,
++ uchar c,
++ int previous,
++ bool replace_invalid = false);
+ static const byte* ReadBlock(Buffer<const char*> str, byte* buffer,
+ unsigned capacity, unsigned* chars_read, unsigned* offset);
+ static uchar CalculateValue(const byte* str,
+ unsigned length,
+ unsigned* cursor);
++
++
++ // The unicode replacement character, used to signal invalid unicode
++ // sequences (e.g. an orphan surrogate) when converting to a UTF-8 encoding.
+ static const uchar kBadChar = 0xFFFD;
+ static const unsigned kMaxEncodedSize = 4;
+ static const unsigned kMaxOneByteChar = 0x7f;
+@@ -170,6 +179,9 @@ class Utf8 {
+ // that match are coded as a 4 byte UTF-8 sequence.
+ static const unsigned kBytesSavedByCombiningSurrogates = 2;
+ static const unsigned kSizeOfUnmatchedSurrogate = 3;
++ // The maximum size a single UTF-16 code unit may take up when encoded as
++ // UTF-8.
++ static const unsigned kMax16BitCodeUnitSize = 3;
+
+ private:
+ template <unsigned s> friend class Utf8InputBuffer;
+diff -up v8-3.14.5.10/src/unicode-inl.h.riu v8-3.14.5.10/src/unicode-inl.h
+--- v8-3.14.5.10/src/unicode-inl.h.riu 2015-09-21 13:37:36.002136853 -0400
++++ v8-3.14.5.10/src/unicode-inl.h 2015-09-21 13:38:41.566589411 -0400
+@@ -79,7 +79,10 @@ template <class T, int s> int Mapping<T,
+ }
+
+
+-unsigned Utf8::Encode(char* str, uchar c, int previous) {
++unsigned Utf8::Encode(char* str,
++ uchar c,
++ int previous,
++ bool replace_invalid) {
+ static const int kMask = ~(1 << 6);
+ if (c <= kMaxOneByteChar) {
+ str[0] = c;
+@@ -89,12 +92,16 @@ unsigned Utf8::Encode(char* str, uchar c
+ str[1] = 0x80 | (c & kMask);
+ return 2;
+ } else if (c <= kMaxThreeByteChar) {
+- if (Utf16::IsTrailSurrogate(c) &&
+- Utf16::IsLeadSurrogate(previous)) {
++ if (Utf16::IsSurrogatePair(previous, c)) {
+ const int kUnmatchedSize = kSizeOfUnmatchedSurrogate;
+ return Encode(str - kUnmatchedSize,
+ Utf16::CombineSurrogatePair(previous, c),
+- Utf16::kNoPreviousCharacter) - kUnmatchedSize;
++ Utf16::kNoPreviousCharacter,
++ replace_invalid) - kUnmatchedSize;
++ } else if (replace_invalid &&
++ (Utf16::IsLeadSurrogate(c) ||
++ Utf16::IsTrailSurrogate(c))) {
++ c = kBadChar;
+ }
+ str[0] = 0xE0 | (c >> 12);
+ str[1] = 0x80 | ((c >> 6) & kMask);
diff --git a/v8-3.14.5.10-abort-uncaught-exception.patch
b/v8-3.14.5.10-abort-uncaught-exception.patch
new file mode 100644
index 0000000..b2f2a08
--- /dev/null
+++ b/v8-3.14.5.10-abort-uncaught-exception.patch
@@ -0,0 +1,139 @@
+From fbff7054a47551387a99244e2cf0631f30406798 Mon Sep 17 00:00:00 2001
+From: Trevor Norris <trev.norris(a)gmail.com>
+Date: Tue, 18 Nov 2014 16:37:54 -0800
+Subject: [PATCH] v8: add api for aborting on uncaught exception
+
+Add v8::Isolate::SetAbortOnUncaughtException() so the user can be
+notified when an uncaught exception has bubbled.
+
+PR-URL:
https://github.com/joyent/node/pull/8666
+Reviewed-by: Trevor Norris <trev.norris(a)gmail.com>
+---
+ include/v8.h | 11 +++++++++++
+ src/api.cc | 5 +++++
+ src/isolate.cc | 33 +++++++++++++++++++++++----------
+ src/isolate.h | 5 +++++
+ 4 files changed, 44 insertions(+), 10 deletions(-)
+
+diff --git a/include/v8.h b/include/v8.h
+index 71a0d01..e229ed9 100644
+--- a/include/v8.h
++++ b/include/v8.h
+@@ -2842,6 +2842,17 @@ class V8EXPORT Isolate {
+ static Isolate* GetCurrent();
+
+ /**
++ * Custom callback used by embedders to help V8 determine if it should abort
++ * when it throws and no internal handler can catch the exception.
++ * If FLAG_abort_on_uncaught_exception is true, then V8 will abort if either:
++ * - no custom callback is set.
++ * - the custom callback set returns true.
++ * Otherwise it won't abort.
++ */
++ typedef bool (*abort_on_uncaught_exception_t)();
++ void SetAbortOnUncaughtException(abort_on_uncaught_exception_t callback);
++
++ /**
+ * Methods below this point require holding a lock (using Locker) in
+ * a multi-threaded environment.
+ */
+diff --git a/src/api.cc b/src/api.cc
+index 96d564f..4b1aa67 100644
+--- a/src/api.cc
++++ b/src/api.cc
+@@ -5550,6 +5550,11 @@ void Isolate::Enter() {
+ isolate->Enter();
+ }
+
++void Isolate::SetAbortOnUncaughtException(
++ abort_on_uncaught_exception_t callback) {
++ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
++ isolate->SetAbortOnUncaughtException(callback);
++}
+
+ void Isolate::Exit() {
+ i::Isolate* isolate = reinterpret_cast<i::Isolate*>(this);
+diff --git a/src/isolate.cc b/src/isolate.cc
+index 5a5293e..0b38616 100644
+--- a/src/isolate.cc
++++ b/src/isolate.cc
+@@ -1152,18 +1152,26 @@ void Isolate::DoThrow(Object* exception, MessageLocation*
location) {
+ thread_local_top()->pending_message_end_pos_ = location->end_pos();
+ }
+
+- // If the abort-on-uncaught-exception flag is specified, abort on any
+- // exception not caught by JavaScript, even when an external handler is
+- // present. This flag is intended for use by JavaScript developers, so
+- // print a user-friendly stack trace (not an internal one).
++ // If the abort-on-uncaught-exception flag is specified, and if the
++ // exception is not caught by JavaScript (even when an external handler is
++ // present).
+ if (fatal_exception_depth == 0 &&
+ FLAG_abort_on_uncaught_exception &&
+ (report_exception || can_be_caught_externally)) {
+- fatal_exception_depth++;
+- fprintf(stderr, "%s\n\nFROM\n",
+- *MessageHandler::GetLocalizedMessage(message_obj));
+- PrintCurrentStackTrace(stderr);
+- OS::Abort();
++ // If the embedder didn't specify a custom uncaught exception callback,
++ // or if the custom callback determined that V8 should abort, then
++ // abort
++ bool should_abort = !abort_on_uncaught_exception_callback_ ||
++ abort_on_uncaught_exception_callback_();
++ if (should_abort) {
++ fatal_exception_depth++;
++ // This flag is intended for use by JavaScript developers, so
++ // print a user-friendly stack trace (not an internal one).
++ fprintf(stderr, "%s\n\nFROM\n",
++ *MessageHandler::GetLocalizedMessage(message_obj));
++ PrintCurrentStackTrace(stderr);
++ OS::Abort();
++ }
+ }
+ } else if (location != NULL && !location->script().is_null()) {
+ // We are bootstrapping and caught an error where the location is set
+@@ -1339,6 +1347,10 @@ void Isolate::SetCaptureStackTraceForUncaughtExceptions(
+ stack_trace_for_uncaught_exceptions_options_ = options;
+ }
+
++void Isolate::SetAbortOnUncaughtException(
++ v8::Isolate::abort_on_uncaught_exception_t callback) {
++ abort_on_uncaught_exception_callback_ = callback;
++}
+
+ bool Isolate::is_out_of_memory() {
+ if (has_pending_exception()) {
+@@ -1534,7 +1546,8 @@ Isolate::Isolate()
+ date_cache_(NULL),
+ context_exit_happened_(false),
+ deferred_handles_head_(NULL),
+- optimizing_compiler_thread_(this) {
++ optimizing_compiler_thread_(this),
++ abort_on_uncaught_exception_callback_(NULL) {
+ TRACE_ISOLATE(constructor);
+
+ memset(isolate_addresses_, 0,
+diff --git a/src/isolate.h b/src/isolate.h
+index 2769ca7..8719aa1 100644
+--- a/src/isolate.h
++++ b/src/isolate.h
+@@ -692,6 +692,9 @@ class Isolate {
+ int frame_limit,
+ StackTrace::StackTraceOptions options);
+
++ typedef bool (*abort_on_uncaught_exception_t)();
++ void SetAbortOnUncaughtException(abort_on_uncaught_exception_t callback);
++
+ // Tells whether the current context has experienced an out of memory
+ // exception.
+ bool is_out_of_memory();
+@@ -1292,6 +1295,8 @@ class Isolate {
+ DeferredHandles* deferred_handles_head_;
+ OptimizingCompilerThread optimizing_compiler_thread_;
+
++ abort_on_uncaught_exception_t abort_on_uncaught_exception_callback_;
++
+ friend class ExecutionAccess;
+ friend class HandleScopeImplementer;
+ friend class IsolateInitializer;
diff --git a/v8-3.14.5.10-busy-loop.patch b/v8-3.14.5.10-busy-loop.patch
new file mode 100644
index 0000000..fcc906c
--- /dev/null
+++ b/v8-3.14.5.10-busy-loop.patch
@@ -0,0 +1,137 @@
+From 6ebd85e10535dfaa9181842fe73834e51d4d3e6c Mon Sep 17 00:00:00 2001
+From: Ben Noordhuis <info(a)bnoordhuis.nl>
+Date: Thu, 27 Nov 2014 07:15:54 +0100
+Subject: [PATCH] v8: don't busy loop in cpu profiler thread
+
+Reduce the overhead of the CPU profiler by replacing sched_yield() with
+nanosleep() in V8's tick event processor thread. The former only yields
+the CPU when there is another process scheduled on the same CPU.
+
+Before this commit, the thread would effectively busy loop and consume
+100% CPU time. By forcing a one nanosecond sleep period rounded up to
+the task scheduler's granularity (about 50 us on Linux), CPU usage for
+the processor thread now hovers around 10-20% for a busy application.
+
+PR-URL:
https://github.com/joyent/node/pull/8789
+Ref:
https://github.com/strongloop/strong-agent/issues/3
+Reviewed-by: Trevor Norris <trev.norris(a)gmail.com>
+---
+ src/platform-freebsd.cc | 5 -----
+ src/platform-linux.cc | 5 -----
+ src/platform-macos.cc | 5 -----
+ src/platform-openbsd.cc | 5 -----
+ src/platform-posix.cc | 6 ++++++
+ src/platform-solaris.cc | 5 -----
+ tools/gyp/v8.gyp | 2 +-
+ 7 files changed, 7 insertions(+), 26 deletions(-)
+
+diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
+index 511759c..5c90c6b 100644
+--- a/src/platform-freebsd.cc
++++ b/src/platform-freebsd.cc
+@@ -539,11 +539,6 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ }
+
+
+-void Thread::YieldCPU() {
+- sched_yield();
+-}
+-
+-
+ class FreeBSDMutex : public Mutex {
+ public:
+ FreeBSDMutex() {
+diff --git a/src/platform-linux.cc b/src/platform-linux.cc
+index beb2cce..3d6b304 100644
+--- a/src/platform-linux.cc
++++ b/src/platform-linux.cc
+@@ -812,11 +812,6 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ }
+
+
+-void Thread::YieldCPU() {
+- sched_yield();
+-}
+-
+-
+ class LinuxMutex : public Mutex {
+ public:
+ LinuxMutex() {
+diff --git a/src/platform-macos.cc b/src/platform-macos.cc
+index a216f6e..e54e3e4 100644
+--- a/src/platform-macos.cc
++++ b/src/platform-macos.cc
+@@ -640,11 +640,6 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ }
+
+
+-void Thread::YieldCPU() {
+- sched_yield();
+-}
+-
+-
+ class MacOSMutex : public Mutex {
+ public:
+ MacOSMutex() {
+diff --git a/src/platform-openbsd.cc b/src/platform-openbsd.cc
+index 408d4dc..72167de 100644
+--- a/src/platform-openbsd.cc
++++ b/src/platform-openbsd.cc
+@@ -593,11 +593,6 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ }
+
+
+-void Thread::YieldCPU() {
+- sched_yield();
+-}
+-
+-
+ class OpenBSDMutex : public Mutex {
+ public:
+ OpenBSDMutex() {
+diff --git a/src/platform-posix.cc b/src/platform-posix.cc
+index 5c3529d..8aecd56 100644
+--- a/src/platform-posix.cc
++++ b/src/platform-posix.cc
+@@ -392,6 +392,12 @@ void OS::StrNCpy(Vector<char> dest, const char* src, size_t n)
{
+ }
+
+
++void Thread::YieldCPU() {
++ const timespec delay = { 0, 1 };
++ nanosleep(&delay, NULL);
++}
++
++
+ // ----------------------------------------------------------------------------
+ // POSIX socket support.
+ //
+diff --git a/src/platform-solaris.cc b/src/platform-solaris.cc
+index 07718fe..4e95ecc 100644
+--- a/src/platform-solaris.cc
++++ b/src/platform-solaris.cc
+@@ -527,11 +527,6 @@ void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
+ }
+
+
+-void Thread::YieldCPU() {
+- sched_yield();
+-}
+-
+-
+ class SolarisMutex : public Mutex {
+ public:
+ SolarisMutex() {
+diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
+index 71cf366..c304925 100644
+--- a/tools/gyp/v8.gyp
++++ b/tools/gyp/v8.gyp
+@@ -715,7 +715,7 @@
+ ['OS=="solaris"', {
+ 'link_settings': {
+ 'libraries': [
+- '-lsocket -lnsl',
++ '-lsocket -lnsl -lrt',
+ ]},
+ 'sources': [
+ '../../src/platform-solaris.cc',
diff --git a/v8-3.14.5.10-enumeration.patch b/v8-3.14.5.10-enumeration.patch
new file mode 100644
index 0000000..4dea2a5
--- /dev/null
+++ b/v8-3.14.5.10-enumeration.patch
@@ -0,0 +1,30 @@
+From 196184d332ba2d2defc56ad0b37653659a7d3ec0 Mon Sep 17 00:00:00 2001
+From: "svenpanne(a)chromium.org" <svenpanne(a)chromium.org>
+Date: Fri, 9 Nov 2012 11:30:05 +0000
+Subject: [PATCH] v8: backport
codereview.chromium.org/11362182
+
+Keep the number of descriptors below
+DescriptorArray::kMaxNumberOfDescriptors even for accessors
+
+Review URL:
https://codereview.chromium.org/11362182
+---
+ src/objects.cc | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/src/objects.cc b/src/objects.cc
+--- a/src/objects.cc
++++ b/src/objects.cc
+@@ -4453,7 +4453,9 @@ MaybeObject* JSObject::DefinePropertyAccessor(String* name,
+ // to do a lookup, which seems to be a bit of overkill.
+ Heap* heap = GetHeap();
+ bool only_attribute_changes = getter->IsNull() && setter->IsNull();
+- if (HasFastProperties() && !only_attribute_changes) {
++ if (HasFastProperties() && !only_attribute_changes &&
++ (map()->NumberOfOwnDescriptors() <
++ DescriptorArray::kMaxNumberOfDescriptors)) {
+ MaybeObject* getterOk = heap->undefined_value();
+ if (!getter->IsNull()) {
+ getterOk = DefineFastAccessor(name, ACCESSOR_GETTER, getter, attributes);
+--
+1.8.5.1
+
diff --git a/v8-3.14.5.10-gcc7.patch b/v8-3.14.5.10-gcc7.patch
new file mode 100644
index 0000000..f929b4a
--- /dev/null
+++ b/v8-3.14.5.10-gcc7.patch
@@ -0,0 +1,223 @@
+diff -up v8-3.14.5.10/src/arm/assembler-arm.cc.gcc7
v8-3.14.5.10/src/arm/assembler-arm.cc
+diff -up v8-3.14.5.10/src/deoptimizer.cc.gcc7 v8-3.14.5.10/src/deoptimizer.cc
+--- v8-3.14.5.10/src/deoptimizer.cc.gcc7 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/src/deoptimizer.cc 2017-02-28 16:55:25.553045035 -0500
+@@ -1141,7 +1141,7 @@ bool Deoptimizer::DoOsrTranslateCommand(
+ }
+ output->SetRegister(output_reg, static_cast<int32_t>(uint32_value));
+ }
+-
++ // intentional fallthrough
+
+ case Translation::DOUBLE_REGISTER: {
+ // Abort OSR if we don't have a number.
+diff -up v8-3.14.5.10/src/ia32/assembler-ia32.cc.gcc7
v8-3.14.5.10/src/ia32/assembler-ia32.cc
+--- v8-3.14.5.10/src/ia32/assembler-ia32.cc.gcc7 2017-03-01 10:21:11.271775490 -0500
++++ v8-3.14.5.10/src/ia32/assembler-ia32.cc 2017-03-01 10:21:44.242983779 -0500
+@@ -420,6 +420,7 @@ void Assembler::Nop(int bytes) {
+ switch (bytes) {
+ case 2:
+ EMIT(0x66);
++ // intentional fallthrough
+ case 1:
+ EMIT(0x90);
+ return;
+@@ -436,6 +437,7 @@ void Assembler::Nop(int bytes) {
+ return;
+ case 6:
+ EMIT(0x66);
++ // intentional fallthrough
+ case 5:
+ EMIT(0xf);
+ EMIT(0x1f);
+@@ -456,12 +458,15 @@ void Assembler::Nop(int bytes) {
+ case 11:
+ EMIT(0x66);
+ bytes--;
++ // intentional fallthrough
+ case 10:
+ EMIT(0x66);
+ bytes--;
++ // intentional fallthrough
+ case 9:
+ EMIT(0x66);
+ bytes--;
++ // intentional fallthrough
+ case 8:
+ EMIT(0xf);
+ EMIT(0x1f);
+diff -up v8-3.14.5.10/src/ic.cc.gcc7 v8-3.14.5.10/src/ic.cc
+--- v8-3.14.5.10/src/ic.cc.gcc7 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/src/ic.cc 2017-02-28 16:55:25.554045011 -0500
+@@ -1989,8 +1989,8 @@ void KeyedStoreIC::UpdateCaches(LookupRe
+ name, receiver, field_index, transition, strict_mode);
+ break;
+ }
+- // fall through.
+ }
++ // intentional fallthrough
+ case NORMAL:
+ case CONSTANT_FUNCTION:
+ case CALLBACKS:
+diff -up v8-3.14.5.10/src/objects.cc.gcc7 v8-3.14.5.10/src/objects.cc
+--- v8-3.14.5.10/src/objects.cc.gcc7 2017-02-28 16:55:25.516045908 -0500
++++ v8-3.14.5.10/src/objects.cc 2017-02-28 16:55:25.555044988 -0500
+@@ -10302,7 +10302,7 @@ void JSObject::GetElementsCapacityAndUsa
+ *used = Smi::cast(JSArray::cast(this)->length())->value();
+ break;
+ }
+- // Fall through if packing is not guaranteed.
++ // intentional fallthrough
+ case FAST_HOLEY_SMI_ELEMENTS:
+ case FAST_HOLEY_ELEMENTS:
+ backing_store = FixedArray::cast(backing_store_base);
+@@ -10324,7 +10324,7 @@ void JSObject::GetElementsCapacityAndUsa
+ *used = Smi::cast(JSArray::cast(this)->length())->value();
+ break;
+ }
+- // Fall through if packing is not guaranteed.
++ // intentional fallthrough
+ case FAST_HOLEY_DOUBLE_ELEMENTS: {
+ FixedDoubleArray* elms = FixedDoubleArray::cast(elements());
+ *capacity = elms->length();
+diff -up v8-3.14.5.10/src/objects.h.gcc7 v8-3.14.5.10/src/objects.h
+--- v8-3.14.5.10/src/objects.h.gcc7 2017-02-28 16:55:25.517045885 -0500
++++ v8-3.14.5.10/src/objects.h 2017-02-28 16:55:25.556044964 -0500
+@@ -2785,24 +2785,10 @@ class HashTable: public FixedArray {
+ USE_CUSTOM_MINIMUM_CAPACITY
+ };
+
+- // Wrapper methods
+- inline uint32_t Hash(Key key) {
+- if (Shape::UsesSeed) {
+- return Shape::SeededHash(key,
+- GetHeap()->HashSeed());
+- } else {
+- return Shape::Hash(key);
+- }
+- }
+-
+- inline uint32_t HashForObject(Key key, Object* object) {
+- if (Shape::UsesSeed) {
+- return Shape::SeededHashForObject(key,
+- GetHeap()->HashSeed(), object);
+- } else {
+- return Shape::HashForObject(key, object);
+- }
+- }
++ // Wrapper methods. Defined in src/objects-inl.h
++ // to break a cycle with src/heap/heap.h.
++ inline uint32_t Hash(Key key);
++ inline uint32_t HashForObject(Key key, Object* object);
+
+ // Returns the number of elements in the hash table.
+ int NumberOfElements() {
+diff -up v8-3.14.5.10/src/objects-inl.h.gcc7 v8-3.14.5.10/src/objects-inl.h
+--- v8-3.14.5.10/src/objects-inl.h.gcc7 2017-02-28 16:55:25.517045885 -0500
++++ v8-3.14.5.10/src/objects-inl.h 2017-02-28 16:55:25.556044964 -0500
+@@ -52,6 +52,26 @@
+ namespace v8 {
+ namespace internal {
+
++template<typename Shape, typename Key>
++uint32_t HashTable<Shape, Key>::Hash(Key key) {
++ if (Shape::UsesSeed) {
++ return Shape::SeededHash(key,
++ GetHeap()->HashSeed());
++ } else {
++ return Shape::Hash(key);
++ }
++}
++
++template<typename Shape, typename Key>
++uint32_t HashTable<Shape, Key>::HashForObject(Key key, Object* object) {
++ if (Shape::UsesSeed) {
++ return Shape::SeededHashForObject(key,
++ GetHeap()->HashSeed(), object);
++ } else {
++ return Shape::HashForObject(key, object);
++ }
++}
++
+ PropertyDetails::PropertyDetails(Smi* smi) {
+ value_ = smi->value();
+ }
+diff -up v8-3.14.5.10/src/parser.cc.gcc7 v8-3.14.5.10/src/parser.cc
+--- v8-3.14.5.10/src/parser.cc.gcc7 2017-02-28 16:55:25.450047466 -0500
++++ v8-3.14.5.10/src/parser.cc 2017-02-28 16:55:25.557044941 -0500
+@@ -3649,8 +3649,7 @@ Expression* Parser::ParsePrimaryExpressi
+ result = ParseV8Intrinsic(CHECK_OK);
+ break;
+ }
+- // If we're not allowing special syntax we fall-through to the
+- // default case.
++ // intentional fallthrough
+
+ default: {
+ Token::Value tok = Next();
+@@ -5376,8 +5375,8 @@ RegExpTree* RegExpParser::ParseDisjuncti
+ if (ParseIntervalQuantifier(&dummy, &dummy)) {
+ ReportError(CStrVector("Nothing to repeat") CHECK_FAILED);
+ }
+- // fallthrough
+ }
++ // intentional fallthrough
+ default:
+ builder->AddCharacter(current());
+ Advance();
+diff -up v8-3.14.5.10/src/spaces.h.gcc7 v8-3.14.5.10/src/spaces.h
+--- v8-3.14.5.10/src/spaces.h.gcc7 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/src/spaces.h 2017-02-28 16:55:25.557044941 -0500
+@@ -2613,15 +2613,15 @@ class PointerChunkIterator BASE_EMBEDDED
+ return old_pointer_iterator_.next();
+ }
+ state_ = kMapState;
+- // Fall through.
+ }
++ // intentional fallthrough
+ case kMapState: {
+ if (map_iterator_.has_next()) {
+ return map_iterator_.next();
+ }
+ state_ = kLargeObjectState;
+- // Fall through.
+ }
++ // intentional fallthrough
+ case kLargeObjectState: {
+ HeapObject* heap_object;
+ do {
+diff -up v8-3.14.5.10/src/x64/assembler-x64.cc.gcc7
v8-3.14.5.10/src/x64/assembler-x64.cc
+--- v8-3.14.5.10/src/x64/assembler-x64.cc.gcc7 2017-03-01 10:19:40.086088012 -0500
++++ v8-3.14.5.10/src/x64/assembler-x64.cc 2017-03-01 10:20:51.859241627 -0500
+@@ -1800,6 +1800,7 @@ void Assembler::Nop(int n) {
+ switch (n) {
+ case 2:
+ emit(0x66);
++ // intentional fallthrough
+ case 1:
+ emit(0x90);
+ return;
+@@ -1816,6 +1817,7 @@ void Assembler::Nop(int n) {
+ return;
+ case 6:
+ emit(0x66);
++ // intentional fallthrough
+ case 5:
+ emit(0x0f);
+ emit(0x1f);
+@@ -1836,12 +1838,15 @@ void Assembler::Nop(int n) {
+ case 11:
+ emit(0x66);
+ n--;
++ // intentional fallthrough
+ case 10:
+ emit(0x66);
+ n--;
++ // intentional fallthrough
+ case 9:
+ emit(0x66);
+ n--;
++ // intentional fallthrough
+ case 8:
+ emit(0x0f);
+ emit(0x1f);
diff --git a/v8-3.14.5.10-gcc8.patch b/v8-3.14.5.10-gcc8.patch
new file mode 100644
index 0000000..ab6a2a3
--- /dev/null
+++ b/v8-3.14.5.10-gcc8.patch
@@ -0,0 +1,12 @@
+diff -up v8-3.14.5.10/src/frames.h.gcc8 v8-3.14.5.10/src/frames.h
+--- v8-3.14.5.10/src/frames.h.gcc8 2018-05-18 14:41:15.119069125 -0400
++++ v8-3.14.5.10/src/frames.h 2018-05-18 14:42:07.421810155 -0400
+@@ -67,7 +67,7 @@ class InnerPointerToCodeCache {
+ Code* GcSafeCastToCode(HeapObject* object, Address inner_pointer);
+
+ void Flush() {
+- memset(&cache_[0], 0, sizeof(cache_));
++ memset(static_cast<void*>(&cache_[0]), 0, sizeof(cache_));
+ }
+
+ InnerPointerToCodeCacheEntry* GetCacheEntry(Address inner_pointer);
diff --git a/v8-3.14.5.10-mem-corruption-stack-overflow.patch
b/v8-3.14.5.10-mem-corruption-stack-overflow.patch
new file mode 100644
index 0000000..452464b
--- /dev/null
+++ b/v8-3.14.5.10-mem-corruption-stack-overflow.patch
@@ -0,0 +1,33 @@
+From 530af9cb8e700e7596b3ec812bad123c9fa06356 Mon Sep 17 00:00:00 2001
+From: Fedor Indutny <fedor(a)indutny.com>
+Date: Wed, 30 Jul 2014 15:33:52 -0700
+Subject: [PATCH] v8: Interrupts must not mask stack overflow.
+
+Backport of
https://codereview.chromium.org/339883002
+---
+ src/isolate.h | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+diff --git a/src/isolate.h b/src/isolate.h
+index b90191d..2769ca7 100644
+--- a/src/isolate.h
++++ b/src/isolate.h
+@@ -1392,14 +1392,9 @@ class StackLimitCheck BASE_EMBEDDED {
+ public:
+ explicit StackLimitCheck(Isolate* isolate) : isolate_(isolate) { }
+
+- bool HasOverflowed() const {
++ inline bool HasOverflowed() const {
+ StackGuard* stack_guard = isolate_->stack_guard();
+- // Stack has overflowed in C++ code only if stack pointer exceeds the C++
+- // stack guard and the limits are not set to interrupt values.
+- // TODO(214): Stack overflows are ignored if a interrupt is pending. This
+- // code should probably always use the initial C++ limit.
+- return (reinterpret_cast<uintptr_t>(this) < stack_guard->climit())
&&
+- stack_guard->IsStackOverflow();
++ return reinterpret_cast<uintptr_t>(this) < stack_guard->real_climit();
+ }
+ private:
+ Isolate* isolate_;
+--
+2.0.3
diff --git a/v8-3.14.5.10-profiler-log.patch b/v8-3.14.5.10-profiler-log.patch
new file mode 100644
index 0000000..2530e54
--- /dev/null
+++ b/v8-3.14.5.10-profiler-log.patch
@@ -0,0 +1,43 @@
+From 431eb172f97434a3b0868a610bc14d8ff7d9efd9 Mon Sep 17 00:00:00 2001
+From: Ben Noordhuis <info(a)bnoordhuis.nl>
+Date: Fri, 16 Jan 2015 13:44:42 +0100
+Subject: [PATCH] deps: log V8 version in profiler log file
+
+Patch from issue 800293002 authored by ben(a)strongloop.com
+
+Review URL:
https://codereview.chromium.org/806143002
+
+PR-URL:
https://github.com/joyent/node/pull/9043
+Reviewed-by: Trevor Norris <trev.norris(a)gmail.com>
+Reviewed-By: Timothy J Fontaine <tjfontaine(a)gmail.com>
+---
+ src/log-utils.cc | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/src/log-utils.cc b/src/log-utils.cc
+index 5e607da..622cc51 100644
+--- a/src/log-utils.cc
++++ b/src/log-utils.cc
+@@ -29,6 +29,7 @@
+
+ #include "log-utils.h"
+ #include "string-stream.h"
++#include "version.h"
+
+ namespace v8 {
+ namespace internal {
+@@ -136,6 +137,14 @@ void Log::Initialize() {
+ }
+ }
+ }
++
++ if (output_handle_ != NULL) {
++ LogMessageBuilder msg(logger_);
++ msg.Append("v8-version,%d,%d,%d,%d,%d\n", Version::GetMajor(),
++ Version::GetMinor(), Version::GetBuild(), Version::GetPatch(),
++ Version::IsCandidate());
++ msg.WriteToLogFile();
++ }
+ }
+
+
diff --git a/v8-3.14.5.10-report-builtins-by-name.patch
b/v8-3.14.5.10-report-builtins-by-name.patch
new file mode 100644
index 0000000..1d2410b
--- /dev/null
+++ b/v8-3.14.5.10-report-builtins-by-name.patch
@@ -0,0 +1,16 @@
+diff -up v8-3.14.5.10/src/log.cc.builtinnames v8-3.14.5.10/src/log.cc
+--- v8-3.14.5.10/src/log.cc.builtinnames 2016-07-06 11:25:12.341766992 -0400
++++ v8-3.14.5.10/src/log.cc 2016-07-06 11:25:41.065609632 -0400
+@@ -1485,7 +1485,11 @@ void Logger::LogCodeObject(Object* objec
+ tag = Logger::STUB_TAG;
+ break;
+ case Code::BUILTIN:
+- description = "A builtin from the snapshot";
++ description =
++ Isolate::Current()->builtins()->Lookup(code_object->entry());
++ if (description == NULL) {
++ description = "A builtin from the snapshot";
++ }
+ tag = Logger::BUILTIN_TAG;
+ break;
+ case Code::KEYED_LOAD_IC:
diff --git a/v8-3.14.5.10-system-valgrind.patch b/v8-3.14.5.10-system-valgrind.patch
new file mode 100644
index 0000000..1e5b0cb
--- /dev/null
+++ b/v8-3.14.5.10-system-valgrind.patch
@@ -0,0 +1,44 @@
+diff -up v8-3.14.5.10/src/ia32/cpu-ia32.cc.system-valgrind
v8-3.14.5.10/src/ia32/cpu-ia32.cc
+--- v8-3.14.5.10/src/ia32/cpu-ia32.cc.system-valgrind 2012-01-16 06:42:08.000000000
-0500
++++ v8-3.14.5.10/src/ia32/cpu-ia32.cc 2014-12-02 15:15:07.819525430 -0500
+@@ -28,7 +28,7 @@
+ // CPU specific code for ia32 independent of OS goes here.
+
+ #ifdef __GNUC__
+-#include "third_party/valgrind/valgrind.h"
++#include <valgrind/valgrind.h>
+ #endif
+
+ #include "v8.h"
+@@ -67,8 +67,7 @@ void CPU::FlushICache(void* start, size_
+ // solution is to run valgrind with --smc-check=all, but this comes at a big
+ // performance cost. We can notify valgrind to invalidate its cache.
+ #ifdef VALGRIND_DISCARD_TRANSLATIONS
+- unsigned res = VALGRIND_DISCARD_TRANSLATIONS(start, size);
+- USE(res);
++ VALGRIND_DISCARD_TRANSLATIONS(start, size);
+ #endif
+ }
+
+diff -up v8-3.14.5.10/src/x64/cpu-x64.cc.system-valgrind v8-3.14.5.10/src/x64/cpu-x64.cc
+--- v8-3.14.5.10/src/x64/cpu-x64.cc.system-valgrind 2012-02-23 03:45:21.000000000 -0500
++++ v8-3.14.5.10/src/x64/cpu-x64.cc 2014-12-02 15:14:51.289621074 -0500
+@@ -28,7 +28,7 @@
+ // CPU specific code for x64 independent of OS goes here.
+
+ #if defined(__GNUC__) && !defined(__MINGW64__)
+-#include "third_party/valgrind/valgrind.h"
++#include <valgrind/valgrind.h>
+ #endif
+
+ #include "v8.h"
+@@ -67,8 +67,7 @@ void CPU::FlushICache(void* start, size_
+ // solution is to run valgrind with --smc-check=all, but this comes at a big
+ // performance cost. We can notify valgrind to invalidate its cache.
+ #ifdef VALGRIND_DISCARD_TRANSLATIONS
+- unsigned res = VALGRIND_DISCARD_TRANSLATIONS(start, size);
+- USE(res);
++ VALGRIND_DISCARD_TRANSLATIONS(start, size);
+ #endif
+ }
+
diff --git a/v8-3.14.5.10-unhandled-ReferenceError.patch
b/v8-3.14.5.10-unhandled-ReferenceError.patch
new file mode 100644
index 0000000..7762fa8
--- /dev/null
+++ b/v8-3.14.5.10-unhandled-ReferenceError.patch
@@ -0,0 +1,37 @@
+From 0ff51c6e063e3eea9e4d9ea68edc82d935626fc7 Mon Sep 17 00:00:00 2001
+From: Julien Gilli <julien.gilli(a)joyent.com>
+Date: Fri, 28 Nov 2014 15:33:35 -0800
+Subject: [PATCH] deps: backport 2ad2237 from v8 upstream
+
+Original commit message:
+
+Fix Unhandled ReferenceError in debug-debugger.js
+
+This fixes following exception in Sky on attempt to set a breakpoint
+"Unhandled: Uncaught ReferenceError: break_point is not defined"
+I think this happens in Sky but not in Chrome because Sky scripts are executed in strict
mode.
+
+BUG=None
+LOG=N
+R=yangguo(a)chromium.org
+
+Review URL:
https://codereview.chromium.org/741683002
+
+Cr-Commit-Position: refs/heads/master@{#25415}
+---
+ src/debug-debugger.js | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/debug-debugger.js b/src/debug-debugger.js
+index dfad902..a27961f 100644
+--- a/src/debug-debugger.js
++++ b/src/debug-debugger.js
+@@ -442,7 +442,7 @@ ScriptBreakPoint.prototype.set = function (script) {
+ if (position === null) return;
+
+ // Create a break point object and set the break point.
+- break_point = MakeBreakPoint(position, this);
++ var break_point = MakeBreakPoint(position, this);
+ break_point.setIgnoreCount(this.ignoreCount());
+ var actual_position = %SetScriptBreakPoint(script, position, break_point);
+ if (IS_UNDEFINED(actual_position)) {
diff --git a/v8-3.14.5.10-unused-local-typedefs.patch
b/v8-3.14.5.10-unused-local-typedefs.patch
new file mode 100644
index 0000000..c168875
--- /dev/null
+++ b/v8-3.14.5.10-unused-local-typedefs.patch
@@ -0,0 +1,39 @@
+From 53b4accb6e5747b156be91a2b90f42607e33a7cc Mon Sep 17 00:00:00 2001
+From: Timothy J Fontaine <tjfontaine(a)gmail.com>
+Date: Mon, 4 Aug 2014 13:43:50 -0700
+Subject: [PATCH] v8: Fix compliation with GCC 4.8
+
+Supresses a very loud warning from GCC 4.8 about unused typedefs
+
+Original url
https://codereview.chromium.org/69413002
+---
+ src/checks.h | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/src/checks.h b/src/checks.h
+index d0a0c2b..4396ada 100644
+--- a/src/checks.h
++++ b/src/checks.h
+@@ -230,6 +230,13 @@ inline void CheckNonEqualsHelper(const char* file,
+ #define CHECK_LE(a, b) CHECK((a) <= (b))
+
+
++#if defined(__clang__) || defined(__GNUC__)
++# define V8_UNUSED __attribute__((unused))
++#else
++# define V8_UNUSED
++#endif
++
++
+ // This is inspired by the static assertion facility in boost. This
+ // is pretty magical. If it causes you trouble on a platform you may
+ // find a fix in the boost code.
+@@ -248,7 +255,7 @@ template <int> class StaticAssertionHelper { };
+ #define STATIC_CHECK(test) \
+ typedef \
+
StaticAssertionHelper<sizeof(StaticAssertion<static_cast<bool>((test))>)>
\
+- SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__)
++ SEMI_STATIC_JOIN(__StaticAssertTypedef__, __LINE__) V8_UNUSED
+
+
+ extern bool FLAG_enable_slow_asserts;
diff --git a/v8-3.14.5.10-use-clock_gettime.patch b/v8-3.14.5.10-use-clock_gettime.patch
new file mode 100644
index 0000000..cb25591
--- /dev/null
+++ b/v8-3.14.5.10-use-clock_gettime.patch
@@ -0,0 +1,122 @@
+From f9ced08de30c37838756e8227bd091f80ad9cafa Mon Sep 17 00:00:00 2001
+From: Ben Noordhuis <ben(a)strongloop.com>
+Date: Thu, 24 Apr 2014 04:27:40 +0200
+Subject: [PATCH] deps: make v8 use CLOCK_REALTIME_COARSE
+
+Date.now() indirectly calls gettimeofday() on Linux and that's a system
+call that is extremely expensive on virtualized systems when the host
+operating system has to emulate access to the hardware clock.
+
+Case in point: output from `perf record -c 10000 -e cycles:u -g -i`
+for a benchmark/http_simple bytes/8 benchmark with a light load of
+50 concurrent clients:
+
+ 53.69% node node [.] v8::internal::OS::TimeCurrentMillis()
+ |
+ --- v8::internal::OS::TimeCurrentMillis()
+ |
+ |--99.77%--
v8::internal::Runtime_DateCurrentTime(v8::internal::Arguments, v8::internal::Isolate*)
+ | 0x23587880618e
+
+That's right - over half of user time spent inside the V8 function that
+calls gettimeofday().
+
+Notably, nearly all system time gets attributed to acpi_pm_read(), the
+kernel function that reads the ACPI power management timer:
+
+ 32.49% node [kernel.kallsyms] [k] acpi_pm_read
+ |
+ --- acpi_pm_read
+ |
+ |--98.40%-- __getnstimeofday
+ | getnstimeofday
+ | |
+ | |--71.61%-- do_gettimeofday
+ | | sys_gettimeofday
+ | | system_call_fastpath
+ | | 0x7fffbbaf6dbc
+ | | |
+ | | |--98.72%--
v8::internal::OS::TimeCurrentMillis()
+
+The cost of the gettimeofday() system call is normally measured in
+nanoseconds but we were seeing 100 us averages and spikes >= 1000 us.
+The numbers were so bad, my initial hunch was that the node process was
+continuously getting rescheduled inside the system call...
+
+v8::internal::OS::TimeCurrentMillis()'s most frequent caller is
+v8::internal::Runtime_DateCurrentTime(), the V8 run-time function
+that's behind Date.now(). The timeout handling logic in lib/http.js
+and lib/net.js calls into lib/timers.js and that module will happily
+call Date.now() hundreds or even thousands of times per second.
+If you saw exports._unrefActive() show up in --prof output a lot,
+now you know why.
+
+That's why this commit makes V8 switch over to clock_gettime() on Linux.
+In particular, it checks if CLOCK_REALTIME_COARSE is available and has
+a resolution <= 1 ms because in that case the clock_gettime() call can
+be fully serviced from the vDSO.
+
+It speeds up the aforementioned benchmark by about 100% on the affected
+systems and should go a long way toward addressing the latency issues
+that StrongLoop customers have been reporting.
+
+This patch will be upstreamed as a CR against V8 3.26. I'm sending it
+as a pull request for v0.10 first because that's what our users are
+running and because the delta between 3.26 and 3.14 is too big to
+reasonably back-port the patch. I'll open a pull request for the
+master branch once the CR lands upstream.
+
+Signed-off-by: Trevor Norris <trev.norris(a)gmail.com>
+Signed-off-by: Fedor Indutny <fedor(a)indutny.com>
+---
+ src/platform-posix.cc | 26 ++++++++++++++++++++++----
+ 1 file changed, 22 insertions(+), 4 deletions(-)
+
+diff --git a/src/platform-posix.cc b/src/platform-posix.cc
+index ad74eba..3c86868 100644
+--- a/src/platform-posix.cc
++++ b/src/platform-posix.cc
+@@ -188,19 +188,37 @@ int OS::GetUserTime(uint32_t* secs, uint32_t* usecs) {
+
+
+ double OS::TimeCurrentMillis() {
+- struct timeval tv;
+- if (gettimeofday(&tv, NULL) < 0) return 0.0;
+- return (static_cast<double>(tv.tv_sec) * 1000) +
+- (static_cast<double>(tv.tv_usec) / 1000);
++ return static_cast<double>(Ticks()) / 1000;
+ }
+
+
+ int64_t OS::Ticks() {
++#if defined(__linux__)
++ static clockid_t clock_id = static_cast<clockid_t>(-1);
++ struct timespec spec;
++ if (clock_id == static_cast<clockid_t>(-1)) {
++ // CLOCK_REALTIME_COARSE may not be defined by the system headers but
++ // might still be supported by the kernel so use the clock id directly.
++ // Only use CLOCK_REALTIME_COARSE when its granularity <= 1 ms.
++ const clockid_t clock_realtime_coarse = 5;
++ if (clock_getres(clock_realtime_coarse, &spec) == 0 &&
++ spec.tv_nsec <= 1000 * 1000) {
++ clock_id = clock_realtime_coarse;
++ } else {
++ clock_id = CLOCK_REALTIME;
++ }
++ }
++ if (clock_gettime(clock_id, &spec) != 0) {
++ return 0; // Not really possible.
++ }
++ return static_cast<int64_t>(spec.tv_sec) * 1000000 + (spec.tv_nsec / 1000);
++#else
+ // gettimeofday has microsecond resolution.
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0)
+ return 0;
+ return (static_cast<int64_t>(tv.tv_sec) * 1000000) + tv.tv_usec;
++#endif
+ }
+
+
+--
+1.9.1
diff --git a/v8-3.14.5.10-x64-MathMinMax.patch b/v8-3.14.5.10-x64-MathMinMax.patch
new file mode 100644
index 0000000..e7b167d
--- /dev/null
+++ b/v8-3.14.5.10-x64-MathMinMax.patch
@@ -0,0 +1,105 @@
+From 3530fa9cd09f8db8101c4649cab03bcdf760c434 Mon Sep 17 00:00:00 2001
+From: Fedor Indutny <fedor(a)indutny.com>
+Date: Fri, 21 Dec 2012 17:52:00 +0000
+Subject: [PATCH] deps: backport 4ed5fde4f from v8 upstream
+
+Original commit message:
+
+ Fix x64 MathMinMax for negative untagged int32 arguments.
+
+ An untagged int32 has zeros in the upper half even if it is negative.
+ Using cmpq to compare such numbers will incorrectly ignore the sign.
+
+ BUG=164442
+ R=mvstanton(a)chromium.org
+
+ Review URL:
https://chromiumcodereview.appspot.com/11665007
+
+ git-svn-id:
https://v8.googlecode.com/svn/branches/bleeding_edge@13273
ce2b1a6d-e550-0410-aec6-3dcde31c8c00
+
+Signed-off-by: Fedor Indutny <fedor(a)indutny.com>
+---
+ src/x64/lithium-codegen-x64.cc | 6 ++--
+ test/mjsunit/regress/regress-164442.js | 45 ++++++++++++++++++++++++++
+ 2 files changed, 48 insertions(+), 3 deletions(-)
+ create mode 100644 test/mjsunit/regress/regress-164442.js
+
+diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
+index b461e62..ff01f44 100644
+--- a/src/x64/lithium-codegen-x64.cc
++++ b/src/x64/lithium-codegen-x64.cc
+@@ -1457,17 +1457,17 @@ void LCodeGen::DoMathMinMax(LMathMinMax* instr) {
+ if (right->IsConstantOperand()) {
+ Immediate right_imm =
+ Immediate(ToInteger32(LConstantOperand::cast(right)));
+- __ cmpq(left_reg, right_imm);
++ __ cmpl(left_reg, right_imm);
+ __ j(condition, &return_left, Label::kNear);
+ __ movq(left_reg, right_imm);
+ } else if (right->IsRegister()) {
+ Register right_reg = ToRegister(right);
+- __ cmpq(left_reg, right_reg);
++ __ cmpl(left_reg, right_reg);
+ __ j(condition, &return_left, Label::kNear);
+ __ movq(left_reg, right_reg);
+ } else {
+ Operand right_op = ToOperand(right);
+- __ cmpq(left_reg, right_op);
++ __ cmpl(left_reg, right_op);
+ __ j(condition, &return_left, Label::kNear);
+ __ movq(left_reg, right_op);
+ }
+diff --git a/test/mjsunit/regress/regress-164442.js
b/test/mjsunit/regress/regress-164442.js
+new file mode 100644
+index 0000000..1160d87
+--- /dev/null
++++ b/test/mjsunit/regress/regress-164442.js
+@@ -0,0 +1,45 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// Flags: --allow-natives-syntax
++
++// Should not take a very long time (n^2 algorithms are bad)
++
++
++function ensureNotNegative(x) {
++ return Math.max(0, x | 0);
++}
++
++
++ensureNotNegative(1);
++ensureNotNegative(2);
++
++%OptimizeFunctionOnNextCall(ensureNotNegative);
++
++var r = ensureNotNegative(-1);
++
++assertEquals(0, r);
+--
+2.0.3
+
diff --git a/v8-3.14.5.10-x64-compare-stubs.patch b/v8-3.14.5.10-x64-compare-stubs.patch
new file mode 100644
index 0000000..bf7c542
--- /dev/null
+++ b/v8-3.14.5.10-x64-compare-stubs.patch
@@ -0,0 +1,116 @@
+From a960d1707a0038bfa5546c669b5b63c35bdb75c5 Mon Sep 17 00:00:00 2001
+From: Fedor Indutny <fedor(a)indutny.com>
+Date: Fri, 2 May 2014 22:44:45 +0400
+Subject: [PATCH] deps: backport 23f2736a from v8 upstream
+
+Original text:
+
+ Fix corner case in x64 compare stubs.
+
+ BUG=v8:2416
+
+ Review URL:
https://codereview.chromium.org/11413087
+
+fix #7528
+---
+ src/x64/code-stubs-x64.cc | 2 +-
+ test/mjsunit/regress/regress-2416.js | 75 ++++++++++++++++++++++++++++
+ 2 files changed, 76 insertions(+), 1 deletion(-)
+ create mode 100644 test/mjsunit/regress/regress-2416.js
+
+diff --git a/src/x64/code-stubs-x64.cc b/deps/v8/src/x64/code-stubs-x64.cc
+index f0f9c5d..9ad0167 100644
+--- a/src/x64/code-stubs-x64.cc
++++ b/src/x64/code-stubs-x64.cc
+@@ -5580,7 +5580,7 @@ void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
+ __ subq(rdx, rax);
+ __ j(no_overflow, &done, Label::kNear);
+ // Correct sign of result in case of overflow.
+- __ SmiNot(rdx, rdx);
++ __ not_(rdx);
+ __ bind(&done);
+ __ movq(rax, rdx);
+ }
+diff --git a/test/mjsunit/regress/regress-2416.js
b/deps/v8/test/mjsunit/regress/regress-2416.js
+new file mode 100644
+index 0000000..02afeb9
+--- /dev/null
++++ b/test/mjsunit/regress/regress-2416.js
+@@ -0,0 +1,75 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++assertFalse(2147483647 < -2147483648)
++assertFalse(2147483647 <= -2147483648)
++assertFalse(2147483647 == -2147483648)
++assertTrue(2147483647 >= -2147483648)
++assertTrue(2147483647 > -2147483648)
++
++assertTrue(-2147483648 < 2147483647)
++assertTrue(-2147483648 <= 2147483647)
++assertFalse(-2147483648 == 2147483647)
++assertFalse(-2147483648 >= 2147483647)
++assertFalse(-2147483648 > 2147483647)
++
++assertFalse(2147483647 < 2147483647)
++assertTrue(2147483647 <= 2147483647)
++assertTrue(2147483647 == 2147483647)
++assertTrue(2147483647 >= 2147483647)
++assertFalse(2147483647 > 2147483647)
++
++assertFalse(-2147483648 < -2147483648)
++assertTrue(-2147483648 <= -2147483648)
++assertTrue(-2147483648 == -2147483648)
++assertTrue(-2147483648 >= -2147483648)
++assertFalse(-2147483648 > -2147483648)
++
++
++assertFalse(1073741823 < -1073741824)
++assertFalse(1073741823 <= -1073741824)
++assertFalse(1073741823 == -1073741824)
++assertTrue(1073741823 >= -1073741824)
++assertTrue(1073741823 > -1073741824)
++
++assertTrue(-1073741824 < 1073741823)
++assertTrue(-1073741824 <= 1073741823)
++assertFalse(-1073741824 == 1073741823)
++assertFalse(-1073741824 >= 1073741823)
++assertFalse(-1073741824 > 1073741823)
++
++assertFalse(1073741823 < 1073741823)
++assertTrue(1073741823 <= 1073741823)
++assertTrue(1073741823 == 1073741823)
++assertTrue(1073741823 >= 1073741823)
++assertFalse(1073741823 > 1073741823)
++
++assertFalse(-1073741824 < -1073741824)
++assertTrue(-1073741824 <= -1073741824)
++assertTrue(-1073741824 == -1073741824)
++assertTrue(-1073741824 >= -1073741824)
++assertFalse(-1073741824 > -1073741824)
+--
+1.9.3
diff --git a/v8-3.14.5.8-CVE-2013-2634.patch b/v8-3.14.5.8-CVE-2013-2634.patch
new file mode 100644
index 0000000..6fdafce
--- /dev/null
+++ b/v8-3.14.5.8-CVE-2013-2634.patch
@@ -0,0 +1,134 @@
+From 5b1d2144ebd47ea768ca5b3cfcda830433c88efe Mon Sep 17 00:00:00 2001
+From: "T.C. Hollingsworth" <tchollingsworth(a)gmail.com>
+Date: Thu, 21 Mar 2013 17:34:19 -0700
+Subject: [PATCH] backport fix for CVE-2013-2632 from SVN r13964
+
+---
+ src/objects-inl.h | 3 ++-
+ src/objects.h | 7 +++++--
+ src/parser.cc | 4 ++--
+ src/parser.h | 5 -----
+ src/stub-cache.cc | 8 ++++----
+ 5 files changed, 13 insertions(+), 14 deletions(-)
+
+diff --git a/src/objects-inl.h b/src/objects-inl.h
+index ea5a93f..4834fa6 100644
+--- a/src/objects-inl.h
++++ b/src/objects-inl.h
+@@ -3500,8 +3500,9 @@ Code::Flags Code::ComputeFlags(Kind kind,
+ kind == CALL_IC ||
+ kind == STORE_IC ||
+ kind == KEYED_STORE_IC);
++ ASSERT(argc <= Code::kMaxArguments);
+ // Compute the bit mask.
+- int bits = KindField::encode(kind)
++ unsigned int bits = KindField::encode(kind)
+ | ICStateField::encode(ic_state)
+ | TypeField::encode(type)
+ | ExtraICStateField::encode(extra_ic_state)
+diff --git a/src/objects.h b/src/objects.h
+index 755dd42..47d7757 100644
+--- a/src/objects.h
++++ b/src/objects.h
+@@ -4180,8 +4180,8 @@ class Code: public HeapObject {
+ // FLAGS_MIN_VALUE and FLAGS_MAX_VALUE are specified to ensure that
+ // enumeration type has correct value range (see Issue 830 for more details).
+ enum Flags {
+- FLAGS_MIN_VALUE = kMinInt,
+- FLAGS_MAX_VALUE = kMaxInt
++ FLAGS_MIN_VALUE = 0,
++ FLAGS_MAX_VALUE = kMaxUInt32
+ };
+
+ #define CODE_KIND_LIST(V) \
+@@ -4644,6 +4644,9 @@ class Code: public HeapObject {
+ // Signed field cannot be encoded using the BitField class.
+ static const int kArgumentsCountShift = 14;
+ static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1);
++ static const int kArgumentsBits =
++ PlatformSmiTagging::kSmiValueSize - Code::kArgumentsCountShift + 1;
++ static const int kMaxArguments = (1 << kArgumentsBits) - 1;
+
+ // This constant should be encodable in an ARM instruction.
+ static const int kFlagsNotUsedInLookup =
+diff --git a/src/parser.cc b/src/parser.cc
+index 03e4b03..6da414a 100644
+--- a/src/parser.cc
++++ b/src/parser.cc
+@@ -4243,7 +4243,7 @@ ZoneList<Expression*>* Parser::ParseArguments(bool* ok) {
+ while (!done) {
+ Expression* argument = ParseAssignmentExpression(true, CHECK_OK);
+ result->Add(argument, zone());
+- if (result->length() > kMaxNumFunctionParameters) {
++ if (result->length() > Code::kMaxArguments) {
+ ReportMessageAt(scanner().location(), "too_many_arguments",
+ Vector<const char*>::empty());
+ *ok = false;
+@@ -4420,7 +4420,7 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String>
function_name,
+
+ top_scope_->DeclareParameter(param_name, VAR);
+ num_parameters++;
+- if (num_parameters > kMaxNumFunctionParameters) {
++ if (num_parameters > Code::kMaxArguments) {
+ ReportMessageAt(scanner().location(), "too_many_parameters",
+ Vector<const char*>::empty());
+ *ok = false;
+diff --git a/src/parser.h b/src/parser.h
+index 93fd1b8..e36a9b3 100644
+--- a/src/parser.h
++++ b/src/parser.h
+@@ -449,11 +449,6 @@ class Parser {
+ Vector<Handle<String> > args);
+
+ private:
+- // Limit on number of function parameters is chosen arbitrarily.
+- // Code::Flags uses only the low 17 bits of num-parameters to
+- // construct a hashable id, so if more than 2^17 are allowed, this
+- // should be checked.
+- static const int kMaxNumFunctionParameters = 32766;
+ static const int kMaxNumFunctionLocals = 131071; // 2^17-1
+
+ enum Mode {
+diff --git a/src/stub-cache.cc b/src/stub-cache.cc
+index 4119147..8490c7e 100644
+--- a/src/stub-cache.cc
++++ b/src/stub-cache.cc
+@@ -617,7 +617,7 @@ Handle<Code> StubCache::ComputeCallConstant(int argc,
+ Handle<Code> code =
+ compiler.CompileCallConstant(object, holder, function, name, check);
+ code->set_check_type(check);
+- ASSERT_EQ(flags, code->flags());
++ ASSERT(flags == code->flags());
+ PROFILE(isolate_,
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+@@ -655,7 +655,7 @@ Handle<Code> StubCache::ComputeCallField(int argc,
+ Handle<Code> code =
+ compiler.CompileCallField(Handle<JSObject>::cast(object),
+ holder, index, name);
+- ASSERT_EQ(flags, code->flags());
++ ASSERT(flags == code->flags());
+ PROFILE(isolate_,
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+@@ -692,7 +692,7 @@ Handle<Code> StubCache::ComputeCallInterceptor(int argc,
+ Handle<Code> code =
+ compiler.CompileCallInterceptor(Handle<JSObject>::cast(object),
+ holder, name);
+- ASSERT_EQ(flags, code->flags());
++ ASSERT(flags == code->flags());
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+@@ -721,7 +721,7 @@ Handle<Code> StubCache::ComputeCallGlobal(int argc,
+ CallStubCompiler compiler(isolate(), argc, kind, extra_state, cache_holder);
+ Handle<Code> code =
+ compiler.CompileCallGlobal(receiver, holder, cell, function, name);
+- ASSERT_EQ(flags, code->flags());
++ ASSERT(flags == code->flags());
+ PROFILE(isolate(),
+ CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG), *code, *name));
+ GDBJIT(AddCode(GDBJITInterface::CALL_IC, *name, *code));
+--
+1.8.1.4
+
diff --git a/v8-3.4.14-CVE-2014-3152.patch b/v8-3.4.14-CVE-2014-3152.patch
new file mode 100644
index 0000000..a399c79
--- /dev/null
+++ b/v8-3.4.14-CVE-2014-3152.patch
@@ -0,0 +1,13 @@
+diff -up v8-3.14.5.10/src/arm/lithium-codegen-arm.cc.cve20143152
v8-3.14.5.10/src/arm/lithium-codegen-arm.cc
+--- v8-3.14.5.10/src/arm/lithium-codegen-arm.cc.cve20143152 2015-04-23 14:51:20.095648219
-0400
++++ v8-3.14.5.10/src/arm/lithium-codegen-arm.cc 2015-04-23 14:53:28.834149299 -0400
+@@ -3034,7 +3034,8 @@ MemOperand LCodeGen::PrepareKeyedOperand
+ return MemOperand(base, scratch0(), LSL, shift_size);
+ } else {
+ ASSERT_EQ(-1, shift_size);
+- return MemOperand(base, scratch0(), LSR, 1);
++ // key can be negative, so using ASR here.
++ return MemOperand(base, scratch0(), ASR, 1);
+ }
+ }
+
diff --git a/v8-314.spec b/v8-314.spec
new file mode 100644
index 0000000..c2328c8
--- /dev/null
+++ b/v8-314.spec
@@ -0,0 +1,839 @@
+# Hi Googlers! If you're looking in here for patches, nifty.
+# You (and everyone else) are welcome to use any of my Chromium spec files and
+# patches under the terms of the GPLv2 or later.
+# You (and everyone else) are welcome to use any of my V8-specific spec files
+# and patches under the terms of the BSD license.
+# You (and everyone else) may NOT use my spec files or patches under any other
+# terms.
+# I hate to be a party-pooper here, but I really don't want to help Google
+# make a proprietary browser. There are enough of those already.
+# All copyrightable work in these spec files and patches is Copyright 2011
+# Tom Callaway <spot(a)fedoraproject.org>
+
+# For the 1.2 branch, we use 0s here
+# For 1.3+, we use the three digit versions
+# Hey, now there are four digits. What do they mean? Popsicle.
+%global somajor 3
+%global sominor 14
+%global sobuild 5
+%global sotiny 10
+%global sover %{somajor}.%{sominor}.%{sobuild}
+%global truename v8
+# You don't really want to turn this on, because the "v8" package has this,
and we'd
+# conflict for no good reason.
+%global with_python 0
+
+Name: %{truename}-314
+Version: %{somajor}.%{sominor}.%{sobuild}.%{sotiny}
+Release: 16%{?dist}
+Summary: JavaScript Engine
+License: BSD
+URL:
https://developers.google.com/v8/
+# Once found at
http://commondatastorage.googleapis.com/chromium-browser-official/
+# Now, we're the canonical source for the tarball. :/
+Source0: v8-%{version}.tar.bz2
+ExclusiveArch: %{ix86} x86_64 %{arm} mips mipsel ppc ppc64
+BuildRequires: scons, readline-devel, libicu-devel
+BuildRequires: valgrind-devel
+BuildRequires: gcc, gcc-c++
+
+#backport fix for CVE-2013-2634 (RHBZ#924495)
+Patch1: v8-3.14.5.8-CVE-2013-2634.patch
+
+#backport fix for CVE-2013-2882 (RHBZ#991116)
+Patch2: v8-3.14.5.10-CVE-2013-2882.patch
+
+#backport fix for CVE-2013-6640 (RHBZ#1039889)
+Patch3: v8-3.14.5.10-CVE-2013-6640.patch
+
+#backport fix for enumeration for objects with lots of properties
+#
https://codereview.chromium.org/11362182
+Patch4: v8-3.14.5.10-enumeration.patch
+
+#backport fix for CVE-2013-6640 (RHBZ#1059070)
+Patch5: v8-3.14.5.10-CVE-2013-6650.patch
+
+#backport only applicable fix for CVE-2014-1704 (RHBZ#1077136)
+#the other two patches don't affect this version of v8
+Patch6: v8-3.14.5.10-CVE-2014-1704-1.patch
+
+# use clock_gettime() instead of gettimeofday(), which increases performance
+# dramatically on virtual machines
+#
https://github.com/joyent/node/commit/f9ced08de30c37838756e8227bd091f80ad...
+# see above link or head of patch for complete rationale
+Patch7: v8-3.14.5.10-use-clock_gettime.patch
+
+# fix corner case in x64 compare stubs
+# fixes bug resulting in an incorrect result when comparing certain integers
+# (e.g. 2147483647 > -2147483648 is false instead of true)
+#
https://code.google.com/p/v8/issues/detail?id=2416
+#
https://github.com/joyent/node/issues/7528
+Patch8: v8-3.14.5.10-x64-compare-stubs.patch
+
+# backport security fix for memory corruption/stack overflow (RHBZ#1125464)
+#
https://groups.google.com/d/msg/nodejs/-siJEObdp10/2xcqqmTHiEMJ
+#
https://github.com/joyent/node/commit/530af9cb8e700e7596b3ec812bad123c9fa...
+Patch9: v8-3.14.5.10-mem-corruption-stack-overflow.patch
+
+# backport bugfix for x64 MathMinMax:
+# Fix x64 MathMinMax for negative untagged int32 arguments.
+# An untagged int32 has zeros in the upper half even if it is negative.
+# Using cmpq to compare such numbers will incorrectly ignore the sign.
+#
https://github.com/joyent/node/commit/3530fa9cd09f8db8101c4649cab03bcdf76...
+Patch10: v8-3.14.5.10-x64-MathMinMax.patch
+
+# backport bugfix that eliminates unused-local-typedefs warning
+#
https://github.com/joyent/node/commit/53b4accb6e5747b156be91a2b90f42607e3...
+Patch11: v8-3.14.5.10-unused-local-typedefs.patch
+
+# backport security fix: Fix Hydrogen bounds check elimination
+# resolves CVE-2013-6668 (RHBZ#1086120)
+#
https://github.com/joyent/node/commit/fd80a31e0697d6317ce8c2d289575399f4e...
+Patch12: v8-3.14.5.10-CVE-2013-6668.patch
+
+# backport fix to segfault caused by the above patch
+#
https://github.com/joyent/node/commit/3122e0eae64c5ab494b29d0a9cadef902d9...
+Patch13: v8-3.14.5.10-CVE-2013-6668-segfault.patch
+
+# Use system valgrind header
+#
https://bugzilla.redhat.com/show_bug.cgi?id=1141483
+Patch14: v8-3.14.5.10-system-valgrind.patch
+
+# Fix issues with abort on uncaught exception
+#
https://github.com/joyent/node/pull/8666
+#
https://github.com/joyent/node/issues/8631
+#
https://github.com/joyent/node/issues/8630
+Patch15: v8-3.14.5.10-abort-uncaught-exception.patch
+
+# Fix unhandled ReferenceError in debug-debugger.js
+#
https://github.com/joyent/node/commit/0ff51c6e063e3eea9e4d9ea68edc82d9356...
+#
https://codereview.chromium.org/741683002
+Patch16: v8-3.14.5.10-unhandled-ReferenceError.patch
+
+# Don't busy loop in CPU profiler thread
+#
https://github.com/joyent/node/pull/8789
+Patch17: v8-3.14.5.10-busy-loop.patch
+
+# Log V8 version in profiler log file
+# (needed for compatibility with profiler tools)
+#
https://github.com/joyent/node/pull/9043
+#
https://codereview.chromium.org/806143002
+Patch18: v8-3.14.5.10-profiler-log.patch
+
+# Fix CVE in ARM code
+#
https://bugzilla.redhat.com/show_bug.cgi?id=1101057
+#
https://codereview.chromium.org/219473002
+Patch19: v8-3.4.14-CVE-2014-3152.patch
+
+# Add REPLACE_INVALID_UTF8 handling that nodejs needs
+Patch20: v8-3.14.5.10-REPLACE_INVALID_UTF8.patch
+
+# mips support (from debian)
+Patch21: 0002_mips.patch
+Patch22: 0002_mips_r15102_backport.patch
+Patch23: 0002_mips_r19121_backport.patch
+
+# Forced whole instruction cache flushing on Loongson (from debian)
+Patch24: 0012_loongson_force_cache_flush.patch
+
+# ppc/ppc64 support (from Ubuntu, who got it from IBM)
+# Rediffed from 0099_powerpc_support.patch
+Patch25: v8-powerpc-support.patch
+
+# Fix for CVE-2016-1669 (thanks to bhoordhuis)
+Patch26: v8-3.14.5.10-CVE-2016-1669.patch
+
+# Report builtins by name
+#
https://github.com/nodejs/node/commit/5a60e0d904c38c2bdb04785203b1b784967...
+Patch27: v8-3.14.5.10-report-builtins-by-name.patch
+
+# Fix compile with gcc7
+# (thanks to Ben Noordhuis)
+Patch28: v8-3.14.5.10-gcc7.patch
+
+# MOAR PPC
+Patch29: v8-powerpc-support-SConstruct.patch
+
+# GCC8 HAPPY FUN TIME
+Patch30: v8-3.14.5.10-gcc8.patch
+
+%description
+V8 is Google's open source JavaScript engine. V8 is written in C++ and is used
+in Google Chrome, the open source browser from Google. V8 implements ECMAScript
+as specified in ECMA-262, 3rd edition. This is version 3.14, which is no longer
+maintained by Google, but was adopted by a lot of other software.
+
+%package devel
+Summary: Development headers and libraries for v8
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description devel
+Development headers and libraries for v8 3.14.
+
+%if 0%{?with_python}
+%package python
+Summary: Python libraries from v8
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description python
+Python libraries from v8.
+%endif
+
+%prep
+%setup -q -n %{truename}-%{version}
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
+%patch6 -p1
+%patch7 -p1
+%patch8 -p1
+%patch9 -p1
+%patch10 -p1
+%patch11 -p1
+%patch12 -p1
+%patch13 -p1
+%patch14 -p1 -b .system-valgrind
+%patch15 -p1 -b .abort-uncaught-exception
+%patch16 -p1 -b .unhandled-ReferenceError
+%patch17 -p1 -b .busy-loop
+%patch18 -p1 -b .profiler-log
+%patch19 -p1 -b .cve20143152
+%patch20 -p1 -b .riu
+%patch21 -p1 -b .mips
+%patch22 -p1 -b .r15102
+%patch23 -p1 -b .r19121
+%patch24 -p1 -b .loongson
+%patch25 -p1 -b .ppc
+%patch26 -p1 -b .CVE-2016-1669
+%patch27 -p1 -b .builtinname
+%patch28 -p1 -b .gcc7
+%patch29 -p1 -b .ppc-harder
+%patch30 -p1 -b .gcc8
+
+# Do not need this lying about.
+rm -rf src/third_party/valgrind
+
+#Patch7 needs -lrt on glibc < 2.17 (RHEL <= 6)
+%if (0%{?rhel} > 6 || 0%{?fedora} > 18)
+%global lrt %{nil}
+%else
+%global lrt -lrt
+%endif
+
+# -fno-strict-aliasing is needed with gcc 4.4 to get past some ugly code
+PARSED_OPT_FLAGS=`echo \'$RPM_OPT_FLAGS %{lrt} -fPIC -fno-strict-aliasing
-Wno-unused-parameter -Wno-error=strict-overflow -Wno-unused-but-set-variable
-Wno-error=cast-function-type -Wno-error=class-memaccess
-fno-delete-null-pointer-checks\'| sed "s/ /',/g" | sed
"s/',/', '/g"`
+sed -i "s|'-O3',|$PARSED_OPT_FLAGS,|g" SConstruct
+
+# clear spurious executable bits
+find . \( -name \*.cc -o -name \*.h -o -name \*.py \) -a -executable \
+ |while read FILE ; do
+ echo $FILE
+ chmod -x $FILE
+ done
+
+%build
+mkdir -p obj/release/
+export GCC_VERSION="44"
+
+# SCons is going away, but for now build with
+# I_know_I_should_build_with_GYP=yes
+scons library=shared snapshots=on \
+%ifarch x86_64
+arch=x64 \
+%endif
+%ifarch ppc64
+arch=ppc64 \
+%endif
+%ifarch ppc
+arch=ppc \
+%endif
+%ifarch armv7hl armv7hnl
+armeabi=hard \
+%endif
+%ifarch armv5tel armv6l armv7l
+armeabi=soft \
+%endif
+visibility=default \
+env=CCFLAGS:"-fPIC" \
+I_know_I_should_build_with_GYP=yes
+
+%if 0%{?fedora} >= 16
+export ICU_LINK_FLAGS=`pkg-config --libs-only-l icu-i18n`
+%else
+export ICU_LINK_FLAGS=`pkg-config --libs-only-l icu`
+%endif
+
+# When will people learn to create versioned shared libraries by default?
+# first, lets get rid of the old .so file
+rm -rf libv8.so libv8preparser.so
+# Now, lets make it right.
+g++ $RPM_OPT_FLAGS -fPIC -o libv8preparser.so.%{sover} -shared
-Wl,-soname,libv8preparser.so.%{somajor} \
+ obj/release/allocation.os \
+ obj/release/bignum.os \
+ obj/release/bignum-dtoa.os \
+ obj/release/cached-powers.os \
+ obj/release/diy-fp.os \
+ obj/release/dtoa.os \
+ obj/release/fast-dtoa.os \
+ obj/release/fixed-dtoa.os \
+ obj/release/preparse-data.os \
+ obj/release/preparser-api.os \
+ obj/release/preparser.os \
+ obj/release/scanner.os \
+ obj/release/strtod.os \
+ obj/release/token.os \
+ obj/release/unicode.os \
+ obj/release/utils.os
+
+# "obj/release/preparser-api.os" should not be included in the libv8.so file.
+export RELEASE_BUILD_OBJS=`echo obj/release/*.os | sed
's|obj/release/preparser-api.os||g'`
+
+%ifarch %{arm}
+g++ $RPM_OPT_FLAGS -fPIC -o libv8.so.%{sover} -shared -Wl,-soname,libv8.so.%{somajor}
$RELEASE_BUILD_OBJS obj/release/extensions/*.os obj/release/arm/*.os $ICU_LINK_FLAGS
+%endif
+%ifarch %{ix86}
+g++ $RPM_OPT_FLAGS -fPIC -o libv8.so.%{sover} -shared -Wl,-soname,libv8.so.%{somajor}
$RELEASE_BUILD_OBJS obj/release/extensions/*.os obj/release/ia32/*.os $ICU_LINK_FLAGS
+%endif
+%ifarch x86_64
+g++ $RPM_OPT_FLAGS -fPIC -o libv8.so.%{sover} -shared -Wl,-soname,libv8.so.%{somajor}
$RELEASE_BUILD_OBJS obj/release/extensions/*.os obj/release/x64/*.os $ICU_LINK_FLAGS
+%endif
+%ifarch mips
+g++ $RPM_OPT_FLAGS -fPIC -o libv8.so.%{sover} -shared -Wl,-soname,libv8.so.%{somajor}
$RELEASE_BUILD_OBJS obj/release/extensions/*.os obj/release/mips/*.os $ICU_LINK_FLAGS
+%endif
+%ifarch mipsel
+g++ $RPM_OPT_FLAGS -fPIC -o libv8.so.%{sover} -shared -Wl,-soname,libv8.so.%{somajor}
$RELEASE_BUILD_OBJS obj/release/extensions/*.os obj/release/mipsel/*.os $ICU_LINK_FLAGS
+%endif
+%ifarch ppc ppc64
+g++ $RPM_OPT_FLAGS -fPIC -o libv8.so.%{sover} -shared -Wl,-soname,libv8.so.%{somajor}
$RELEASE_BUILD_OBJS obj/release/extensions/*.os obj/release/ppc/*.os $ICU_LINK_FLAGS
+%endif
+
+
+# We need to do this so d8 can link against it.
+ln -sf libv8.so.%{sover} libv8.so
+ln -sf libv8preparser.so.%{sover} libv8preparser.so
+
+# This will fail to link d8 because it doesn't use the icu libs.
+# Don't build d8 shared. Stupid Google. Hate.
+# SCons is going away, but for now build with
+# I_know_I_should_build_with_GYP=yes
+scons d8 \
+I_know_I_should_build_with_GYP=yes \
+%ifarch x86_64
+arch=x64 \
+%endif
+%ifarch armv7hl armv7hnl
+armeabi=hard \
+%endif
+%ifarch armv5tel armv6l armv7l
+armeabi=soft \
+%endif
+%ifarch ppc64
+arch=ppc64 \
+%endif
+%ifarch ppc
+arch=ppc \
+%endif
+snapshots=on console=readline visibility=default || :
+# library=shared snapshots=on console=readline visibility=default || :
+
+# Sigh. I f*****g hate scons.
+# But gyp is worse.
+# rm -rf d8
+
+# g++ $RPM_OPT_FLAGS -o d8 obj/release/d8.os -lreadline -lpthread -L. -lv8
$ICU_LINK_FLAGS
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}%{_includedir}/v8-3.14/
+mkdir -p %{buildroot}%{_libdir}
+install -p include/*.h %{buildroot}%{_includedir}/v8-3.14/
+install -p libv8.so.%{sover} %{buildroot}%{_libdir}
+install -p libv8preparser.so.%{sover} %{buildroot}%{_libdir}
+mkdir -p %{buildroot}%{_bindir}
+install -p -m0755 d8 %{buildroot}%{_bindir}/d8-314
+
+pushd %{buildroot}%{_libdir}
+ln -sf libv8.so.%{sover} libv8.so
+ln -sf libv8.so.%{sover} libv8.so.%{somajor}
+ln -sf libv8.so.%{sover} libv8.so.%{somajor}.%{sominor}
+ln -sf libv8preparser.so.%{sover} libv8preparser.so
+ln -sf libv8preparser.so.%{sover} libv8preparser.so.%{somajor}
+ln -sf libv8preparser.so.%{sover} libv8preparser.so.%{somajor}.%{sominor}
+popd
+
+chmod -x %{buildroot}%{_includedir}/v8-3.14/v8*.h
+
+mkdir -p %{buildroot}%{_includedir}/v8-3.14/v8/extensions/
+install -p src/extensions/*.h %{buildroot}%{_includedir}/v8-3.14/v8/extensions/
+
+chmod -x %{buildroot}%{_includedir}/v8-3.14/v8/extensions/*.h
+
+%if 0%{?with_python}
+# install Python JS minifier scripts for nodejs
+install -d %{buildroot}%{python_sitelib}
+sed -i 's|/usr/bin/python2.4|/usr/bin/env python|g' tools/jsmin.py
+sed -i 's|/usr/bin/python2.4|/usr/bin/env python|g' tools/js2c.py
+install -p -m0744 tools/jsmin.py %{buildroot}%{python_sitelib}/
+install -p -m0744 tools/js2c.py %{buildroot}%{python_sitelib}/
+chmod -R -x %{buildroot}%{python_sitelib}/*.py*
+%endif
+
+%ldconfig_scriptlets
+
+%files
+%doc AUTHORS ChangeLog
+%license LICENSE
+%{_bindir}/d8-314
+%{_libdir}/*.so.*
+
+%files devel
+%{_includedir}/v8-3.14/
+%{_libdir}/*.so
+
+%if 0%{?with_python}
+%files python
+%{python2_sitelib}/j*.py*
+%endif
+
+%changelog
+* Sun Feb 17 2019 Igor Gnatenko <ignatenkobrain(a)fedoraproject.org> - 3.14.5.10-16
+- Rebuild for readline 8.0
+
+* Sun Feb 03 2019 Fedora Release Engineering <releng(a)fedoraproject.org> -
3.14.5.10-15
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild
+
+* Wed Jan 23 2019 Pete Walter <pwalter(a)fedoraproject.org> - 3.14.5.10-14
+- Rebuild for ICU 63
+
+* Tue Jul 24 2018 Tom Callaway <spot(a)fedoraproject.org> - 3.14.5.10-13
+- add BuildRequires: gcc, gcc-c++
+
+* Sat Jul 14 2018 Fedora Release Engineering <releng(a)fedoraproject.org> -
3.14.5.10-12
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild
+
+* Tue Jul 10 2018 Pete Walter <pwalter(a)fedoraproject.org> - 3.14.5.10-11
+- Rebuild for ICU 62
+
+* Fri May 18 2018 Tom Callaway <spot(a)fedoraproject.org> - 3.14.5.10-10
+- fix build of this dinosaur with gcc8, mostly by telling the compiler to ignore all the
nasty code errors
+
+* Fri Feb 09 2018 Fedora Release Engineering <releng(a)fedoraproject.org> -
3.14.5.10-9
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Thu Nov 30 2017 Pete Walter <pwalter(a)fedoraproject.org> - 3.14.5.10-8
+- Rebuild for ICU 60.1
+
+* Thu Aug 03 2017 Fedora Release Engineering <releng(a)fedoraproject.org> -
3.14.5.10-7
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Thu Jul 27 2017 Fedora Release Engineering <releng(a)fedoraproject.org> -
3.14.5.10-6
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Tue Feb 28 2017 Tom Callaway <spot(a)fedoraproject.org> - 3.14.5.10-5
+- fix FTBFS (thanks to Ben Noordhuis)
+
+* Sat Feb 11 2017 Fedora Release Engineering <releng(a)fedoraproject.org> -
3.14.5.10-4
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Mon Jul 25 2016 Tom Callaway <spot(a)fedoraproject.org> - 3.14.5.10-3
+- drop epoch (new package, doesn't need it)
+
+* Wed Jul 6 2016 Tom Callaway <spot(a)fedoraproject.org> - 1:3.14.5.10-2
+- apply fixes from nodejs for CVE-2016-1669 and reporting builtins by name
+
+* Tue Jun 7 2016 Tom Callaway <spot(a)fedoraproject.org> - 1:3.14.5.10-1
+- make into v8-314 package
+
+* Mon Jun 06 2016 Vt Ondruch <vondruch(a)redhat.com> - 1:3.14.5.10-24
+- Use "-fno-delete-null-pointer-checks" to workaround GCC 6.x compatibility
+ (rhbz#1331480, rhbz#1331458).
+
+* Fri Feb 05 2016 Fedora Release Engineering <releng(a)fedoraproject.org> -
1:3.14.5.10-23
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild
+
+* Wed Oct 28 2015 David Tardon <dtardon(a)redhat.com> - 1:3.14.5.10-22
+- rebuild for ICU 56.1
+
+* Mon Sep 21 2015 Tom Callaway <spot(a)fedoraproject.org> - 1:3.14.5.10-21
+- add REPLACE_INVALID_UTF8 code needed for nodejs
+
+* Fri Jun 19 2015 Fedora Release Engineering <rel-eng(a)lists.fedoraproject.org> -
1:3.14.5.10-20
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Mon Jun 8 2015 Tom Callaway <spot(a)fedoraproject.org> - 1:3.14.5.10-19
+- split off python subpackage (bz 959145)
+
+* Thu Apr 23 2015 Tom Callaway <spot(a)fedoraproject.org> - 1:3.14.5.10-18
+- backport security fix for ARM - CVE-2014-3152
+
+* Thu Feb 19 2015 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-17
+- backports for nodejs 0.10.36
+
+* Mon Jan 26 2015 David Tardon <dtardon(a)redhat.com> - 1:3.14.5.10-16
+- rebuild for ICU 54.1
+
+* Tue Dec 2 2014 Tom Callaway <spot(a)fedoraproject.org> - 1:3.14.5.10-15
+- use system valgrind header (bz1141483)
+
+* Wed Sep 17 2014 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-14
+- backport bugfix that eliminates unused-local-typedefs warning
+- backport security fix: Fix Hydrogen bounds check elimination (CVE-2013-6668;
RHBZ#1086120)
+- backport fix to segfault caused by the above patch
+
+* Tue Aug 26 2014 David Tardon <dtardon(a)redhat.com> - 1:3.14.5.10-13
+- rebuild for ICU 53.1
+
+* Mon Aug 18 2014 Fedora Release Engineering <rel-eng(a)lists.fedoraproject.org> -
1:3.14.5.10-12
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Thu Jul 31 2014 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-11
+- backport security fix for memory corruption and stack overflow (RHBZ#1125464)
+
https://groups.google.com/d/msg/nodejs/-siJEObdp10/2xcqqmTHiEMJ
+- backport bug fix for x64 MathMinMax for negative untagged int32 arguments.
+
https://github.com/joyent/node/commit/3530fa9cd09f8db8101c4649cab03bcdf76...
+
+* Thu Jun 19 2014 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-10
+- fix corner case in integer comparisons (v8 bug#2416; nodejs bug#7528)
+
+* Sun Jun 08 2014 Fedora Release Engineering <rel-eng(a)lists.fedoraproject.org> -
1:3.14.5.10-9
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Sat May 03 2014 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-8
+- use clock_gettime() instead of gettimeofday(), which increases V8 performance
+ dramatically on virtual machines
+
+* Tue Mar 18 2014 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-7
+- backport fix for unsigned integer arithmetic (RHBZ#1077136; CVE-2014-1704)
+
+* Mon Feb 24 2014 Tomas Hrcka <thrcka(a)redhat.com> - 1:3.14.5.10-6
+- Backport fix for incorrect handling of popular pages (RHBZ#1059070; CVE-2013-6640)
+
+* Fri Feb 14 2014 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-5
+- rebuild for icu-52
+
+* Mon Jan 27 2014 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-4
+- backport fix for enumeration for objects with lots of properties
+
+* Fri Dec 13 2013 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-3
+- backport fix for out-of-bounds read DoS (RHBZ#1039889; CVE-2013-6640)
+
+* Fri Aug 02 2013 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-2
+- backport fix for remote DoS or unspecified other impact via type confusion
+ (RHBZ#991116; CVE-2013-2882)
+
+* Wed May 29 2013 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.10-1
+- new upstream release 3.14.5.10
+
+* Mon May 06 2013 Stanislav Ochotnicky <sochotnicky(a)redhat.com> - 1:3.14.5.8-2
+- Fix ownership of include directory (#958729)
+
+* Fri Mar 22 2013 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.14.5.8-1
+- new upstream release 3.14.5.8
+- backport security fix for remote DoS via crafted javascript (RHBZ#924495;
CVE-2013-2632)
+
+* Mon Mar 11 2013 Stephen Gallagher <sgallagh(a)redhat.com> - 1:3.14.5.7-3
+- Update to v8 3.14.5.7 for Node.js 0.10.0
+
+* Sat Jan 26 2013 T.C. Hollingsworth <tchollingsworth(a)gmail.com> - 1:3.13.7.5-2
+- rebuild for icu-50
+- ignore new GCC 4.8 warning
+
+* Tue Dec 4 2012 Tom Callaway <spot(a)fedoraproject.org> - 1:3.13.7.5-1
+- update to 3.13.7.5 (needed for chromium 23)
+- Resolves multiple security issues (CVE-2012-5120, CVE-2012-5128)
+- d8 is now using a static libv8, resolves bz 881973)
+
+* Sun Jul 22 2012 Fedora Release Engineering <rel-eng(a)lists.fedoraproject.org> -
1:3.10.8-2
+- Rebuilt for
https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Fri Jul 6 2012 Tom Callaway <spot(a)fedoraproject.org> 1:3.10.8-1
+- update to 3.10.8 (chromium 20)
+
+* Tue Jun 12 2012 Tom Callaway <spot(a)fedoraproject.org> 1:3.9.24-1
+- update to 3.9.24 (chromium 19)
+
+* Mon Apr 23 2012 Thomas Spura <tomspur(a)fedoraproject.org> 1:3.7.12.6
+- rebuild for icu-49
+
+* Fri Mar 30 2012 Dennis Gilmore <dennis(a)ausil.us> 1:3.7.12-5
+- make sure the right arm abi is used in the second call of scons
+
+* Thu Mar 29 2012 Dennis Gilmore <dennis(a)ausil.us> 1:3.7.12-4
+- use correct arm macros
+- use the correct abis for hard and soft float
+
+* Tue Mar 20 2012 Tom Callaway <spot(a)fedoraproject.org> 3.7.12-3
+- merge changes from Fedora spec file, sync, add epoch
+
+* Fri Feb 17 2012 Tom Callaway <spot(a)fedoraproject.org> 3.7.12-2
+- add -Wno-error=strict-overflow for gcc 4.7 (hack, hack, hack)
+
+* Mon Feb 13 2012 Tom Callaway <spot(a)fedoraproject.org> 3.7.12-1
+- update to 3.7.12
+
+* Thu Nov 3 2011 Tom Callaway <spot(a)fedoraproject.org> 3.5.10-1
+- update to 3.5.10
+
+* Mon Sep 26 2011 Tom Callaway <spot(a)fedoraproject.org> 3.4.14-2
+- final 3.4.14 tag
+- include JavaScript minifier scripts in -devel
+
+* Fri Jun 10 2011 Tom Callaway <spot(a)fedoraproject.org> 3.2.10-1
+- tag 3.2.10
+
+* Thu Apr 28 2011 Tom Callaway <spot(a)fedoraproject.org> 3.1.8-1
+- "stable" v8 match for "stable" chromium (tag 3.1.8)
+
+* Tue Feb 22 2011 Tom Callaway <spot(a)fedoraproject.org> 3.1.5-1.20110222svn6902
+- update to 3.1.5
+- enable experimental i18n icu stuff for chromium
+
+* Tue Jan 11 2011 Tom Callaway <spot(a)fedoraproject.org> 3.0.7-1.20110111svn6276
+- update to 3.0.7
+
+* Tue Dec 14 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
3.0.0-2.20101209svn5957
+- fix sloppy code where NULL is used
+
+* Thu Dec 9 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
3.0.0-1.20101209svn5957
+- update to 3.0.0
+
+* Fri Oct 22 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.5.1-1.20101022svn5692
+- update to 2.5.1
+- fix another fwrite with no return checking case
+
+* Thu Oct 14 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.5.0-1.20101014svn5625
+- update to 2.5.0
+
+* Mon Oct 4 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.4.8-1.20101004svn5585
+- update to 2.4.8
+
+* Tue Sep 14 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.4.3-1.20100914svn5450
+- update to 2.4.3
+
+* Tue Aug 31 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.11-1.20100831svn5385
+- update to svn5385
+
+* Fri Aug 27 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.11-1.20100827svn5365
+- update to 2.3.11, svn5365
+
+* Tue Aug 24 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.10-1.20100824svn5332
+- update to 2.3.10, svn5332
+
+* Wed Aug 18 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.9-1.20100819svn5308
+- update to 2.3.9, svn5308
+
+* Wed Aug 11 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.7-1.20100812svn5251
+- update to svn5251
+
+* Wed Aug 11 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.7-1.20100811svn5248
+- update to 2.3.7, svn5248
+
+* Tue Aug 10 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.6-1.20100809svn5217
+- update to 2.3.6, svn5217
+
+* Fri Aug 6 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.5-1.20100806svn5198
+- update to 2.3.5, svn5198
+
+* Mon Jul 26 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.3-1.20100726svn5134
+- update to 2.3.3, svn5134
+
+* Fri Jul 16 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.3.0-1.20100716svn5088
+- update to 2.3.0, svn5088
+
+* Tue Jul 6 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.22-1.20100706svn5023
+- update to 2.2.22, svn5023
+
+* Fri Jul 2 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.21-1.20100702svn5010
+- update to svn5010
+
+* Wed Jun 30 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.21-1.20100630svn4993
+- update to 2.2.21, svn4993
+- include checkout script
+
+* Thu Jun 3 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.14-1.20100603svn4792
+- update to 2.2.14, svn4792
+
+* Tue Jun 1 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.13-1.20100601svn4772
+- update to 2.2.13, svn4772
+
+* Thu May 27 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.12-1.20100527svn4747
+- update to 2.2.12, svn4747
+
+* Tue May 25 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.11-1.20100525svn4718
+- update to 2.2.11, svn4718
+
+* Thu May 20 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.10-1.20100520svn4684
+- update to svn4684
+
+* Mon May 17 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.10-1.20100517svn4664
+- update to 2.2.10, svn4664
+
+* Thu May 13 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.9-1.20100513svn4653
+- update to svn4653
+
+* Mon May 10 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.9-1.20100510svn4636
+- update to 2.2.9, svn4636
+
+* Tue May 4 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.7-1.20100504svn4581
+- update to 2.2.7, svn4581
+
+* Mon Apr 19 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.3-1.20100419svn4440
+- update to 2.2.3, svn4440
+
+* Tue Apr 13 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.2-1.20100413svn4397
+- update to 2.2.2, svn4397
+
+* Thu Apr 8 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.1-1.20100408svn4359
+- update to 2.2.1, svn4359
+
+* Mon Mar 29 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.2.0-1.20100329svn4309
+- update to 2.2.0, svn4309
+
+* Thu Mar 25 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.1.8-1.20100325svn4273
+- update to 2.1.8, svn4273
+
+* Mon Mar 22 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.1.5-1.20100322svn4204
+- update to 2.1.5, svn4204
+
+* Mon Mar 15 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.1.4-1.20100315svn4129
+- update to 2.1.4, svn4129
+
+* Wed Mar 10 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.1.0-1.20100310svn4088
+- update to 2.1.3, svn4088
+
+* Thu Feb 18 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.1.0-1.20100218svn3902
+- update to 2.1.0, svn3902
+
+* Fri Jan 22 2010 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.0.6-1.20100122svn3681
+- update to 2.0.6, svn3681
+
+* Tue Dec 29 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.0.5-1.20091229svn3528
+- svn3528
+
+* Mon Dec 21 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.0.5-1.20091221svn3511
+- update to 2.0.5, svn3511
+
+* Wed Dec 9 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.0.3-1.20091209svn3443
+- update to 2.0.3, svn3443
+
+* Tue Nov 24 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.0.2-1.20091124svn3353
+- update to 2.0.2, svn3353
+
+* Wed Nov 18 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
2.0.0-1.20091118svn3334
+- update to 2.0.0, svn3334
+
+* Tue Oct 27 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.16-1.20091027svn3152
+- update to 1.3.16, svn3152
+
+* Tue Oct 13 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.15-1.20091013svn3058
+- update to svn3058
+
+* Thu Oct 8 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.15-1.20091008svn3036
+- update to 1.3.15, svn3036
+
+* Tue Sep 29 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.13-1.20090929svn2985
+- update to svn2985
+- drop unused parameter patch, figured out how to work around it with optflag mangling
+- have I mentioned lately that scons is garbage?
+
+* Mon Sep 28 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.13-1.20090928svn2980
+- update to 1.3.13, svn2980
+
+* Wed Sep 16 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.11-1.20090916svn2903
+- update to 1.3.11, svn2903
+
+* Wed Sep 9 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.9-1.20090909svn2862
+- update to 1.3.9, svn2862
+
+* Thu Aug 27 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.8-1.20090827svn2777
+- update to 1.3.8, svn2777
+
+* Mon Aug 24 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.6-1.20090824svn2747
+- update to 1.3.6, svn2747
+
+* Tue Aug 18 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.4-1.20090818svn2708
+- update to svn2708, build and package d8
+
+* Fri Aug 14 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.4-1.20090814svn2692
+- update to 1.3.4, svn2692
+
+* Wed Aug 12 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.3-1.20090812svn2669
+- update to 1.3.3, svn2669
+
+* Mon Aug 10 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.2-1.20090810svn2658
+- update to svn2658
+
+* Fri Aug 7 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.2-1.20090807svn2653
+- update to svn2653
+
+* Wed Aug 5 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.2-1.20090805svn2628
+- update to 1.3.2, svn2628
+
+* Mon Aug 3 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.1-1.20090803svn2607
+- update to svn2607
+
+* Fri Jul 31 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.1-1.20090731svn2602
+- update to svn2602
+
+* Thu Jul 30 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.1-1.20090730svn2592
+- update to 1.3.1, svn 2592
+
+* Mon Jul 27 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.3.0-1.20090727svn2543
+- update to 1.3.0, svn 2543
+
+* Fri Jul 24 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.14-1.20090724svn2534
+- update to svn2534
+
+* Mon Jul 20 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.14-1.20090720svn2510
+- update to svn2510
+
+* Thu Jul 16 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.14-1.20090716svn2488
+- update to svn2488
+
+* Wed Jul 15 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.14-1.20090715svn2477
+- update to 1.2.14, svn2477
+
+* Mon Jul 13 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.13-1.20090713svn2434
+- update to svn2434
+
+* Sat Jul 11 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.13-1.20090711svn2430
+- update to 1.2.13, svn2430
+
+* Wed Jul 8 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.12-1.20090708svn2391
+- update to 1.2.12, svn2391
+
+* Sat Jul 4 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.11-1.20090704svn2356
+- update to 1.2.11, svn2356
+
+* Fri Jun 26 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.9-1.20090626svn2284
+- update to svn2284
+
+* Wed Jun 24 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.9-1.20090624svn2262
+- update to 1.2.9, svn2262
+
+* Thu Jun 18 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.7-2.20090618svn2219
+- fix unused-parameter patch
+
+* Thu Jun 18 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.7-1.20090618svn2219
+- update to 1.2.8, svn2219
+
+* Mon Jun 8 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.7-2.20090608svn2123
+- fix gcc44 compile for Fedora 11
+
+* Mon Jun 8 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.7-1.20090608svn2123
+- update to 1.2.7, svn2123
+
+* Thu May 28 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.2.5-1.20090528svn2072
+- update to newer svn checkout
+
+* Sun Feb 22 2009 Tom "spot" Callaway <tcallawa(a)redhat.com>
1.0.1-1.20090222svn1332
+- update to newer svn checkout
+
+* Sun Sep 14 2008 Tom "spot" Callaway <tcallawa(a)redhat.com>
0.2-2.20080914svn300
+- make a versioned shared library properly
+
+* Sun Sep 14 2008 Tom "spot" Callaway <tcallawa(a)redhat.com>
0.2-1.20080914svn300
+- Initial package for Fedora
+
diff --git a/v8-powerpc-support-SConstruct.patch b/v8-powerpc-support-SConstruct.patch
new file mode 100644
index 0000000..b17a222
--- /dev/null
+++ b/v8-powerpc-support-SConstruct.patch
@@ -0,0 +1,1640 @@
+diff -up v8-3.14.5.10/SConstruct.ppc-harder v8-3.14.5.10/SConstruct
+--- v8-3.14.5.10/SConstruct.ppc-harder 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/SConstruct 2017-03-01 12:47:36.529605806 -0500
+@@ -143,6 +143,12 @@ LIBRARY_FLAGS = {
+ 'CCFLAGS': ['-m32'],
+ 'LINKFLAGS': ['-m32']
+ },
++ 'arch:ppc': {
++ 'CPPDEFINES': ['V8_TARGET_ARCH_PPC'],
++ },
++ 'arch:ppc64': {
++ 'CPPDEFINES': ['V8_TARGET_ARCH_PPC64',
'V8_TARGET_ARCH_PPC'],
++ },
+ 'arch:arm': {
+ 'CPPDEFINES': ['V8_TARGET_ARCH_ARM'],
+ 'unalignedaccesses:on' : {
+@@ -994,7 +1000,7 @@ def GuessStrictAliasing(env):
+
+ PLATFORM_OPTIONS = {
+ 'arch': {
+- 'values': ['arm', 'ia32', 'x64', 'mips'],
++ 'values': ['arm', 'ia32', 'x64', 'mips',
'ppc64', 'ppc'],
+ 'guess': GuessArch,
+ 'help': 'the architecture to build for'
+ },
+diff -up v8-3.14.5.10/src/ppc/assembler-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/assembler-ppc.cc
+--- v8-3.14.5.10/src/ppc/assembler-ppc.cc.ppc-harder 2017-03-01 12:47:36.471607257 -0500
++++ v8-3.14.5.10/src/ppc/assembler-ppc.cc 2017-03-01 12:47:36.516606131 -0500
+@@ -72,7 +72,7 @@ static bool is_processor(const char* p)
+
+ read_tried = true;
+ if (fd != -1) {
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ static Elf64_auxv_t buffer[16];
+ Elf64_auxv_t *auxv_element;
+ #else
+@@ -359,7 +359,7 @@ Register Assembler::GetRB(Instr instr) {
+ return reg;
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ // This code assumes a FIXED_SEQUENCE for 64bit loads (lis/ori)
+ bool Assembler::Is64BitLoadIntoR12(Instr instr1, Instr instr2,
+ Instr instr3, Instr instr4, Instr instr5) {
+@@ -392,7 +392,7 @@ bool Assembler::IsRlwinm(Instr instr) {
+ return ((instr & kOpcodeMask) == RLWINMX);
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ bool Assembler::IsRldicl(Instr instr) {
+ return (((instr & kOpcodeMask) == EXT5) &&
+ ((instr & kExt5OpcodeMask) == RLDICL));
+@@ -903,7 +903,7 @@ void Assembler::orx(Register dst, Regist
+
+ void Assembler::cmpi(Register src1, const Operand& src2, CRegister cr) {
+ intptr_t imm16 = src2.imm_;
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ int L = 1;
+ #else
+ int L = 0;
+@@ -916,7 +916,7 @@ void Assembler::cmpi(Register src1, cons
+
+ void Assembler::cmpli(Register src1, const Operand& src2, CRegister cr) {
+ uintptr_t uimm16 = src2.imm_;
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ int L = 1;
+ #else
+ int L = 0;
+@@ -928,7 +928,7 @@ void Assembler::cmpli(Register src1, con
+ }
+
+ void Assembler::cmp(Register src1, Register src2, CRegister cr) {
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ int L = 1;
+ #else
+ int L = 0;
+@@ -939,7 +939,7 @@ void Assembler::cmp(Register src1, Regis
+ }
+
+ void Assembler::cmpl(Register src1, Register src2, CRegister cr) {
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ int L = 1;
+ #else
+ int L = 0;
+@@ -1027,7 +1027,7 @@ void Assembler::lwzux(Register rt, const
+ }
+
+ void Assembler::lwa(Register dst, const MemOperand &src) {
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ int offset = src.offset();
+ ASSERT(!src.ra_.is(r0));
+ ASSERT(!(offset & 3) && is_int16(offset));
+@@ -1116,7 +1116,7 @@ void Assembler::andc(Register dst, Regis
+ x_form(EXT2 | ANDCX, dst, src1, src2, rc);
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ // 64bit specific instructions
+ void Assembler::ld(Register rd, const MemOperand &src) {
+ int offset = src.offset();
+@@ -1273,7 +1273,7 @@ void Assembler::marker_asm(int mcode) {
+ // TOC and static chain are ignored and set to 0.
+ void Assembler::function_descriptor() {
+ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE);
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ uint64_t value = reinterpret_cast<uint64_t>(pc_) + 3 * kPointerSize;
+ #if __BYTE_ORDER == __LITTLE_ENDIAN
+ emit(static_cast<uint32_t>(value & 0xFFFFFFFF));
+@@ -1307,7 +1307,7 @@ void Assembler::mov(Register dst, const
+ RecordRelocInfo(src.rmode_, src.imm_);
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ int64_t value = src.immediate();
+ int32_t hi_32 = static_cast<int64_t>(value) >> 32;
+ int32_t lo_32 = static_cast<int32_t>(value);
+@@ -1394,7 +1394,7 @@ void Assembler::info(const char* msg, Co
+ CRegister cr) {
+ if (::v8::internal::FLAG_trace_sim_stubs) {
+ emit(0x7d9ff808);
+-#if V8_TARGET_ARCH_PPC64
++#if defined(V8_TARGET_ARCH_PPC64)
+ uint64_t value = reinterpret_cast<uint64_t>(msg);
+ emit(static_cast<uint32_t>(value >> 32));
+ emit(static_cast<uint32_t>(value & 0xFFFFFFFF));
+@@ -1759,7 +1759,7 @@ void Assembler::GrowBuffer() {
+ // buffer nor pc absolute pointing inside the code buffer, so there is no need
+ // to relocate any emitted relocation entries.
+
+-#if ABI_USES_FUNCTION_DESCRIPTORS
++#if defined(ABI_USES_FUNCTION_DESCRIPTORS)
+ // Relocate runtime entries.
+ for (RelocIterator it(desc); !it.done(); it.next()) {
+ RelocInfo::Mode rmode = it.rinfo()->rmode();
+diff -up v8-3.14.5.10/src/ppc/assembler-ppc-inl.h.ppc-harder
v8-3.14.5.10/src/ppc/assembler-ppc-inl.h
+diff -up v8-3.14.5.10/src/ppc/builtins-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/builtins-ppc.cc
+--- v8-3.14.5.10/src/ppc/builtins-ppc.cc.ppc-harder 2017-03-01 12:47:36.473607207 -0500
++++ v8-3.14.5.10/src/ppc/builtins-ppc.cc 2017-03-01 12:47:36.516606131 -0500
+@@ -1412,7 +1412,7 @@ void Builtins::Generate_FunctionCall(Mac
+ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
+ __ lwz(r6, FieldMemOperand(r5, SharedFunctionInfo::kCompilerHintsOffset));
+ __ TestBit(r6,
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SharedFunctionInfo::kStrictModeFunction,
+ #else
+ SharedFunctionInfo::kStrictModeFunction + kSmiTagSize,
+@@ -1422,7 +1422,7 @@ void Builtins::Generate_FunctionCall(Mac
+
+ // Do not transform the receiver for native (Compilerhints already in r6).
+ __ TestBit(r6,
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SharedFunctionInfo::kNative,
+ #else
+ SharedFunctionInfo::kNative + kSmiTagSize,
+@@ -1650,7 +1650,7 @@ void Builtins::Generate_FunctionApply(Ma
+ Label call_to_object, use_global_receiver;
+ __ lwz(r5, FieldMemOperand(r5, SharedFunctionInfo::kCompilerHintsOffset));
+ __ TestBit(r5,
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SharedFunctionInfo::kStrictModeFunction,
+ #else
+ SharedFunctionInfo::kStrictModeFunction + kSmiTagSize,
+@@ -1660,7 +1660,7 @@ void Builtins::Generate_FunctionApply(Ma
+
+ // Do not transform the receiver for strict mode functions.
+ __ TestBit(r5,
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SharedFunctionInfo::kNative,
+ #else
+ SharedFunctionInfo::kNative + kSmiTagSize,
+diff -up v8-3.14.5.10/src/ppc/codegen-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/codegen-ppc.cc
+--- v8-3.14.5.10/src/ppc/codegen-ppc.cc.ppc-harder 2017-03-01 12:47:36.474607182 -0500
++++ v8-3.14.5.10/src/ppc/codegen-ppc.cc 2017-03-01 12:47:36.516606131 -0500
+@@ -181,7 +181,7 @@ void ElementsTransitionGenerator::Genera
+ __ addi(r10, r9, Operand(FixedDoubleArray::kHeaderSize));
+ __ SmiToDoubleArrayOffset(r9, r8);
+ __ add(r9, r10, r9);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ mov(r7, Operand(kHoleNanInt64));
+ #else
+ __ mov(r7, Operand(kHoleNanLower32));
+@@ -236,7 +236,7 @@ void ElementsTransitionGenerator::Genera
+ __ CompareRoot(r22, Heap::kTheHoleValueRootIndex);
+ __ Assert(eq, "object found in smi-only array");
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ std(r7, MemOperand(r10, 0));
+ #else
+ #if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
+@@ -330,7 +330,7 @@ void ElementsTransitionGenerator::Genera
+ // Non-hole double, copy value into a heap number.
+ __ AllocateHeapNumber(r5, r3, r4, r22, &gc_required);
+ // r5: new heap number
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ ld(r3, MemOperand(r7, -8));
+ __ addi(r4, r5, Operand(-1)); // subtract tag for std
+ __ std(r3, MemOperand(r4, HeapNumber::kValueOffset));
+diff -up v8-3.14.5.10/src/ppc/code-stubs-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/code-stubs-ppc.cc
+--- v8-3.14.5.10/src/ppc/code-stubs-ppc.cc.ppc-harder 2017-03-01 12:47:36.477607107
-0500
++++ v8-3.14.5.10/src/ppc/code-stubs-ppc.cc 2017-03-01 12:47:36.517606106 -0500
+@@ -660,7 +660,7 @@ void FloatingPointHelper::ConvertIntToDo
+ __ subi(sp, sp, Operand(8)); // reserve one temporary double on the stack
+
+ // sign-extend src to 64-bit and store it to temp double on the stack
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(r0, src);
+ __ std(r0, MemOperand(sp, 0));
+ #else
+@@ -692,7 +692,7 @@ void FloatingPointHelper::ConvertUnsigne
+ __ subi(sp, sp, Operand(8)); // reserve one temporary double on the stack
+
+ // zero-extend src to 64-bit and store it to temp double on the stack
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ clrldi(r0, src, Operand(32));
+ __ std(r0, MemOperand(sp, 0));
+ #else
+@@ -722,7 +722,7 @@ void FloatingPointHelper::ConvertIntToFl
+ __ subi(sp, sp, Operand(8)); // reserve one temporary double on the stack
+
+ // sign-extend src to 64-bit and store it to temp double on the stack
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(int_scratch, src);
+ __ std(int_scratch, MemOperand(sp, 0));
+ #else
+@@ -1559,7 +1559,7 @@ void ToBooleanStub::Generate(MacroAssemb
+ __ lfd(d1, FieldMemOperand(tos_, HeapNumber::kValueOffset));
+ __ li(r0, Operand::Zero());
+ __ push(r0);
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ __ push(r0);
+ #endif
+ __ lfd(d2, MemOperand(sp, 0));
+@@ -1847,7 +1847,7 @@ void UnaryOpStub::GenerateHeapNumberCode
+ // Do the bitwise operation and check if the result fits in a smi.
+ __ notx(r4, r4);
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ Label try_float;
+ __ JumpIfNotSmiCandidate(r4, r5, &try_float);
+ #endif
+@@ -1856,7 +1856,7 @@ void UnaryOpStub::GenerateHeapNumberCode
+ __ SmiTag(r3, r4);
+ __ Ret();
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ // Try to store the result in a heap number.
+ __ bind(&try_float);
+ if (mode_ == UNARY_NO_OVERWRITE) {
+@@ -2073,7 +2073,7 @@ void BinaryOpStub::GenerateSmiSmiOperati
+ }
+ case Token::MUL: {
+ Label mul_zero, mul_neg_zero;
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // Remove tag from both operands.
+ __ SmiUntag(ip, right);
+ __ SmiUntag(r0, left);
+@@ -2102,7 +2102,7 @@ void BinaryOpStub::GenerateSmiSmiOperati
+ // Go slow on zero result to handle -0.
+ __ cmpi(scratch1, Operand::Zero());
+ __ beq(&mul_zero);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ SmiTag(right, scratch1);
+ #else
+ __ mr(right, scratch1);
+@@ -2160,7 +2160,7 @@ void BinaryOpStub::GenerateSmiSmiOperati
+ __ sub(scratch1, ip, scratch1, LeaveOE, SetRC);
+ // If the result is 0, we need to check for the -0 case.
+ __ beq(&check_neg_zero, cr0);
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ // Check that the signed result fits in a Smi.
+ __ JumpIfNotSmiCandidate(scratch1, scratch2, ¬_smi_result);
+ #endif
+@@ -2212,7 +2212,7 @@ void BinaryOpStub::GenerateSmiSmiOperati
+ __ SmiUntag(scratch1, left);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+ __ ShiftLeft(scratch1, scratch1, scratch2);
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ // Check that the signed result fits in a Smi.
+ __ JumpIfNotSmiCandidate(scratch1, scratch2, ¬_smi_result);
+ #endif
+@@ -2358,7 +2358,7 @@ void BinaryOpStub::GenerateFPOperation(M
+ // The code below for writing into heap numbers isn't capable of
+ // writing the register as an unsigned int so we go to slow case if we
+ // hit this case.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ const Condition cond = ne;
+ __ srw(r5, r6, r5);
+ __ TestSignBit32(r5, r0);
+@@ -2378,7 +2378,7 @@ void BinaryOpStub::GenerateFPOperation(M
+ UNREACHABLE();
+ }
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ // Check that the *signed* result fits in a smi.
+ __ JumpIfNotSmiCandidate(r5, r6, &result_not_a_smi);
+ #endif
+@@ -2631,7 +2631,7 @@ void BinaryOpStub::GenerateInt32Stub(Mac
+ &transition : &return_heap_number);
+ __ bne(not_int32);
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ // Check if the result fits in a smi.
+ // If not try to return a heap number.
+ __ JumpIfNotSmiCandidate(scratch1, scratch2, &return_heap_number);
+@@ -2643,7 +2643,7 @@ void BinaryOpStub::GenerateInt32Stub(Mac
+
+ __ subi(sp, sp, Operand(8));
+ __ stfd(d1, MemOperand(sp, 0));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ ld(scratch2, MemOperand(sp, 0));
+ #else
+ #if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
+@@ -2743,7 +2743,7 @@ void BinaryOpStub::GenerateInt32Stub(Mac
+ // We only get a negative result if the shift value (r5) is 0.
+ // This result cannot be respresented as a signed 32-bit integer, try
+ // to return a heap number if we can.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ const Condition cond = ne;
+ __ srw(r5, r6, r5);
+ __ TestSignBit32(r5, r0);
+@@ -2764,7 +2764,7 @@ void BinaryOpStub::GenerateInt32Stub(Mac
+ UNREACHABLE();
+ }
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ // Check if the result fits in a smi.
+ // If not try to return a heap number. (We know the result is an int32.)
+ __ JumpIfNotSmiCandidate(r5, scratch1, &return_heap_number);
+@@ -3084,7 +3084,7 @@ void TranscendentalCacheStub::Generate(M
+ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
+ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
+ // Two uint_32's and a pointer.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ CHECK_EQ(16, static_cast<int>(elem2_start - elem_start));
+ #else
+ CHECK_EQ(12, static_cast<int>(elem2_start - elem_start));
+@@ -3095,7 +3095,7 @@ void TranscendentalCacheStub::Generate(M
+ }
+ #endif
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // Find the address of the r4'th entry in the cache, i.e., &r3[r4*16].
+ __ ShiftLeftImm(scratch0, r4, Operand(4));
+ #else
+@@ -3598,12 +3598,12 @@ void CEntryStub::GenerateCore(MacroAssem
+
+ __ mov(isolate_reg, Operand(ExternalReference::isolate_address()));
+
+-#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++#if defined(ABI_USES_FUNCTION_DESCRIPTORS) && !defined(USE_SIMULATOR)
+ // Native AIX/PPC64 Linux use a function descriptor.
+ __ LoadP(ToRegister(2), MemOperand(r15, kPointerSize)); // TOC
+ __ LoadP(ip, MemOperand(r15, 0)); // Instruction address
+ Register target = ip;
+-#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++#elif defined(ABI_TOC_ADDRESSABILITY_VIA_IP)
+ Register target = ip;
+ __ Move(ip, r15);
+ #else
+@@ -3814,7 +3814,7 @@ void JSEntryStub::GenerateBody(MacroAsse
+ Label invoke, handler_entry, exit;
+
+ // Called from C
+-#if ABI_USES_FUNCTION_DESCRIPTORS
++#ifdef ABI_USES_FUNCTION_DESCRIPTORS
+ __ function_descriptor();
+ #endif
+
+@@ -3993,7 +3993,7 @@ void InstanceofStub::Generate(MacroAssem
+ const Register scratch2 = r8;
+ Register scratch3 = no_reg;
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ const int32_t kDeltaToLoadBoolResult = 9 * Assembler::kInstrSize;
+ #else
+ const int32_t kDeltaToLoadBoolResult = 5 * Assembler::kInstrSize;
+@@ -4875,7 +4875,7 @@ void RegExpExecStub::Generate(MacroAssem
+ __ addi(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
+
+
+-#if ABI_USES_FUNCTION_DESCRIPTORS && defined(USE_SIMULATOR)
++#if defined(ABI_USES_FUNCTION_DESCRIPTORS) && defined(USE_SIMULATOR)
+ // Even Simulated AIX/PPC64 Linux uses a function descriptor for the
+ // RegExp routine. Extract the instruction address here since
+ // DirectCEntryStub::GenerateCall will not do it for calls out to
+@@ -6777,12 +6777,12 @@ void DirectCEntryStub::GenerateCall(Macr
+ void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
+ Register target) {
+ Register scratch = r11;
+-#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++#if defined(ABI_USES_FUNCTION_DESCRIPTORS) && !defined(USE_SIMULATOR)
+ Register dest = ip;
+ // Native AIX/PPC64 Linux use a function descriptor.
+ __ LoadP(ToRegister(2), MemOperand(target, kPointerSize)); // TOC
+ __ LoadP(ip, MemOperand(target, 0)); // Instruction address
+-#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++#elif defined(ABI_TOC_ADDRESSABILITY_VIA_IP)
+ Register dest = ip;
+ __ Move(ip, target);
+ #else
+@@ -7411,7 +7411,7 @@ void StoreArrayLiteralElementStub::Gener
+ __ LoadP(r8, FieldMemOperand(r4, JSObject::kElementsOffset));
+ __ SmiToPtrArrayOffset(r9, r6);
+ __ add(r9, r8, r9);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // add due to offset alignment requirements of StorePU
+ __ addi(r9, r9, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ StoreP(r3, MemOperand(r9));
+@@ -7485,11 +7485,11 @@ void ProfileEntryHookStub::Generate(Macr
+ __ mov(ip, Operand(reinterpret_cast<intptr_t>(&entry_hook_)));
+ __ LoadP(ip, MemOperand(ip));
+
+-#if ABI_USES_FUNCTION_DESCRIPTORS
++#ifdef ABI_USES_FUNCTION_DESCRIPTORS
+ // Function descriptor
+ __ LoadP(ToRegister(2), MemOperand(ip, kPointerSize));
+ __ LoadP(ip, MemOperand(ip, 0));
+-#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++#elif defined(ABI_TOC_ADDRESSABILITY_VIA_IP)
+ // ip already set.
+ #endif
+
+diff -up v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc
+--- v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc.ppc-harder 2017-03-01 12:47:36.480607032
-0500
++++ v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc 2017-03-01 12:47:36.518606081 -0500
+@@ -42,7 +42,7 @@ const int Deoptimizer::table_entry_size_
+
+
+ int Deoptimizer::patch_size() {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ const int kCallInstructionSizeInWords = 7;
+ #else
+ const int kCallInstructionSizeInWords = 4;
+@@ -121,7 +121,7 @@ void Deoptimizer::DeoptimizeFunction(JSF
+ }
+
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ static const int32_t kBranchBeforeStackCheck = 0x409c0020;
+ static const int32_t kBranchBeforeInterrupt = 0x409c0044;
+ #else
+@@ -154,7 +154,7 @@ void Deoptimizer::PatchStackCheckCodeAt(
+ ASSERT(Memory::int32_at(pc_after - 2 * kInstrSize) == 0x7d8803a6);
+ ASSERT(Memory::int32_at(pc_after - kInstrSize) == 0x4e800021);
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ASSERT(Assembler::Is64BitLoadIntoR12(
+ Assembler::instr_at(pc_after - 7 * kInstrSize),
+ Assembler::instr_at(pc_after - 6 * kInstrSize),
+@@ -188,7 +188,7 @@ void Deoptimizer::PatchStackCheckCodeAt(
+ // 7d8803a6 mtlr r12
+ // 4e800021 blrl
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ CodePatcher patcher(pc_after - 8 * kInstrSize, 6);
+
+ // Assemble the 64 bit value from the five part load and verify
+@@ -222,7 +222,7 @@ void Deoptimizer::PatchStackCheckCodeAt(
+ patcher.masm()->mov(ip,
+ Operand(reinterpret_cast<uintptr_t>(replacement_code->entry())));
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
+ unoptimized_code, pc_after - 7 * kInstrSize, replacement_code);
+ #else
+@@ -242,7 +242,7 @@ void Deoptimizer::RevertStackCheckCodeAt
+ ASSERT(Memory::int32_at(pc_after - 2 * kInstrSize) == 0x7d8803a6);
+ ASSERT(Memory::int32_at(pc_after - kInstrSize) == 0x4e800021);
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ASSERT(Assembler::Is64BitLoadIntoR12(
+ Assembler::instr_at(pc_after - 7 * kInstrSize),
+ Assembler::instr_at(pc_after - 6 * kInstrSize),
+@@ -255,7 +255,7 @@ void Deoptimizer::RevertStackCheckCodeAt
+ Assembler::instr_at(pc_after - 3 * kInstrSize)));
+ #endif
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // Replace NOP with conditional jump.
+ CodePatcher patcher(pc_after - 8 * kInstrSize, 6);
+ if (FLAG_count_based_interrupts) {
+@@ -285,7 +285,7 @@ void Deoptimizer::RevertStackCheckCodeAt
+ }
+ #endif
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // Assemble the 64 bit value from the five part load and verify
+ // that it is the stack guard code
+ uint64_t stack_check_address =
+@@ -313,7 +313,7 @@ void Deoptimizer::RevertStackCheckCodeAt
+ patcher.masm()->mov(ip,
+ Operand(reinterpret_cast<uintptr_t>(check_code->entry())));
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
+ unoptimized_code, pc_after - 7 * kInstrSize, check_code);
+ #else
+diff -up v8-3.14.5.10/src/ppc/disasm-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/disasm-ppc.cc
+--- v8-3.14.5.10/src/ppc/disasm-ppc.cc.ppc-harder 2017-03-01 12:47:36.480607032 -0500
++++ v8-3.14.5.10/src/ppc/disasm-ppc.cc 2017-03-01 12:47:36.518606081 -0500
+@@ -346,7 +346,7 @@ int Decoder::FormatOption(Instruction* i
+ return 2;
+ }
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case 'd': { // ds value for offset
+ int32_t value = SIGN_EXT_IMM16(instr->Bits(15, 0) & ~3);
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
+@@ -565,7 +565,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "srw'. 'ra, 'rs, 'rb");
+ return;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case SRDX: {
+ Format(instr, "srd'. 'ra, 'rs, 'rb");
+ return;
+@@ -575,7 +575,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "sraw'. 'ra, 'rs, 'rb");
+ return;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case SRAD: {
+ Format(instr, "srad'. 'ra, 'rs, 'rb");
+ return;
+@@ -589,7 +589,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "extsh'. 'ra, 'rs");
+ return;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case EXTSW: {
+ Format(instr, "extsw'. 'ra, 'rs");
+ return;
+@@ -650,7 +650,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "slw'. 'ra, 'rs, 'rb");
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case SLDX: {
+ Format(instr, "sld'. 'ra, 'rs, 'rb");
+ break;
+@@ -668,7 +668,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "cntlzw'. 'ra, 'rs");
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case CNTLZDX: {
+ Format(instr, "cntlzd'. 'ra, 'rs");
+ break;
+@@ -710,7 +710,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "mullw'o'. 'rt, 'ra, 'rb");
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case MULLD: {
+ Format(instr, "mulld'o'. 'rt, 'ra, 'rb");
+ break;
+@@ -720,7 +720,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "divw'o'. 'rt, 'ra, 'rb");
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case DIVD: {
+ Format(instr, "divd'o'. 'rt, 'ra, 'rb");
+ break;
+@@ -814,7 +814,7 @@ void Decoder::DecodeExt2(Instruction* in
+ Format(instr, "lhzux 'rt, 'ra, 'rb");
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case LDX: {
+ Format(instr, "ldx 'rt, 'ra, 'rb");
+ break;
+@@ -1210,7 +1210,7 @@ int Decoder::InstructionDecode(byte* ins
+ DecodeExt5(instr);
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case LD: {
+ switch (instr->Bits(1, 0)) {
+ case 0:
+diff -up v8-3.14.5.10/src/ppc/full-codegen-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/full-codegen-ppc.cc
+--- v8-3.14.5.10/src/ppc/full-codegen-ppc.cc.ppc-harder 2017-03-01 12:47:36.482606982
-0500
++++ v8-3.14.5.10/src/ppc/full-codegen-ppc.cc 2017-03-01 12:47:36.518606081 -0500
+@@ -451,7 +451,7 @@ void FullCodeGenerator::EmitReturnSequen
+ masm_->mtlr(r0);
+ masm_->Add(sp, sp, (uint32_t)(sp_delta + (2 * kPointerSize)), r0);
+ masm_->blr();
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // With 64bit we need a couple of nop() instructions to ensure we have
+ // enough space to SetDebugBreakAtReturn()
+ masm_->nop();
+@@ -1974,7 +1974,7 @@ void FullCodeGenerator::EmitInlineSmiBin
+ case Token::SHL: {
+ __ b(&stub_call);
+ __ GetLeastBitsFromSmi(scratch2, right, 5);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ ShiftLeft(right, left, scratch2);
+ #else
+ __ SmiUntag(scratch1, left);
+@@ -2025,7 +2025,7 @@ void FullCodeGenerator::EmitInlineSmiBin
+ }
+ case Token::MUL: {
+ Label mul_zero;
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // Remove tag from both operands.
+ __ SmiUntag(ip, right);
+ __ SmiUntag(r0, left);
+@@ -2046,7 +2046,7 @@ void FullCodeGenerator::EmitInlineSmiBin
+ // Go slow on zero result to handle -0.
+ __ cmpi(scratch1, Operand::Zero());
+ __ beq(&mul_zero);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ SmiTag(right, scratch1);
+ #else
+ __ mr(right, scratch1);
+@@ -3695,7 +3695,7 @@ void FullCodeGenerator::EmitFastAsciiArr
+ // string_length to get the length of the result string.
+ __ LoadP(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset));
+ __ sub(string_length, string_length, scratch1);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ SmiUntag(scratch1, scratch1);
+ __ Mul(scratch2, array_length, scratch1);
+ // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
+diff -up v8-3.14.5.10/src/ppc/ic-ppc.cc.ppc-harder v8-3.14.5.10/src/ppc/ic-ppc.cc
+--- v8-3.14.5.10/src/ppc/ic-ppc.cc.ppc-harder 2017-03-01 12:47:36.483606957 -0500
++++ v8-3.14.5.10/src/ppc/ic-ppc.cc 2017-03-01 12:47:36.519606056 -0500
+@@ -1807,7 +1807,7 @@ void PatchInlinedSmiCode(Address address
+ patcher.masm()->TestIfSmi(reg, r0);
+ } else {
+ ASSERT(check == DISABLE_INLINED_SMI_CHECK);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ASSERT(Assembler::IsRldicl(instr_at_patch));
+ #else
+ ASSERT(Assembler::IsRlwinm(instr_at_patch));
+diff -up v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc
+--- v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc.ppc-harder 2017-03-01 12:47:36.486606882
-0500
++++ v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc 2017-03-01 12:47:36.519606056 -0500
+@@ -922,7 +922,7 @@ void LCodeGen::DoModI(LModI* instr) {
+ DeoptimizeIf(eq, instr->environment());
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(scratch, scratch);
+ #endif
+ __ Mul(scratch, divisor, scratch);
+@@ -973,7 +973,7 @@ void LCodeGen::DoDivI(LDivI* instr) {
+ __ bind(&left_not_min_int);
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(result, result);
+ #endif
+
+@@ -1049,7 +1049,7 @@ void LCodeGen::DoMathFloorOfDiv(LMathFlo
+ // The multiplier is a uint32.
+ ASSERT(multiplier > 0 &&
+ multiplier < (static_cast<int64_t>(1) << 32));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(scratch, dividend);
+ if (divisor < 0 &&
+ instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
+@@ -1175,7 +1175,7 @@ void LCodeGen::DoMulI(LMulI* instr) {
+
+ if (can_overflow) {
+ // scratch:result = left * right.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ Mul(result, left, right);
+ __ TestIfInt32(result, scratch, r0);
+ DeoptimizeIf(ne, instr->environment());
+@@ -1269,14 +1269,14 @@ void LCodeGen::DoShiftI(LShiftI* instr)
+ switch (instr->op()) {
+ case Token::SAR:
+ __ sraw(result, left, scratch);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(result, result);
+ #endif
+ break;
+ case Token::SHR:
+ if (instr->can_deopt()) {
+ __ srw(result, left, scratch, SetRC);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(result, result, SetRC);
+ #endif
+ DeoptimizeIf(lt, instr->environment(), cr0);
+@@ -1286,7 +1286,7 @@ void LCodeGen::DoShiftI(LShiftI* instr)
+ break;
+ case Token::SHL:
+ __ slw(result, left, scratch);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(result, result);
+ #endif
+ break;
+@@ -1302,7 +1302,7 @@ void LCodeGen::DoShiftI(LShiftI* instr)
+ case Token::SAR:
+ if (shift_count != 0) {
+ __ srawi(result, left, shift_count);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(result, result);
+ #endif
+ } else {
+@@ -1323,7 +1323,7 @@ void LCodeGen::DoShiftI(LShiftI* instr)
+ case Token::SHL:
+ if (shift_count != 0) {
+ __ slwi(result, left, Operand(shift_count));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(result, result);
+ #endif
+ } else {
+@@ -1360,7 +1360,7 @@ void LCodeGen::DoSubI(LSubI* instr) {
+ right_reg,
+ scratch0(), r0);
+ // Doptimize on overflow
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(scratch0(), scratch0(), SetRC);
+ #endif
+ DeoptimizeIf(lt, instr->environment(), cr0);
+@@ -1530,7 +1530,7 @@ void LCodeGen::DoAddI(LAddI* instr) {
+ ToRegister(left),
+ right_reg,
+ scratch0(), r0);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(scratch0(), scratch0(), SetRC);
+ #endif
+ // Doptimize on overflow
+@@ -2359,7 +2359,7 @@ void LCodeGen::DoDeferredInstanceOfKnown
+ Register temp = ToRegister(instr->temp());
+ ASSERT(temp.is(r7));
+ __ LoadHeapObject(InstanceofStub::right(), instr->function());
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ static const int kAdditionalDelta = 13;
+ #else
+ static const int kAdditionalDelta = 7;
+@@ -2887,7 +2887,7 @@ MemOperand LCodeGen::PrepareKeyedOperand
+
+ if (additional_index) {
+ if (key_is_tagged) {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // more efficient to just untag
+ __ SmiUntag(scratch, key);
+ key_is_tagged = false;
+@@ -2988,7 +2988,7 @@ void LCodeGen::DoLoadKeyedSpecializedArr
+ } else {
+ __ lwzx(result, mem_operand);
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(result, result);
+ #endif
+ break;
+@@ -3096,7 +3096,7 @@ void LCodeGen::DoWrapReceiver(LWrapRecei
+ __ lwz(scratch,
+ FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
+ __ TestBit(scratch,
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SharedFunctionInfo::kStrictModeFunction,
+ #else
+ SharedFunctionInfo::kStrictModeFunction + kSmiTagSize,
+@@ -3106,7 +3106,7 @@ void LCodeGen::DoWrapReceiver(LWrapRecei
+
+ // Do not transform the receiver to object for builtins.
+ __ TestBit(scratch,
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SharedFunctionInfo::kNative,
+ #else
+ SharedFunctionInfo::kNative + kSmiTagSize,
+@@ -4402,7 +4402,7 @@ void LCodeGen::DoNumberTagI(LNumberTagI*
+ Register dst = ToRegister(instr->result());
+
+ DeferredNumberTagI* deferred = new(zone()) DeferredNumberTagI(this, instr);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ SmiTag(dst, src);
+ #else
+ __ SmiTagCheckOverflow(dst, src, r0);
+diff -up v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc
+--- v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc.ppc-harder 2017-03-01
12:47:36.487606857 -0500
++++ v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc 2017-03-01 12:47:36.519606056 -0500
+@@ -280,7 +280,7 @@ void LGapResolver::EmitMove(int index) {
+ if (in_cycle_) {
+ // kSavedDoubleValueRegister was used to break the cycle,
+ // but kSavedValueRegister is free.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ ld(kSavedValueRegister, source_operand);
+ __ std(kSavedValueRegister, destination_operand);
+ #else
+diff -up v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc
+--- v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc.ppc-harder 2017-03-01 12:47:36.491606757
-0500
++++ v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc 2017-03-01 12:47:36.520606031 -0500
+@@ -132,7 +132,7 @@ int MacroAssembler::CallSize(
+ }
+ #else
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ movSize = 5;
+ #else
+ movSize = 2;
+@@ -160,7 +160,7 @@ int MacroAssembler::CallSizeNotPredictab
+ }
+ #else
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ movSize = 5;
+ #else
+ movSize = 2;
+@@ -196,7 +196,7 @@ void MacroAssembler::Call(Address target
+ mtlr(ip);
+ bclr(BA, SetLK);
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ASSERT(kCallTargetAddressOffset == 7 * kInstrSize);
+ #else
+ ASSERT(kCallTargetAddressOffset == 4 * kInstrSize);
+@@ -1773,7 +1773,7 @@ void MacroAssembler::StoreNumberToDouble
+ Register scratch4,
+ Label* fail) {
+ Label smi_value, maybe_nan, have_double_value, is_nan, done;
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ Register double_reg = scratch2;
+ #else
+ Register mantissa_reg = scratch2;
+@@ -1792,7 +1792,7 @@ void MacroAssembler::StoreNumberToDouble
+
+ // Check for nan: all NaN values have a value greater (signed) than 0x7ff00000
+ // in the exponent.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ mov(scratch1, Operand(kLastNonNaNInt64));
+ addi(scratch3, value_reg, Operand(-kHeapObjectTag));
+ ld(double_reg, MemOperand(scratch3, HeapNumber::kValueOffset));
+@@ -1804,14 +1804,14 @@ void MacroAssembler::StoreNumberToDouble
+ #endif
+ bge(&maybe_nan);
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ lwz(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset));
+ #endif
+
+ bind(&have_double_value);
+ SmiToDoubleArrayOffset(scratch1, key_reg);
+ add(scratch1, elements_reg, scratch1);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ addi(scratch1, scratch1, Operand(-kHeapObjectTag));
+ std(double_reg, MemOperand(scratch1, FixedDoubleArray::kHeaderSize));
+ #else
+@@ -1831,7 +1831,7 @@ void MacroAssembler::StoreNumberToDouble
+ // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise
+ // it's an Infinity, and the non-NaN code path applies.
+ bgt(&is_nan);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ clrldi(r0, double_reg, Operand(32), SetRC);
+ beq(&have_double_value, cr0);
+ #else
+@@ -1843,7 +1843,7 @@ void MacroAssembler::StoreNumberToDouble
+ // Load canonical NaN for storing into the double array.
+ uint64_t nan_int64 = BitCast<uint64_t>(
+ FixedDoubleArray::canonical_not_the_hole_nan_as_double());
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ mov(double_reg, Operand(nan_int64));
+ #else
+ mov(mantissa_reg, Operand(static_cast<intptr_t>(nan_int64)));
+@@ -2036,7 +2036,7 @@ void MacroAssembler::TryGetFunctionProto
+ lwz(scratch,
+ FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
+ TestBit(scratch,
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SharedFunctionInfo::kBoundFunction,
+ #else
+ SharedFunctionInfo::kBoundFunction + kSmiTagSize,
+@@ -2122,7 +2122,7 @@ void MacroAssembler::CallApiFunctionAndR
+ addi(r29, r29, Operand(1));
+ stw(r29, MemOperand(r26, kLevelOffset));
+
+-#if !ABI_RETURNS_HANDLES_IN_REGS
++#if !defined(ABI_RETURNS_HANDLES_IN_REGS)
+ // PPC LINUX ABI
+ // The return value is pointer-sized non-scalar value.
+ // Space has already been allocated on the stack which will pass as an
+@@ -2136,7 +2136,7 @@ void MacroAssembler::CallApiFunctionAndR
+ DirectCEntryStub stub;
+ stub.GenerateCall(this, function);
+
+-#if !ABI_RETURNS_HANDLES_IN_REGS
++#if !defined(ABI_RETURNS_HANDLES_IN_REGS)
+ // Retrieve return value from stack buffer
+ LoadP(r3, MemOperand(r3));
+ #endif
+@@ -2228,7 +2228,7 @@ void MacroAssembler::IndexFromHash(Regis
+ STATIC_ASSERT(String::kHashShift == 2);
+ STATIC_ASSERT(String::kArrayIndexValueBits == 24);
+ // index = SmiTag((hash >> 2) & 0x00FFFFFF);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ExtractBitRange(index, hash, 25, 2);
+ SmiTag(index);
+ #else
+@@ -2264,7 +2264,7 @@ void MacroAssembler::ConvertToInt32(Regi
+
+ addi(sp, sp, Operand(-kDoubleSize));
+ stfd(double_scratch, MemOperand(sp, 0));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ld(dest, MemOperand(sp, 0));
+ #else
+ #if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
+@@ -2279,7 +2279,7 @@ void MacroAssembler::ConvertToInt32(Regi
+
+ // The result is not a 32-bit integer when the high 33 bits of the
+ // result are not identical.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ TestIfInt32(dest, scratch, scratch2);
+ #else
+ TestIfInt32(scratch, dest, scratch2);
+@@ -2305,7 +2305,7 @@ void MacroAssembler::EmitVFPTruncate(VFP
+
+ addi(sp, sp, Operand(-kDoubleSize));
+ stfd(double_scratch, MemOperand(sp, 0));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ld(result, MemOperand(sp, 0));
+ #else
+ #if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
+@@ -2320,7 +2320,7 @@ void MacroAssembler::EmitVFPTruncate(VFP
+
+ // The result is a 32-bit integer when the high 33 bits of the
+ // result are identical.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ TestIfInt32(result, scratch, r0);
+ #else
+ TestIfInt32(scratch, result, r0);
+@@ -2439,7 +2439,7 @@ void MacroAssembler::EmitECMATruncate(Re
+
+ // reserve a slot on the stack
+ stfdu(double_scratch, MemOperand(sp, -8));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ld(result, MemOperand(sp, 0));
+ #else
+ #if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
+@@ -2453,7 +2453,7 @@ void MacroAssembler::EmitECMATruncate(Re
+
+ // The result is a 32-bit integer when the high 33 bits of the
+ // result are identical.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ TestIfInt32(result, scratch, r0);
+ #else
+ TestIfInt32(scratch, result, r0);
+@@ -2484,7 +2484,7 @@ void MacroAssembler::EmitECMATruncate(Re
+ void MacroAssembler::GetLeastBitsFromSmi(Register dst,
+ Register src,
+ int num_least_bits) {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ rldicl(dst, src, kBitsPerPointer - kSmiShift,
+ kBitsPerPointer - num_least_bits);
+ #else
+@@ -2519,7 +2519,7 @@ void MacroAssembler::CallRuntime(const R
+ // smarter.
+ mov(r3, Operand(num_arguments));
+ mov(r4, Operand(ExternalReference(f, isolate())));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ CEntryStub stub(f->result_size);
+ #else
+ CEntryStub stub(1);
+@@ -2859,7 +2859,7 @@ void MacroAssembler::JumpIfNotPowerOfTwo
+ bne(not_power_of_two, cr0);
+ }
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ void MacroAssembler::SmiTagCheckOverflow(Register reg, Register overflow) {
+ ASSERT(!reg.is(overflow));
+ mr(overflow, reg); // Save original value.
+@@ -3141,7 +3141,7 @@ void MacroAssembler::CopyBytes(Register
+ stb(scratch, MemOperand(dst, 2));
+ ShiftRightImm(scratch, scratch, Operand(8));
+ stb(scratch, MemOperand(dst, 3));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ShiftRightImm(scratch, scratch, Operand(8));
+ stb(scratch, MemOperand(dst, 4));
+ ShiftRightImm(scratch, scratch, Operand(8));
+@@ -3152,7 +3152,7 @@ void MacroAssembler::CopyBytes(Register
+ stb(scratch, MemOperand(dst, 7));
+ #endif
+ #else
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ stb(scratch, MemOperand(dst, 7));
+ ShiftRightImm(scratch, scratch, Operand(8));
+ stb(scratch, MemOperand(dst, 6));
+@@ -3356,13 +3356,13 @@ void MacroAssembler::CallCFunctionHelper
+ // Just call directly. The function called cannot cause a GC, or
+ // allow preemption, so the return address in the link register
+ // stays correct.
+-#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++#if defined(ABI_USES_FUNCTION_DESCRIPTORS) && !defined(USE_SIMULATOR)
+ // AIX uses a function descriptor. When calling C code be aware
+ // of this descriptor and pick up values from it
+ Register dest = ip;
+ LoadP(ToRegister(2), MemOperand(function, kPointerSize));
+ LoadP(dest, MemOperand(function, 0));
+-#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++#elif defined(ABI_TOC_ADDRESSABILITY_VIA_IP)
+ Register dest = ip;
+ Move(ip, function);
+ #else
+@@ -3427,7 +3427,7 @@ void MacroAssembler::PatchRelocatedValue
+ }
+
+ // insert new high word into lis instruction
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ srdi(ip, new_value, Operand(32));
+ rlwimi(scratch, ip, 16, 16, 31);
+ #else
+@@ -3446,14 +3446,14 @@ void MacroAssembler::PatchRelocatedValue
+ }
+
+ // insert new low word into ori instruction
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ rlwimi(scratch, ip, 0, 16, 31);
+ #else
+ rlwimi(scratch, new_value, 0, 16, 31);
+ #endif
+ stw(scratch, MemOperand(lis_location, kInstrSize));
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ if (emit_debug_code()) {
+ lwz(scratch, MemOperand(lis_location, 2*kInstrSize));
+ // scratch is now sldi.
+@@ -3487,7 +3487,7 @@ void MacroAssembler::PatchRelocatedValue
+ #endif
+
+ // Update the I-cache so the new lis and addic can be executed.
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ FlushICache(lis_location, 5 * kInstrSize, scratch);
+ #else
+ FlushICache(lis_location, 2 * kInstrSize, scratch);
+@@ -3519,7 +3519,7 @@ void MacroAssembler::GetRelocatedValueLo
+ // Copy the low 16bits from ori instruction into result
+ rlwimi(result, scratch, 0, 16, 31);
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ if (emit_debug_code()) {
+ lwz(scratch, MemOperand(lis_location, 2*kInstrSize));
+ // scratch is now sldi.
+@@ -3698,7 +3698,7 @@ void MacroAssembler::EnsureNotWhite(
+ Register map = load_scratch; // Holds map while checking type.
+ Register length = load_scratch; // Holds length of object after testing type.
+ Label is_data_object, maybe_string_object, is_string_object, is_encoded;
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ Label length_computed;
+ #endif
+
+@@ -3744,11 +3744,11 @@ void MacroAssembler::EnsureNotWhite(
+ andi(r0, instance_type, Operand(kStringEncodingMask));
+ beq(&is_encoded, cr0);
+ SmiUntag(ip);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ b(&length_computed);
+ #endif
+ bind(&is_encoded);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ SmiToShortArrayOffset(ip, ip);
+ bind(&length_computed);
+ #else
+@@ -3932,7 +3932,7 @@ void MacroAssembler::LoadIntLiteral(Regi
+
+ void MacroAssembler::LoadSmiLiteral(Register dst, Smi *smi) {
+ intptr_t value = reinterpret_cast<intptr_t>(smi);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ASSERT((value & 0xffffffff) == 0);
+ LoadIntLiteral(dst, value >> 32);
+ ShiftLeftImm(dst, dst, Operand(32));
+@@ -3949,7 +3949,7 @@ void MacroAssembler::LoadDoubleLiteral(D
+ // avoid gcc strict aliasing error using union cast
+ union {
+ double dval;
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ intptr_t ival;
+ #else
+ intptr_t ival[2];
+@@ -3957,7 +3957,7 @@ void MacroAssembler::LoadDoubleLiteral(D
+ } litVal;
+
+ litVal.dval = value;
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ mov(scratch, Operand(litVal.ival));
+ std(scratch, MemOperand(sp));
+ #else
+@@ -4053,7 +4053,7 @@ void MacroAssembler::Xor(Register ra, Re
+
+ void MacroAssembler::CmpSmiLiteral(Register src1, Smi *smi, Register scratch,
+ CRegister cr) {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ LoadSmiLiteral(scratch, smi);
+ cmp(src1, scratch, cr);
+ #else
+@@ -4063,7 +4063,7 @@ void MacroAssembler::CmpSmiLiteral(Regis
+
+ void MacroAssembler::CmplSmiLiteral(Register src1, Smi *smi, Register scratch,
+ CRegister cr) {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ LoadSmiLiteral(scratch, smi);
+ cmpl(src1, scratch, cr);
+ #else
+@@ -4073,7 +4073,7 @@ void MacroAssembler::CmplSmiLiteral(Regi
+
+ void MacroAssembler::AddSmiLiteral(Register dst, Register src, Smi *smi,
+ Register scratch) {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ LoadSmiLiteral(scratch, smi);
+ add(dst, src, scratch);
+ #else
+@@ -4083,7 +4083,7 @@ void MacroAssembler::AddSmiLiteral(Regis
+
+ void MacroAssembler::SubSmiLiteral(Register dst, Register src, Smi *smi,
+ Register scratch) {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ LoadSmiLiteral(scratch, smi);
+ sub(dst, src, scratch);
+ #else
+@@ -4093,7 +4093,7 @@ void MacroAssembler::SubSmiLiteral(Regis
+
+ void MacroAssembler::AndSmiLiteral(Register dst, Register src, Smi *smi,
+ Register scratch, RCBit rc) {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ LoadSmiLiteral(scratch, smi);
+ and_(dst, src, scratch, rc);
+ #else
+@@ -4110,13 +4110,13 @@ void MacroAssembler::LoadP(Register dst,
+ if (!scratch.is(no_reg) && !is_int16(offset)) {
+ /* cannot use d-form */
+ LoadIntLiteral(scratch, offset);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ ldx(dst, MemOperand(mem.ra(), scratch));
+ #else
+ lwzx(dst, MemOperand(mem.ra(), scratch));
+ #endif
+ } else {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ int misaligned = (offset & 3);
+ if (misaligned) {
+ // adjust base to conform to offset alignment requirements
+@@ -4141,13 +4141,13 @@ void MacroAssembler::StoreP(Register src
+ if (!scratch.is(no_reg) && !is_int16(offset)) {
+ /* cannot use d-form */
+ LoadIntLiteral(scratch, offset);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ stdx(src, MemOperand(mem.ra(), scratch));
+ #else
+ stwx(src, MemOperand(mem.ra(), scratch));
+ #endif
+ } else {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ int misaligned = (offset & 3);
+ if (misaligned) {
+ // adjust base to conform to offset alignment requirements
+@@ -4176,14 +4176,14 @@ void MacroAssembler::LoadWordArith(Regis
+ if (!scratch.is(no_reg) && !is_int16(offset)) {
+ /* cannot use d-form */
+ LoadIntLiteral(scratch, offset);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // lwax(dst, MemOperand(mem.ra(), scratch));
+ ASSERT(0); // lwax not yet implemented
+ #else
+ lwzx(dst, MemOperand(mem.ra(), scratch));
+ #endif
+ } else {
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ int misaligned = (offset & 3);
+ if (misaligned) {
+ // adjust base to conform to offset alignment requirements
+diff -up v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc
+--- v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc.ppc-harder 2017-03-01
12:47:36.493606707 -0500
++++ v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc 2017-03-01 12:47:36.521606006
-0500
+@@ -139,7 +139,7 @@ RegExpMacroAssemblerPPC::RegExpMacroAsse
+ ASSERT_EQ(0, registers_to_save % 2);
+
+ // Called from C
+-#if ABI_USES_FUNCTION_DESCRIPTORS
++#ifdef ABI_USES_FUNCTION_DESCRIPTORS
+ __ function_descriptor();
+ #endif
+
+@@ -1434,7 +1434,7 @@ void RegExpCEntryStub::Generate(MacroAss
+ extra_stack_slots += kNumRequiredStackFrameSlots;
+ __ addi(sp, sp, Operand(-extra_stack_slots * kPointerSize));
+
+-#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++#if defined(ABI_USES_FUNCTION_DESCRIPTORS) && !defined(USE_SIMULATOR)
+ // Native AIX/PPC64 Linux use a function descriptor.
+ __ LoadP(ToRegister(2), MemOperand(r26, kPointerSize)); // TOC
+ __ LoadP(ip, MemOperand(r26, 0)); // Instruction address
+diff -up v8-3.14.5.10/src/ppc/simulator-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/simulator-ppc.cc
+--- v8-3.14.5.10/src/ppc/simulator-ppc.cc.ppc-harder 2017-03-01 12:47:36.495606657 -0500
++++ v8-3.14.5.10/src/ppc/simulator-ppc.cc 2017-03-01 12:47:36.521606006 -0500
+@@ -1332,7 +1332,7 @@ void Simulator::SoftwareInterrupt(Instru
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+-#if ABI_RETURNS_HANDLES_IN_REGS
++#ifdef ABI_RETURNS_HANDLES_IN_REGS
+ intptr_t p0 = arg0;
+ #else
+ intptr_t p0 = arg1;
+@@ -1341,7 +1341,7 @@ void Simulator::SoftwareInterrupt(Instru
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
+ }
+-#if ABI_RETURNS_HANDLES_IN_REGS
++#ifdef ABI_RETURNS_HANDLES_IN_REGS
+ arg0 = (intptr_t)*result;
+ #else
+ *(reinterpret_cast<intptr_t*>(arg0)) = (intptr_t) *result;
+@@ -1363,21 +1363,21 @@ void Simulator::SoftwareInterrupt(Instru
+ PrintF("\n");
+ }
+ CHECK(stack_aligned);
+-#if ABI_RETURNS_HANDLES_IN_REGS
++#ifdef ABI_RETURNS_HANDLES_IN_REGS
+ intptr_t p0 = arg0;
+ intptr_t p1 = arg1;
+ #else
+ intptr_t p0 = arg1;
+ intptr_t p1 = arg2;
+ #endif
+-#if !ABI_PASSES_HANDLES_IN_REGS
++#if !defined(ABI_PASSES_HANDLES_IN_REGS)
+ p0 = *(reinterpret_cast<intptr_t *>(p0));
+ #endif
+ v8::Handle<v8::Value> result = target(p0, p1);
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
+ }
+-#if ABI_RETURNS_HANDLES_IN_REGS
++#ifdef ABI_RETURNS_HANDLES_IN_REGS
+ arg0 = (intptr_t)*result;
+ #else
+ *(reinterpret_cast<intptr_t*>(arg0)) = (intptr_t) *result;
+@@ -1408,7 +1408,7 @@ void Simulator::SoftwareInterrupt(Instru
+ }
+ CHECK(stack_aligned);
+ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ if (::v8::internal::FLAG_trace_sim) {
+ PrintF("Returned %08" V8PRIxPTR "\n", result);
+ }
+@@ -1671,7 +1671,7 @@ bool Simulator::DecodeExt2_10bit(Instruc
+ }
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case SRDX: {
+ int rs = instr->RSValue();
+ int ra = instr->RAValue();
+@@ -1699,7 +1699,7 @@ bool Simulator::DecodeExt2_10bit(Instruc
+ }
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case SRAD: {
+ int rs = instr->RSValue();
+ int ra = instr->RAValue();
+@@ -1726,7 +1726,7 @@ bool Simulator::DecodeExt2_10bit(Instruc
+ }
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case EXTSW: {
+ const int shift = kBitsPerPointer - 32;
+ int ra = instr->RAValue();
+@@ -1980,7 +1980,7 @@ void Simulator::DecodeExt2_9bit(Instruct
+ }
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case SLDX: {
+ int rs = instr->RSValue();
+ int ra = instr->RAValue();
+@@ -2017,7 +2017,7 @@ void Simulator::DecodeExt2_9bit(Instruct
+ }
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case CNTLZDX: {
+ int rs = instr->RSValue();
+ int ra = instr->RAValue();
+@@ -2139,7 +2139,7 @@ void Simulator::DecodeExt2_9bit(Instruct
+ // todo - handle OE bit
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case MULLD: {
+ int rt = instr->RTValue();
+ int ra = instr->RAValue();
+@@ -2170,7 +2170,7 @@ void Simulator::DecodeExt2_9bit(Instruct
+ // todo - handle OE bit
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case DIVD: {
+ int rt = instr->RTValue();
+ int ra = instr->RAValue();
+@@ -2316,7 +2316,7 @@ void Simulator::DecodeExt2_9bit(Instruct
+ }
+ break;
+ }
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case LDX:
+ case LDUX: {
+ int rt = instr->RTValue();
+@@ -2672,7 +2672,7 @@ void Simulator::DecodeExt4(Instruction*
+ UNIMPLEMENTED(); // Not used by V8.
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ void Simulator::DecodeExt5(Instruction* instr) {
+ switch (instr->Bits(4, 2) << 2) {
+ case RLDICL: {
+@@ -3195,7 +3195,7 @@ void Simulator::InstructionDecode(Instru
+ break;
+ }
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ case EXT5: {
+ DecodeExt5(instr);
+ break;
+@@ -3339,7 +3339,7 @@ intptr_t Simulator::Call(byte* entry, in
+ set_register(sp, entry_stack);
+
+ // Prepare to execute the code at entry
+-#if ABI_USES_FUNCTION_DESCRIPTORS
++#ifdef ABI_USES_FUNCTION_DESCRIPTORS
+ // entry is the function descriptor
+ set_pc(*(reinterpret_cast<intptr_t *>(entry)));
+ #else
+diff -up v8-3.14.5.10/src/ppc/stub-cache-ppc.cc.ppc-harder
v8-3.14.5.10/src/ppc/stub-cache-ppc.cc
+--- v8-3.14.5.10/src/ppc/stub-cache-ppc.cc.ppc-harder 2017-03-01 12:47:36.497606607
-0500
++++ v8-3.14.5.10/src/ppc/stub-cache-ppc.cc 2017-03-01 12:47:36.521606006 -0500
+@@ -198,7 +198,7 @@ void StubCache::GenerateProbe(MacroAssem
+ Isolate* isolate = masm->isolate();
+ Label miss;
+
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // Make sure that code is valid. The multiplying code relies on the
+ // entry size being 24.
+ ASSERT(sizeof(Entry) == 24);
+@@ -239,7 +239,7 @@ void StubCache::GenerateProbe(MacroAssem
+ __ lwz(scratch, FieldMemOperand(name, String::kHashFieldOffset));
+ __ LoadP(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
+ __ add(scratch, scratch, ip);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ // Use only the low 32 bits of the map pointer.
+ __ rldicl(scratch, scratch, 0, 32);
+ #endif
+@@ -728,7 +728,7 @@ static void GenerateFastApiDirectCall(Ma
+ // Prepare arguments.
+ __ addi(r5, sp, Operand(3 * kPointerSize));
+
+-#if !ABI_RETURNS_HANDLES_IN_REGS
++#if !defined(ABI_RETURNS_HANDLES_IN_REGS)
+ bool alloc_return_buf = true;
+ #else
+ bool alloc_return_buf = false;
+@@ -1213,7 +1213,7 @@ void StubCompiler::GenerateLoadCallback(
+ Handle<AccessorInfo> callback,
+ Handle<String> name,
+ Label* miss) {
+-#if !ABI_RETURNS_HANDLES_IN_REGS
++#if !defined(ABI_RETURNS_HANDLES_IN_REGS)
+ bool alloc_return_buf = true;
+ #else
+ bool alloc_return_buf = false;
+@@ -1263,7 +1263,7 @@ void StubCompiler::GenerateLoadCallback(
+ // If alloc_return_buf, we shift the arguments over a register
+ // (e.g. r3 -> r4) to allow for the return value buffer in implicit
+ // first arg. CallApiFunctionAndReturn will setup r3.
+-#if ABI_PASSES_HANDLES_IN_REGS
++#ifdef ABI_PASSES_HANDLES_IN_REGS
+ const int kAccessorInfoSlot = kStackFrameExtraParamSlot +
+ (alloc_return_buf ? 2 : 1);
+ #else
+@@ -1281,7 +1281,7 @@ void StubCompiler::GenerateLoadCallback(
+ FrameScope frame_scope(masm(), StackFrame::MANUAL);
+ __ EnterExitFrame(false, kApiStackSpace);
+
+-#if !ABI_PASSES_HANDLES_IN_REGS
++#if !defined(ABI_PASSES_HANDLES_IN_REGS)
+ // pass 1st arg by reference
+ __ StoreP(arg0, MemOperand(sp, kArg0Slot * kPointerSize));
+ __ addi(arg0, sp, Operand(kArg0Slot * kPointerSize));
+@@ -2155,7 +2155,7 @@ Handle<Code> CallStubCompiler::CompileMa
+ // The frim instruction is only supported on POWER5
+ // and higher
+ __ frim(d1, d1);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ fctidz(d1, d1);
+ #else
+ __ fctiwz(d1, d1);
+@@ -2166,7 +2166,7 @@ Handle<Code> CallStubCompiler::CompileMa
+ // perf benefit or if we can simply use the compatible sequence
+ // always
+ __ SetRoundingMode(kRoundToMinusInf);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ fctid(d1, d1);
+ #else
+ __ fctiw(d1, d1);
+@@ -2175,7 +2175,7 @@ Handle<Code> CallStubCompiler::CompileMa
+ }
+ // Convert the argument to an integer.
+ __ stfdu(d1, MemOperand(sp, -8));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ ld(r3, MemOperand(sp, 0));
+ #else
+ #if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
+@@ -3623,7 +3623,7 @@ static void GenerateSmiKeyCheck(MacroAss
+ double_scratch1,
+ kCheckForInexactConversion);
+ __ bne(fail);
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ SmiTag(key, scratch0);
+ #else
+ __ SmiTagCheckOverflow(scratch1, scratch0, r0);
+@@ -3692,7 +3692,7 @@ void KeyedLoadStubCompiler::GenerateLoad
+ case EXTERNAL_INT_ELEMENTS:
+ __ SmiToIntArrayOffset(value, key);
+ __ lwzx(value, MemOperand(r6, value));
+-#if V8_TARGET_ARCH_PPC64
++#ifdef V8_TARGET_ARCH_PPC64
+ __ extsw(value, value);
+ #endif
+ break;
+@@ -3731,7 +3731,7 @@ void KeyedLoadStubCompiler::GenerateLoad
+ // For the Int and UnsignedInt array types, we need to see whether
+ // the value can be represented in a Smi. If not, we need to convert
+ // it to a HeapNumber.
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ Label box_int;
+ // Check that the value fits in a smi.
+ __ JumpIfNotSmiCandidate(value, r0, &box_int);
+@@ -3740,7 +3740,7 @@ void KeyedLoadStubCompiler::GenerateLoad
+ __ SmiTag(r3, value);
+ __ Ret();
+
+-#if !V8_TARGET_ARCH_PPC64
++#if !defined(V8_TARGET_ARCH_PPC64)
+ __ bind(&box_int);
+ // Allocate a HeapNumber for the result and perform int-to-double
+ // conversion. Don't touch r3 or r4 as they are needed if allocation
+diff -up v8-3.14.5.10/src/SConscript.ppc-harder v8-3.14.5.10/src/SConscript
+--- v8-3.14.5.10/src/SConscript.ppc-harder 2012-07-24 03:59:48.000000000 -0400
++++ v8-3.14.5.10/src/SConscript 2017-03-01 14:13:21.244294435 -0500
+@@ -204,6 +204,46 @@ SOURCES = {
+ ia32/regexp-macro-assembler-ia32.cc
+ ia32/stub-cache-ia32.cc
+ """),
++ 'arch:ppc': Split("""
++ ppc/assembler-ppc.cc
++ ppc/builtins-ppc.cc
++ ppc/code-stubs-ppc.cc
++ ppc/codegen-ppc.cc
++ ppc/constants-ppc.cc
++ ppc/cpu-ppc.cc
++ ppc/debug-ppc.cc
++ ppc/deoptimizer-ppc.cc
++ ppc/disasm-ppc.cc
++ ppc/frames-ppc.cc
++ ppc/full-codegen-ppc.cc
++ ppc/ic-ppc.cc
++ ppc/lithium-codegen-ppc.cc
++ ppc/lithium-gap-resolver-ppc.cc
++ ppc/lithium-ppc.cc
++ ppc/macro-assembler-ppc.cc
++ ppc/regexp-macro-assembler-ppc.cc
++ ppc/stub-cache-ppc.cc
++ """),
++ 'arch:ppc64': Split("""
++ ppc/assembler-ppc.cc
++ ppc/builtins-ppc.cc
++ ppc/code-stubs-ppc.cc
++ ppc/codegen-ppc.cc
++ ppc/constants-ppc.cc
++ ppc/cpu-ppc.cc
++ ppc/debug-ppc.cc
++ ppc/deoptimizer-ppc.cc
++ ppc/disasm-ppc.cc
++ ppc/frames-ppc.cc
++ ppc/full-codegen-ppc.cc
++ ppc/ic-ppc.cc
++ ppc/lithium-codegen-ppc.cc
++ ppc/lithium-gap-resolver-ppc.cc
++ ppc/lithium-ppc.cc
++ ppc/macro-assembler-ppc.cc
++ ppc/regexp-macro-assembler-ppc.cc
++ ppc/stub-cache-ppc.cc
++ """),
+ 'arch:x64': Split("""
+ x64/assembler-x64.cc
+ x64/builtins-x64.cc
+@@ -225,6 +265,8 @@ SOURCES = {
+ """),
+ 'simulator:arm': ['arm/simulator-arm.cc'],
+ 'simulator:mips': ['mips/simulator-mips.cc'],
++ 'simulator:ppc': ['ppc/simulator-ppc.cc'],
++ 'simulator:ppc64': ['ppc/simulator-ppc.cc'],
+ 'os:freebsd': ['platform-freebsd.cc', 'platform-posix.cc'],
+ 'os:openbsd': ['platform-openbsd.cc', 'platform-posix.cc'],
+ 'os:linux': ['platform-linux.cc', 'platform-posix.cc'],
diff --git a/v8-powerpc-support.patch b/v8-powerpc-support.patch
new file mode 100644
index 0000000..ed8f129
--- /dev/null
+++ b/v8-powerpc-support.patch
@@ -0,0 +1,57926 @@
+diff -up v8-3.14.5.10/aix_gyp.patch.ppc v8-3.14.5.10/aix_gyp.patch
+--- v8-3.14.5.10/aix_gyp.patch.ppc 2016-06-07 14:15:45.971393122 -0400
++++ v8-3.14.5.10/aix_gyp.patch 2016-06-07 14:15:45.971393122 -0400
+@@ -0,0 +1,62 @@
++--- build/gyp/pylib/gyp/common.py
+++++ build/gyp/pylib/gyp/common.py
++@@ -378,6 +378,8 @@
++ return 'solaris'
++ if sys.platform.startswith('freebsd'):
++ return 'freebsd'
+++ if sys.platform.startswith('aix'):
+++ return 'aix'
++
++ return 'linux'
++
++--- build/gyp/pylib/gyp/generator/make.py
+++++ build/gyp/pylib/gyp/generator/make.py
++@@ -200,6 +200,21 @@
++ """
++
++
+++ LINK_COMMANDS_AIX = """\
+++ quiet_cmd_alink = AR($(TOOLSET)) $@
+++ cmd_alink = rm -f $@ && $(AR.$(TOOLSET)) $(ARFLAGS.$(TOOLSET)) $@ $(filter
%.o,$^)
+++
+++ quiet_cmd_link = LINK($(TOOLSET)) $@
+++ cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(LD_INPUTS)
$(LIBS)
+++
+++ quiet_cmd_solink = SOLINK($(TOOLSET)) $@
+++ cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET))
-Wl,-soname=$(@F) -o $@ -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)
+++
+++ quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
+++ cmd_solink_module = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET))
-Wl,-soname=$(@F) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS)
+++ """
+++
+++
++ # Header of toplevel Makefile.
++ # This should go into the build tree, but it's easier to keep it here for now.
++ SHARED_HEADER = ("""\
++
++--- build/gyp/pylib/gyp/generator/make.py
+++++ build/gyp/pylib/gyp/generator/make.py
++@@ -1933,6 +1948,10 @@
++ cc_command=cc_host):
++ arflags_host = 'crsT'
++
+++ if flavor == 'aix':
+++ arflags_target = '-Xany ' + arflags_target
+++ arflags_host = '-Xany ' + arflags_host
+++
++ return { 'ARFLAGS.target': arflags_target,
++ 'ARFLAGS.host': arflags_host }
++
++--- build/gyp/pylib/gyp/generator/make.py
+++++ build/gyp/pylib/gyp/generator/make.py
++@@ -2026,6 +2045,10 @@
++ elif flavor == 'freebsd':
++ header_params.update({
++ 'flock': 'lockf',
+++ })
+++ elif flavor == 'aix':
+++ header_params.update({
+++ 'link_commands': LINK_COMMANDS_AIX,
++ })
++
++ header_params.update(RunSystemTests(flavor))
+diff -up v8-3.14.5.10/AUTHORS.ppc v8-3.14.5.10/AUTHORS
+--- v8-3.14.5.10/AUTHORS.ppc 2013-02-07 07:26:07.000000000 -0500
++++ v8-3.14.5.10/AUTHORS 2016-06-07 14:15:45.970393128 -0400
+@@ -7,6 +7,7 @@ Google Inc.
+ Sigma Designs Inc.
+ ARM Ltd.
+ Hewlett-Packard Development Company, LP
++IBM Corporation
+ Igalia, S.L.
+ Joyent, Inc.
+
+@@ -15,11 +16,13 @@ Alexander Botero-Lowry <alexbl(a)FreeBSD.o
+ Alexander Karpinsky <homm86(a)gmail.com>
+ Alexandre Vassalotti <avassalotti(a)gmail.com>
+ Andreas Anyuru <andreas.anyuru(a)gmail.com>
++Andrew Low <andrew_low(a)ca.ibm.com>
+ Bert Belder <bertbelder(a)gmail.com>
+ Burcu Dogan <burcujdogan(a)gmail.com>
+ Craig Schlenter <craig.schlenter(a)gmail.com>
+ Daniel Andersson <kodandersson(a)gmail.com>
+ Daniel James <dnljms(a)gmail.com>
++David Eelsohn <dje.gcc(a)gmail.com>
+ Derek J Conrod <dconrod(a)codeaurora.org>
+ Dineel D Sule <dsule(a)codeaurora.org>
+ Erich Ocean <erich.ocean(a)me.com>
+@@ -37,6 +40,7 @@ Kun Zhang <zhangk(a)codeaurora.org>
+ Martyn Capewell <martyn.capewell(a)arm.com>
+ Mathias Bynens <mathias(a)qiwi.be>
+ Matt Hanselman <mjhanselman(a)gmail.com>
++Matthew Brandyberry <mbrandy(a)us.ibm.com>
+ Maxim Mossienko <maxim.mossienko(a)gmail.com>
+ Michael Lutz <michi(a)icosahedron.de>
+ Michael Smith <mike(a)w3.org>
+diff -up v8-3.14.5.10/build/common.gypi.ppc v8-3.14.5.10/build/common.gypi
+--- v8-3.14.5.10/build/common.gypi.ppc 2016-06-07 14:15:45.958393199 -0400
++++ v8-3.14.5.10/build/common.gypi 2016-06-07 14:17:26.697791944 -0400
+@@ -70,9 +70,13 @@
+
+ 'v8_enable_disassembler%': 0,
+
++ 'v8_native_sim%': 'false',
++
+ # Enable extra checks in API functions and other strategic places.
+ 'v8_enable_extra_checks%': 1,
+
++ 'v8_enable_extra_ppcchecks%': 0,
++
+ 'v8_enable_gdbjit%': 0,
+
+ 'v8_object_print%': 0,
+@@ -117,6 +121,9 @@
+ ['v8_enable_extra_checks==1', {
+ 'defines': ['ENABLE_EXTRA_CHECKS',],
+ }],
++ ['v8_enable_extra_ppcchecks==1', {
++ 'defines': ['ENABLE_EXTRA_PPCCHECKS',],
++ }],
+ ['v8_enable_gdbjit==1', {
+ 'defines': ['ENABLE_GDB_JIT_INTERFACE',],
+ }],
+@@ -129,6 +136,12 @@
+ ['v8_interpreted_regexp==1', {
+ 'defines': ['V8_INTERPRETED_REGEXP',],
+ }],
++ ['v8_native_sim=="true"', {
++ 'defines': [
++ 'NATIVE_SIMULATION',
++ 'USE_SIMULATOR',
++ ],
++ }],
+ ['v8_target_arch=="arm"', {
+ 'defines': [
+ 'V8_TARGET_ARCH_ARM',
+@@ -171,6 +184,17 @@
+ }],
+ ],
+ }], # v8_target_arch=="arm"
++ ['v8_target_arch=="ppc"', {
++ 'defines': [
++ 'V8_TARGET_ARCH_PPC',
++ ],
++ }], # v8_target_arch=="ppc"
++ ['v8_target_arch=="ppc64"', {
++ 'defines': [
++ 'V8_TARGET_ARCH_PPC',
++ 'V8_TARGET_ARCH_PPC64',
++ ],
++ }], # v8_target_arch=="ppc64"
+ ['v8_target_arch=="ia32"', {
+ 'defines': [
+ 'V8_TARGET_ARCH_IA32',
+@@ -283,7 +307,7 @@
+ },
+ }],
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd"
or OS=="solaris" \
+- or OS=="netbsd"', {
++ or OS=="netbsd" or OS=="aix"', {
+ 'conditions': [
+ [ 'v8_no_strict_aliasing==1', {
+ 'cflags': [ '-fno-strict-aliasing' ],
+@@ -296,7 +320,7 @@
+ ['(OS=="linux" or OS=="freebsd" or OS=="openbsd"
or OS=="solaris" \
+ or OS=="netbsd" or OS=="mac" or OS=="android")
and \
+ (v8_target_arch=="arm" or v8_target_arch=="ia32" or \
+- v8_target_arch=="mipsel" or v8_target_arch=="mips")',
{
++ v8_target_arch=="mipsel" or v8_target_arch=="mips" or
v8_target_arch=="ppc")', {
+ # Check whether the host compiler and target compiler support the
+ # '-m32' option and set it if so.
+ 'target_conditions': [
+@@ -333,6 +357,20 @@
+ ['OS=="netbsd"', {
+ 'cflags': [ '-I/usr/pkg/include' ],
+ }],
++ ['OS=="aix"', {
++ # AIX is missing /usr/include/endian.h
++ 'defines': [
++ '__LITTLE_ENDIAN=1234',
++ '__BIG_ENDIAN=4321',
++ '__BYTE_ORDER=__BIG_ENDIAN',
++ '__FLOAT_WORD_ORDER=__BIG_ENDIAN'],
++ 'conditions': [
++ [ 'v8_target_arch=="ppc64"', {
++ 'cflags': [ '-maix64' ],
++ 'ldflags': [ '-maix64' ],
++ }],
++ ],
++ }],
+ ], # conditions
+ 'configurations': {
+ 'Debug': {
+@@ -360,10 +398,14 @@
+ },
+ },
+ 'conditions': [
+- ['OS=="linux" or OS=="freebsd" or
OS=="openbsd" or OS=="netbsd"', {
++ ['OS=="linux" or OS=="freebsd" or
OS=="openbsd" or OS=="netbsd" \
++ or OS=="aix"', {
+ 'cflags': [ '-Wall', '<(werror)', '-W',
'-Wno-unused-parameter',
+ '-Wnon-virtual-dtor', '-Woverloaded-virtual' ],
+ }],
++ ['OS=="aix"', {
++ 'ldflags': [ '-Wl,-bbigtoc' ],
++ }],
+ ['OS=="android"', {
+ 'variables': {
+ 'android_full_debug%': 1,
+@@ -383,7 +425,7 @@
+ 'Release': {
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or
OS=="openbsd" or OS=="netbsd" \
+- or OS=="android"', {
++ or OS=="android" or OS=="aix"', {
+ 'cflags!': [
+ '-O2',
+ '-Os',
+diff -up v8-3.14.5.10/build/standalone.gypi.ppc v8-3.14.5.10/build/standalone.gypi
+--- v8-3.14.5.10/build/standalone.gypi.ppc 2016-06-07 14:15:45.958393199 -0400
++++ v8-3.14.5.10/build/standalone.gypi 2016-06-07 14:15:45.971393122 -0400
+@@ -31,7 +31,6 @@
+ 'variables': {
+ 'library%': 'static_library',
+ 'component%': 'static_library',
+- 'visibility%': 'hidden',
+ 'msvs_multi_core_compile%': '1',
+ 'mac_deployment_target%': '10.5',
+ 'variables': {
+@@ -39,7 +38,7 @@
+ 'variables': {
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or
OS=="openbsd" or \
+- OS=="netbsd" or OS=="mac"', {
++ OS=="netbsd" or OS=="mac" or
OS=="aix"', {
+ # This handles the Unix platforms we generally deal with.
+ # Anything else gets passed through, which probably won't work
+ # very well; such hosts should pass an explicit target_arch
+@@ -49,7 +48,7 @@
+ s/x86_64/x64/;s/amd64/x64/;s/arm.*/arm/;s/mips.*/mipsel/")',
+ }, {
+ # OS!="linux" and OS!="freebsd" and
OS!="openbsd" and
+- # OS!="netbsd" and OS!="mac"
++ # OS!="netbsd" and OS!="mac" and OS!="aix"
+ 'host_arch%': 'ia32',
+ }],
+ ],
+@@ -75,6 +74,12 @@
+ }, {
+ 'want_separate_host_toolset': 0,
+ }],
++ #
++ ['OS=="aix"', {
++ 'visibility%': '',
++ }, {
++ 'visibility%': 'hidden',
++ }],
+ ],
+ # Default ARM variable settings.
+ 'armv7%': 1,
+@@ -86,12 +91,17 @@
+ 'configurations': {
+ 'Debug': {
+ 'cflags': [ '-g', '-O0' ],
++ 'conditions': [
++ [ 'OS=="aix"', {
++ 'cflags': [ '-gxcoff' ],
++ }],
++ ],
+ },
+ },
+ },
+ 'conditions': [
+ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or
OS=="solaris" \
+- or OS=="netbsd"', {
++ or OS=="netbsd" or OS=="aix"', {
+ 'target_defaults': {
+ 'cflags': [ '-Wall', '<(werror)', '-W',
'-Wno-unused-parameter',
+ '-Wnon-virtual-dtor', '-pthread',
'-fno-rtti',
+@@ -101,6 +111,9 @@
+ [ 'OS=="linux"', {
+ 'cflags': [ '-ansi' ],
+ }],
++ [ 'host_arch=="ppc64"', {
++ 'cflags': [ '-mminimal-toc' ],
++ }],
+ [ 'visibility=="hidden"', {
+ 'cflags': [ '-fvisibility=hidden' ],
+ }],
+diff -up v8-3.14.5.10/.gitignore.ppc v8-3.14.5.10/.gitignore
+--- v8-3.14.5.10/.gitignore.ppc 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/.gitignore 2016-06-07 14:15:45.970393128 -0400
+@@ -12,6 +12,7 @@
+ *.sln
+ *.so
+ *.suo
++*.swp
+ *.user
+ *.vcproj
+ *.xcodeproj
+diff -up v8-3.14.5.10/Makefile.ppc v8-3.14.5.10/Makefile
+--- v8-3.14.5.10/Makefile.ppc 2016-06-07 14:15:45.958393199 -0400
++++ v8-3.14.5.10/Makefile 2016-06-07 14:15:45.970393128 -0400
+@@ -73,6 +73,10 @@ endif
+ ifeq ($(extrachecks), off)
+ GYPFLAGS += -Dv8_enable_extra_checks=0
+ endif
++# extrachecks=off
++ifeq ($(extrappcchecks), on)
++ GYPFLAGS += -Dv8_enable_extra_ppcchecks=1
++endif
+ # gdbjit=on
+ ifeq ($(gdbjit), on)
+ GYPFLAGS += -Dv8_enable_gdbjit=1
+@@ -115,6 +119,10 @@ endif
+ ifeq ($(hardfp), on)
+ GYPFLAGS += -Dv8_use_arm_eabi_hardfloat=true
+ endif
++# nativesim=true
++ifeq ($(nativesim), true)
++ GYPFLAGS += -Dv8_native_sim=true
++endif
+
+ # ----------------- available targets: --------------------
+ # - "dependencies": pulls in external dependencies (currently: GYP)
+@@ -133,8 +141,8 @@ endif
+
+ # Architectures and modes to be compiled. Consider these to be internal
+ # variables, don't override them (use the targets instead).
+-ARCHES = ia32 x64 arm mipsel mips
+-DEFAULT_ARCHES = ia32 x64 arm
++ARCHES = ia32 x64 arm ppc mipsel mips ppc64
++DEFAULT_ARCHES = ia32 x64 arm ppc ppc64
+ MODES = release debug
+ ANDROID_ARCHES = android_ia32 android_arm
+
+diff -up v8-3.14.5.10/README.md.ppc v8-3.14.5.10/README.md
+--- v8-3.14.5.10/README.md.ppc 2016-06-07 14:15:45.970393128 -0400
++++ v8-3.14.5.10/README.md 2016-06-07 14:15:45.970393128 -0400
+@@ -0,0 +1,10 @@
++v8ppc
++=====
++
++Port of Google V8 javascript engine to PowerPC - PowerLinux and AIX.
++
++This branch of the code (libv8-3.14) is intended to match the 3.14.5.8
++level of V8 that is used by the libv8 library built as part of Ubuntu
++
++http://packages.ubuntu.com/source/trusty/libv8-3.14
++
+diff -up v8-3.14.5.10/src/assembler.cc.ppc v8-3.14.5.10/src/assembler.cc
+--- v8-3.14.5.10/src/assembler.cc.ppc 2012-10-15 07:51:39.000000000 -0400
++++ v8-3.14.5.10/src/assembler.cc 2016-06-07 14:15:45.971393122 -0400
+@@ -61,6 +61,8 @@
+ #include "x64/assembler-x64-inl.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/assembler-arm-inl.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/assembler-ppc-inl.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/assembler-mips-inl.h"
+ #else
+@@ -75,6 +77,8 @@
+ #include "x64/regexp-macro-assembler-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/regexp-macro-assembler-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/regexp-macro-assembler-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/regexp-macro-assembler-mips.h"
+ #else // Unknown architecture.
+@@ -1064,6 +1068,8 @@ ExternalReference ExternalReference::re_
+ function = FUNCTION_ADDR(RegExpMacroAssemblerIA32::CheckStackGuardState);
+ #elif V8_TARGET_ARCH_ARM
+ function = FUNCTION_ADDR(RegExpMacroAssemblerARM::CheckStackGuardState);
++#elif V8_TARGET_ARCH_PPC
++ function = FUNCTION_ADDR(RegExpMacroAssemblerPPC::CheckStackGuardState);
+ #elif V8_TARGET_ARCH_MIPS
+ function = FUNCTION_ADDR(RegExpMacroAssemblerMIPS::CheckStackGuardState);
+ #else
+@@ -1221,6 +1227,21 @@ double power_double_double(double x, dou
+ }
+
+ if (x == 2.0) {
++ int y_int = static_cast<int>(y);
++ if (y == y_int) return ldexp(1.0, y_int);
++ }
++#elif defined(_AIX)
++ // AIX has a custom implementation for pow. This handles certain
++ // special cases that are different.
++ if ((x == 0.0 || isinf(x)) && y != 0.0 && isfinite(y)) {
++ double f;
++ double result = ((x == 0.0) ^ (y > 0)) ? V8_INFINITY : 0;
++ /* retain sign if odd integer exponent */
++ return ((modf(y, &f) == 0.0) && (static_cast<int64_t>(y) & 1))
?
++ copysign(result, x) : result;
++ }
++
++ if (x == 2.0) {
+ int y_int = static_cast<int>(y);
+ if (y == y_int) return ldexp(1.0, y_int);
+ }
+diff -up v8-3.14.5.10/src/assembler.h.ppc v8-3.14.5.10/src/assembler.h
+--- v8-3.14.5.10/src/assembler.h.ppc 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/src/assembler.h 2016-06-07 14:15:45.971393122 -0400
+@@ -829,31 +829,33 @@ class PreservePositionScope BASE_EMBEDDE
+ // -----------------------------------------------------------------------------
+ // Utility functions
+
+-inline bool is_intn(int x, int n) {
+- return -(1 << (n-1)) <= x && x < (1 << (n-1));
++inline bool is_intn(intptr_t x, int n) {
++ return -(1L << (n-1)) <= x && x < (1L << (n-1));
+ }
+
+-inline bool is_int8(int x) { return is_intn(x, 8); }
+-inline bool is_int16(int x) { return is_intn(x, 16); }
+-inline bool is_int18(int x) { return is_intn(x, 18); }
+-inline bool is_int24(int x) { return is_intn(x, 24); }
++inline bool is_int8(intptr_t x) { return is_intn(x, 8); }
++inline bool is_int16(intptr_t x) { return is_intn(x, 16); }
++inline bool is_int18(intptr_t x) { return is_intn(x, 18); }
++inline bool is_int24(intptr_t x) { return is_intn(x, 24); }
++inline bool is_int26(intptr_t x) { return is_intn(x, 26); }
+
+-inline bool is_uintn(int x, int n) {
+- return (x & -(1 << n)) == 0;
++
++inline bool is_uintn(intptr_t x, int n) {
++ return (x & -(1L << n)) == 0;
+ }
+
+-inline bool is_uint2(int x) { return is_uintn(x, 2); }
+-inline bool is_uint3(int x) { return is_uintn(x, 3); }
+-inline bool is_uint4(int x) { return is_uintn(x, 4); }
+-inline bool is_uint5(int x) { return is_uintn(x, 5); }
+-inline bool is_uint6(int x) { return is_uintn(x, 6); }
+-inline bool is_uint8(int x) { return is_uintn(x, 8); }
+-inline bool is_uint10(int x) { return is_uintn(x, 10); }
+-inline bool is_uint12(int x) { return is_uintn(x, 12); }
+-inline bool is_uint16(int x) { return is_uintn(x, 16); }
+-inline bool is_uint24(int x) { return is_uintn(x, 24); }
+-inline bool is_uint26(int x) { return is_uintn(x, 26); }
+-inline bool is_uint28(int x) { return is_uintn(x, 28); }
++inline bool is_uint2(intptr_t x) { return is_uintn(x, 2); }
++inline bool is_uint3(intptr_t x) { return is_uintn(x, 3); }
++inline bool is_uint4(intptr_t x) { return is_uintn(x, 4); }
++inline bool is_uint5(intptr_t x) { return is_uintn(x, 5); }
++inline bool is_uint6(intptr_t x) { return is_uintn(x, 6); }
++inline bool is_uint8(intptr_t x) { return is_uintn(x, 8); }
++inline bool is_uint10(intptr_t x) { return is_uintn(x, 10); }
++inline bool is_uint12(intptr_t x) { return is_uintn(x, 12); }
++inline bool is_uint16(intptr_t x) { return is_uintn(x, 16); }
++inline bool is_uint24(intptr_t x) { return is_uintn(x, 24); }
++inline bool is_uint26(intptr_t x) { return is_uintn(x, 26); }
++inline bool is_uint28(intptr_t x) { return is_uintn(x, 28); }
+
+ inline int NumberOfBitsSet(uint32_t x) {
+ unsigned int num_bits_set;
+diff -up v8-3.14.5.10/src/atomicops.h.ppc v8-3.14.5.10/src/atomicops.h
+--- v8-3.14.5.10/src/atomicops.h.ppc 2012-09-20 08:51:09.000000000 -0400
++++ v8-3.14.5.10/src/atomicops.h 2016-06-07 14:15:45.971393122 -0400
+@@ -69,7 +69,8 @@ typedef intptr_t Atomic64;
+
+ // Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or
+ // Atomic64 routines below, depending on your architecture.
+-#if defined(__OpenBSD__) && defined(__i386__)
++#if !defined(V8_HOST_ARCH_64_BIT) && \
++ ((defined(__OpenBSD__) && defined(__i386__)) || defined(_AIX))
+ typedef Atomic32 AtomicWord;
+ #else
+ typedef intptr_t AtomicWord;
+@@ -162,6 +163,8 @@ Atomic64 Release_Load(volatile const Ato
+ #include "atomicops_internals_x86_gcc.h"
+ #elif defined(__GNUC__) && defined(V8_HOST_ARCH_ARM)
+ #include "atomicops_internals_arm_gcc.h"
++#elif defined(__GNUC__) && defined(V8_HOST_ARCH_PPC)
++#include "atomicops_internals_ppc_gcc.h"
+ #elif defined(__GNUC__) && defined(V8_HOST_ARCH_MIPS)
+ #include "atomicops_internals_mips_gcc.h"
+ #else
+diff -up v8-3.14.5.10/src/atomicops_internals_ppc_gcc.h.ppc
v8-3.14.5.10/src/atomicops_internals_ppc_gcc.h
+--- v8-3.14.5.10/src/atomicops_internals_ppc_gcc.h.ppc 2016-06-07 14:15:45.971393122
-0400
++++ v8-3.14.5.10/src/atomicops_internals_ppc_gcc.h 2016-06-07 14:15:45.971393122 -0400
+@@ -0,0 +1,167 @@
++// Copyright 2010 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// This file is an internal atomic implementation, use atomicops.h instead.
++//
++
++#ifndef V8_ATOMICOPS_INTERNALS_PPC_H_
++#define V8_ATOMICOPS_INTERNALS_PPC_H_
++
++namespace v8 {
++namespace internal {
++
++inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
++ Atomic32 old_value,
++ Atomic32 new_value) {
++ return(__sync_val_compare_and_swap( ptr, old_value, new_value));
++}
++
++inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
++ Atomic32 new_value) {
++ Atomic32 old_value;
++ do {
++ old_value = *ptr;
++ } while (__sync_bool_compare_and_swap(ptr, old_value, new_value));
++ return old_value;
++}
++
++inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
++ Atomic32 increment) {
++ return Barrier_AtomicIncrement(ptr, increment);
++}
++
++inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
++ Atomic32 increment) {
++ for (;;) {
++ Atomic32 old_value = *ptr;
++ Atomic32 new_value = old_value + increment;
++ if (__sync_bool_compare_and_swap(ptr, old_value, new_value)) {
++ return new_value;
++ // The exchange took place as expected.
++ }
++ // Otherwise, *ptr changed mid-loop and we need to retry.
++ }
++}
++
++inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
++ Atomic32 old_value,
++ Atomic32 new_value) {
++ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
++}
++
++inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
++ Atomic32 old_value,
++ Atomic32 new_value) {
++ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
++}
++
++inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
++ *ptr = value;
++}
++
++inline void MemoryBarrier() {
++ __asm__ __volatile__("sync" : : : "memory");
++}
++
++inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
++ *ptr = value;
++ MemoryBarrier();
++}
++
++inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
++ MemoryBarrier();
++ *ptr = value;
++}
++
++inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
++ return *ptr;
++}
++
++inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
++ Atomic32 value = *ptr;
++ MemoryBarrier();
++ return value;
++}
++
++inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
++ MemoryBarrier();
++ return *ptr;
++}
++
++#ifdef V8_TARGET_ARCH_PPC64
++inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
++ Atomic64 old_value,
++ Atomic64 new_value) {
++ return(__sync_val_compare_and_swap( ptr, old_value, new_value));
++}
++
++inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
++ Atomic64 old_value,
++ Atomic64 new_value) {
++ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
++}
++
++inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
++ Atomic64 old_value,
++ Atomic64 new_value) {
++ return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
++}
++
++inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
++ *ptr = value;
++}
++
++inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
++ *ptr = value;
++ MemoryBarrier();
++}
++
++inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
++ MemoryBarrier();
++ *ptr = value;
++}
++
++inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
++ return *ptr;
++}
++
++inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
++ Atomic64 value = *ptr;
++ MemoryBarrier();
++ return value;
++}
++
++inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
++ MemoryBarrier();
++ return *ptr;
++}
++
++#endif
++
++} } // namespace v8::internal
++
++#endif // V8_ATOMICOPS_INTERNALS_PPC_GCC_H_
+diff -up v8-3.14.5.10/src/builtins.cc.ppc v8-3.14.5.10/src/builtins.cc
+--- v8-3.14.5.10/src/builtins.cc.ppc 2012-10-10 13:07:22.000000000 -0400
++++ v8-3.14.5.10/src/builtins.cc 2016-06-07 14:15:45.972393116 -0400
+@@ -1617,10 +1617,15 @@ void Builtins::SetUp(bool create_heap_ob
+
+ const BuiltinDesc* functions = builtin_function_table.functions();
+
++#if V8_TARGET_ARCH_PPC64
++ const int kBufferSize = 9 * KB;
++#else
++ const int kBufferSize = 8 * KB;
++#endif
+ // For now we generate builtin adaptor code into a stack-allocated
+ // buffer, before copying it into individual code objects. Be careful
+ // with alignment, some platforms don't like unaligned code.
+- union { int force_alignment; byte buffer[8*KB]; } u;
++ union { int force_alignment; byte buffer[kBufferSize]; } u;
+
+ // Traverse the list of builtins and generate an adaptor in a
+ // separate code object for each one.
+diff -up v8-3.14.5.10/src/checks.h.ppc v8-3.14.5.10/src/checks.h
+--- v8-3.14.5.10/src/checks.h.ppc 2016-06-07 14:15:45.928393378 -0400
++++ v8-3.14.5.10/src/checks.h 2016-06-07 14:15:45.972393116 -0400
+@@ -299,4 +299,18 @@ extern bool FLAG_enable_slow_asserts;
+ #define EXTRA_CHECK(condition) ((void) 0)
+ #endif
+
++// PENGUIN: Extra checks for PPC PORT
++// - PPCPORT_UNIMPLEMENTED: for unimplemented features
++// - PPCPORT_CHECK: for development phase
++// - PPCPORT_UNSAFE_IMPLEMENTATION: unsafe implementation
++#ifdef ENABLE_EXTRA_PPCCHECKS
++#define PPCPORT_CHECK(condition) CHECK(condition)
++#define PPCPORT_UNIMPLEMENTED() UNIMPLEMENTED()
++#define PPCPORT_UNSAFE_IMPLEMENTATION() ((void)0)
++#else
++#define PPCPORT_CHECK(condition) ((void) 0)
++#define PPCPORT_UNIMPLEMENTED() ((void) 0)
++#define PPCPORT_UNSAFE_IMPLEMENTATION() ((void)0)
++#endif
++
+ #endif // V8_CHECKS_H_
+diff -up v8-3.14.5.10/src/codegen.cc.ppc v8-3.14.5.10/src/codegen.cc
+--- v8-3.14.5.10/src/codegen.cc.ppc 2012-03-14 07:16:03.000000000 -0400
++++ v8-3.14.5.10/src/codegen.cc 2016-06-07 14:15:45.972393116 -0400
+@@ -200,7 +200,7 @@ void ArgumentsAccessStub::Generate(Macro
+ int CEntryStub::MinorKey() {
+ int result = (save_doubles_ == kSaveFPRegs) ? 1 : 0;
+ ASSERT(result_size_ == 1 || result_size_ == 2);
+-#ifdef _WIN64
++#if defined(_WIN64) || defined(V8_TARGET_ARCH_PPC64)
+ return result | ((result_size_ == 1) ? 0 : 2);
+ #else
+ return result;
+diff -up v8-3.14.5.10/src/codegen.h.ppc v8-3.14.5.10/src/codegen.h
+--- v8-3.14.5.10/src/codegen.h.ppc 2012-05-29 09:20:14.000000000 -0400
++++ v8-3.14.5.10/src/codegen.h 2016-06-07 14:15:45.972393116 -0400
+@@ -75,6 +75,8 @@ enum TypeofState { INSIDE_TYPEOF, NOT_IN
+ #include "x64/codegen-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/codegen-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/codegen-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/codegen-mips.h"
+ #else
+diff -up v8-3.14.5.10/src/code-stubs.h.ppc v8-3.14.5.10/src/code-stubs.h
+--- v8-3.14.5.10/src/code-stubs.h.ppc 2012-10-10 13:07:22.000000000 -0400
++++ v8-3.14.5.10/src/code-stubs.h 2016-06-07 14:15:45.972393116 -0400
+@@ -88,6 +88,18 @@ namespace internal {
+ #define CODE_STUB_LIST_ARM(V)
+ #endif
+
++// List of code stubs only used on PPC platforms.
++#ifdef V8_TARGET_ARCH_PPC
++#define CODE_STUB_LIST_PPC(V) \
++ V(GetProperty) \
++ V(SetProperty) \
++ V(InvokeBuiltin) \
++ V(RegExpCEntry) \
++ V(DirectCEntry)
++#else
++#define CODE_STUB_LIST_PPC(V)
++#endif
++
+ // List of code stubs only used on MIPS platforms.
+ #ifdef V8_TARGET_ARCH_MIPS
+ #define CODE_STUB_LIST_MIPS(V) \
+@@ -101,6 +113,7 @@ namespace internal {
+ #define CODE_STUB_LIST(V) \
+ CODE_STUB_LIST_ALL_PLATFORMS(V) \
+ CODE_STUB_LIST_ARM(V) \
++ CODE_STUB_LIST_PPC(V) \
+ CODE_STUB_LIST_MIPS(V)
+
+ // Mode to overwrite BinaryExpression values.
+@@ -254,6 +267,8 @@ class RuntimeCallHelper {
+ #include "x64/code-stubs-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/code-stubs-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/code-stubs-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/code-stubs-mips.h"
+ #else
+diff -up v8-3.14.5.10/src/conversions-inl.h.ppc v8-3.14.5.10/src/conversions-inl.h
+--- v8-3.14.5.10/src/conversions-inl.h.ppc 2016-06-07 14:15:45.972393116 -0400
++++ v8-3.14.5.10/src/conversions-inl.h 2016-06-07 14:19:40.280994665 -0400
+@@ -78,7 +78,7 @@ inline unsigned int FastD2UI(double x) {
+ #ifndef BIG_ENDIAN_FLOATING_POINT
+ Address mantissa_ptr = reinterpret_cast<Address>(&x);
+ #else
+- Address mantissa_ptr = reinterpret_cast<Address>(&x) + 4;
++ Address mantissa_ptr = reinterpret_cast<Address>(&x) + kIntSize;
+ #endif
+ // Copy least significant 32 bits of mantissa.
+ memcpy(&result, mantissa_ptr, sizeof(result));
+diff -up v8-3.14.5.10/src/d8.gyp.ppc v8-3.14.5.10/src/d8.gyp
+--- v8-3.14.5.10/src/d8.gyp.ppc 2012-03-09 05:52:05.000000000 -0500
++++ v8-3.14.5.10/src/d8.gyp 2016-06-07 14:15:45.972393116 -0400
+@@ -62,7 +62,8 @@
+ 'sources': [ 'd8-readline.cc' ],
+ }],
+ ['(OS=="linux" or OS=="mac" or
OS=="freebsd" or OS=="netbsd" \
+- or OS=="openbsd" or OS=="solaris" or
OS=="android")', {
++ or OS=="openbsd" or OS=="solaris" or
OS=="android" \
++ or OS=="aix")', {
+ 'sources': [ 'd8-posix.cc', ]
+ }],
+ [ 'OS=="win"', {
+diff -up v8-3.14.5.10/src/deoptimizer.h.ppc v8-3.14.5.10/src/deoptimizer.h
+--- v8-3.14.5.10/src/deoptimizer.h.ppc 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/src/deoptimizer.h 2016-06-07 14:15:45.972393116 -0400
+@@ -697,7 +697,11 @@ class SlotRef BASE_EMBEDDED {
+ return Handle<Object>(Memory::Object_at(addr_));
+
+ case INT32: {
++#if defined(V8_TARGET_ARCH_PPC64) && __BYTE_ORDER == __BIG_ENDIAN
++ int value = Memory::int32_at(addr_ + kIntSize);
++#else
+ int value = Memory::int32_at(addr_);
++#endif
+ if (Smi::IsValid(value)) {
+ return Handle<Object>(Smi::FromInt(value));
+ } else {
+@@ -706,7 +710,11 @@ class SlotRef BASE_EMBEDDED {
+ }
+
+ case UINT32: {
++#if defined(V8_TARGET_ARCH_PPC64) && __BYTE_ORDER == __BIG_ENDIAN
++ uint32_t value = Memory::uint32_at(addr_ + kIntSize);
++#else
+ uint32_t value = Memory::uint32_at(addr_);
++#endif
+ if (value <= static_cast<uint32_t>(Smi::kMaxValue)) {
+ return Handle<Object>(Smi::FromInt(static_cast<int>(value)));
+ } else {
+diff -up v8-3.14.5.10/src/disassembler.cc.ppc v8-3.14.5.10/src/disassembler.cc
+--- v8-3.14.5.10/src/disassembler.cc.ppc 2012-06-27 07:12:38.000000000 -0400
++++ v8-3.14.5.10/src/disassembler.cc 2016-06-07 14:15:45.972393116 -0400
+@@ -158,7 +158,11 @@ static int DecodeIt(FILE* f,
+ "%08" V8PRIxPTR " jump table entry %4"
V8PRIdPTR,
+ ptr,
+ ptr - begin);
++#if V8_TARGET_ARCH_PPC64
++ pc += 8;
++#else
+ pc += 4;
++#endif
+ } else {
+ decode_buffer[0] = '\0';
+ pc += d.InstructionDecode(decode_buffer, pc);
+diff -up v8-3.14.5.10/src/execution.h.ppc v8-3.14.5.10/src/execution.h
+--- v8-3.14.5.10/src/execution.h.ppc 2012-07-24 03:59:48.000000000 -0400
++++ v8-3.14.5.10/src/execution.h 2016-06-07 14:15:45.972393116 -0400
+@@ -258,7 +258,7 @@ class StackGuard {
+ void EnableInterrupts();
+ void DisableInterrupts();
+
+-#ifdef V8_TARGET_ARCH_X64
++#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_PPC64)
+ static const uintptr_t kInterruptLimit = V8_UINT64_C(0xfffffffffffffffe);
+ static const uintptr_t kIllegalLimit = V8_UINT64_C(0xfffffffffffffff8);
+ #else
+diff -up v8-3.14.5.10/src/flag-definitions.h.ppc v8-3.14.5.10/src/flag-definitions.h
+--- v8-3.14.5.10/src/flag-definitions.h.ppc 2013-05-23 06:49:13.000000000 -0400
++++ v8-3.14.5.10/src/flag-definitions.h 2016-06-07 14:15:45.973393110 -0400
+@@ -442,6 +442,7 @@ DEFINE_bool(trace_parse, false, "trace p
+
+ // simulator-arm.cc and simulator-mips.cc
+ DEFINE_bool(trace_sim, false, "Trace simulator execution")
++DEFINE_bool(trace_sim_stubs, false, "Trace simulator execution w/ stub
markers")
+ DEFINE_bool(check_icache, false,
+ "Check icache flushes in ARM and MIPS simulator")
+ DEFINE_int(stop_sim_at, 0, "Simulator stop after x number of instructions")
+diff -up v8-3.14.5.10/src/frames-inl.h.ppc v8-3.14.5.10/src/frames-inl.h
+--- v8-3.14.5.10/src/frames-inl.h.ppc 2012-02-28 04:49:15.000000000 -0500
++++ v8-3.14.5.10/src/frames-inl.h 2016-06-07 14:15:45.973393110 -0400
+@@ -38,6 +38,8 @@
+ #include "x64/frames-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/frames-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/frames-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/frames-mips.h"
+ #else
+diff -up v8-3.14.5.10/src/full-codegen.h.ppc v8-3.14.5.10/src/full-codegen.h
+--- v8-3.14.5.10/src/full-codegen.h.ppc 2012-08-10 10:46:33.000000000 -0400
++++ v8-3.14.5.10/src/full-codegen.h 2016-06-07 14:15:45.973393110 -0400
+@@ -125,6 +125,8 @@ class FullCodeGenerator: public AstVisit
+ static const int kBackEdgeDistanceUnit = 162;
+ #elif V8_TARGET_ARCH_ARM
+ static const int kBackEdgeDistanceUnit = 142;
++#elif V8_TARGET_ARCH_PPC
++ static const int kBackEdgeDistanceUnit = 142;
+ #elif V8_TARGET_ARCH_MIPS
+ static const int kBackEdgeDistanceUnit = 142;
+ #else
+@@ -333,12 +335,18 @@ class FullCodeGenerator: public AstVisit
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through);
+-#else // All non-mips arch.
++#elif defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64)
++ void Split(Condition cc,
++ Label* if_true,
++ Label* if_false,
++ Label* fall_through,
++ CRegister cr = cr7);
++#else // All other arch.
+ void Split(Condition cc,
+ Label* if_true,
+ Label* if_false,
+ Label* fall_through);
+-#endif // V8_TARGET_ARCH_MIPS
++#endif
+
+ // Load the value of a known (PARAMETER, LOCAL, or CONTEXT) variable into
+ // a register. Emits a context chain walk if if necessary (so does
+diff -up v8-3.14.5.10/src/globals.h.ppc v8-3.14.5.10/src/globals.h
+--- v8-3.14.5.10/src/globals.h.ppc 2016-06-07 14:15:45.958393199 -0400
++++ v8-3.14.5.10/src/globals.h 2016-06-07 14:24:39.511206075 -0400
+@@ -56,6 +56,11 @@
+ #define V8_INFINITY HUGE_VAL
+ #endif
+
++#ifdef _AIX
++#undef V8_INFINITY
++#define V8_INFINITY (__builtin_inff())
++#endif
++
+
+ #include "../include/v8stdint.h"
+
+@@ -86,6 +91,13 @@ namespace internal {
+ #elif defined(__MIPSEL__) || defined(__MIPSEB__)
+ #define V8_HOST_ARCH_MIPS 1
+ #define V8_HOST_ARCH_32_BIT 1
++#elif defined(__PPC__) || defined(_ARCH_PPC)
++#define V8_HOST_ARCH_PPC 1
++#if defined(__PPC64__) || defined(_ARCH_PPC64)
++#define V8_HOST_ARCH_64_BIT 1
++#else
++#define V8_HOST_ARCH_32_BIT 1
++#endif
+ #else
+ #error Host architecture was not detected as supported by v8
+ #endif
+@@ -94,7 +106,8 @@ namespace internal {
+ // in the same way as the host architecture, that is, target the native
+ // environment as presented by the compiler.
+ #if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_IA32) && \
+- !defined(V8_TARGET_ARCH_ARM) && !defined(V8_TARGET_ARCH_MIPS)
++ !defined(V8_TARGET_ARCH_ARM) && !defined(V8_TARGET_ARCH_MIPS) && \
++ !defined(V8_TARGET_ARCH_PPC)
+ #if defined(_M_X64) || defined(__x86_64__)
+ #define V8_TARGET_ARCH_X64 1
+ #elif defined(_M_IX86) || defined(__i386__)
+@@ -112,6 +125,10 @@ namespace internal {
+ #define BIG_ENDIAN_FLOATING_POINT 1
+ #endif
+
++#if __BYTE_ORDER == __BIG_ENDIAN
++#define BIG_ENDIAN_FLOATING_POINT 1
++#endif
++
+ // Check for supported combinations of host and target architectures.
+ #if defined(V8_TARGET_ARCH_IA32) && !defined(V8_HOST_ARCH_IA32)
+ #error Target architecture ia32 is only supported on ia32 host
+@@ -120,8 +137,9 @@ namespace internal {
+ #error Target architecture x64 is only supported on x64 host
+ #endif
+ #if (defined(V8_TARGET_ARCH_ARM) && \
+- !(defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_ARM)))
+-#error Target architecture arm is only supported on arm and ia32 host
++ !(defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_ARM) || \
++ defined(V8_HOST_ARCH_PPC)))
++#error Target architecture arm is only supported on arm, ppc and ia32 host
+ #endif
+ #if (defined(V8_TARGET_ARCH_MIPS) && \
+ !(defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_MIPS)))
+@@ -135,6 +153,9 @@ namespace internal {
+ #if (defined(V8_TARGET_ARCH_ARM) && !defined(V8_HOST_ARCH_ARM))
+ #define USE_SIMULATOR 1
+ #endif
++#if (defined(V8_TARGET_ARCH_PPC) && !defined(V8_HOST_ARCH_PPC))
++#define USE_SIMULATOR 1
++#endif
+ #if (defined(V8_TARGET_ARCH_MIPS) && !defined(V8_HOST_ARCH_MIPS))
+ #define USE_SIMULATOR 1
+ #endif
+@@ -194,6 +215,16 @@ typedef byte* Address;
+ #define V8PRIdPTR V8_PTR_PREFIX "d"
+ #define V8PRIuPTR V8_PTR_PREFIX "u"
+
++// Fix for AIX define intptr_t as "long int":
++#ifdef _AIX
++#undef V8_PTR_PREFIX
++#define V8_PTR_PREFIX "l"
++#undef V8PRIdPTR
++#define V8PRIdPTR "ld"
++#undef V8PRIxPTR
++#define V8PRIxPTR "lx"
++#endif
++
+ // Fix for Mac OS X defining uintptr_t as "unsigned long":
+ #if defined(__APPLE__) && defined(__MACH__)
+ #undef V8PRIxPTR
+diff -up v8-3.14.5.10/src/heap.cc.ppc v8-3.14.5.10/src/heap.cc
+--- v8-3.14.5.10/src/heap.cc.ppc 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/src/heap.cc 2016-06-07 14:15:45.974393104 -0400
+@@ -50,6 +50,10 @@
+ #include "v8threads.h"
+ #include "v8utils.h"
+ #include "vm-state-inl.h"
++#if V8_TARGET_ARCH_PPC && !V8_INTERPRETED_REGEXP
++#include "regexp-macro-assembler.h"
++#include "ppc/regexp-macro-assembler-ppc.h"
++#endif
+ #if V8_TARGET_ARCH_ARM && !V8_INTERPRETED_REGEXP
+ #include "regexp-macro-assembler.h"
+ #include "arm/regexp-macro-assembler-arm.h"
+diff -up v8-3.14.5.10/src/hydrogen.cc.ppc v8-3.14.5.10/src/hydrogen.cc
+--- v8-3.14.5.10/src/hydrogen.cc.ppc 2016-06-07 14:15:45.934393343 -0400
++++ v8-3.14.5.10/src/hydrogen.cc 2016-06-07 14:15:45.975393098 -0400
+@@ -43,6 +43,8 @@
+ #include "x64/lithium-codegen-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/lithium-codegen-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/lithium-codegen-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/lithium-codegen-mips.h"
+ #else
+diff -up v8-3.14.5.10/src/hydrogen-instructions.cc.ppc
v8-3.14.5.10/src/hydrogen-instructions.cc
+--- v8-3.14.5.10/src/hydrogen-instructions.cc.ppc 2013-01-18 05:40:43.000000000 -0500
++++ v8-3.14.5.10/src/hydrogen-instructions.cc 2016-06-07 14:15:45.974393104 -0400
+@@ -36,6 +36,8 @@
+ #include "x64/lithium-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/lithium-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/lithium-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/lithium-mips.h"
+ #else
+@@ -966,8 +968,8 @@ HValue* HUnaryMathOperation::Canonicaliz
+ // introduced.
+ if (value()->representation().IsInteger32()) return value();
+
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_IA32) || \
+- defined(V8_TARGET_ARCH_X64)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_IA32) || \
++ defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_PPC)
+ if (value()->IsDiv() && (value()->UseCount() == 1)) {
+ // TODO(2038): Implement this optimization for non ARM architectures.
+ HDiv* hdiv = HDiv::cast(value());
+diff -up v8-3.14.5.10/src/isolate.cc.ppc v8-3.14.5.10/src/isolate.cc
+--- v8-3.14.5.10/src/isolate.cc.ppc 2016-06-07 14:15:45.948393259 -0400
++++ v8-3.14.5.10/src/isolate.cc 2016-06-07 14:15:45.976393092 -0400
+@@ -116,6 +116,8 @@ void ThreadLocalTop::Initialize() {
+ #ifdef USE_SIMULATOR
+ #ifdef V8_TARGET_ARCH_ARM
+ simulator_ = Simulator::current(isolate_);
++#elif V8_TARGET_ARCH_PPC
++ simulator_ = Simulator::current(isolate_);
+ #elif V8_TARGET_ARCH_MIPS
+ simulator_ = Simulator::current(isolate_);
+ #endif
+@@ -1425,6 +1427,8 @@ char* Isolate::RestoreThread(char* from)
+ #ifdef USE_SIMULATOR
+ #ifdef V8_TARGET_ARCH_ARM
+ thread_local_top()->simulator_ = Simulator::current(this);
++#elif V8_TARGET_ARCH_PPC
++ thread_local_top()->simulator_ = Simulator::current(this);
+ #elif V8_TARGET_ARCH_MIPS
+ thread_local_top()->simulator_ = Simulator::current(this);
+ #endif
+@@ -1562,6 +1566,7 @@ Isolate::Isolate()
+ thread_manager_->isolate_ = this;
+
+ #if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
++ defined(V8_TARGET_ARCH_PPC) && !defined(__PPC__) || \
+ defined(V8_TARGET_ARCH_MIPS) && !defined(__mips__)
+ simulator_initialized_ = false;
+ simulator_i_cache_ = NULL;
+@@ -1879,7 +1884,8 @@ bool Isolate::Init(Deserializer* des) {
+
+ // Initialize other runtime facilities
+ #if defined(USE_SIMULATOR)
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) \
++ || defined(V8_TARGET_ARCH_PPC)
+ Simulator::Initialize(this);
+ #endif
+ #endif
+diff -up v8-3.14.5.10/src/isolate.h.ppc v8-3.14.5.10/src/isolate.h
+--- v8-3.14.5.10/src/isolate.h.ppc 2016-06-07 14:15:45.948393259 -0400
++++ v8-3.14.5.10/src/isolate.h 2016-06-07 14:15:45.976393092 -0400
+@@ -94,7 +94,9 @@ class Debugger;
+ class DebuggerAgent;
+ #endif
+
+-#if !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++#if defined(NATIVE_SIMULATION) || \
++ !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++ !defined(__PPC__) && defined(V8_TARGET_ARCH_PPC) || \
+ !defined(__mips__) && defined(V8_TARGET_ARCH_MIPS)
+ class Redirection;
+ class Simulator;
+@@ -256,7 +258,8 @@ class ThreadLocalTop BASE_EMBEDDED {
+ Address handler_; // try-blocks are chained through the stack
+
+ #ifdef USE_SIMULATOR
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_PPC) || \
++ defined(V8_TARGET_ARCH_MIPS)
+ Simulator* simulator_;
+ #endif
+ #endif // USE_SIMULATOR
+@@ -374,7 +377,9 @@ class Isolate {
+ thread_id_(thread_id),
+ stack_limit_(0),
+ thread_state_(NULL),
+-#if !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++#if defined(NATIVE_SIMULATION) || \
++ !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++ !defined(__PPC__) && defined(V8_TARGET_ARCH_PPC) || \
+ !defined(__mips__) && defined(V8_TARGET_ARCH_MIPS)
+ simulator_(NULL),
+ #endif
+@@ -387,7 +392,9 @@ class Isolate {
+ ThreadState* thread_state() const { return thread_state_; }
+ void set_thread_state(ThreadState* value) { thread_state_ = value; }
+
+-#if !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++#if defined(NATIVE_SIMULATION) || \
++ !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++ !defined(__PPC__) && defined(V8_TARGET_ARCH_PPC) || \
+ !defined(__mips__) && defined(V8_TARGET_ARCH_MIPS)
+ Simulator* simulator() const { return simulator_; }
+ void set_simulator(Simulator* simulator) {
+@@ -405,7 +412,9 @@ class Isolate {
+ uintptr_t stack_limit_;
+ ThreadState* thread_state_;
+
+-#if !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++#if defined(NATIVE_SIMULATION) || \
++ !defined(__arm__) && defined(V8_TARGET_ARCH_ARM) || \
++ !defined(__PPC__) && defined(V8_TARGET_ARCH_PPC) || \
+ !defined(__mips__) && defined(V8_TARGET_ARCH_MIPS)
+ Simulator* simulator_;
+ #endif
+@@ -972,7 +981,9 @@ class Isolate {
+ int* code_kind_statistics() { return code_kind_statistics_; }
+ #endif
+
+-#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
++#if defined(NATIVE_SIMULATION) || \
++ defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
++ defined(V8_TARGET_ARCH_PPC) && !defined(__PPC__) || \
+ defined(V8_TARGET_ARCH_MIPS) && !defined(__mips__)
+ bool simulator_initialized() { return simulator_initialized_; }
+ void set_simulator_initialized(bool initialized) {
+@@ -1252,7 +1263,9 @@ class Isolate {
+ // Time stamp at initialization.
+ double time_millis_at_init_;
+
+-#if defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
++#if defined(NATIVE_SIMULATION) || \
++ defined(V8_TARGET_ARCH_ARM) && !defined(__arm__) || \
++ defined(V8_TARGET_ARCH_PPC) && !defined(__PPC__) || \
+ defined(V8_TARGET_ARCH_MIPS) && !defined(__mips__)
+ bool simulator_initialized_;
+ HashMap* simulator_i_cache_;
+diff -up v8-3.14.5.10/src/jsregexp.cc.ppc v8-3.14.5.10/src/jsregexp.cc
+--- v8-3.14.5.10/src/jsregexp.cc.ppc 2012-09-14 09:28:26.000000000 -0400
++++ v8-3.14.5.10/src/jsregexp.cc 2016-06-07 14:15:45.977393086 -0400
+@@ -50,6 +50,8 @@
+ #include "x64/regexp-macro-assembler-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/regexp-macro-assembler-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/regexp-macro-assembler-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/regexp-macro-assembler-mips.h"
+ #else
+@@ -6131,6 +6133,9 @@ RegExpEngine::CompilationResult RegExpEn
+ #elif V8_TARGET_ARCH_ARM
+ RegExpMacroAssemblerARM macro_assembler(mode, (data->capture_count + 1) * 2,
+ zone);
++#elif V8_TARGET_ARCH_PPC
++ RegExpMacroAssemblerPPC macro_assembler(mode, (data->capture_count + 1) * 2,
++ zone);
+ #elif V8_TARGET_ARCH_MIPS
+ RegExpMacroAssemblerMIPS macro_assembler(mode, (data->capture_count + 1) * 2,
+ zone);
+diff -up v8-3.14.5.10/src/jsregexp.h.ppc v8-3.14.5.10/src/jsregexp.h
+--- v8-3.14.5.10/src/jsregexp.h.ppc 2012-08-29 11:32:24.000000000 -0400
++++ v8-3.14.5.10/src/jsregexp.h 2016-06-07 14:15:45.977393086 -0400
+@@ -1352,6 +1352,13 @@ class BoyerMooreLookahead : public ZoneO
+ // to match foo is generated only once (the traces have a common prefix). The
+ // code to store the capture is deferred and generated (twice) after the places
+ // where baz has been matched.
++
++#ifdef _AIX
++#undef UNKNOWN
++#undef FALSE
++#undef TRUE
++#endif
++
+ class Trace {
+ public:
+ // A value for a property that is either known to be true, know to be false,
+diff -up v8-3.14.5.10/src/lithium-allocator.cc.ppc v8-3.14.5.10/src/lithium-allocator.cc
+--- v8-3.14.5.10/src/lithium-allocator.cc.ppc 2012-07-16 07:31:55.000000000 -0400
++++ v8-3.14.5.10/src/lithium-allocator.cc 2016-06-07 14:15:45.978393080 -0400
+@@ -37,6 +37,8 @@
+ #include "x64/lithium-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/lithium-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/lithium-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/lithium-mips.h"
+ #else
+diff -up v8-3.14.5.10/src/lithium-allocator-inl.h.ppc
v8-3.14.5.10/src/lithium-allocator-inl.h
+--- v8-3.14.5.10/src/lithium-allocator-inl.h.ppc 2011-06-08 06:05:15.000000000 -0400
++++ v8-3.14.5.10/src/lithium-allocator-inl.h 2016-06-07 14:15:45.977393086 -0400
+@@ -36,6 +36,8 @@
+ #include "x64/lithium-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/lithium-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/lithium-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/lithium-mips.h"
+ #else
+diff -up v8-3.14.5.10/src/lithium.cc.ppc v8-3.14.5.10/src/lithium.cc
+--- v8-3.14.5.10/src/lithium.cc.ppc 2016-06-07 14:15:45.911393480 -0400
++++ v8-3.14.5.10/src/lithium.cc 2016-06-07 14:15:45.978393080 -0400
+@@ -38,6 +38,9 @@
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/lithium-arm.h"
+ #include "arm/lithium-codegen-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/lithium-ppc.h"
++#include "ppc/lithium-codegen-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/lithium-mips.h"
+ #include "mips/lithium-codegen-mips.h"
+diff -up v8-3.14.5.10/src/log.cc.ppc v8-3.14.5.10/src/log.cc
+--- v8-3.14.5.10/src/log.cc.ppc 2012-08-29 11:32:24.000000000 -0400
++++ v8-3.14.5.10/src/log.cc 2016-06-07 14:15:45.978393080 -0400
+@@ -1526,6 +1526,8 @@ void Logger::LogCodeInfo() {
+ const char arch[] = "x64";
+ #elif V8_TARGET_ARCH_ARM
+ const char arch[] = "arm";
++#elif V8_TARGET_ARCH_PPC
++ const char arch[] = "ppc";
+ #elif V8_TARGET_ARCH_MIPS
+ const char arch[] = "mips";
+ #else
+diff -up v8-3.14.5.10/src/macro-assembler.h.ppc v8-3.14.5.10/src/macro-assembler.h
+--- v8-3.14.5.10/src/macro-assembler.h.ppc 2012-02-14 06:46:07.000000000 -0500
++++ v8-3.14.5.10/src/macro-assembler.h 2016-06-07 14:15:45.978393080 -0400
+@@ -58,6 +58,13 @@ const int kInvalidProtoDepth = -1;
+ #include "arm/assembler-arm-inl.h"
+ #include "code.h" // must be after assembler_*.h
+ #include "arm/macro-assembler-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/constants-ppc.h"
++#include "assembler.h"
++#include "ppc/assembler-ppc.h"
++#include "ppc/assembler-ppc-inl.h"
++#include "code.h" // must be after assembler_*.h
++#include "ppc/macro-assembler-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/constants-mips.h"
+ #include "assembler.h"
+diff -up v8-3.14.5.10/src/objects.cc.ppc v8-3.14.5.10/src/objects.cc
+--- v8-3.14.5.10/src/objects.cc.ppc 2016-06-07 14:15:45.916393450 -0400
++++ v8-3.14.5.10/src/objects.cc 2016-06-07 14:15:45.981393062 -0400
+@@ -7009,8 +7009,8 @@ static inline bool CompareRawStringConte
+ // then we have to check that the strings are aligned before
+ // comparing them blockwise.
+ const int kAlignmentMask = sizeof(uint32_t) - 1; // NOLINT
+- uint32_t pa_addr = reinterpret_cast<uint32_t>(pa);
+- uint32_t pb_addr = reinterpret_cast<uint32_t>(pb);
++ uintptr_t pa_addr = reinterpret_cast<uintptr_t>(pa);
++ uintptr_t pb_addr = reinterpret_cast<uintptr_t>(pb);
+ if (((pa_addr & kAlignmentMask) | (pb_addr & kAlignmentMask)) == 0) {
+ #endif
+ const int kStepSize = sizeof(int) / sizeof(Char); // NOLINT
+diff -up v8-3.14.5.10/src/objects.h.ppc v8-3.14.5.10/src/objects.h
+--- v8-3.14.5.10/src/objects.h.ppc 2016-06-07 14:15:45.963393170 -0400
++++ v8-3.14.5.10/src/objects.h 2016-06-07 14:15:45.982393056 -0400
+@@ -37,6 +37,8 @@
+ #include "unicode-inl.h"
+ #if V8_TARGET_ARCH_ARM
+ #include "arm/constants-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/constants-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/constants-mips.h"
+ #endif
+@@ -5852,10 +5854,11 @@ class SharedFunctionInfo: public HeapObj
+ // garbage collections.
+ // To avoid wasting space on 64-bit architectures we use
+ // the following trick: we group integer fields into pairs
+- // First integer in each pair is shifted left by 1.
++ // The least significant integer in each pair is shifted left by 1.
+ // By doing this we guarantee that LSB of each kPointerSize aligned
+ // word is not set and thus this word cannot be treated as pointer
+ // to HeapObject during old space traversal.
++#if __BYTE_ORDER == __LITTLE_ENDIAN
+ static const int kLengthOffset =
+ kAstNodeCountOffset + kPointerSize;
+ static const int kFormalParameterCountOffset =
+@@ -5883,6 +5886,38 @@ class SharedFunctionInfo: public HeapObj
+
+ static const int kCountersOffset = kOptCountOffset + kIntSize;
+ static const int kStressDeoptCounterOffset = kCountersOffset + kIntSize;
++#elif __BYTE_ORDER == __BIG_ENDIAN
++ static const int kFormalParameterCountOffset =
++ kAstNodeCountOffset + kPointerSize;
++ static const int kLengthOffset =
++ kFormalParameterCountOffset + kIntSize;
++
++ static const int kNumLiteralsOffset =
++ kLengthOffset + kIntSize;
++ static const int kExpectedNofPropertiesOffset =
++ kNumLiteralsOffset + kIntSize;
++
++ static const int kStartPositionAndTypeOffset =
++ kExpectedNofPropertiesOffset + kIntSize;
++ static const int kEndPositionOffset =
++ kStartPositionAndTypeOffset + kIntSize;
++
++ static const int kCompilerHintsOffset =
++ kEndPositionOffset + kIntSize;
++ static const int kFunctionTokenPositionOffset =
++ kCompilerHintsOffset + kIntSize;
++
++ static const int kOptCountOffset =
++ kFunctionTokenPositionOffset + kIntSize;
++ static const int kThisPropertyAssignmentsCountOffset =
++ kOptCountOffset + kIntSize;
++
++ static const int kStressDeoptCounterOffset =
++ kThisPropertyAssignmentsCountOffset + kIntSize;
++ static const int kCountersOffset = kStressDeoptCounterOffset + kIntSize;
++#else
++#error Unknown byte ordering
++#endif
+
+ // Total size.
+ static const int kSize = kStressDeoptCounterOffset + kIntSize;
+@@ -7322,8 +7357,13 @@ class String: public HeapObject {
+
+ // Layout description.
+ static const int kLengthOffset = HeapObject::kHeaderSize;
+- static const int kHashFieldOffset = kLengthOffset + kPointerSize;
+- static const int kSize = kHashFieldOffset + kPointerSize;
++ static const int kHashFieldSlot = kLengthOffset + kPointerSize;
++#if __BYTE_ORDER == __LITTLE_ENDIAN || !V8_HOST_ARCH_64_BIT
++ static const int kHashFieldOffset = kHashFieldSlot;
++#else
++ static const int kHashFieldOffset = kHashFieldSlot + kIntSize;
++#endif
++ static const int kSize = kHashFieldSlot + kPointerSize;
+
+ // Maximum number of characters to consider when trying to convert a string
+ // value into an array index.
+diff -up v8-3.14.5.10/src/objects-inl.h.ppc v8-3.14.5.10/src/objects-inl.h
+--- v8-3.14.5.10/src/objects-inl.h.ppc 2016-06-07 14:15:45.901393539 -0400
++++ v8-3.14.5.10/src/objects-inl.h 2016-06-07 14:15:45.979393074 -0400
+@@ -1061,7 +1061,7 @@ bool Smi::IsValid(intptr_t value) {
+ bool in_range = (value >= kMinValue) && (value <= kMaxValue);
+ #endif
+
+-#ifdef V8_TARGET_ARCH_X64
++#if defined(V8_TARGET_ARCH_X64) || defined(V8_TARGET_ARCH_PPC64)
+ // To be representable as a long smi, the value must be a 32-bit integer.
+ bool result = (value == static_cast<int32_t>(value));
+ #else
+@@ -2409,10 +2409,7 @@ uint32_t String::hash_field() {
+
+
+ void String::set_hash_field(uint32_t value) {
+- WRITE_UINT32_FIELD(this, kHashFieldOffset, value);
+-#if V8_HOST_ARCH_64_BIT
+- WRITE_UINT32_FIELD(this, kHashFieldOffset + kIntSize, 0);
+-#endif
++ WRITE_INTPTR_FIELD(this, kHashFieldSlot, value);
+ }
+
+
+@@ -3977,25 +3974,33 @@ SMI_ACCESSORS(SharedFunctionInfo,
+ kStressDeoptCounterOffset)
+ #else
+
+-#define PSEUDO_SMI_ACCESSORS_LO(holder, name, offset) \
+- STATIC_ASSERT(holder::offset % kPointerSize == 0); \
+- int holder::name() { \
+- int value = READ_INT_FIELD(this, offset); \
+- ASSERT(kHeapObjectTag == 1); \
+- ASSERT((value & kHeapObjectTag) == 0); \
+- return value >> 1; \
+- } \
+- void holder::set_##name(int value) { \
+- ASSERT(kHeapObjectTag == 1); \
+- ASSERT((value & 0xC0000000) == 0xC0000000 || \
+- (value & 0xC0000000) == 0x000000000); \
+- WRITE_INT_FIELD(this, \
+- offset, \
+- (value << 1) & ~kHeapObjectTag); \
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++#define PSEUDO_SMI_LO_ALIGN 0
++#define PSEUDO_SMI_HI_ALIGN kIntSize
++#else
++#define PSEUDO_SMI_LO_ALIGN kIntSize
++#define PSEUDO_SMI_HI_ALIGN 0
++#endif
++
++#define PSEUDO_SMI_ACCESSORS_LO(holder, name, offset) \
++ STATIC_ASSERT(holder::offset % kPointerSize == PSEUDO_SMI_LO_ALIGN); \
++ int holder::name() { \
++ int value = READ_INT_FIELD(this, offset); \
++ ASSERT(kHeapObjectTag == 1); \
++ ASSERT((value & kHeapObjectTag) == 0); \
++ return value >> 1; \
++ } \
++ void holder::set_##name(int value) { \
++ ASSERT(kHeapObjectTag == 1); \
++ ASSERT((value & 0xC0000000) == 0xC0000000 || \
++ (value & 0xC0000000) == 0x000000000); \
++ WRITE_INT_FIELD(this, \
++ offset, \
++ (value << 1) & ~kHeapObjectTag); \
+ }
+
+-#define PSEUDO_SMI_ACCESSORS_HI(holder, name, offset) \
+- STATIC_ASSERT(holder::offset % kPointerSize == kIntSize); \
++#define PSEUDO_SMI_ACCESSORS_HI(holder, name, offset) \
++ STATIC_ASSERT(holder::offset % kPointerSize == PSEUDO_SMI_HI_ALIGN); \
+ INT_ACCESSORS(holder, name, offset)
+
+
+diff -up v8-3.14.5.10/src/platform-aix.cc.ppc v8-3.14.5.10/src/platform-aix.cc
+--- v8-3.14.5.10/src/platform-aix.cc.ppc 2016-06-07 14:15:45.983393050 -0400
++++ v8-3.14.5.10/src/platform-aix.cc 2016-06-07 14:15:45.983393050 -0400
+@@ -0,0 +1,894 @@
++// Copyright 2013 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// Platform specific code for AIX goes here. For the POSIX comaptible parts
++// the implementation is in platform-posix.cc.
++
++#include <pthread.h>
++#include <semaphore.h>
++#include <signal.h>
++#include <sys/time.h>
++#include <sys/resource.h>
++#include <sys/types.h>
++#include <sys/ucontext.h>
++#include <stdlib.h>
++
++#include <sys/types.h> // mmap & munmap
++#include <sys/mman.h> // mmap & munmap
++#include <sys/stat.h> // open
++#include <fcntl.h> // open
++#include <unistd.h> // getpagesize
++#include <strings.h> // index
++#include <errno.h>
++#include <stdarg.h>
++#include <limits.h>
++
++#undef MAP_TYPE
++
++#include "v8.h"
++#include "v8threads.h"
++
++#include "platform-posix.h"
++#include "platform.h"
++#include "vm-state-inl.h"
++
++
++namespace v8 {
++namespace internal {
++
++// 0 is never a valid thread id on AIX since tids and pids share a
++// name space and pid 0 is used to kill the group (see man 2 kill).
++static const pthread_t kNoThread = (pthread_t) 0;
++
++
++double ceiling(double x) {
++ // Correct as on OS X
++ if (-1.0 < x && x < 0.0) {
++ return -0.0;
++ } else {
++ return ceil(x);
++ }
++}
++
++
++static Mutex* limit_mutex = NULL;
++
++
++void OS::PostSetUp() {
++ POSIXPostSetUp();
++}
++
++
++void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
++ __asm__ __volatile__("" : : : "memory");
++ *ptr = value;
++}
++
++
++uint64_t OS::CpuFeaturesImpliedByPlatform() {
++ return 0; // AIX runs on anything.
++}
++
++
++int OS::ActivationFrameAlignment() {
++ // 8 byte alignment on AIX
++ return 8;
++}
++
++
++const char* OS::LocalTimezone(double time) {
++ if (isnan(time)) return "";
++ time_t tv = static_cast<time_t>(floor(time/msPerSecond));
++ struct tm* t = localtime(&tv);
++ if (NULL == t) return "";
++ return tzname[0]; // The location of the timezone string on AIX.
++}
++
++
++double OS::LocalTimeOffset() {
++ // On AIX, struct tm does not contain a tm_gmtoff field.
++ time_t utc = time(NULL);
++ ASSERT(utc != -1);
++ struct tm* loc = localtime(&utc);
++ ASSERT(loc != NULL);
++ return static_cast<double>((mktime(loc) - utc) * msPerSecond);
++}
++
++
++// We keep the lowest and highest addresses mapped as a quick way of
++// determining that pointers are outside the heap (used mostly in assertions
++// and verification). The estimate is conservative, i.e., not all addresses in
++// 'allocated' space are actually allocated to our heap. The range is
++// [lowest, highest), inclusive on the low and and exclusive on the high end.
++static void* lowest_ever_allocated = reinterpret_cast<void*>(-1);
++static void* highest_ever_allocated = reinterpret_cast<void*>(0);
++
++
++static void UpdateAllocatedSpaceLimits(void* address, int size) {
++ ASSERT(limit_mutex != NULL);
++ ScopedLock lock(limit_mutex);
++
++ lowest_ever_allocated = Min(lowest_ever_allocated, address);
++ highest_ever_allocated =
++ Max(highest_ever_allocated,
++ reinterpret_cast<void*>(reinterpret_cast<char*>(address) +
size));
++}
++
++
++bool OS::IsOutsideAllocatedSpace(void* address) {
++ return address < lowest_ever_allocated || address >= highest_ever_allocated;
++}
++
++
++size_t OS::AllocateAlignment() {
++ return getpagesize();
++}
++
++
++void* OS::Allocate(const size_t requested,
++ size_t* allocated,
++ bool executable) {
++ const size_t msize = RoundUp(requested, getpagesize());
++ int prot = PROT_READ | PROT_WRITE | (executable ? PROT_EXEC : 0);
++ void* mbase = mmap(NULL, msize, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
++
++ if (mbase == MAP_FAILED) {
++ LOG(ISOLATE, StringEvent("OS::Allocate", "mmap failed"));
++ return NULL;
++ }
++ *allocated = msize;
++ UpdateAllocatedSpaceLimits(mbase, msize);
++ return mbase;
++}
++
++
++void OS::Free(void* buf, const size_t length) {
++ // TODO(1240712): munmap has a return value which is ignored here.
++ int result = munmap(buf, length);
++ USE(result);
++ ASSERT(result == 0);
++}
++
++
++void OS::Sleep(int milliseconds) {
++ unsigned int ms = static_cast<unsigned int>(milliseconds);
++ usleep(1000 * ms);
++}
++
++
++void OS::Abort() {
++ // Redirect to std abort to signal abnormal program termination.
++ abort();
++}
++
++
++void OS::DebugBreak() {
++#if (defined(__arm__) || defined(__thumb__))
++# if defined(CAN_USE_ARMV5_INSTRUCTIONS)
++ asm("bkpt 0");
++# endif
++#elif defined(_ARCH_PPC)
++ asm("trap");
++#else
++ asm("int $3");
++#endif
++}
++
++
++class PosixMemoryMappedFile : public OS::MemoryMappedFile {
++ public:
++ PosixMemoryMappedFile(FILE* file, void* memory, int size)
++ : file_(file), memory_(memory), size_(size) { }
++ virtual ~PosixMemoryMappedFile();
++ virtual void* memory() { return memory_; }
++ virtual int size() { return size_; }
++ private:
++ FILE* file_;
++ void* memory_;
++ int size_;
++};
++
++
++OS::MemoryMappedFile* OS::MemoryMappedFile::open(const char* name) {
++ FILE* file = fopen(name, "r+");
++ if (file == NULL) return NULL;
++
++ fseek(file, 0, SEEK_END);
++ int size = ftell(file);
++
++ void* memory =
++ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
++ return new PosixMemoryMappedFile(file, memory, size);
++}
++
++
++OS::MemoryMappedFile* OS::MemoryMappedFile::create(const char* name, int size,
++ void* initial) {
++ FILE* file = fopen(name, "w+");
++ if (file == NULL) return NULL;
++ int result = fwrite(initial, size, 1, file);
++ if (result < 1) {
++ fclose(file);
++ return NULL;
++ }
++ void* memory =
++ mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileno(file), 0);
++ return new PosixMemoryMappedFile(file, memory, size);
++}
++
++
++PosixMemoryMappedFile::~PosixMemoryMappedFile() {
++ if (memory_) munmap(memory_, size_);
++ fclose(file_);
++}
++
++
++static unsigned StringToLong(char* buffer) {
++ return static_cast<unsigned>(strtol(buffer, NULL, 16)); // NOLINT
++}
++
++
++void OS::LogSharedLibraryAddresses() {
++ static const int MAP_LENGTH = 1024;
++ int fd = open("/proc/self/maps", O_RDONLY);
++ if (fd < 0) return;
++ while (true) {
++ char addr_buffer[11];
++ addr_buffer[0] = '0';
++ addr_buffer[1] = 'x';
++ addr_buffer[10] = 0;
++ int result = read(fd, addr_buffer + 2, 8);
++ if (result < 8) break;
++ unsigned start = StringToLong(addr_buffer);
++ result = read(fd, addr_buffer + 2, 1);
++ if (result < 1) break;
++ if (addr_buffer[2] != '-') break;
++ result = read(fd, addr_buffer + 2, 8);
++ if (result < 8) break;
++ unsigned end = StringToLong(addr_buffer);
++ char buffer[MAP_LENGTH];
++ int bytes_read = -1;
++ do {
++ bytes_read++;
++ if (bytes_read >= MAP_LENGTH - 1)
++ break;
++ result = read(fd, buffer + bytes_read, 1);
++ if (result < 1) break;
++ } while (buffer[bytes_read] != '\n');
++ buffer[bytes_read] = 0;
++ // Ignore mappings that are not executable.
++ if (buffer[3] != 'x') continue;
++ char* start_of_path = index(buffer, '/');
++ // There may be no filename in this line. Skip to next.
++ if (start_of_path == NULL) continue;
++ buffer[bytes_read] = 0;
++ LOG(i::Isolate::Current(), SharedLibraryEvent(start_of_path, start, end));
++ }
++ close(fd);
++}
++
++
++void OS::SignalCodeMovingGC() {
++}
++
++
++int OS::StackWalk(Vector<OS::StackFrame> frames) {
++ return 0;
++}
++
++
++// Constants used for mmap.
++static const int kMmapFd = -1;
++static const int kMmapFdOffset = 0;
++
++VirtualMemory::VirtualMemory() : address_(NULL), size_(0) { }
++
++VirtualMemory::VirtualMemory(size_t size) {
++ address_ = ReserveRegion(size);
++ size_ = size;
++}
++
++
++VirtualMemory::VirtualMemory(size_t size, size_t alignment)
++ : address_(NULL), size_(0) {
++ ASSERT(IsAligned(alignment, static_cast<intptr_t>(OS::AllocateAlignment())));
++ size_t request_size = RoundUp(size + alignment,
++ static_cast<intptr_t>(OS::AllocateAlignment()));
++ void* reservation = mmap(OS::GetRandomMmapAddr(),
++ request_size,
++ PROT_NONE,
++ MAP_PRIVATE | MAP_ANONYMOUS,
++ kMmapFd,
++ kMmapFdOffset);
++ if (reservation == MAP_FAILED) return;
++
++ Address base = static_cast<Address>(reservation);
++ Address aligned_base = RoundUp(base, alignment);
++ ASSERT_LE(base, aligned_base);
++
++ // Unmap extra memory reserved before and after the desired block.
++ if (aligned_base != base) {
++ size_t prefix_size = static_cast<size_t>(aligned_base - base);
++ OS::Free(base, prefix_size);
++ request_size -= prefix_size;
++ }
++
++ size_t aligned_size = RoundUp(size, OS::AllocateAlignment());
++ ASSERT_LE(aligned_size, request_size);
++
++ if (aligned_size != request_size) {
++ size_t suffix_size = request_size - aligned_size;
++ OS::Free(aligned_base + aligned_size, suffix_size);
++ request_size -= suffix_size;
++ }
++
++ ASSERT(aligned_size == request_size);
++
++ address_ = static_cast<void*>(aligned_base);
++ size_ = aligned_size;
++}
++
++
++VirtualMemory::~VirtualMemory() {
++ if (IsReserved()) {
++ bool result = ReleaseRegion(address(), size());
++ ASSERT(result);
++ USE(result);
++ }
++}
++
++
++bool VirtualMemory::IsReserved() {
++ return address_ != NULL;
++}
++
++
++void VirtualMemory::Reset() {
++ address_ = NULL;
++ size_ = 0;
++}
++
++
++bool VirtualMemory::Commit(void* address, size_t size, bool is_executable) {
++ return CommitRegion(address, size, is_executable);
++}
++
++
++bool VirtualMemory::Uncommit(void* address, size_t size) {
++ return UncommitRegion(address, size);
++}
++
++
++bool VirtualMemory::Guard(void* address) {
++ OS::Guard(address, OS::CommitPageSize());
++ return true;
++}
++
++
++void* VirtualMemory::ReserveRegion(size_t size) {
++ void* result = mmap(OS::GetRandomMmapAddr(),
++ size,
++ PROT_NONE,
++ MAP_PRIVATE | MAP_ANONYMOUS,
++ kMmapFd,
++ kMmapFdOffset);
++
++ if (result == MAP_FAILED) return NULL;
++
++ return result;
++}
++
++
++bool VirtualMemory::CommitRegion(void* base, size_t size, bool is_executable) {
++ int prot = PROT_READ | PROT_WRITE | (is_executable ? PROT_EXEC : 0);
++ if (mprotect(base, size, prot) == -1) return false;
++
++ UpdateAllocatedSpaceLimits(base, size);
++ return true;
++}
++
++
++bool VirtualMemory::UncommitRegion(void* base, size_t size) {
++ return mprotect(base, size, PROT_NONE) != -1;
++}
++
++
++bool VirtualMemory::ReleaseRegion(void* base, size_t size) {
++ return munmap(base, size) == 0;
++}
++
++
++class Thread::PlatformData : public Malloced {
++ public:
++ pthread_t thread_; // Thread handle for pthread.
++};
++
++
++Thread::Thread(const Options& options)
++ : data_(new PlatformData),
++ stack_size_(options.stack_size()) {
++ set_name(options.name());
++}
++
++
++Thread::~Thread() {
++ delete data_;
++}
++
++
++static void* ThreadEntry(void* arg) {
++ Thread* thread = reinterpret_cast<Thread*>(arg);
++ // This is also initialized by the first argument to pthread_create() but we
++ // don't know which thread will run first (the original thread or the new
++ // one) so we initialize it here too.
++ thread->data()->thread_ = pthread_self();
++ ASSERT(thread->data()->thread_ != kNoThread);
++ thread->Run();
++ return NULL;
++}
++
++
++void Thread::set_name(const char* name) {
++ strncpy(name_, name, sizeof(name_));
++ name_[sizeof(name_) - 1] = '\0';
++}
++
++
++void Thread::Start() {
++ pthread_attr_t attr;
++ size_t stack_size = stack_size_;
++
++ if (stack_size == 0) {
++ // Default is 96KB -- bump up to 2MB
++ stack_size = 2 * MB;
++ }
++ pthread_attr_init(&attr);
++ pthread_attr_setstacksize(&attr, static_cast<size_t>(stack_size));
++ pthread_create(&data_->thread_, &attr, ThreadEntry, this);
++ ASSERT(data_->thread_ != kNoThread);
++}
++
++
++void Thread::Join() {
++ pthread_join(data_->thread_, NULL);
++}
++
++
++Thread::LocalStorageKey Thread::CreateThreadLocalKey() {
++ pthread_key_t key;
++ int result = pthread_key_create(&key, NULL);
++ USE(result);
++ ASSERT(result == 0);
++ return static_cast<LocalStorageKey>(key);
++}
++
++
++void Thread::DeleteThreadLocalKey(LocalStorageKey key) {
++ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
++ int result = pthread_key_delete(pthread_key);
++ USE(result);
++ ASSERT(result == 0);
++}
++
++
++void* Thread::GetThreadLocal(LocalStorageKey key) {
++ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
++ return pthread_getspecific(pthread_key);
++}
++
++
++void Thread::SetThreadLocal(LocalStorageKey key, void* value) {
++ pthread_key_t pthread_key = static_cast<pthread_key_t>(key);
++ pthread_setspecific(pthread_key, value);
++}
++
++
++void Thread::YieldCPU() {
++ sched_yield();
++}
++
++
++class AIXMutex : public Mutex {
++ public:
++ AIXMutex() {
++ pthread_mutexattr_t attrs;
++ int result = pthread_mutexattr_init(&attrs);
++ ASSERT(result == 0);
++ result = pthread_mutexattr_settype(&attrs, PTHREAD_MUTEX_RECURSIVE);
++ ASSERT(result == 0);
++ result = pthread_mutex_init(&mutex_, &attrs);
++ ASSERT(result == 0);
++ USE(result);
++ }
++
++ virtual ~AIXMutex() { pthread_mutex_destroy(&mutex_); }
++
++ virtual int Lock() {
++ int result = pthread_mutex_lock(&mutex_);
++ return result;
++ }
++
++ virtual int Unlock() {
++ int result = pthread_mutex_unlock(&mutex_);
++ return result;
++ }
++
++ virtual bool TryLock() {
++ int result = pthread_mutex_trylock(&mutex_);
++ // Return false if the lock is busy and locking failed.
++ if (result == EBUSY) {
++ return false;
++ }
++ ASSERT(result == 0); // Verify no other errors.
++ return true;
++ }
++
++ private:
++ pthread_mutex_t mutex_; // Pthread mutex for POSIX platforms.
++};
++
++
++Mutex* OS::CreateMutex() {
++ return new AIXMutex();
++}
++
++
++class AIXSemaphore : public Semaphore {
++ public:
++ explicit AIXSemaphore(int count) { sem_init(&sem_, 0, count); }
++ virtual ~AIXSemaphore() { sem_destroy(&sem_); }
++
++ virtual void Wait();
++ virtual bool Wait(int timeout);
++ virtual void Signal() { sem_post(&sem_); }
++ private:
++ sem_t sem_;
++};
++
++
++void AIXSemaphore::Wait() {
++ while (true) {
++ int result = sem_wait(&sem_);
++ if (result == 0) return; // Successfully got semaphore.
++ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
++ }
++}
++
++
++#ifndef TIMEVAL_TO_TIMESPEC
++#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
++ (ts)->tv_sec = (tv)->tv_sec; \
++ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
++} while (false)
++#endif
++
++
++#ifndef timeradd
++#define timeradd(a, b, result) \
++ do { \
++ (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \
++ (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \
++ if ((result)->tv_usec >= 1000000) { \
++ ++(result)->tv_sec; \
++ (result)->tv_usec -= 1000000; \
++ } \
++ } while (0)
++#endif
++
++
++bool AIXSemaphore::Wait(int timeout) {
++ const long kOneSecondMicros = 1000000; // NOLINT
++
++ // Split timeout into second and nanosecond parts.
++ struct timeval delta;
++ delta.tv_usec = timeout % kOneSecondMicros;
++ delta.tv_sec = timeout / kOneSecondMicros;
++
++ struct timeval current_time;
++ // Get the current time.
++ if (gettimeofday(¤t_time, NULL) == -1) {
++ return false;
++ }
++
++ // Calculate time for end of timeout.
++ struct timeval end_time;
++ timeradd(¤t_time, &delta, &end_time);
++
++ struct timespec ts;
++ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
++ while (true) {
++ int result = sem_timedwait(&sem_, &ts);
++ if (result == 0) return true; // Successfully got semaphore.
++ if (result == -1 && errno == ETIMEDOUT) return false; // Timeout.
++ CHECK(result == -1 && errno == EINTR); // Signal caused spurious wakeup.
++ }
++}
++
++
++Semaphore* OS::CreateSemaphore(int count) {
++ return new AIXSemaphore(count);
++}
++
++
++static pthread_t GetThreadID() {
++ pthread_t thread_id = pthread_self();
++ return thread_id;
++}
++
++
++class Sampler::PlatformData : public Malloced {
++ public:
++ PlatformData() : vm_tid_(GetThreadID()) {}
++
++ pthread_t vm_tid() const { return vm_tid_; }
++
++ private:
++ pthread_t vm_tid_;
++};
++
++
++static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
++ USE(info);
++ if (signal != SIGPROF) return;
++ Isolate* isolate = Isolate::UncheckedCurrent();
++ if (isolate == NULL || !isolate->IsInitialized() || !isolate->IsInUse()) {
++ // We require a fully initialized and entered isolate.
++ return;
++ }
++ if (v8::Locker::IsActive() &&
++ !isolate->thread_manager()->IsLockedByCurrentThread()) {
++ return;
++ }
++
++ Sampler* sampler = isolate->logger()->sampler();
++ if (sampler == NULL || !sampler->IsActive()) return;
++
++ TickSample sample_obj;
++ TickSample* sample = CpuProfiler::TickSampleEvent(isolate);
++ if (sample == NULL) sample = &sample_obj;
++
++ // Extracting the sample from the context is extremely machine dependent.
++ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
++ mcontext_t& mcontext = ucontext->uc_mcontext;
++ sample->state = isolate->current_vm_state();
++#if V8_HOST_ARCH_IA32
++ sample->pc = reinterpret_cast<Address>(mcontext.mc_eip);
++ sample->sp = reinterpret_cast<Address>(mcontext.mc_esp);
++ sample->fp = reinterpret_cast<Address>(mcontext.mc_ebp);
++#elif V8_HOST_ARCH_X64
++ sample->pc = reinterpret_cast<Address>(mcontext.mc_rip);
++ sample->sp = reinterpret_cast<Address>(mcontext.mc_rsp);
++ sample->fp = reinterpret_cast<Address>(mcontext.mc_rbp);
++#elif V8_HOST_ARCH_ARM
++ sample->pc = reinterpret_cast<Address>(mcontext.mc_r15);
++ sample->sp = reinterpret_cast<Address>(mcontext.mc_r13);
++ sample->fp = reinterpret_cast<Address>(mcontext.mc_r11);
++#elif V8_HOST_ARCH_PPC
++ sample->pc = reinterpret_cast<Address>(mcontext.jmp_context.iar);
++ sample->sp = reinterpret_cast<Address>(mcontext.jmp_context.gpr[1]);
++ sample->fp = reinterpret_cast<Address>(mcontext.jmp_context.gpr[1]);
++#endif
++ sampler->SampleStack(sample);
++ sampler->Tick(sample);
++}
++
++
++class SignalSender : public Thread {
++ public:
++ enum SleepInterval {
++ HALF_INTERVAL,
++ FULL_INTERVAL
++ };
++
++ static const int kSignalSenderStackSize = 64 * KB;
++
++ explicit SignalSender(int interval)
++ : Thread(Thread::Options("SignalSender", kSignalSenderStackSize)),
++ interval_(interval) {}
++
++ static void SetUp() { if (!mutex_) mutex_ = OS::CreateMutex(); }
++ static void TearDown() { delete mutex_; }
++
++ static void AddActiveSampler(Sampler* sampler) {
++ ScopedLock lock(mutex_);
++ SamplerRegistry::AddActiveSampler(sampler);
++ if (instance_ == NULL) {
++ // Install a signal handler.
++ struct sigaction sa;
++ sa.sa_sigaction = ProfilerSignalHandler;
++ sigemptyset(&sa.sa_mask);
++ sa.sa_flags = SA_RESTART | SA_SIGINFO;
++ signal_handler_installed_ =
++ (sigaction(SIGPROF, &sa, &old_signal_handler_) == 0);
++
++ // Start a thread that sends SIGPROF signal to VM threads.
++ instance_ = new SignalSender(sampler->interval());
++ instance_->Start();
++ } else {
++ ASSERT(instance_->interval_ == sampler->interval());
++ }
++ }
++
++ static void RemoveActiveSampler(Sampler* sampler) {
++ ScopedLock lock(mutex_);
++ SamplerRegistry::RemoveActiveSampler(sampler);
++ if (SamplerRegistry::GetState() == SamplerRegistry::HAS_NO_SAMPLERS) {
++ RuntimeProfiler::StopRuntimeProfilerThreadBeforeShutdown(instance_);
++ delete instance_;
++ instance_ = NULL;
++
++ // Restore the old signal handler.
++ if (signal_handler_installed_) {
++ sigaction(SIGPROF, &old_signal_handler_, 0);
++ signal_handler_installed_ = false;
++ }
++ }
++ }
++
++ // Implement Thread::Run().
++ virtual void Run() {
++ SamplerRegistry::State state;
++ while ((state = SamplerRegistry::GetState()) !=
++ SamplerRegistry::HAS_NO_SAMPLERS) {
++ bool cpu_profiling_enabled =
++ (state == SamplerRegistry::HAS_CPU_PROFILING_SAMPLERS);
++ bool runtime_profiler_enabled = RuntimeProfiler::IsEnabled();
++ // When CPU profiling is enabled both JavaScript and C++ code is
++ // profiled. We must not suspend.
++ if (!cpu_profiling_enabled) {
++ if (rate_limiter_.SuspendIfNecessary()) continue;
++ }
++ if (cpu_profiling_enabled && runtime_profiler_enabled) {
++ if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile, this)) {
++ return;
++ }
++ Sleep(HALF_INTERVAL);
++ if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile, NULL)) {
++ return;
++ }
++ Sleep(HALF_INTERVAL);
++ } else {
++ if (cpu_profiling_enabled) {
++ if (!SamplerRegistry::IterateActiveSamplers(&DoCpuProfile,
++ this)) {
++ return;
++ }
++ }
++ if (runtime_profiler_enabled) {
++ if (!SamplerRegistry::IterateActiveSamplers(&DoRuntimeProfile,
++ NULL)) {
++ return;
++ }
++ }
++ Sleep(FULL_INTERVAL);
++ }
++ }
++ }
++
++ static void DoCpuProfile(Sampler* sampler, void* raw_sender) {
++ if (!sampler->IsProfiling()) return;
++ SignalSender* sender = reinterpret_cast<SignalSender*>(raw_sender);
++ sender->SendProfilingSignal(sampler->platform_data()->vm_tid());
++ }
++
++ static void DoRuntimeProfile(Sampler* sampler, void* ignored) {
++ if (!sampler->isolate()->IsInitialized()) return;
++ sampler->isolate()->runtime_profiler()->NotifyTick();
++ }
++
++ void SendProfilingSignal(pthread_t tid) {
++ if (!signal_handler_installed_) return;
++ pthread_kill(tid, SIGPROF);
++ }
++
++ void Sleep(SleepInterval full_or_half) {
++ // Convert ms to us and subtract 100 us to compensate delays
++ // occuring during signal delivery.
++ useconds_t interval = interval_ * 1000 - 100;
++ if (full_or_half == HALF_INTERVAL) interval /= 2;
++ int result = usleep(interval);
++#ifdef DEBUG
++ if (result != 0 && errno != EINTR) {
++ fprintf(stderr,
++ "SignalSender usleep error; interval = %u, errno = %d\n",
++ interval,
++ errno);
++ ASSERT(result == 0 || errno == EINTR);
++ }
++#endif
++ USE(result);
++ }
++
++ const int interval_;
++ RuntimeProfilerRateLimiter rate_limiter_;
++
++ // Protects the process wide state below.
++ static Mutex* mutex_;
++ static SignalSender* instance_;
++ static bool signal_handler_installed_;
++ static struct sigaction old_signal_handler_;
++
++ private:
++ DISALLOW_COPY_AND_ASSIGN(SignalSender);
++};
++
++Mutex* SignalSender::mutex_ = NULL;
++SignalSender* SignalSender::instance_ = NULL;
++struct sigaction SignalSender::old_signal_handler_;
++bool SignalSender::signal_handler_installed_ = false;
++
++
++void OS::SetUp() {
++ // Seed the random number generator.
++ // Convert the current time to a 64-bit integer first, before converting it
++ // to an unsigned. Going directly can cause an overflow and the seed to be
++ // set to all ones. The seed will be identical for different instances that
++ // call this setup code within the same millisecond.
++ uint64_t seed = static_cast<uint64_t>(TimeCurrentMillis());
++ srandom(static_cast<unsigned int>(seed));
++ limit_mutex = CreateMutex();
++ SignalSender::SetUp();
++}
++
++
++void OS::TearDown() {
++ SignalSender::TearDown();
++ delete limit_mutex;
++}
++
++
++Sampler::Sampler(Isolate* isolate, int interval)
++ : isolate_(isolate),
++ interval_(interval),
++ profiling_(false),
++ active_(false),
++ samples_taken_(0) {
++ data_ = new PlatformData;
++}
++
++
++Sampler::~Sampler() {
++ ASSERT(!IsActive());
++ delete data_;
++}
++
++
++void Sampler::Start() {
++ ASSERT(!IsActive());
++ SetActive(true);
++ SignalSender::AddActiveSampler(this);
++}
++
++
++void Sampler::Stop() {
++ ASSERT(IsActive());
++ SignalSender::RemoveActiveSampler(this);
++ SetActive(false);
++}
++
++
++} } // namespace v8::internal
+diff -up v8-3.14.5.10/src/platform-freebsd.cc.ppc v8-3.14.5.10/src/platform-freebsd.cc
+--- v8-3.14.5.10/src/platform-freebsd.cc.ppc 2016-06-07 14:15:45.950393247 -0400
++++ v8-3.14.5.10/src/platform-freebsd.cc 2016-06-07 14:15:45.983393050 -0400
+@@ -693,6 +693,10 @@ static void ProfilerSignalHandler(int si
+ sample->pc = reinterpret_cast<Address>(mcontext.mc_r15);
+ sample->sp = reinterpret_cast<Address>(mcontext.mc_r13);
+ sample->fp = reinterpret_cast<Address>(mcontext.mc_r11);
++#elif V8_HOST_ARCH_PPC
++ sample->pc = reinterpret_cast<Address>(mcontext.mc_r15);
++ sample->sp = reinterpret_cast<Address>(mcontext.mc_r13);
++ sample->fp = reinterpret_cast<Address>(mcontext.mc_r11);
+ #endif
+ sampler->SampleStack(sample);
+ sampler->Tick(sample);
+diff -up v8-3.14.5.10/src/platform.h.ppc v8-3.14.5.10/src/platform.h
+--- v8-3.14.5.10/src/platform.h.ppc 2012-10-22 09:09:53.000000000 -0400
++++ v8-3.14.5.10/src/platform.h 2016-06-07 14:15:45.983393050 -0400
+@@ -107,7 +107,8 @@ namespace internal {
+
+ // Use AtomicWord for a machine-sized pointer. It is assumed that
+ // reads and writes of naturally aligned values of this type are atomic.
+-#if defined(__OpenBSD__) && defined(__i386__)
++#if !defined(V8_HOST_ARCH_64_BIT) && \
++ ((defined(__OpenBSD__) && defined(__i386__)) || defined(_AIX))
+ typedef Atomic32 AtomicWord;
+ #else
+ typedef intptr_t AtomicWord;
+diff -up v8-3.14.5.10/src/platform-linux.cc.ppc v8-3.14.5.10/src/platform-linux.cc
+--- v8-3.14.5.10/src/platform-linux.cc.ppc 2016-06-07 14:15:45.950393247 -0400
++++ v8-3.14.5.10/src/platform-linux.cc 2016-06-07 14:15:45.983393050 -0400
+@@ -291,6 +291,8 @@ int OS::ActivationFrameAlignment() {
+ return 8;
+ #elif V8_TARGET_ARCH_MIPS
+ return 8;
++#elif V8_TARGET_ARCH_PPC
++ return 8;
+ #endif
+ // With gcc 4.4 the tree vectorization optimizer can generate code
+ // that requires 16 byte alignment such as movdqa on x86.
+@@ -300,6 +302,7 @@ int OS::ActivationFrameAlignment() {
+
+ void OS::ReleaseStore(volatile AtomicWord* ptr, AtomicWord value) {
+ #if (defined(V8_TARGET_ARCH_ARM) && defined(__arm__)) || \
++ (defined(V8_TARGET_ARCH_PPC) && defined(__PPC__)) || \
+ (defined(V8_TARGET_ARCH_MIPS) && defined(__mips__))
+ // Only use on ARM or MIPS hardware.
+ MemoryBarrier();
+@@ -409,6 +412,9 @@ void OS::DebugBreak() {
+ # endif
+ #elif defined(__mips__)
+ asm("break");
++#elif defined(__PPC__)
++ asm("twge 2,2");
++// asm("nop"); // roohack - nothing for now;
+ #else
+ asm("int $3");
+ #endif
+@@ -1034,7 +1040,9 @@ static void ProfilerSignalHandler(int si
+
+ // Extracting the sample from the context is extremely machine dependent.
+ ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context);
++#ifndef V8_HOST_ARCH_PPC
+ mcontext_t& mcontext = ucontext->uc_mcontext;
++#endif
+ sample->state = isolate->current_vm_state();
+ #if V8_HOST_ARCH_IA32
+ sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
+@@ -1062,6 +1070,12 @@ static void ProfilerSignalHandler(int si
+ sample->pc = reinterpret_cast<Address>(mcontext.pc);
+ sample->sp = reinterpret_cast<Address>(mcontext.gregs[29]);
+ sample->fp = reinterpret_cast<Address>(mcontext.gregs[30]);
++#elif V8_HOST_ARCH_PPC
++ sample->pc =
reinterpret_cast<Address>(ucontext->uc_mcontext.regs->nip);
++ sample->sp =
++ reinterpret_cast<Address>(ucontext->uc_mcontext.regs->gpr[PT_R1]);
++ sample->fp =
++ reinterpret_cast<Address>(ucontext->uc_mcontext.regs->gpr[PT_R31]);
+ #endif // V8_HOST_ARCH_*
+ sampler->SampleStack(sample);
+ sampler->Tick(sample);
+diff -up v8-3.14.5.10/src/platform-posix.cc.ppc v8-3.14.5.10/src/platform-posix.cc
+--- v8-3.14.5.10/src/platform-posix.cc.ppc 2016-06-07 14:15:45.950393247 -0400
++++ v8-3.14.5.10/src/platform-posix.cc 2016-06-07 14:15:45.983393050 -0400
+@@ -53,6 +53,10 @@
+ #include <android/log.h>
+ #endif
+
++#if defined(_AIX)
++#include <fenv.h>
++#endif
++
+ #include "v8.h"
+
+ #include "codegen.h"
+@@ -112,9 +116,15 @@ void* OS::GetRandomMmapAddr() {
+ // The range 0x20000000 - 0x60000000 is relatively unpopulated across a
+ // variety of ASLR modes (PAE kernel, NX compat mode, etc) and on macos
+ // 10.6 and 10.7.
++ // The range 0x30000000 - 0xD0000000 is available on AIX;
++ // choose the upper range.
+ raw_addr &= 0x3ffff000;
++#ifdef _AIX
++ raw_addr += 0x90000000;
++#else
+ raw_addr += 0x20000000;
+ #endif
++#endif
+ return reinterpret_cast<void*>(raw_addr);
+ }
+ return NULL;
+@@ -125,7 +135,17 @@ void* OS::GetRandomMmapAddr() {
+ // Math functions
+
+ double modulo(double x, double y) {
++#if defined(_AIX)
++ // AIX raises an underflow exception for (Number.MIN_VALUE % Number.MAX_VALUE)
++ double result;
++ int exception;
++ feclearexcept(FE_ALL_EXCEPT);
++ result = fmod(x, y);
++ exception = fetestexcept(FE_UNDERFLOW);
++ return (exception ? x : result);
++#else
+ return fmod(x, y);
++#endif
+ }
+
+
+@@ -147,6 +167,11 @@ UNARY_MATH_FUNCTION(sqrt, CreateSqrtFunc
+ #undef MATH_FUNCTION
+
+
++#ifdef _AIX
++#undef NAN
++#define NAN (__builtin_nanf(""))
++#endif
++
+ double OS::nan_value() {
+ // NAN from math.h is defined in C99 and not in POSIX.
+ return NAN;
+diff -up v8-3.14.5.10/src/ppc/assembler-ppc.cc.ppc v8-3.14.5.10/src/ppc/assembler-ppc.cc
+--- v8-3.14.5.10/src/ppc/assembler-ppc.cc.ppc 2016-06-07 14:15:45.984393044 -0400
++++ v8-3.14.5.10/src/ppc/assembler-ppc.cc 2016-06-07 14:15:45.984393044 -0400
+@@ -0,0 +1,1881 @@
++// Copyright (c) 1994-2006 Sun Microsystems Inc.
++// All Rights Reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions
++// are met:
++//
++// - Redistributions of source code must retain the above copyright notice,
++// this list of conditions and the following disclaimer.
++//
++// - Redistribution in binary form must reproduce the above copyright
++// notice, this list of conditions and the following disclaimer in the
++// documentation and/or other materials provided with the
++// distribution.
++//
++// - Neither the name of Sun Microsystems or the names of contributors may
++// be used to endorse or promote products derived from this software without
++// specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++// OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// The original source code covered by the above license above has been
++// modified significantly by Google Inc.
++// Copyright 2012 the V8 project authors. All rights reserved.
++
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "ppc/assembler-ppc-inl.h"
++#include "serialize.h"
++
++namespace v8 {
++namespace internal {
++
++#ifdef DEBUG
++bool CpuFeatures::initialized_ = false;
++#endif
++unsigned CpuFeatures::supported_ = 0;
++unsigned CpuFeatures::found_by_runtime_probing_ = 0;
++
++// Get the CPU features enabled by the build.
++static unsigned CpuFeaturesImpliedByCompiler() {
++ unsigned answer = 0;
++ return answer;
++}
++
++#if !defined(_AIX)
++// This function uses types in elf.h
++static bool is_processor(const char* p) {
++ static bool read_tried = false;
++ static char *auxv_cpu_type = NULL;
++
++ if (!read_tried) {
++ // Open the AUXV (auxilliary vector) psuedo-file
++ int fd = open("/proc/self/auxv", O_RDONLY);
++
++ read_tried = true;
++ if (fd != -1) {
++#if V8_TARGET_ARCH_PPC64
++ static Elf64_auxv_t buffer[16];
++ Elf64_auxv_t *auxv_element;
++#else
++ static Elf32_auxv_t buffer[16];
++ Elf32_auxv_t *auxv_element;
++#endif
++ int bytes_read = 0;
++ while (bytes_read >= 0) {
++ // Read a chunk of the AUXV
++ bytes_read = read(fd, buffer, sizeof(buffer));
++ // Locate and read the platform field of AUXV if it is in the chunk
++ for (auxv_element = buffer;
++ auxv_element+sizeof(auxv_element) <= buffer+bytes_read &&
++ auxv_element->a_type != AT_NULL;
++ auxv_element++) {
++ if (auxv_element->a_type == AT_PLATFORM) {
++ /* Note: Both auxv_cpu_type and buffer are static */
++ auxv_cpu_type = reinterpret_cast<char*>(auxv_element->a_un.a_val);
++ goto done_reading;
++ }
++ }
++ }
++ done_reading:
++ close(fd);
++ }
++ }
++
++ if (auxv_cpu_type == NULL) {
++ return false;
++ }
++ return (strcmp(auxv_cpu_type, p) == 0);
++}
++#endif
++
++void CpuFeatures::Probe() {
++ unsigned standard_features = static_cast<unsigned>(
++ OS::CpuFeaturesImpliedByPlatform()) | CpuFeaturesImpliedByCompiler();
++ ASSERT(supported_ == 0 || supported_ == standard_features);
++#ifdef DEBUG
++ initialized_ = true;
++#endif
++
++ // Get the features implied by the OS and the compiler settings. This is the
++ // minimal set of features which is also alowed for generated code in the
++ // snapshot.
++ supported_ |= standard_features;
++
++ if (Serializer::enabled()) {
++ // No probing for features if we might serialize (generate snapshot).
++ return;
++ }
++
++ // Detect whether frim instruction is supported (POWER5+)
++ // For now we will just check for processors we know do not
++ // support it
++#if !defined(_AIX)
++ if (!is_processor("ppc970") /* G5 */ &&
!is_processor("ppc7450") /* G4 */) {
++ // Assume support
++ supported_ |= (1u << FPU);
++ }
++#else
++ // Fallback: assume frim is supported -- will implement processor
++ // detection for other PPC platforms in is_processor() if required
++ supported_ |= (1u << FPU);
++#endif
++}
++
++Register ToRegister(int num) {
++ ASSERT(num >= 0 && num < kNumRegisters);
++ const Register kRegisters[] = {
++ r0,
++ sp,
++ r2, r3, r4, r5, r6, r7, r8, r9, r10,
++ r11, ip, r13, r14, r15,
++ r16, r17, r18, r19, r20, r21, r22, r23, r24,
++ r25, r26, r27, r28, r29, r30, fp
++ };
++ return kRegisters[num];
++}
++
++
++// -----------------------------------------------------------------------------
++// Implementation of RelocInfo
++
++const int RelocInfo::kApplyMask = 1 << RelocInfo::INTERNAL_REFERENCE;
++
++
++bool RelocInfo::IsCodedSpecially() {
++ // The deserializer needs to know whether a pointer is specially
++ // coded. Being specially coded on PPC means that it is a lis/ori
++ // instruction sequence, and that is always the case inside code
++ // objects.
++ return true;
++}
++
++
++void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
++ // Patch the code at the current address with the supplied instructions.
++ Instr* pc = reinterpret_cast<Instr*>(pc_);
++ Instr* instr = reinterpret_cast<Instr*>(instructions);
++ for (int i = 0; i < instruction_count; i++) {
++ *(pc + i) = *(instr + i);
++ }
++
++ // Indicate that code has changed.
++ CPU::FlushICache(pc_, instruction_count * Assembler::kInstrSize);
++}
++
++
++// Patch the code at the current PC with a call to the target address.
++// Additional guard instructions can be added if required.
++void RelocInfo::PatchCodeWithCall(Address target, int guard_bytes) {
++ // Patch the code at the current address with a call to the target.
++ UNIMPLEMENTED();
++}
++
++
++// -----------------------------------------------------------------------------
++// Implementation of Operand and MemOperand
++// See assembler-ppc-inl.h for inlined constructors
++
++Operand::Operand(Handle<Object> handle) {
++ rm_ = no_reg;
++ // Verify all Objects referred by code are NOT in new space.
++ Object* obj = *handle;
++ ASSERT(!HEAP->InNewSpace(obj));
++ if (obj->IsHeapObject()) {
++ imm_ = reinterpret_cast<intptr_t>(handle.location());
++ rmode_ = RelocInfo::EMBEDDED_OBJECT;
++ } else {
++ // no relocation needed
++ imm_ = reinterpret_cast<intptr_t>(obj);
++ rmode_ = RelocInfo::NONE;
++ }
++}
++
++MemOperand::MemOperand(Register rn, int32_t offset) {
++ ra_ = rn;
++ rb_ = no_reg;
++ offset_ = offset;
++}
++
++MemOperand::MemOperand(Register ra, Register rb) {
++ ra_ = ra;
++ rb_ = rb;
++ offset_ = 0;
++}
++
++// -----------------------------------------------------------------------------
++// Specific instructions, constants, and masks.
++
++// Spare buffer.
++static const int kMinimalBufferSize = 4*KB;
++
++
++Assembler::Assembler(Isolate* arg_isolate, void* buffer, int buffer_size)
++ : AssemblerBase(arg_isolate),
++ recorded_ast_id_(TypeFeedbackId::None()),
++ positions_recorder_(this),
++ emit_debug_code_(FLAG_debug_code),
++ predictable_code_size_(false) {
++ if (buffer == NULL) {
++ // Do our own buffer management.
++ if (buffer_size <= kMinimalBufferSize) {
++ buffer_size = kMinimalBufferSize;
++
++ if (isolate()->assembler_spare_buffer() != NULL) {
++ buffer = isolate()->assembler_spare_buffer();
++ isolate()->set_assembler_spare_buffer(NULL);
++ }
++ }
++ if (buffer == NULL) {
++ buffer_ = NewArray<byte>(buffer_size);
++ } else {
++ buffer_ = static_cast<byte*>(buffer);
++ }
++ buffer_size_ = buffer_size;
++ own_buffer_ = true;
++
++ } else {
++ // Use externally provided buffer instead.
++ ASSERT(buffer_size > 0);
++ buffer_ = static_cast<byte*>(buffer);
++ buffer_size_ = buffer_size;
++ own_buffer_ = false;
++ }
++
++ // Set up buffer pointers.
++ ASSERT(buffer_ != NULL);
++ pc_ = buffer_;
++ reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
++
++ no_trampoline_pool_before_ = 0;
++ trampoline_pool_blocked_nesting_ = 0;
++ // We leave space (kMaxBlockTrampolineSectionSize)
++ // for BlockTrampolinePoolScope buffer.
++ next_buffer_check_ = kMaxCondBranchReach - kMaxBlockTrampolineSectionSize;
++ internal_trampoline_exception_ = false;
++ last_bound_pos_ = 0;
++
++ trampoline_emitted_ = false;
++ unbound_labels_count_ = 0;
++
++ ClearRecordedAstId();
++}
++
++
++Assembler::~Assembler() {
++ if (own_buffer_) {
++ if (isolate()->assembler_spare_buffer() == NULL &&
++ buffer_size_ == kMinimalBufferSize) {
++ isolate()->set_assembler_spare_buffer(buffer_);
++ } else {
++ DeleteArray(buffer_);
++ }
++ }
++}
++
++
++void Assembler::GetCode(CodeDesc* desc) {
++ // Set up code descriptor.
++ desc->buffer = buffer_;
++ desc->buffer_size = buffer_size_;
++ desc->instr_size = pc_offset();
++ desc->reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
++}
++
++
++void Assembler::Align(int m) {
++ ASSERT(m >= 4 && IsPowerOf2(m));
++ while ((pc_offset() & (m - 1)) != 0) {
++ nop();
++ }
++}
++
++
++void Assembler::CodeTargetAlign() {
++ Align(8);
++}
++
++
++Condition Assembler::GetCondition(Instr instr) {
++ switch (instr & kCondMask) {
++ case BT:
++ return eq;
++ case BF:
++ return ne;
++ default:
++ UNIMPLEMENTED();
++ }
++ return al;
++}
++
++// PowerPC
++
++bool Assembler::IsLis(Instr instr) {
++ return (instr & kOpcodeMask) == ADDIS;
++}
++
++bool Assembler::IsAddic(Instr instr) {
++ return (instr & kOpcodeMask) == ADDIC;
++}
++
++bool Assembler::IsOri(Instr instr) {
++ return (instr & kOpcodeMask) == ORI;
++}
++
++
++bool Assembler::IsBranch(Instr instr) {
++ return ((instr & kOpcodeMask) == BCX);
++}
++
++// end PowerPC
++
++Register Assembler::GetRA(Instr instr) {
++ Register reg;
++ reg.code_ = Instruction::RAValue(instr);
++ return reg;
++}
++
++Register Assembler::GetRB(Instr instr) {
++ Register reg;
++ reg.code_ = Instruction::RBValue(instr);
++ return reg;
++}
++
++#if V8_TARGET_ARCH_PPC64
++// This code assumes a FIXED_SEQUENCE for 64bit loads (lis/ori)
++bool Assembler::Is64BitLoadIntoR12(Instr instr1, Instr instr2,
++ Instr instr3, Instr instr4, Instr instr5) {
++ // Check the instructions are indeed a five part load (into r12)
++ // 3d800000 lis r12, 0
++ // 618c0000 ori r12, r12, 0
++ // 798c07c6 rldicr r12, r12, 32, 31
++ // 658c00c3 oris r12, r12, 195
++ // 618ccd40 ori r12, r12, 52544
++ return(((instr1 >> 16) == 0x3d80) && ((instr2 >> 16) == 0x618c)
&&
++ (instr3 == 0x798c07c6) &&
++ ((instr4 >> 16) == 0x658c) && ((instr5 >> 16) == 0x618c));
++}
++#else
++// This code assumes a FIXED_SEQUENCE for 32bit loads (lis/ori)
++bool Assembler::Is32BitLoadIntoR12(Instr instr1, Instr instr2) {
++ // Check the instruction is indeed a two part load (into r12)
++ // 3d802553 lis r12, 9555
++ // 618c5000 ori r12, r12, 20480
++ return(((instr1 >> 16) == 0x3d80) && ((instr2 >> 16) == 0x618c));
++}
++#endif
++
++bool Assembler::IsCmpRegister(Instr instr) {
++ return (((instr & kOpcodeMask) == EXT2) &&
++ ((instr & kExt2OpcodeMask) == CMP));
++}
++
++bool Assembler::IsRlwinm(Instr instr) {
++ return ((instr & kOpcodeMask) == RLWINMX);
++}
++
++#if V8_TARGET_ARCH_PPC64
++bool Assembler::IsRldicl(Instr instr) {
++ return (((instr & kOpcodeMask) == EXT5) &&
++ ((instr & kExt5OpcodeMask) == RLDICL));
++}
++#endif
++
++bool Assembler::IsCmpImmediate(Instr instr) {
++ return ((instr & kOpcodeMask) == CMPI);
++}
++
++Register Assembler::GetCmpImmediateRegister(Instr instr) {
++ ASSERT(IsCmpImmediate(instr));
++ return GetRA(instr);
++}
++
++int Assembler::GetCmpImmediateRawImmediate(Instr instr) {
++ ASSERT(IsCmpImmediate(instr));
++ return instr & kOff16Mask;
++}
++
++// Labels refer to positions in the (to be) generated code.
++// There are bound, linked, and unused labels.
++//
++// Bound labels refer to known positions in the already
++// generated code. pos() is the position the label refers to.
++//
++// Linked labels refer to unknown positions in the code
++// to be generated; pos() is the position of the last
++// instruction using the label.
++
++
++// The link chain is terminated by a negative code position (must be aligned)
++const int kEndOfChain = -4;
++
++
++int Assembler::target_at(int pos) {
++ Instr instr = instr_at(pos);
++ // check which type of branch this is 16 or 26 bit offset
++ int opcode = instr & kOpcodeMask;
++ if (BX == opcode) {
++ int imm26 = ((instr & kImm26Mask) << 6) >> 6;
++ imm26 &= ~(kAAMask|kLKMask); // discard AA|LK bits if present
++ if (imm26 == 0)
++ return kEndOfChain;
++ return pos + imm26;
++ } else if (BCX == opcode) {
++ int imm16 = SIGN_EXT_IMM16((instr & kImm16Mask));
++ imm16 &= ~(kAAMask|kLKMask); // discard AA|LK bits if present
++ if (imm16 == 0)
++ return kEndOfChain;
++ return pos + imm16;
++ } else if ((instr & ~kImm16Mask) == 0) {
++ // Emitted label constant, not part of a branch (regexp PushBacktrack).
++ if (instr == 0) {
++ return kEndOfChain;
++ } else {
++ int32_t imm16 = SIGN_EXT_IMM16(instr);
++ return (imm16 + pos);
++ }
++ }
++
++ PPCPORT_UNIMPLEMENTED();
++ ASSERT(false);
++ return -1;
++}
++
++void Assembler::target_at_put(int pos, int target_pos) {
++ Instr instr = instr_at(pos);
++ int opcode = instr & kOpcodeMask;
++
++ // check which type of branch this is 16 or 26 bit offset
++ if (BX == opcode) {
++ int imm26 = target_pos - pos;
++ ASSERT((imm26 & (kAAMask|kLKMask)) == 0);
++ instr &= ((~kImm26Mask)|kAAMask|kLKMask);
++ ASSERT(is_int26(imm26));
++ instr_at_put(pos, instr | (imm26 & kImm26Mask));
++ return;
++ } else if (BCX == opcode) {
++ int imm16 = target_pos - pos;
++ ASSERT((imm16 & (kAAMask|kLKMask)) == 0);
++ instr &= ((~kImm16Mask)|kAAMask|kLKMask);
++ ASSERT(is_int16(imm16));
++ instr_at_put(pos, instr | (imm16 & kImm16Mask));
++ return;
++ } else if ((instr & ~kImm16Mask) == 0) {
++ ASSERT(target_pos == kEndOfChain || target_pos >= 0);
++ // Emitted label constant, not part of a branch.
++ // Make label relative to Code* of generated Code object.
++ instr_at_put(pos, target_pos + (Code::kHeaderSize - kHeapObjectTag));
++ return;
++ }
++
++ ASSERT(false);
++}
++
++int Assembler::max_reach_from(int pos) {
++ Instr instr = instr_at(pos);
++ int opcode = instr & kOpcodeMask;
++
++ // check which type of branch this is 16 or 26 bit offset
++ if (BX == opcode) {
++ return 26;
++ } else if (BCX == opcode) {
++ return 16;
++ } else if ((instr & ~kImm16Mask) == 0) {
++ // Emitted label constant, not part of a branch (regexp PushBacktrack).
++ return 16;
++ }
++
++ ASSERT(false);
++ return 0;
++}
++
++void Assembler::bind_to(Label* L, int pos) {
++ ASSERT(0 <= pos && pos <= pc_offset()); // must have a valid binding
position
++ int32_t trampoline_pos = kInvalidSlotPos;
++ if (L->is_linked() && !trampoline_emitted_) {
++ unbound_labels_count_--;
++ next_buffer_check_ += kTrampolineSlotsSize;
++ }
++
++ while (L->is_linked()) {
++ int fixup_pos = L->pos();
++ int32_t offset = pos - fixup_pos;
++ int maxReach = max_reach_from(fixup_pos);
++ next(L); // call next before overwriting link with target at fixup_pos
++ if (is_intn(offset, maxReach) == false) {
++ if (trampoline_pos == kInvalidSlotPos) {
++ trampoline_pos = get_trampoline_entry();
++ CHECK(trampoline_pos != kInvalidSlotPos);
++ target_at_put(trampoline_pos, pos);
++ }
++ target_at_put(fixup_pos, trampoline_pos);
++ } else {
++ target_at_put(fixup_pos, pos);
++ }
++ }
++ L->bind_to(pos);
++
++ // Keep track of the last bound label so we don't eliminate any instructions
++ // before a bound label.
++ if (pos > last_bound_pos_)
++ last_bound_pos_ = pos;
++}
++
++void Assembler::bind(Label* L) {
++ ASSERT(!L->is_bound()); // label can only be bound once
++ bind_to(L, pc_offset());
++}
++
++
++void Assembler::next(Label* L) {
++ ASSERT(L->is_linked());
++ int link = target_at(L->pos());
++ if (link == kEndOfChain) {
++ L->Unuse();
++ } else {
++ ASSERT(link >= 0);
++ L->link_to(link);
++ }
++}
++
++bool Assembler::is_near(Label* L, Condition cond) {
++ ASSERT(L->is_bound());
++ if (L->is_bound() == false)
++ return false;
++
++ int maxReach = ((cond == al) ? 26 : 16);
++ int offset = L->pos() - pc_offset();
++
++ return is_intn(offset, maxReach);
++}
++
++void Assembler::a_form(Instr instr,
++ DwVfpRegister frt,
++ DwVfpRegister fra,
++ DwVfpRegister frb,
++ RCBit r) {
++ emit(instr | frt.code()*B21 | fra.code()*B16 | frb.code()*B11 | r);
++}
++
++void Assembler::d_form(Instr instr,
++ Register rt,
++ Register ra,
++ const intptr_t val,
++ bool signed_disp) {
++ if (signed_disp) {
++ if (!is_int16(val)) {
++ PrintF("val = %" V8PRIdPTR ", 0x%" V8PRIxPTR "\n",
val, val);
++ }
++ ASSERT(is_int16(val));
++ } else {
++ if (!is_uint16(val)) {
++ PrintF("val = %" V8PRIdPTR ", 0x%" V8PRIxPTR
++ ", is_unsigned_imm16(val)=%d, kImm16Mask=0x%x\n",
++ val, val, is_uint16(val), kImm16Mask);
++ }
++ ASSERT(is_uint16(val));
++ }
++ emit(instr | rt.code()*B21 | ra.code()*B16 | (kImm16Mask & val));
++}
++
++void Assembler::x_form(Instr instr,
++ Register ra,
++ Register rs,
++ Register rb,
++ RCBit r) {
++ emit(instr | rs.code()*B21 | ra.code()*B16 | rb.code()*B11 | r);
++}
++
++void Assembler::xo_form(Instr instr,
++ Register rt,
++ Register ra,
++ Register rb,
++ OEBit o,
++ RCBit r) {
++ emit(instr | rt.code()*B21 | ra.code()*B16 | rb.code()*B11 | o | r);
++}
++
++void Assembler::md_form(Instr instr,
++ Register ra,
++ Register rs,
++ int shift,
++ int maskbit,
++ RCBit r) {
++ int sh0_4 = shift & 0x1f;
++ int sh5 = (shift >> 5) & 0x1;
++ int m0_4 = maskbit & 0x1f;
++ int m5 = (maskbit >> 5) & 0x1;
++
++ emit(instr | rs.code()*B21 | ra.code()*B16 |
++ sh0_4*B11 | m0_4*B6 | m5*B5 | sh5*B1 | r);
++}
++
++// Returns the next free trampoline entry.
++int32_t Assembler::get_trampoline_entry() {
++ int32_t trampoline_entry = kInvalidSlotPos;
++
++ if (!internal_trampoline_exception_) {
++ trampoline_entry = trampoline_.take_slot();
++
++ if (kInvalidSlotPos == trampoline_entry) {
++ internal_trampoline_exception_ = true;
++ }
++ }
++ return trampoline_entry;
++}
++
++int Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
++ int target_pos;
++ if (L->is_bound()) {
++ target_pos = L->pos();
++ } else {
++ if (L->is_linked()) {
++ target_pos = L->pos(); // L's link
++ } else {
++ // was: target_pos = kEndOfChain;
++ // However, using branch to self to mark the first reference
++ // should avoid most instances of branch offset overflow. See
++ // target_at() for where this is converted back to kEndOfChain.
++ target_pos = pc_offset();
++ if (!trampoline_emitted_) {
++ unbound_labels_count_++;
++ next_buffer_check_ -= kTrampolineSlotsSize;
++ }
++ }
++ L->link_to(pc_offset());
++ }
++
++ return target_pos - pc_offset();
++}
++
++
++void Assembler::label_at_put(Label* L, int at_offset) {
++ int target_pos;
++ if (L->is_bound()) {
++ target_pos = L->pos();
++ instr_at_put(at_offset, target_pos + (Code::kHeaderSize - kHeapObjectTag));
++ } else {
++ if (L->is_linked()) {
++ target_pos = L->pos(); // L's link
++ } else {
++ // was: target_pos = kEndOfChain;
++ // However, using branch to self to mark the first reference
++ // should avoid most instances of branch offset overflow. See
++ // target_at() for where this is converted back to kEndOfChain.
++ target_pos = at_offset;
++ if (!trampoline_emitted_) {
++ unbound_labels_count_++;
++ next_buffer_check_ -= kTrampolineSlotsSize;
++ }
++ }
++ L->link_to(at_offset);
++
++ Instr constant = target_pos - at_offset;
++ ASSERT(is_int16(constant));
++ instr_at_put(at_offset, constant);
++ }
++}
++
++
++// Branch instructions.
++
++// PowerPC
++void Assembler::bclr(BOfield bo, LKBit lk) {
++ positions_recorder()->WriteRecordedPositions();
++ emit(EXT1 | bo | BCLRX | lk);
++}
++
++void Assembler::bcctr(BOfield bo, LKBit lk) {
++ positions_recorder()->WriteRecordedPositions();
++ emit(EXT1 | bo | BCCTRX | lk);
++}
++
++// Pseudo op - branch to link register
++void Assembler::blr() {
++ bclr(BA, LeaveLK);
++}
++
++// Pseudo op - branch to count register -- used for "jump"
++void Assembler::bcr() {
++ bcctr(BA, LeaveLK);
++}
++
++void Assembler::bc(int branch_offset, BOfield bo, int condition_bit, LKBit lk) {
++ positions_recorder()->WriteRecordedPositions();
++ ASSERT(is_int16(branch_offset));
++ emit(BCX | bo | condition_bit*B16 | (kImm16Mask & branch_offset) | lk);
++}
++
++void Assembler::b(int branch_offset, LKBit lk) {
++ positions_recorder()->WriteRecordedPositions();
++ ASSERT((branch_offset & 3) == 0);
++ int imm26 = branch_offset;
++ ASSERT(is_int26(imm26));
++ // todo add AA and LK bits
++ emit(BX | (imm26 & kImm26Mask) | lk);
++}
++
++void Assembler::xori(Register dst, Register src, const Operand& imm) {
++ d_form(XORI, src, dst, imm.imm_, false);
++}
++
++void Assembler::xoris(Register ra, Register rs, const Operand& imm) {
++ d_form(XORIS, rs, ra, imm.imm_, false);
++}
++
++void Assembler::xor_(Register dst, Register src1, Register src2, RCBit rc) {
++ x_form(EXT2 | XORX, dst, src1, src2, rc);
++}
++
++void Assembler::cntlzw_(Register ra, Register rs, RCBit rc) {
++ x_form(EXT2 | CNTLZWX, ra, rs, r0, rc);
++}
++
++void Assembler::and_(Register ra, Register rs, Register rb, RCBit rc) {
++ x_form(EXT2 | ANDX, ra, rs, rb, rc);
++}
++
++
++void Assembler::rlwinm(Register ra, Register rs,
++ int sh, int mb, int me, RCBit rc) {
++ sh &= 0x1f;
++ mb &= 0x1f;
++ me &= 0x1f;
++ emit(RLWINMX | rs.code()*B21 | ra.code()*B16 | sh*B11 | mb*B6 | me << 1 | rc);
++}
++
++void Assembler::rlwimi(Register ra, Register rs,
++ int sh, int mb, int me, RCBit rc) {
++ sh &= 0x1f;
++ mb &= 0x1f;
++ me &= 0x1f;
++ emit(RLWIMIX | rs.code()*B21 | ra.code()*B16 | sh*B11 | mb*B6 | me << 1 | rc);
++}
++
++void Assembler::slwi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((32 > val.imm_)&&(val.imm_ >= 0));
++ rlwinm(dst, src, val.imm_, 0, 31-val.imm_, rc);
++}
++void Assembler::srwi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((32 > val.imm_)&&(val.imm_ >= 0));
++ rlwinm(dst, src, 32-val.imm_, val.imm_, 31, rc);
++}
++void Assembler::clrrwi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((32 > val.imm_)&&(val.imm_ >= 0));
++ rlwinm(dst, src, 0, 0, 31-val.imm_, rc);
++}
++void Assembler::clrlwi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((32 > val.imm_)&&(val.imm_ >= 0));
++ rlwinm(dst, src, 0, val.imm_, 31, rc);
++}
++
++
++void Assembler::srawi(Register ra, Register rs, int sh, RCBit r) {
++ emit(EXT2 | SRAWIX | rs.code()*B21 | ra.code()*B16 | sh*B11 | r);
++}
++
++void Assembler::srw(Register dst, Register src1, Register src2, RCBit r) {
++ x_form(EXT2 | SRWX, dst, src1, src2, r);
++}
++
++void Assembler::slw(Register dst, Register src1, Register src2, RCBit r) {
++ x_form(EXT2 | SLWX, dst, src1, src2, r);
++}
++
++void Assembler::sraw(Register ra, Register rs, Register rb, RCBit r) {
++ x_form(EXT2 | SRAW, ra, rs, rb, r);
++}
++
++void Assembler::subi(Register dst, Register src, const Operand& imm) {
++ addi(dst, src, Operand(-(imm.imm_)));
++}
++
++void Assembler::addc(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | ADDCX, dst, src1, src2, o, r);
++}
++
++void Assembler::addze(Register dst, Register src1, OEBit o, RCBit r) {
++ // a special xo_form
++ emit(EXT2 | ADDZEX | dst.code()*B21 | src1.code()*B16 | o | r);
++}
++
++void Assembler::sub(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | SUBFX, dst, src2, src1, o, r);
++}
++
++void Assembler::subfc(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | SUBFCX, dst, src2, src1, o, r);
++}
++
++void Assembler::subfic(Register dst, Register src, const Operand& imm) {
++ d_form(SUBFIC, dst, src, imm.imm_, true);
++}
++
++void Assembler::add(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | ADDX, dst, src1, src2, o, r);
++}
++
++// Multiply low word
++void Assembler::mullw(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | MULLW, dst, src1, src2, o, r);
++}
++
++// Multiply hi word
++void Assembler::mulhw(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | MULHWX, dst, src1, src2, o, r);
++}
++
++// Divide word
++void Assembler::divw(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | DIVW, dst, src1, src2, o, r);
++}
++
++void Assembler::addi(Register dst, Register src, const Operand& imm) {
++ ASSERT(!src.is(r0)); // use li instead to show intent
++ d_form(ADDI, dst, src, imm.imm_, true);
++}
++
++void Assembler::addis(Register dst, Register src, const Operand& imm) {
++ ASSERT(!src.is(r0)); // use lis instead to show intent
++ d_form(ADDIS, dst, src, imm.imm_, true);
++}
++
++void Assembler::addic(Register dst, Register src, const Operand& imm) {
++ d_form(ADDIC, dst, src, imm.imm_, true);
++}
++
++void Assembler::andi(Register ra, Register rs, const Operand& imm) {
++ d_form(ANDIx, rs, ra, imm.imm_, false);
++}
++
++void Assembler::andis(Register ra, Register rs, const Operand& imm) {
++ d_form(ANDISx, rs, ra, imm.imm_, false);
++}
++
++void Assembler::nor(Register dst, Register src1, Register src2, RCBit r) {
++ x_form(EXT2 | NORX, dst, src1, src2, r);
++}
++
++void Assembler::notx(Register dst, Register src, RCBit r) {
++ x_form(EXT2 | NORX, dst, src, src, r);
++}
++
++void Assembler::ori(Register ra, Register rs, const Operand& imm) {
++ d_form(ORI, rs, ra, imm.imm_, false);
++}
++
++void Assembler::oris(Register dst, Register src, const Operand& imm) {
++ d_form(ORIS, src, dst, imm.imm_, false);
++}
++
++void Assembler::orx(Register dst, Register src1, Register src2, RCBit rc) {
++ x_form(EXT2 | ORX, dst, src1, src2, rc);
++}
++
++void Assembler::cmpi(Register src1, const Operand& src2, CRegister cr) {
++ intptr_t imm16 = src2.imm_;
++#if V8_TARGET_ARCH_PPC64
++ int L = 1;
++#else
++ int L = 0;
++#endif
++ ASSERT(is_int16(imm16));
++ ASSERT(cr.code() >= 0 && cr.code() <= 7);
++ imm16 &= kImm16Mask;
++ emit(CMPI | cr.code()*B23 | L*B21 | src1.code()*B16 | imm16);
++}
++
++void Assembler::cmpli(Register src1, const Operand& src2, CRegister cr) {
++ uintptr_t uimm16 = src2.imm_;
++#if V8_TARGET_ARCH_PPC64
++ int L = 1;
++#else
++ int L = 0;
++#endif
++ ASSERT(is_uint16(uimm16));
++ ASSERT(cr.code() >= 0 && cr.code() <= 7);
++ uimm16 &= kImm16Mask;
++ emit(CMPLI | cr.code()*B23 | L*B21 | src1.code()*B16 | uimm16);
++}
++
++void Assembler::cmp(Register src1, Register src2, CRegister cr) {
++#if V8_TARGET_ARCH_PPC64
++ int L = 1;
++#else
++ int L = 0;
++#endif
++ ASSERT(cr.code() >= 0 && cr.code() <= 7);
++ emit(EXT2 | CMP | cr.code()*B23 | L*B21 | src1.code()*B16 |
++ src2.code()*B11);
++}
++
++void Assembler::cmpl(Register src1, Register src2, CRegister cr) {
++#if V8_TARGET_ARCH_PPC64
++ int L = 1;
++#else
++ int L = 0;
++#endif
++ ASSERT(cr.code() >= 0 && cr.code() <= 7);
++ emit(EXT2 | CMPL | cr.code()*B23 | L*B21 | src1.code()*B16 |
++ src2.code()*B11);
++}
++
++// Pseudo op - load immediate
++void Assembler::li(Register dst, const Operand &imm) {
++ d_form(ADDI, dst, r0, imm.imm_, true);
++}
++
++void Assembler::lis(Register dst, const Operand& imm) {
++ d_form(ADDIS, dst, r0, imm.imm_, true);
++}
++
++// Pseudo op - move register
++void Assembler::mr(Register dst, Register src) {
++ // actually or(dst, src, src)
++ orx(dst, src, src);
++}
++
++void Assembler::lbz(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(LBZ, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::lbzx(Register rt, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LBZX | rt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lbzux(Register rt, const MemOperand & src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LBZUX | rt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lhz(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(LHZ, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::lhzx(Register rt, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LHZX | rt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lhzux(Register rt, const MemOperand & src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LHZUX | rt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lwz(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(LWZ, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::lwzu(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(LWZU, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::lwzx(Register rt, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LWZX | rt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lwzux(Register rt, const MemOperand & src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LWZUX | rt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lwa(Register dst, const MemOperand &src) {
++#if V8_TARGET_ARCH_PPC64
++ int offset = src.offset();
++ ASSERT(!src.ra_.is(r0));
++ ASSERT(!(offset & 3) && is_int16(offset));
++ offset = kImm16Mask & offset;
++ emit(LD | dst.code()*B21 | src.ra().code()*B16 | offset | 2);
++#else
++ lwz(dst, src);
++#endif
++}
++
++void Assembler::stb(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(STB, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::stbx(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STBX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::stbux(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STBUX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::sth(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(STH, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::sthx(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STHX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::sthux(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STHUX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::stw(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(STW, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::stwu(Register dst, const MemOperand &src) {
++ ASSERT(!src.ra_.is(r0));
++ d_form(STWU, dst, src.ra(), src.offset(), true);
++}
++
++void Assembler::stwx(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STWX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::stwux(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STWUX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::extsb(Register rs, Register ra, RCBit rc) {
++ emit(EXT2 | EXTSB | ra.code()*B21 | rs.code()*B16 | rc);
++}
++
++void Assembler::extsh(Register rs, Register ra, RCBit rc) {
++ emit(EXT2 | EXTSH | ra.code()*B21 | rs.code()*B16 | rc);
++}
++
++void Assembler::neg(Register rt, Register ra, OEBit o, RCBit r) {
++ emit(EXT2 | NEGX | rt.code()*B21 | ra.code()*B16 | o | r);
++}
++
++void Assembler::andc(Register dst, Register src1, Register src2, RCBit rc) {
++ x_form(EXT2 | ANDCX, dst, src1, src2, rc);
++}
++
++#if V8_TARGET_ARCH_PPC64
++// 64bit specific instructions
++void Assembler::ld(Register rd, const MemOperand &src) {
++ int offset = src.offset();
++ ASSERT(!src.ra_.is(r0));
++ ASSERT(!(offset & 3) && is_int16(offset));
++ offset = kImm16Mask & offset;
++ emit(LD | rd.code()*B21 | src.ra().code()*B16 | offset);
++}
++
++void Assembler::ldx(Register rd, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LDX | rd.code()*B21 | ra.code()*B16 | rb.code()*B11);
++}
++
++void Assembler::ldu(Register rd, const MemOperand &src) {
++ int offset = src.offset();
++ ASSERT(!src.ra_.is(r0));
++ ASSERT(!(offset & 3) && is_int16(offset));
++ offset = kImm16Mask & offset;
++ emit(LD | rd.code()*B21 | src.ra().code()*B16 | offset | 1);
++}
++
++void Assembler::ldux(Register rd, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LDUX | rd.code()*B21 | ra.code()*B16 | rb.code()*B11);
++}
++
++void Assembler::std(Register rs, const MemOperand &src) {
++ int offset = src.offset();
++ ASSERT(!src.ra_.is(r0));
++ ASSERT(!(offset & 3) && is_int16(offset));
++ offset = kImm16Mask & offset;
++ emit(STD | rs.code()*B21 | src.ra().code()*B16 | offset);
++}
++
++void Assembler::stdx(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STDX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11);
++}
++
++void Assembler::stdu(Register rs, const MemOperand &src) {
++ int offset = src.offset();
++ ASSERT(!src.ra_.is(r0));
++ ASSERT(!(offset & 3) && is_int16(offset));
++ offset = kImm16Mask & offset;
++ emit(STD | rs.code()*B21 | src.ra().code()*B16 | offset | 1);
++}
++
++void Assembler::stdux(Register rs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STDUX | rs.code()*B21 | ra.code()*B16 | rb.code()*B11);
++}
++
++void Assembler::rldic(Register ra, Register rs, int sh, int mb, RCBit r) {
++ md_form(EXT5 | RLDIC, ra, rs, sh, mb, r);
++}
++
++void Assembler::rldicl(Register ra, Register rs, int sh, int mb, RCBit r) {
++ md_form(EXT5 | RLDICL, ra, rs, sh, mb, r);
++}
++
++void Assembler::rldicr(Register ra, Register rs, int sh, int me, RCBit r) {
++ md_form(EXT5 | RLDICR, ra, rs, sh, me, r);
++}
++
++void Assembler::sldi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((64 > val.imm_)&&(val.imm_ >= 0));
++ rldicr(dst, src, val.imm_, 63-val.imm_, rc);
++}
++void Assembler::srdi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((64 > val.imm_)&&(val.imm_ >= 0));
++ rldicl(dst, src, 64-val.imm_, val.imm_, rc);
++}
++void Assembler::clrrdi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((64 > val.imm_)&&(val.imm_ >= 0));
++ rldicr(dst, src, 0, 63-val.imm_, rc);
++}
++void Assembler::clrldi(Register dst, Register src, const Operand& val,
++ RCBit rc) {
++ ASSERT((64 > val.imm_)&&(val.imm_ >= 0));
++ rldicl(dst, src, 0, val.imm_, rc);
++}
++
++
++void Assembler::rldimi(Register ra, Register rs, int sh, int mb, RCBit r) {
++ md_form(EXT5 | RLDIMI, ra, rs, sh, mb, r);
++}
++
++
++void Assembler::sradi(Register ra, Register rs, int sh, RCBit r) {
++ int sh0_4 = sh & 0x1f;
++ int sh5 = (sh >> 5) & 0x1;
++
++ emit(EXT2 | SRADIX | rs.code()*B21 | ra.code()*B16 | sh0_4*B11 | sh5*B1 | r);
++}
++
++void Assembler::srd(Register dst, Register src1, Register src2, RCBit r) {
++ x_form(EXT2 | SRDX, dst, src1, src2, r);
++}
++
++void Assembler::sld(Register dst, Register src1, Register src2, RCBit r) {
++ x_form(EXT2 | SLDX, dst, src1, src2, r);
++}
++
++void Assembler::srad(Register ra, Register rs, Register rb, RCBit r) {
++ x_form(EXT2 | SRAD, ra, rs, rb, r);
++}
++
++void Assembler::cntlzd_(Register ra, Register rs, RCBit rc) {
++ x_form(EXT2 | CNTLZDX, ra, rs, r0, rc);
++}
++
++void Assembler::extsw(Register rs, Register ra, RCBit rc) {
++ emit(EXT2 | EXTSW | ra.code()*B21 | rs.code()*B16 | rc);
++}
++
++void Assembler::mulld(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | MULLD, dst, src1, src2, o, r);
++}
++
++void Assembler::divd(Register dst, Register src1, Register src2,
++ OEBit o, RCBit r) {
++ xo_form(EXT2 | DIVD, dst, src1, src2, o, r);
++}
++#endif
++
++
++void Assembler::fake_asm(enum FAKE_OPCODE_T fopcode) {
++ ASSERT(fopcode < fLastFaker);
++ emit(FAKE_OPCODE | FAKER_SUBOPCODE | fopcode);
++}
++
++void Assembler::marker_asm(int mcode) {
++ if (::v8::internal::FLAG_trace_sim_stubs) {
++ ASSERT(mcode < F_NEXT_AVAILABLE_STUB_MARKER);
++ emit(FAKE_OPCODE | MARKER_SUBOPCODE | mcode);
++ }
++}
++
++// Function descriptor for AIX.
++// Code address skips the function descriptor "header".
++// TOC and static chain are ignored and set to 0.
++void Assembler::function_descriptor() {
++ RecordRelocInfo(RelocInfo::INTERNAL_REFERENCE);
++#if V8_TARGET_ARCH_PPC64
++ uint64_t value = reinterpret_cast<uint64_t>(pc_) + 3 * kPointerSize;
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ emit(static_cast<uint32_t>(value & 0xFFFFFFFF));
++ emit(static_cast<uint32_t>(value >> 32));
++#else
++ emit(static_cast<uint32_t>(value >> 32));
++ emit(static_cast<uint32_t>(value & 0xFFFFFFFF));
++#endif
++ emit(static_cast<Instr>(0));
++ emit(static_cast<Instr>(0));
++ emit(static_cast<Instr>(0));
++ emit(static_cast<Instr>(0));
++#else
++ emit(reinterpret_cast<Instr>(pc_) + 3 * kPointerSize);
++ emit(static_cast<Instr>(0));
++ emit(static_cast<Instr>(0));
++#endif
++}
++// end PowerPC
++
++// Primarily used for loading constants
++// This should really move to be in macro-assembler as it
++// is really a pseudo instruction
++// Some usages of this intend for a FIXED_SEQUENCE to be used
++// Todo - break this dependency so we can optimize mov() in general
++// and only use the generic version when we require a fixed sequence
++void Assembler::mov(Register dst, const Operand& src) {
++ BlockTrampolinePoolScope block_trampoline_pool(this);
++ if (src.rmode_ != RelocInfo::NONE) {
++ // some form of relocation needed
++ RecordRelocInfo(src.rmode_, src.imm_);
++ }
++
++#if V8_TARGET_ARCH_PPC64
++ int64_t value = src.immediate();
++ int32_t hi_32 = static_cast<int64_t>(value) >> 32;
++ int32_t lo_32 = static_cast<int32_t>(value);
++ int hi_word = static_cast<int>(hi_32) >> 16;
++ int lo_word = static_cast<int>(hi_32) & 0xFFFF;
++ lis(dst, Operand(SIGN_EXT_IMM16(hi_word)));
++ ori(dst, dst, Operand(lo_word));
++ sldi(dst, dst, Operand(32));
++ hi_word = (static_cast<int>(lo_32) >> 16) & 0xFFFF;
++ lo_word = static_cast<int>(lo_32) & 0xFFFF;
++ oris(dst, dst, Operand(hi_word));
++ ori(dst, dst, Operand(lo_word));
++#else
++ int value = src.immediate();
++ if (!is_trampoline_pool_blocked()) {
++ if (is_int16(value)) {
++ li(dst, Operand(value));
++ return;
++ }
++ }
++ int hi_word = static_cast<int>(value) >> 16;
++ int lo_word = static_cast<int>(value) & 0XFFFF;
++
++ lis(dst, Operand(SIGN_EXT_IMM16(hi_word)));
++ if ((!is_trampoline_pool_blocked()) && (lo_word == 0)) {
++ return;
++ }
++ ori(dst, dst, Operand(lo_word));
++#endif
++}
++
++// Special register instructions
++void Assembler::crxor(int bt, int ba, int bb) {
++ emit(EXT1 | CRXOR | bt*B21 | ba*B16 | bb*B11);
++}
++
++void Assembler::mflr(Register dst) {
++ emit(EXT2 | MFSPR | dst.code()*B21 | 256 << 11); // Ignore RC bit
++}
++
++void Assembler::mtlr(Register src) {
++ emit(EXT2 | MTSPR | src.code()*B21 | 256 << 11); // Ignore RC bit
++}
++
++void Assembler::mtctr(Register src) {
++ emit(EXT2 | MTSPR | src.code()*B21 | 288 << 11); // Ignore RC bit
++}
++
++void Assembler::mtxer(Register src) {
++ emit(EXT2 | MTSPR | src.code()*B21 | 32 << 11);
++}
++
++void Assembler::mcrfs(int bf, int bfa) {
++ emit(EXT4 | MCRFS | bf*B23 | bfa*B18);
++}
++
++void Assembler::mfcr(Register dst) {
++ emit(EXT2 | MFCR | dst.code()*B21);
++}
++
++// end PowerPC
++
++// Exception-generating instructions and debugging support.
++// Stops with a non-negative code less than kNumOfWatchedStops support
++// enabling/disabling and a counter feature. See simulator-ppc.h .
++void Assembler::stop(const char* msg, Condition cond, int32_t code,
++ CRegister cr) {
++ if (cond != al) {
++ Label skip;
++ b(NegateCondition(cond), &skip, cr);
++ bkpt(0);
++ bind(&skip);
++ } else {
++ bkpt(0);
++ }
++}
++
++void Assembler::bkpt(uint32_t imm16) {
++ emit(0x7d821008);
++}
++
++
++void Assembler::info(const char* msg, Condition cond, int32_t code,
++ CRegister cr) {
++ if (::v8::internal::FLAG_trace_sim_stubs) {
++ emit(0x7d9ff808);
++#if V8_TARGET_ARCH_PPC64
++ uint64_t value = reinterpret_cast<uint64_t>(msg);
++ emit(static_cast<uint32_t>(value >> 32));
++ emit(static_cast<uint32_t>(value & 0xFFFFFFFF));
++#else
++ emit(reinterpret_cast<Instr>(msg));
++#endif
++ }
++}
++
++void Assembler::dcbf(Register ra, Register rb) {
++ emit(EXT2 | DCBF | ra.code()*B16 | rb.code()*B11);
++}
++
++void Assembler::sync() {
++ emit(EXT2 | SYNC);
++}
++
++void Assembler::icbi(Register ra, Register rb) {
++ emit(EXT2 | ICBI | ra.code()*B16 | rb.code()*B11);
++}
++
++void Assembler::isync() {
++ emit(EXT1 | ISYNC);
++}
++
++// Floating point support
++
++void Assembler::lfd(const DwVfpRegister frt, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(LFD | frt.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::lfdu(const DwVfpRegister frt, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(LFDU | frt.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::lfdx(const DwVfpRegister frt, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LFDX | frt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lfdux(const DwVfpRegister frt, const MemOperand & src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LFDUX | frt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lfs(const DwVfpRegister frt, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ ASSERT(!ra.is(r0));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(LFS | frt.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::lfsu(const DwVfpRegister frt, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ ASSERT(!ra.is(r0));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(LFSU | frt.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::lfsx(const DwVfpRegister frt, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LFSX | frt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::lfsux(const DwVfpRegister frt, const MemOperand & src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | LFSUX | frt.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::stfd(const DwVfpRegister frs, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ ASSERT(!ra.is(r0));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(STFD | frs.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::stfdu(const DwVfpRegister frs, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ ASSERT(!ra.is(r0));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(STFDU | frs.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::stfdx(const DwVfpRegister frs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STFDX | frs.code()*B21 | ra.code()*B16 | rb.code()*B11 | LeaveRC);
++}
++
++void Assembler::stfdux(const DwVfpRegister frs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STFDUX | frs.code()*B21 | ra.code()*B16 | rb.code()*B11 |LeaveRC);
++}
++
++void Assembler::stfs(const DwVfpRegister frs, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ ASSERT(!ra.is(r0));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(STFS | frs.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::stfsu(const DwVfpRegister frs, const MemOperand &src) {
++ int offset = src.offset();
++ Register ra = src.ra();
++ ASSERT(is_int16(offset));
++ ASSERT(!ra.is(r0));
++ int imm16 = offset & kImm16Mask;
++ // could be x_form instruction with some casting magic
++ emit(STFSU | frs.code()*B21 | ra.code()*B16 | imm16);
++}
++
++void Assembler::stfsx(const DwVfpRegister frs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STFSX | frs.code()*B21 | ra.code()*B16 | rb.code()*B11 |LeaveRC);
++}
++
++void Assembler::stfsux(const DwVfpRegister frs, const MemOperand &src) {
++ Register ra = src.ra();
++ Register rb = src.rb();
++ ASSERT(!ra.is(r0));
++ emit(EXT2 | STFSUX | frs.code()*B21 | ra.code()*B16 | rb.code()*B11 |LeaveRC);
++}
++
++void Assembler::fsub(const DwVfpRegister frt,
++ const DwVfpRegister fra,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ a_form(EXT4 | FSUB, frt, fra, frb, rc);
++}
++
++void Assembler::fadd(const DwVfpRegister frt,
++ const DwVfpRegister fra,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ a_form(EXT4 | FADD, frt, fra, frb, rc);
++}
++void Assembler::fmul(const DwVfpRegister frt,
++ const DwVfpRegister fra,
++ const DwVfpRegister frc,
++ RCBit rc) {
++ emit(EXT4 | FMUL | frt.code()*B21 | fra.code()*B16 | frc.code()*B6 | rc);
++}
++void Assembler::fdiv(const DwVfpRegister frt,
++ const DwVfpRegister fra,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ a_form(EXT4 | FDIV, frt, fra, frb, rc);
++}
++
++void Assembler::fcmpu(const DwVfpRegister fra,
++ const DwVfpRegister frb,
++ CRegister cr) {
++ ASSERT(cr.code() >= 0 && cr.code() <= 7);
++ emit(EXT4 | FCMPU | cr.code()*B23 | fra.code()*B16 | frb.code()*B11);
++}
++
++void Assembler::fmr(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FMR | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++void Assembler::fctiwz(const DwVfpRegister frt,
++ const DwVfpRegister frb) {
++ emit(EXT4 | FCTIWZ | frt.code()*B21 | frb.code()*B11);
++}
++
++void Assembler::fctiw(const DwVfpRegister frt,
++ const DwVfpRegister frb) {
++ emit(EXT4 | FCTIW | frt.code()*B21 | frb.code()*B11);
++}
++
++void Assembler::frim(const DwVfpRegister frt,
++ const DwVfpRegister frb) {
++ emit(EXT4 | FRIM | frt.code()*B21 | frb.code()*B11);
++}
++
++void Assembler::frsp(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FRSP | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++void Assembler::fcfid(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FCFID | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++void Assembler::fctid(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FCTID | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++void Assembler::fctidz(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FCTIDZ | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++void Assembler::fsel(const DwVfpRegister frt, const DwVfpRegister fra,
++ const DwVfpRegister frc, const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FSEL | frt.code()*B21 | fra.code()*B16 | frb.code()*B11 |
++ frc.code()*B6 | rc);
++}
++
++void Assembler::fneg(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FNEG | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++void Assembler::mtfsfi(int bf, int immediate, RCBit rc) {
++ emit(EXT4 | MTFSFI | bf*B23 | immediate*B12 | rc);
++}
++
++void Assembler::mffs(const DwVfpRegister frt, RCBit rc) {
++ emit(EXT4 | MFFS | frt.code()*B21 | rc);
++}
++
++void Assembler::mtfsf(const DwVfpRegister frb, bool L,
++ int FLM, bool W, RCBit rc) {
++ emit(EXT4 | MTFSF | frb.code()*B11 | W*B16 | FLM*B17 | L*B25 | rc);
++}
++
++void Assembler::fsqrt(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FSQRT | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++void Assembler::fabs(const DwVfpRegister frt,
++ const DwVfpRegister frb,
++ RCBit rc) {
++ emit(EXT4 | FABS | frt.code()*B21 | frb.code()*B11 | rc);
++}
++
++// Pseudo instructions.
++void Assembler::nop(int type) {
++ switch (type) {
++ case 0:
++ ori(r0, r0, Operand::Zero());
++ break;
++ case DEBUG_BREAK_NOP:
++ ori(r3, r3, Operand::Zero());
++ break;
++ default:
++ UNIMPLEMENTED();
++ }
++}
++
++
++bool Assembler::IsNop(Instr instr, int type) {
++ ASSERT((0 == type) || (DEBUG_BREAK_NOP == type));
++ int reg = 0;
++ if (DEBUG_BREAK_NOP == type) {
++ reg = 3;
++ }
++ return instr == (ORI | reg*B21 | reg*B16);
++}
++
++// Debugging.
++void Assembler::RecordJSReturn() {
++ positions_recorder()->WriteRecordedPositions();
++ CheckBuffer();
++ RecordRelocInfo(RelocInfo::JS_RETURN);
++}
++
++
++void Assembler::RecordDebugBreakSlot() {
++ positions_recorder()->WriteRecordedPositions();
++ CheckBuffer();
++ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
++}
++
++
++void Assembler::RecordComment(const char* msg) {
++ if (FLAG_code_comments) {
++ CheckBuffer();
++ RecordRelocInfo(RelocInfo::COMMENT, reinterpret_cast<intptr_t>(msg));
++ }
++}
++
++
++void Assembler::GrowBuffer() {
++ if (!own_buffer_) FATAL("external code buffer is too small");
++
++ // Compute new buffer size.
++ CodeDesc desc; // the new buffer
++ if (buffer_size_ < 4*KB) {
++ desc.buffer_size = 4*KB;
++ } else if (buffer_size_ < 1*MB) {
++ desc.buffer_size = 2*buffer_size_;
++ } else {
++ desc.buffer_size = buffer_size_ + 1*MB;
++ }
++ CHECK_GT(desc.buffer_size, 0); // no overflow
++
++ // Set up new buffer.
++ desc.buffer = NewArray<byte>(desc.buffer_size);
++
++ desc.instr_size = pc_offset();
++ desc.reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
++
++ // Copy the data.
++ intptr_t pc_delta = desc.buffer - buffer_;
++ intptr_t rc_delta = (desc.buffer + desc.buffer_size) -
++ (buffer_ + buffer_size_);
++ memmove(desc.buffer, buffer_, desc.instr_size);
++ memmove(reloc_info_writer.pos() + rc_delta,
++ reloc_info_writer.pos(), desc.reloc_size);
++
++ // Switch buffers.
++ DeleteArray(buffer_);
++ buffer_ = desc.buffer;
++ buffer_size_ = desc.buffer_size;
++ pc_ += pc_delta;
++ reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
++ reloc_info_writer.last_pc() + pc_delta);
++
++ // None of our relocation types are pc relative pointing outside the code
++ // buffer nor pc absolute pointing inside the code buffer, so there is no need
++ // to relocate any emitted relocation entries.
++
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ // Relocate runtime entries.
++ for (RelocIterator it(desc); !it.done(); it.next()) {
++ RelocInfo::Mode rmode = it.rinfo()->rmode();
++ if (rmode == RelocInfo::INTERNAL_REFERENCE) {
++ intptr_t* p = reinterpret_cast<intptr_t*>(it.rinfo()->pc());
++ if (*p != 0) { // 0 means uninitialized.
++ *p += pc_delta;
++ }
++ }
++ }
++#endif
++}
++
++
++void Assembler::db(uint8_t data) {
++ CheckBuffer();
++ *reinterpret_cast<uint8_t*>(pc_) = data;
++ pc_ += sizeof(uint8_t);
++}
++
++
++void Assembler::dd(uint32_t data) {
++ CheckBuffer();
++ *reinterpret_cast<uint32_t*>(pc_) = data;
++ pc_ += sizeof(uint32_t);
++}
++
++
++void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
++ RelocInfo rinfo(pc_, rmode, data, NULL);
++ if (rmode >= RelocInfo::JS_RETURN && rmode <=
RelocInfo::DEBUG_BREAK_SLOT) {
++ // Adjust code for new modes.
++ ASSERT(RelocInfo::IsDebugBreakSlot(rmode)
++ || RelocInfo::IsJSReturn(rmode)
++ || RelocInfo::IsComment(rmode)
++ || RelocInfo::IsPosition(rmode));
++ }
++ if (rinfo.rmode() != RelocInfo::NONE) {
++ // Don't record external references unless the heap will be serialized.
++ if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
++#ifdef DEBUG
++ if (!Serializer::enabled()) {
++ Serializer::TooLateToEnableNow();
++ }
++#endif
++ if (!Serializer::enabled() && !emit_debug_code()) {
++ return;
++ }
++ }
++ ASSERT(buffer_space() >= kMaxRelocSize); // too late to grow buffer here
++ if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
++ RelocInfo reloc_info_with_ast_id(pc_,
++ rmode,
++ RecordedAstId().ToInt(),
++ NULL);
++ ClearRecordedAstId();
++ reloc_info_writer.Write(&reloc_info_with_ast_id);
++ } else {
++ reloc_info_writer.Write(&rinfo);
++ }
++ }
++}
++
++
++void Assembler::BlockTrampolinePoolFor(int instructions) {
++ BlockTrampolinePoolBefore(pc_offset() + instructions * kInstrSize);
++}
++
++
++void Assembler::CheckTrampolinePool() {
++ // Some small sequences of instructions must not be broken up by the
++ // insertion of a trampoline pool; such sequences are protected by setting
++ // either trampoline_pool_blocked_nesting_ or no_trampoline_pool_before_,
++ // which are both checked here. Also, recursive calls to CheckTrampolinePool
++ // are blocked by trampoline_pool_blocked_nesting_.
++ if ((trampoline_pool_blocked_nesting_ > 0) ||
++ (pc_offset() < no_trampoline_pool_before_)) {
++ // Emission is currently blocked; make sure we try again as soon as
++ // possible.
++ if (trampoline_pool_blocked_nesting_ > 0) {
++ next_buffer_check_ = pc_offset() + kInstrSize;
++ } else {
++ next_buffer_check_ = no_trampoline_pool_before_;
++ }
++ return;
++ }
++
++ ASSERT(!trampoline_emitted_);
++ ASSERT(unbound_labels_count_ >= 0);
++ if (unbound_labels_count_ > 0) {
++ // First we emit jump, then we emit trampoline pool.
++ { BlockTrampolinePoolScope block_trampoline_pool(this);
++ Label after_pool;
++ b(&after_pool);
++
++ int pool_start = pc_offset();
++ for (int i = 0; i < unbound_labels_count_; i++) {
++ b(&after_pool);
++ }
++ bind(&after_pool);
++ trampoline_ = Trampoline(pool_start, unbound_labels_count_);
++
++ trampoline_emitted_ = true;
++ // As we are only going to emit trampoline once, we need to prevent any
++ // further emission.
++ next_buffer_check_ = kMaxInt;
++ }
++ } else {
++ // Number of branches to unbound label at this point is zero, so we can
++ // move next buffer check to maximum.
++ next_buffer_check_ = pc_offset() +
++ kMaxCondBranchReach - kMaxBlockTrampolineSectionSize;
++ }
++ return;
++}
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/assembler-ppc.h.ppc v8-3.14.5.10/src/ppc/assembler-ppc.h
+--- v8-3.14.5.10/src/ppc/assembler-ppc.h.ppc 2016-06-07 14:15:45.985393038 -0400
++++ v8-3.14.5.10/src/ppc/assembler-ppc.h 2016-06-07 14:15:45.985393038 -0400
+@@ -0,0 +1,1382 @@
++// Copyright (c) 1994-2006 Sun Microsystems Inc.
++// All Rights Reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions
++// are met:
++//
++// - Redistributions of source code must retain the above copyright notice,
++// this list of conditions and the following disclaimer.
++//
++// - Redistribution in binary form must reproduce the above copyright
++// notice, this list of conditions and the following disclaimer in the
++// documentation and/or other materials provided with the
++// distribution.
++//
++// - Neither the name of Sun Microsystems or the names of contributors may
++// be used to endorse or promote products derived from this software without
++// specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++// OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// The original source code covered by the above license above has been
++// modified significantly by Google Inc.
++// Copyright 2012 the V8 project authors. All rights reserved.
++
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++
++// A light-weight PPC Assembler
++// Generates user mode instructions for the PPC architecture up
++
++#ifndef V8_PPC_ASSEMBLER_PPC_H_
++#define V8_PPC_ASSEMBLER_PPC_H_
++#include <stdio.h>
++#if !defined(_AIX)
++#include <elf.h>
++#include <fcntl.h>
++#include <unistd.h>
++#endif
++#include "assembler.h"
++#include "constants-ppc.h"
++#include "serialize.h"
++
++#define ABI_USES_FUNCTION_DESCRIPTORS \
++ (V8_HOST_ARCH_PPC && \
++ (defined(_AIX) || \
++ (defined(V8_TARGET_ARCH_PPC64) && (__BYTE_ORDER != __LITTLE_ENDIAN))))
++
++#define ABI_PASSES_HANDLES_IN_REGS \
++ (!V8_HOST_ARCH_PPC || defined(_AIX) || defined(V8_TARGET_ARCH_PPC64))
++
++#define ABI_RETURNS_HANDLES_IN_REGS \
++ (!V8_HOST_ARCH_PPC || (__BYTE_ORDER == __LITTLE_ENDIAN))
++
++#define ABI_RETURNS_OBJECT_PAIRS_IN_REGS \
++ (!V8_HOST_ARCH_PPC || (__BYTE_ORDER == __LITTLE_ENDIAN))
++
++#define ABI_TOC_ADDRESSABILITY_VIA_IP \
++ (V8_HOST_ARCH_PPC && defined(V8_TARGET_ARCH_PPC64) && \
++ (__BYTE_ORDER == __LITTLE_ENDIAN))
++
++namespace v8 {
++namespace internal {
++
++// CPU Registers.
++//
++// 1) We would prefer to use an enum, but enum values are assignment-
++// compatible with int, which has caused code-generation bugs.
++//
++// 2) We would prefer to use a class instead of a struct but we don't like
++// the register initialization to depend on the particular initialization
++// order (which appears to be different on OS X, Linux, and Windows for the
++// installed versions of C++ we tried). Using a struct permits C-style
++// "initialization". Also, the Register objects cannot be const as this
++// forces initialization stubs in MSVC, making us dependent on initialization
++// order.
++//
++// 3) By not using an enum, we are possibly preventing the compiler from
++// doing certain constant folds, which may significantly reduce the
++// code generated for some assembly instructions (because they boil down
++// to a few constants). If this is a problem, we could change the code
++// such that we use an enum in optimized mode, and the struct in debug
++// mode. This way we get the compile-time error checking in debug mode
++// and best performance in optimized code.
++
++// Core register
++struct Register {
++ static const int kNumRegisters = 32;
++ static const int kNumAllocatableRegisters = 8; // r3-r10
++ static const int kSizeInBytes = 4;
++
++ static int ToAllocationIndex(Register reg) {
++ int index = reg.code() - 3; // r0-r2 are skipped
++ ASSERT(index < kNumAllocatableRegisters);
++ return index;
++ }
++
++ static Register FromAllocationIndex(int index) {
++ ASSERT(index >= 0 && index < kNumAllocatableRegisters);
++ return from_code(index + 3); // r0-r2 are skipped
++ }
++
++ static const char* AllocationIndexToString(int index) {
++ ASSERT(index >= 0 && index < kNumAllocatableRegisters);
++ const char* const names[] = {
++ "r3",
++ "r4",
++ "r5",
++ "r6",
++ "r7",
++ "r8",
++ "r9",
++ "r10", // currently last allocated register
++ "r11", // lithium scratch
++ "r12", // ip
++ "r13",
++ "r14",
++ "r15",
++ "r16",
++ "r17",
++ "r18",
++ "r19",
++ "r20",
++ "r21",
++ "r22",
++ "r23",
++ "r24",
++ "r25",
++ "r26",
++ "r27",
++ "r28",
++ "r29",
++ "r30",
++ };
++ return names[index];
++ }
++
++ static Register from_code(int code) {
++ Register r = { code };
++ return r;
++ }
++
++ bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; }
++ bool is(Register reg) const { return code_ == reg.code_; }
++ int code() const {
++ ASSERT(is_valid());
++ return code_;
++ }
++ int bit() const {
++ ASSERT(is_valid());
++ return 1 << code_;
++ }
++
++ void set_code(int code) {
++ code_ = code;
++ ASSERT(is_valid());
++ }
++
++ // Unfortunately we can't make this private in a struct.
++ int code_;
++};
++
++// These constants are used in several locations, including static initializers
++const int kRegister_no_reg_Code = -1;
++const int kRegister_r0_Code = 0;
++const int kRegister_sp_Code = 1; // todo - rename to SP
++const int kRegister_r2_Code = 2; // special on PowerPC
++const int kRegister_r3_Code = 3;
++const int kRegister_r4_Code = 4;
++const int kRegister_r5_Code = 5;
++const int kRegister_r6_Code = 6;
++const int kRegister_r7_Code = 7;
++const int kRegister_r8_Code = 8;
++const int kRegister_r9_Code = 9;
++const int kRegister_r10_Code = 10;
++const int kRegister_r11_Code = 11;
++const int kRegister_ip_Code = 12; // todo - fix
++const int kRegister_r13_Code = 13;
++const int kRegister_r14_Code = 14;
++const int kRegister_r15_Code = 15;
++
++const int kRegister_r16_Code = 16;
++const int kRegister_r17_Code = 17;
++const int kRegister_r18_Code = 18;
++const int kRegister_r19_Code = 19;
++const int kRegister_r20_Code = 20;
++const int kRegister_r21_Code = 21;
++const int kRegister_r22_Code = 22;
++const int kRegister_r23_Code = 23;
++const int kRegister_r24_Code = 24;
++const int kRegister_r25_Code = 25;
++const int kRegister_r26_Code = 26;
++const int kRegister_r27_Code = 27;
++const int kRegister_r28_Code = 28;
++const int kRegister_r29_Code = 29;
++const int kRegister_r30_Code = 30;
++const int kRegister_fp_Code = 31;
++
++const Register no_reg = { kRegister_no_reg_Code };
++
++const Register r0 = { kRegister_r0_Code };
++const Register sp = { kRegister_sp_Code };
++const Register r2 = { kRegister_r2_Code };
++const Register r3 = { kRegister_r3_Code };
++const Register r4 = { kRegister_r4_Code };
++const Register r5 = { kRegister_r5_Code };
++const Register r6 = { kRegister_r6_Code };
++const Register r7 = { kRegister_r7_Code };
++const Register r8 = { kRegister_r8_Code };
++const Register r9 = { kRegister_r9_Code };
++const Register r10 = { kRegister_r10_Code };
++// Used as lithium codegen scratch register.
++const Register r11 = { kRegister_r11_Code };
++const Register ip = { kRegister_ip_Code };
++// Used as roots register.
++const Register r13 = { kRegister_r13_Code };
++const Register r14 = { kRegister_r14_Code };
++const Register r15 = { kRegister_r15_Code };
++
++const Register r16 = { kRegister_r16_Code };
++const Register r17 = { kRegister_r17_Code };
++const Register r18 = { kRegister_r18_Code };
++const Register r19 = { kRegister_r19_Code };
++// Used as context register.
++const Register r20 = { kRegister_r20_Code };
++const Register r21 = { kRegister_r21_Code };
++const Register r22 = { kRegister_r22_Code };
++const Register r23 = { kRegister_r23_Code };
++const Register r24 = { kRegister_r24_Code };
++const Register r25 = { kRegister_r25_Code };
++const Register r26 = { kRegister_r26_Code };
++const Register r27 = { kRegister_r27_Code };
++const Register r28 = { kRegister_r28_Code };
++const Register r29 = { kRegister_r29_Code };
++const Register r30 = { kRegister_r30_Code };
++const Register fp = { kRegister_fp_Code };
++
++// Double word FP register.
++struct DwVfpRegister {
++ static const int kNumRegisters = 32;
++ static const int kNumVolatileRegisters = 14; // d0-d13
++ static const int kNumAllocatableRegisters = 12; // d1-d12
++
++ inline static int ToAllocationIndex(DwVfpRegister reg);
++
++ static DwVfpRegister FromAllocationIndex(int index) {
++ ASSERT(index >= 0 && index < kNumAllocatableRegisters);
++ return from_code(index + 1); // d0 is skipped
++ }
++
++ static const char* AllocationIndexToString(int index) {
++ ASSERT(index >= 0 && index < kNumAllocatableRegisters);
++ const char* const names[] = {
++ "d1",
++ "d2",
++ "d3",
++ "d4",
++ "d5",
++ "d6",
++ "d7",
++ "d8",
++ "d9",
++ "d10",
++ "d11",
++ "d12",
++ };
++ return names[index];
++ }
++
++ static DwVfpRegister from_code(int code) {
++ DwVfpRegister r = { code };
++ return r;
++ }
++
++ // Supporting d0 to d15, can be later extended to d31.
++ bool is_valid() const { return 0 <= code_ && code_ < kNumRegisters; }
++ bool is(DwVfpRegister reg) const { return code_ == reg.code_; }
++
++ int code() const {
++ ASSERT(is_valid());
++ return code_;
++ }
++ int bit() const {
++ ASSERT(is_valid());
++ return 1 << code_;
++ }
++ void split_code(int* vm, int* m) const {
++ ASSERT(is_valid());
++ *m = (code_ & 0x10) >> 4;
++ *vm = code_ & 0x0F;
++ }
++
++ int code_;
++};
++
++
++typedef DwVfpRegister DoubleRegister;
++
++const DwVfpRegister no_dreg = { -1 };
++const DwVfpRegister d0 = { 0 };
++const DwVfpRegister d1 = { 1 };
++const DwVfpRegister d2 = { 2 };
++const DwVfpRegister d3 = { 3 };
++const DwVfpRegister d4 = { 4 };
++const DwVfpRegister d5 = { 5 };
++const DwVfpRegister d6 = { 6 };
++const DwVfpRegister d7 = { 7 };
++const DwVfpRegister d8 = { 8 };
++const DwVfpRegister d9 = { 9 };
++const DwVfpRegister d10 = { 10 };
++const DwVfpRegister d11 = { 11 };
++const DwVfpRegister d12 = { 12 };
++const DwVfpRegister d13 = { 13 };
++const DwVfpRegister d14 = { 14 };
++const DwVfpRegister d15 = { 15 };
++const DwVfpRegister d16 = { 16 };
++const DwVfpRegister d17 = { 17 };
++const DwVfpRegister d18 = { 18 };
++const DwVfpRegister d19 = { 19 };
++const DwVfpRegister d20 = { 20 };
++const DwVfpRegister d21 = { 21 };
++const DwVfpRegister d22 = { 22 };
++const DwVfpRegister d23 = { 23 };
++const DwVfpRegister d24 = { 24 };
++const DwVfpRegister d25 = { 25 };
++const DwVfpRegister d26 = { 26 };
++const DwVfpRegister d27 = { 27 };
++const DwVfpRegister d28 = { 28 };
++const DwVfpRegister d29 = { 29 };
++const DwVfpRegister d30 = { 30 };
++const DwVfpRegister d31 = { 31 };
++
++// Aliases for double registers. Defined using #define instead of
++// "static const DwVfpRegister&" because Clang complains otherwise when a
++// compilation unit that includes this header doesn't use the variables.
++#define kFirstCalleeSavedDoubleReg d14
++#define kLastCalleeSavedDoubleReg d31
++#define kDoubleRegZero d14
++#define kScratchDoubleReg d13
++
++Register ToRegister(int num);
++
++// Coprocessor register
++struct CRegister {
++ bool is_valid() const { return 0 <= code_ && code_ < 16; }
++ bool is(CRegister creg) const { return code_ == creg.code_; }
++ int code() const {
++ ASSERT(is_valid());
++ return code_;
++ }
++ int bit() const {
++ ASSERT(is_valid());
++ return 1 << code_;
++ }
++
++ // Unfortunately we can't make this private in a struct.
++ int code_;
++};
++
++
++const CRegister no_creg = { -1 };
++
++const CRegister cr0 = { 0 };
++const CRegister cr1 = { 1 };
++const CRegister cr2 = { 2 };
++const CRegister cr3 = { 3 };
++const CRegister cr4 = { 4 };
++const CRegister cr5 = { 5 };
++const CRegister cr6 = { 6 };
++const CRegister cr7 = { 7 };
++const CRegister cr8 = { 8 };
++const CRegister cr9 = { 9 };
++const CRegister cr10 = { 10 };
++const CRegister cr11 = { 11 };
++const CRegister cr12 = { 12 };
++const CRegister cr13 = { 13 };
++const CRegister cr14 = { 14 };
++const CRegister cr15 = { 15 };
++
++// -----------------------------------------------------------------------------
++// Machine instruction Operands
++
++// Class Operand represents a shifter operand in data processing instructions
++class Operand BASE_EMBEDDED {
++ public:
++ // immediate
++ INLINE(explicit Operand(intptr_t immediate,
++ RelocInfo::Mode rmode = RelocInfo::NONE));
++ INLINE(static Operand Zero()) {
++ return Operand(static_cast<intptr_t>(0));
++ }
++ INLINE(explicit Operand(const ExternalReference& f));
++ explicit Operand(Handle<Object> handle);
++ INLINE(explicit Operand(Smi* value));
++
++ // rm
++ INLINE(explicit Operand(Register rm));
++
++ // Return true if this is a register operand.
++ INLINE(bool is_reg() const);
++
++ inline intptr_t immediate() const {
++ ASSERT(!rm_.is_valid());
++ return imm_;
++ }
++
++ Register rm() const { return rm_; }
++
++ private:
++ Register rm_;
++ intptr_t imm_; // valid if rm_ == no_reg
++ RelocInfo::Mode rmode_;
++
++ friend class Assembler;
++ friend class MacroAssembler;
++};
++
++
++// Class MemOperand represents a memory operand in load and store instructions
++// On PowerPC we have base register + 16bit signed value
++// Alternatively we can have a 16bit signed value immediate
++class MemOperand BASE_EMBEDDED {
++ public:
++ explicit MemOperand(Register rn, int32_t offset = 0);
++
++ explicit MemOperand(Register ra, Register rb);
++
++ int32_t offset() const {
++ ASSERT(rb_.is(no_reg));
++ return offset_;
++ }
++
++ // PowerPC - base register
++ Register ra() const {
++ ASSERT(!ra_.is(no_reg));
++ return ra_;
++ }
++
++ Register rb() const {
++ ASSERT(offset_ == 0 && !rb_.is(no_reg));
++ return rb_;
++ }
++
++ private:
++ Register ra_; // base
++ int32_t offset_; // offset
++ Register rb_; // index
++
++ friend class Assembler;
++};
++
++// CpuFeatures keeps track of which features are supported by the target CPU.
++// Supported features must be enabled by a Scope before use.
++class CpuFeatures : public AllStatic {
++ public:
++ // Detect features of the target CPU. Set safe defaults if the serializer
++ // is enabled (snapshots must be portable).
++ static void Probe();
++
++ // Check whether a feature is supported by the target CPU.
++ static bool IsSupported(CpuFeature f) {
++ ASSERT(initialized_);
++ return (supported_ & (1u << f)) != 0;
++ }
++
++#ifdef DEBUG
++ // Check whether a feature is currently enabled.
++ static bool IsEnabled(CpuFeature f) {
++ ASSERT(initialized_);
++ Isolate* isolate = Isolate::UncheckedCurrent();
++ if (isolate == NULL) {
++ // When no isolate is available, work as if we're running in
++ // release mode.
++ return IsSupported(f);
++ }
++ unsigned enabled = static_cast<unsigned>(isolate->enabled_cpu_features());
++ return (enabled & (1u << f)) != 0;
++ }
++#endif
++
++ // Enable a specified feature within a scope.
++ class Scope BASE_EMBEDDED {
++#ifdef DEBUG
++
++ public:
++ explicit Scope(CpuFeature f) {
++ unsigned mask = 1u << f;
++ ASSERT(CpuFeatures::IsSupported(f));
++ ASSERT(!Serializer::enabled() ||
++ (CpuFeatures::found_by_runtime_probing_ & mask) == 0);
++ isolate_ = Isolate::UncheckedCurrent();
++ old_enabled_ = 0;
++ if (isolate_ != NULL) {
++ old_enabled_ =
static_cast<unsigned>(isolate_->enabled_cpu_features());
++ isolate_->set_enabled_cpu_features(old_enabled_ | mask);
++ }
++ }
++ ~Scope() {
++ ASSERT_EQ(Isolate::UncheckedCurrent(), isolate_);
++ if (isolate_ != NULL) {
++ isolate_->set_enabled_cpu_features(old_enabled_);
++ }
++ }
++
++ private:
++ Isolate* isolate_;
++ unsigned old_enabled_;
++#else
++
++ public:
++ explicit Scope(CpuFeature f) {}
++#endif
++ };
++
++ class TryForceFeatureScope BASE_EMBEDDED {
++ public:
++ explicit TryForceFeatureScope(CpuFeature f)
++ : old_supported_(CpuFeatures::supported_) {
++ if (CanForce()) {
++ CpuFeatures::supported_ |= (1u << f);
++ }
++ }
++
++ ~TryForceFeatureScope() {
++ if (CanForce()) {
++ CpuFeatures::supported_ = old_supported_;
++ }
++ }
++
++ private:
++ static bool CanForce() {
++ // It's only safe to temporarily force support of CPU features
++ // when there's only a single isolate, which is guaranteed when
++ // the serializer is enabled.
++ return Serializer::enabled();
++ }
++
++ const unsigned old_supported_;
++ };
++
++ private:
++#ifdef DEBUG
++ static bool initialized_;
++#endif
++ static unsigned supported_;
++ static unsigned found_by_runtime_probing_;
++
++ DISALLOW_COPY_AND_ASSIGN(CpuFeatures);
++};
++
++
++class Assembler : public AssemblerBase {
++ public:
++ // Create an assembler. Instructions and relocation information are emitted
++ // into a buffer, with the instructions starting from the beginning and the
++ // relocation information starting from the end of the buffer. See CodeDesc
++ // for a detailed comment on the layout (globals.h).
++ //
++ // If the provided buffer is NULL, the assembler allocates and grows its own
++ // buffer, and buffer_size determines the initial buffer size. The buffer is
++ // owned by the assembler and deallocated upon destruction of the assembler.
++ //
++ // If the provided buffer is not NULL, the assembler uses the provided buffer
++ // for code generation and assumes its size to be buffer_size. If the buffer
++ // is too small, a fatal error occurs. No deallocation of the buffer is done
++ // upon destruction of the assembler.
++ Assembler(Isolate* isolate, void* buffer, int buffer_size);
++ ~Assembler();
++
++ // Overrides the default provided by FLAG_debug_code.
++ void set_emit_debug_code(bool value) { emit_debug_code_ = value; }
++
++ // Avoids using instructions that vary in size in unpredictable ways between
++ // the snapshot and the running VM. This is needed by the full compiler so
++ // that it can recompile code with debug support and fix the PC.
++ void set_predictable_code_size(bool value) { predictable_code_size_ = value; }
++
++ // GetCode emits any pending (non-emitted) code and fills the descriptor
++ // desc. GetCode() is idempotent; it returns the same result if no other
++ // Assembler functions are invoked in between GetCode() calls.
++ void GetCode(CodeDesc* desc);
++
++ // Label operations & relative jumps (PPUM Appendix D)
++ //
++ // Takes a branch opcode (cc) and a label (L) and generates
++ // either a backward branch or a forward branch and links it
++ // to the label fixup chain. Usage:
++ //
++ // Label L; // unbound label
++ // j(cc, &L); // forward branch to unbound label
++ // bind(&L); // bind label to the current pc
++ // j(cc, &L); // backward branch to bound label
++ // bind(&L); // illegal: a label may be bound only once
++ //
++ // Note: The same Label can be used for forward and backward branches
++ // but it may be bound only once.
++
++ void bind(Label* L); // binds an unbound label L to the current code position
++ // Determines if Label is bound and near enough so that a single
++ // branch instruction can be used to reach it.
++ bool is_near(Label* L, Condition cond);
++
++ // Returns the branch offset to the given label from the current code position
++ // Links the label to the current position if it is still unbound
++ // Manages the jump elimination optimization if the second parameter is true.
++ int branch_offset(Label* L, bool jump_elimination_allowed);
++
++ // Puts a labels target address at the given position.
++ // The high 8 bits are set to zero.
++ void label_at_put(Label* L, int at_offset);
++
++ // Read/Modify the code target address in the branch/call instruction at pc.
++ INLINE(static Address target_address_at(Address pc));
++ INLINE(static void set_target_address_at(Address pc, Address target));
++
++ // Return the code target address at a call site from the return address
++ // of that call in the instruction stream.
++ inline static Address target_address_from_return_address(Address pc);
++
++ // This sets the branch destination.
++ // This is for calls and branches within generated code.
++ inline static void deserialization_set_special_target_at(
++ Address instruction_payload, Address target);
++
++ // Size of an instruction.
++ static const int kInstrSize = sizeof(Instr);
++
++ // Here we are patching the address in the LUI/ORI instruction pair.
++ // These values are used in the serialization process and must be zero for
++ // PPC platform, as Code, Embedded Object or External-reference pointers
++ // are split across two consecutive instructions and don't exist separately
++ // in the code, so the serializer should not step forwards in memory after
++ // a target is resolved and written.
++ static const int kSpecialTargetSize = 0;
++
++ // Number of consecutive instructions used to store pointer sized constant.
++#if V8_TARGET_ARCH_PPC64
++ static const int kInstructionsForPtrConstant = 5;
++#else
++ static const int kInstructionsForPtrConstant = 2;
++#endif
++
++ // Distance between the instruction referring to the address of the call
++ // target and the return address.
++
++ // Call sequence is a FIXED_SEQUENCE:
++ // lis r8, 2148 @ call address hi
++ // ori r8, r8, 5728 @ call address lo
++ // mtlr r8
++ // blrl
++ // @ return address
++ // in 64bit mode, the addres load is a 5 instruction sequence
++#if V8_TARGET_ARCH_PPC64
++ static const int kCallTargetAddressOffset = 7 * kInstrSize;
++#else
++ static const int kCallTargetAddressOffset = 4 * kInstrSize;
++#endif
++
++ // Distance between start of patched return sequence and the emitted address
++ // to jump to.
++ // Patched return sequence is a FIXED_SEQUENCE:
++ // lis r0, <address hi>
++ // ori r0, r0, <address lo>
++ // mtlr r0
++ // blrl
++ static const int kPatchReturnSequenceAddressOffset = 0 * kInstrSize;
++
++ // Distance between start of patched debug break slot and the emitted address
++ // to jump to.
++ // Patched debug break slot code is a FIXED_SEQUENCE:
++ // lis r0, <address hi>
++ // ori r0, r0, <address lo>
++ // mtlr r0
++ // blrl
++ static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
++
++ // Difference between address of current opcode and value read from pc
++ // register.
++ static const int kPcLoadDelta = 0; // Todo: remove
++
++#if V8_TARGET_ARCH_PPC64
++ static const int kPatchDebugBreakSlotReturnOffset = 7 * kInstrSize;
++#else
++ static const int kPatchDebugBreakSlotReturnOffset = 4 * kInstrSize;
++#endif
++
++ // This is the length of the BreakLocationIterator::SetDebugBreakAtReturn()
++ // code patch FIXED_SEQUENCE
++#if V8_TARGET_ARCH_PPC64
++ static const int kJSReturnSequenceInstructions = 8;
++#else
++ static const int kJSReturnSequenceInstructions = 5;
++#endif
++
++ // This is the length of the code sequence from SetDebugBreakAtSlot()
++ // FIXED_SEQUENCE
++#if V8_TARGET_ARCH_PPC64
++ static const int kDebugBreakSlotInstructions = 7;
++#else
++ static const int kDebugBreakSlotInstructions = 4;
++#endif
++ static const int kDebugBreakSlotLength =
++ kDebugBreakSlotInstructions * kInstrSize;
++
++ static inline int encode_crbit(const CRegister& cr, enum CRBit crbit) {
++ return ((cr.code() * CRWIDTH) + crbit);
++ }
++
++ // ---------------------------------------------------------------------------
++ // Code generation
++
++ // Insert the smallest number of nop instructions
++ // possible to align the pc offset to a multiple
++ // of m. m must be a power of 2 (>= 4).
++ void Align(int m);
++ // Aligns code to something that's optimal for a jump target for the platform.
++ void CodeTargetAlign();
++
++ // Branch instructions
++ void bclr(BOfield bo, LKBit lk);
++ void blr();
++ void bc(int branch_offset, BOfield bo, int condition_bit, LKBit lk = LeaveLK);
++ void b(int branch_offset, LKBit lk);
++
++ void bcctr(BOfield bo, LKBit lk);
++ void bcr();
++
++ // Convenience branch instructions using labels
++ void b(Label* L, LKBit lk = LeaveLK) {
++ b(branch_offset(L, false), lk);
++ }
++
++ void bc_short(Condition cond, Label* L, CRegister cr = cr7,
++ LKBit lk = LeaveLK) {
++ ASSERT(cond != al);
++ ASSERT(cr.code() >= 0 && cr.code() <= 7);
++
++ int b_offset = branch_offset(L, false);
++
++ switch (cond) {
++ case eq:
++ bc(b_offset, BT, encode_crbit(cr, CR_EQ), lk);
++ break;
++ case ne:
++ bc(b_offset, BF, encode_crbit(cr, CR_EQ), lk);
++ break;
++ case gt:
++ bc(b_offset, BT, encode_crbit(cr, CR_GT), lk);
++ break;
++ case le:
++ bc(b_offset, BF, encode_crbit(cr, CR_GT), lk);
++ break;
++ case lt:
++ bc(b_offset, BT, encode_crbit(cr, CR_LT), lk);
++ break;
++ case ge:
++ bc(b_offset, BF, encode_crbit(cr, CR_LT), lk);
++ break;
++ case unordered:
++ bc(b_offset, BT, encode_crbit(cr, CR_FU), lk);
++ break;
++ case ordered:
++ bc(b_offset, BF, encode_crbit(cr, CR_FU), lk);
++ break;
++ case overflow:
++ bc(b_offset, BT, encode_crbit(cr, CR_SO), lk);
++ break;
++ case nooverflow:
++ bc(b_offset, BF, encode_crbit(cr, CR_SO), lk);
++ break;
++ default:
++ UNIMPLEMENTED();
++ }
++ }
++
++ void b(Condition cond, Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ if (cond == al) {
++ b(L, lk);
++ return;
++ }
++
++ if ((L->is_bound() && is_near(L, cond)) ||
++ !is_trampoline_emitted()) {
++ bc_short(cond, L, cr, lk);
++ return;
++ }
++
++ Label skip;
++ Condition neg_cond = NegateCondition(cond);
++ bc_short(neg_cond, &skip, cr);
++ b(L, lk);
++ bind(&skip);
++ }
++
++ void bne(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(ne, L, cr, lk); }
++ void beq(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(eq, L, cr, lk); }
++ void blt(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(lt, L, cr, lk); }
++ void bge(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(ge, L, cr, lk); }
++ void ble(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(le, L, cr, lk); }
++ void bgt(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(gt, L, cr, lk); }
++ void bunordered(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(unordered, L, cr, lk); }
++ void bordered(Label* L, CRegister cr = cr7, LKBit lk = LeaveLK) {
++ b(ordered, L, cr, lk); }
++ void boverflow(Label* L, CRegister cr = cr0, LKBit lk = LeaveLK) {
++ b(overflow, L, cr, lk); }
++ void bnooverflow(Label* L, CRegister cr = cr0, LKBit lk = LeaveLK) {
++ b(nooverflow, L, cr, lk); }
++
++ // Decrement CTR; branch if CTR != 0
++ void bdnz(Label* L, LKBit lk = LeaveLK) {
++ bc(branch_offset(L, false), DCBNZ, 0, lk);
++ }
++
++ // Data-processing instructions
++
++ // PowerPC
++ void sub(Register dst, Register src1, Register src2,
++ OEBit s = LeaveOE, RCBit r = LeaveRC);
++
++ void subfic(Register dst, Register src, const Operand& imm);
++
++ void subfc(Register dst, Register src1, Register src2,
++ OEBit s = LeaveOE, RCBit r = LeaveRC);
++
++ void add(Register dst, Register src1, Register src2,
++ OEBit s = LeaveOE, RCBit r = LeaveRC);
++
++ void addc(Register dst, Register src1, Register src2,
++ OEBit o = LeaveOE, RCBit r = LeaveRC);
++
++ void addze(Register dst, Register src1, OEBit o, RCBit r);
++
++ void mullw(Register dst, Register src1, Register src2,
++ OEBit o = LeaveOE, RCBit r = LeaveRC);
++
++ void mulhw(Register dst, Register src1, Register src2,
++ OEBit o = LeaveOE, RCBit r = LeaveRC);
++
++ void divw(Register dst, Register src1, Register src2,
++ OEBit o = LeaveOE, RCBit r = LeaveRC);
++
++ void addi(Register dst, Register src, const Operand& imm);
++ void addis(Register dst, Register src, const Operand& imm);
++ void addic(Register dst, Register src, const Operand& imm);
++
++ void and_(Register dst, Register src1, Register src2, RCBit rc = LeaveRC);
++ void andc(Register dst, Register src1, Register src2, RCBit rc = LeaveRC);
++ void andi(Register ra, Register rs, const Operand& imm);
++ void andis(Register ra, Register rs, const Operand& imm);
++ void nor(Register dst, Register src1, Register src2, RCBit r = LeaveRC);
++ void notx(Register dst, Register src, RCBit r = LeaveRC);
++ void ori(Register dst, Register src, const Operand& imm);
++ void oris(Register dst, Register src, const Operand& imm);
++ void orx(Register dst, Register src1, Register src2, RCBit rc = LeaveRC);
++ void xori(Register dst, Register src, const Operand& imm);
++ void xoris(Register ra, Register rs, const Operand& imm);
++ void xor_(Register dst, Register src1, Register src2, RCBit rc = LeaveRC);
++ void cmpi(Register src1, const Operand& src2, CRegister cr = cr7);
++ void cmpli(Register src1, const Operand& src2, CRegister cr = cr7);
++ void li(Register dst, const Operand& src);
++ void lis(Register dst, const Operand& imm);
++ void mr(Register dst, Register src);
++
++ void lbz(Register dst, const MemOperand& src);
++ void lbzx(Register dst, const MemOperand& src);
++ void lbzux(Register dst, const MemOperand& src);
++ void lhz(Register dst, const MemOperand& src);
++ void lhzx(Register dst, const MemOperand& src);
++ void lhzux(Register dst, const MemOperand& src);
++ void lwz(Register dst, const MemOperand& src);
++ void lwzu(Register dst, const MemOperand& src);
++ void lwzx(Register dst, const MemOperand& src);
++ void lwzux(Register dst, const MemOperand& src);
++ void lwa(Register dst, const MemOperand& src);
++ void stb(Register dst, const MemOperand& src);
++ void stbx(Register dst, const MemOperand& src);
++ void stbux(Register dst, const MemOperand& src);
++ void sth(Register dst, const MemOperand& src);
++ void sthx(Register dst, const MemOperand& src);
++ void sthux(Register dst, const MemOperand& src);
++ void stw(Register dst, const MemOperand& src);
++ void stwu(Register dst, const MemOperand& src);
++ void stwx(Register rs, const MemOperand& src);
++ void stwux(Register rs, const MemOperand& src);
++
++ void extsb(Register rs, Register ra, RCBit r = LeaveRC);
++ void extsh(Register rs, Register ra, RCBit r = LeaveRC);
++
++ void neg(Register rt, Register ra, OEBit o = LeaveOE, RCBit c = LeaveRC);
++
++#if V8_TARGET_ARCH_PPC64
++ void ld(Register rd, const MemOperand &src);
++ void ldx(Register rd, const MemOperand &src);
++ void ldu(Register rd, const MemOperand &src);
++ void ldux(Register rd, const MemOperand &src);
++ void std(Register rs, const MemOperand &src);
++ void stdx(Register rs, const MemOperand &src);
++ void stdu(Register rs, const MemOperand &src);
++ void stdux(Register rs, const MemOperand &src);
++ void rldic(Register dst, Register src, int sh, int mb, RCBit r = LeaveRC);
++ void rldicl(Register dst, Register src, int sh, int mb, RCBit r = LeaveRC);
++ void rldicr(Register dst, Register src, int sh, int me, RCBit r = LeaveRC);
++ void rldimi(Register dst, Register src, int sh, int mb, RCBit r = LeaveRC);
++ void sldi(Register dst, Register src, const Operand& val, RCBit rc = LeaveRC);
++ void srdi(Register dst, Register src, const Operand& val, RCBit rc = LeaveRC);
++ void clrrdi(Register dst, Register src, const Operand& val,
++ RCBit rc = LeaveRC);
++ void clrldi(Register dst, Register src, const Operand& val,
++ RCBit rc = LeaveRC);
++ void sradi(Register ra, Register rs, int sh, RCBit r = LeaveRC);
++ void srd(Register dst, Register src1, Register src2, RCBit r = LeaveRC);
++ void sld(Register dst, Register src1, Register src2, RCBit r = LeaveRC);
++ void srad(Register dst, Register src1, Register src2, RCBit r = LeaveRC);
++ void cntlzd_(Register dst, Register src, RCBit rc = LeaveRC);
++ void extsw(Register rs, Register ra, RCBit r = LeaveRC);
++ void mulld(Register dst, Register src1, Register src2,
++ OEBit o = LeaveOE, RCBit r = LeaveRC);
++ void divd(Register dst, Register src1, Register src2,
++ OEBit o = LeaveOE, RCBit r = LeaveRC);
++#endif
++
++ void rlwinm(Register ra, Register rs, int sh, int mb, int me,
++ RCBit rc = LeaveRC);
++ void rlwimi(Register ra, Register rs, int sh, int mb, int me,
++ RCBit rc = LeaveRC);
++ void slwi(Register dst, Register src, const Operand& val, RCBit rc = LeaveRC);
++ void srwi(Register dst, Register src, const Operand& val, RCBit rc = LeaveRC);
++ void clrrwi(Register dst, Register src, const Operand& val,
++ RCBit rc = LeaveRC);
++ void clrlwi(Register dst, Register src, const Operand& val,
++ RCBit rc = LeaveRC);
++ void srawi(Register ra, Register rs, int sh, RCBit r = LeaveRC);
++ void srw(Register dst, Register src1, Register src2, RCBit r = LeaveRC);
++ void slw(Register dst, Register src1, Register src2, RCBit r = LeaveRC);
++ void sraw(Register dst, Register src1, Register src2, RCBit r = LeaveRC);
++
++ void cntlzw_(Register dst, Register src, RCBit rc = LeaveRC);
++ // end PowerPC
++
++ void subi(Register dst, Register src1, const Operand& src2);
++
++ void cmp(Register src1, Register src2, CRegister cr = cr7);
++ void cmpl(Register src1, Register src2, CRegister cr = cr7);
++
++ void mov(Register dst, const Operand& src);
++
++ // Multiply instructions
++
++ // PowerPC
++ void mul(Register dst, Register src1, Register src2,
++ OEBit s = LeaveOE, RCBit r = LeaveRC);
++
++ // Miscellaneous arithmetic instructions
++
++ // Special register access
++ // PowerPC
++ void crxor(int bt, int ba, int bb);
++ void mflr(Register dst);
++ void mtlr(Register src);
++ void mtctr(Register src);
++ void mtxer(Register src);
++ void mcrfs(int bf, int bfa);
++ void mfcr(Register dst);
++
++ void fake_asm(enum FAKE_OPCODE_T fopcode);
++ void marker_asm(int mcode);
++ void function_descriptor();
++ // end PowerPC
++
++ // Exception-generating instructions and debugging support
++ void stop(const char* msg,
++ Condition cond = al,
++ int32_t code = kDefaultStopCode,
++ CRegister cr = cr7);
++
++ void bkpt(uint32_t imm16); // v5 and above
++
++ // Informational messages when simulating
++ void info(const char* msg,
++ Condition cond = al,
++ int32_t code = kDefaultStopCode,
++ CRegister cr = cr7);
++
++ void dcbf(Register ra, Register rb);
++ void sync();
++ void icbi(Register ra, Register rb);
++ void isync();
++
++ // Support for floating point
++ void lfd(const DwVfpRegister frt, const MemOperand& src);
++ void lfdu(const DwVfpRegister frt, const MemOperand& src);
++ void lfdx(const DwVfpRegister frt, const MemOperand& src);
++ void lfdux(const DwVfpRegister frt, const MemOperand& src);
++ void lfs(const DwVfpRegister frt, const MemOperand& src);
++ void lfsu(const DwVfpRegister frt, const MemOperand& src);
++ void lfsx(const DwVfpRegister frt, const MemOperand& src);
++ void lfsux(const DwVfpRegister frt, const MemOperand& src);
++ void stfd(const DwVfpRegister frs, const MemOperand& src);
++ void stfdu(const DwVfpRegister frs, const MemOperand& src);
++ void stfdx(const DwVfpRegister frs, const MemOperand& src);
++ void stfdux(const DwVfpRegister frs, const MemOperand& src);
++ void stfs(const DwVfpRegister frs, const MemOperand& src);
++ void stfsu(const DwVfpRegister frs, const MemOperand& src);
++ void stfsx(const DwVfpRegister frs, const MemOperand& src);
++ void stfsux(const DwVfpRegister frs, const MemOperand& src);
++
++ void fadd(const DwVfpRegister frt, const DwVfpRegister fra,
++ const DwVfpRegister frb, RCBit rc = LeaveRC);
++ void fsub(const DwVfpRegister frt, const DwVfpRegister fra,
++ const DwVfpRegister frb, RCBit rc = LeaveRC);
++ void fdiv(const DwVfpRegister frt, const DwVfpRegister fra,
++ const DwVfpRegister frb, RCBit rc = LeaveRC);
++ void fmul(const DwVfpRegister frt, const DwVfpRegister fra,
++ const DwVfpRegister frc, RCBit rc = LeaveRC);
++ void fcmpu(const DwVfpRegister fra, const DwVfpRegister frb,
++ CRegister cr = cr7);
++ void fmr(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void fctiwz(const DwVfpRegister frt, const DwVfpRegister frb);
++ void fctiw(const DwVfpRegister frt, const DwVfpRegister frb);
++ void frim(const DwVfpRegister frt, const DwVfpRegister frb);
++ void frsp(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void fcfid(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void fctid(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void fctidz(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void fsel(const DwVfpRegister frt, const DwVfpRegister fra,
++ const DwVfpRegister frc, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void fneg(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void mtfsfi(int bf, int immediate, RCBit rc = LeaveRC);
++ void mffs(const DwVfpRegister frt, RCBit rc = LeaveRC);
++ void mtfsf(const DwVfpRegister frb, bool L = 1, int FLM = 0, bool W = 0,
++ RCBit rc = LeaveRC);
++ void fsqrt(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++ void fabs(const DwVfpRegister frt, const DwVfpRegister frb,
++ RCBit rc = LeaveRC);
++
++ // Pseudo instructions
++
++ // Different nop operations are used by the code generator to detect certain
++ // states of the generated code.
++ enum NopMarkerTypes {
++ NON_MARKING_NOP = 0,
++ DEBUG_BREAK_NOP,
++ // IC markers.
++ PROPERTY_ACCESS_INLINED,
++ PROPERTY_ACCESS_INLINED_CONTEXT,
++ PROPERTY_ACCESS_INLINED_CONTEXT_DONT_DELETE,
++ // Helper values.
++ LAST_CODE_MARKER,
++ FIRST_IC_MARKER = PROPERTY_ACCESS_INLINED
++ };
++
++ void nop(int type = 0); // 0 is the default non-marking type.
++
++ void push(Register src) {
++#if V8_TARGET_ARCH_PPC64
++ stdu(src, MemOperand(sp, -8));
++#else
++ stwu(src, MemOperand(sp, -4));
++#endif
++ }
++
++ void pop(Register dst) {
++#if V8_TARGET_ARCH_PPC64
++ ld(dst, MemOperand(sp));
++ addi(sp, sp, Operand(8));
++#else
++ lwz(dst, MemOperand(sp));
++ addi(sp, sp, Operand(4));
++#endif
++ }
++
++ void pop() {
++ addi(sp, sp, Operand(kPointerSize));
++ }
++
++ // Jump unconditionally to given label.
++ void jmp(Label* L) { b(L); }
++
++ bool predictable_code_size() const { return predictable_code_size_; }
++
++ // Check the code size generated from label to here.
++ int SizeOfCodeGeneratedSince(Label* label) {
++ return pc_offset() - label->pos();
++ }
++
++ // Check the number of instructions generated from label to here.
++ int InstructionsGeneratedSince(Label* label) {
++ return SizeOfCodeGeneratedSince(label) / kInstrSize;
++ }
++
++ // Class for scoping postponing the trampoline pool generation.
++ class BlockTrampolinePoolScope {
++ public:
++ explicit BlockTrampolinePoolScope(Assembler* assem) : assem_(assem) {
++ assem_->StartBlockTrampolinePool();
++ }
++ ~BlockTrampolinePoolScope() {
++ assem_->EndBlockTrampolinePool();
++ }
++
++ private:
++ Assembler* assem_;
++
++ DISALLOW_IMPLICIT_CONSTRUCTORS(BlockTrampolinePoolScope);
++ };
++
++ // Debugging
++
++ // Mark address of the ExitJSFrame code.
++ void RecordJSReturn();
++
++ // Mark address of a debug break slot.
++ void RecordDebugBreakSlot();
++
++ // Record the AST id of the CallIC being compiled, so that it can be placed
++ // in the relocation information.
++ void SetRecordedAstId(TypeFeedbackId ast_id) {
++// PPC - this shouldn't be failing roohack ASSERT(recorded_ast_id_.IsNone());
++ recorded_ast_id_ = ast_id;
++ }
++
++ TypeFeedbackId RecordedAstId() {
++ // roohack - another issue??? ASSERT(!recorded_ast_id_.IsNone());
++ return recorded_ast_id_;
++ }
++
++ void ClearRecordedAstId() { recorded_ast_id_ = TypeFeedbackId::None(); }
++
++ // Record a comment relocation entry that can be used by a disassembler.
++ // Use --code-comments to enable.
++ void RecordComment(const char* msg);
++
++ // Writes a single byte or word of data in the code stream. Used
++ // for inline tables, e.g., jump-tables.
++ void db(uint8_t data);
++ void dd(uint32_t data);
++
++ int pc_offset() const { return pc_ - buffer_; }
++
++ PositionsRecorder* positions_recorder() { return &positions_recorder_; }
++
++ // Read/patch instructions
++ Instr instr_at(int pos) { return *reinterpret_cast<Instr*>(buffer_ + pos); }
++ void instr_at_put(int pos, Instr instr) {
++ *reinterpret_cast<Instr*>(buffer_ + pos) = instr;
++ }
++ static Instr instr_at(byte* pc) { return *reinterpret_cast<Instr*>(pc); }
++ static void instr_at_put(byte* pc, Instr instr) {
++ *reinterpret_cast<Instr*>(pc) = instr;
++ }
++ static Condition GetCondition(Instr instr);
++
++ static bool IsLis(Instr instr);
++ static bool IsAddic(Instr instr);
++ static bool IsOri(Instr instr);
++
++ static bool IsBranch(Instr instr);
++ static Register GetRA(Instr instr);
++ static Register GetRB(Instr instr);
++#if V8_TARGET_ARCH_PPC64
++ static bool Is64BitLoadIntoR12(Instr instr1, Instr instr2,
++ Instr instr3, Instr instr4, Instr instr5);
++#else
++ static bool Is32BitLoadIntoR12(Instr instr1, Instr instr2);
++#endif
++
++ static bool IsCmpRegister(Instr instr);
++ static bool IsCmpImmediate(Instr instr);
++ static bool IsRlwinm(Instr instr);
++#if V8_TARGET_ARCH_PPC64
++ static bool IsRldicl(Instr instr);
++#endif
++ static Register GetCmpImmediateRegister(Instr instr);
++ static int GetCmpImmediateRawImmediate(Instr instr);
++ static bool IsNop(Instr instr, int type = NON_MARKING_NOP);
++
++ // Postpone the generation of the trampoline pool for the specified number of
++ // instructions.
++ void BlockTrampolinePoolFor(int instructions);
++ void CheckTrampolinePool();
++
++ protected:
++ // Relocation for a type-recording IC has the AST id added to it. This
++ // member variable is a way to pass the information from the call site to
++ // the relocation info.
++ TypeFeedbackId recorded_ast_id_;
++
++ bool emit_debug_code() const { return emit_debug_code_; }
++
++ int buffer_space() const { return reloc_info_writer.pos() - pc_; }
++
++ // Decode branch instruction at pos and return branch target pos
++ int target_at(int pos);
++
++ // Patch branch instruction at pos to branch to given branch target pos
++ void target_at_put(int pos, int target_pos);
++
++ // Record reloc info for current pc_
++ void RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data = 0);
++
++ // Block the emission of the trampoline pool before pc_offset.
++ void BlockTrampolinePoolBefore(int pc_offset) {
++ if (no_trampoline_pool_before_ < pc_offset)
++ no_trampoline_pool_before_ = pc_offset;
++ }
++
++ void StartBlockTrampolinePool() {
++ trampoline_pool_blocked_nesting_++;
++ }
++
++ void EndBlockTrampolinePool() {
++ trampoline_pool_blocked_nesting_--;
++ }
++
++ bool is_trampoline_pool_blocked() const {
++ return trampoline_pool_blocked_nesting_ > 0;
++ }
++
++ bool has_exception() const {
++ return internal_trampoline_exception_;
++ }
++
++ bool is_trampoline_emitted() const {
++ return trampoline_emitted_;
++ }
++
++
++ private:
++ // Code buffer:
++ // The buffer into which code and relocation info are generated.
++ byte* buffer_;
++ int buffer_size_;
++ // True if the assembler owns the buffer, false if buffer is external.
++ bool own_buffer_;
++
++ // Code generation
++ // The relocation writer's position is at least kGap bytes below the end of
++ // the generated instructions. This is so that multi-instruction sequences do
++ // not have to check for overflow. The same is true for writes of large
++ // relocation info entries.
++ static const int kGap = 32;
++ byte* pc_; // the program counter; moves forward
++
++ // Repeated checking whether the trampoline pool should be emitted is rather
++ // expensive. By default we only check again once a number of instructions
++ // has been generated.
++ int next_buffer_check_; // pc offset of next buffer check.
++
++ // Emission of the trampoline pool may be blocked in some code sequences.
++ int trampoline_pool_blocked_nesting_; // Block emission if this is not zero.
++ int no_trampoline_pool_before_; // Block emission before this pc offset.
++
++ // Relocation info generation
++ // Each relocation is encoded as a variable size value
++ static const int kMaxRelocSize = RelocInfoWriter::kMaxSize;
++ RelocInfoWriter reloc_info_writer;
++
++ // The bound position, before this we cannot do instruction elimination.
++ int last_bound_pos_;
++
++ // Code emission
++ inline void CheckBuffer();
++ void GrowBuffer();
++ inline void emit(Instr x);
++ inline void CheckTrampolinePoolQuick();
++
++ // Instruction generation
++ void a_form(Instr instr, DwVfpRegister frt, DwVfpRegister fra,
++ DwVfpRegister frb, RCBit r);
++ void d_form(Instr instr, Register rt, Register ra, const intptr_t val,
++ bool signed_disp);
++ void x_form(Instr instr, Register ra, Register rs, Register rb, RCBit r);
++ void xo_form(Instr instr, Register rt, Register ra, Register rb,
++ OEBit o, RCBit r);
++ void md_form(Instr instr, Register ra, Register rs, int shift, int maskbit,
++ RCBit r);
++
++ // Labels
++ void print(Label* L);
++ int max_reach_from(int pos);
++ void bind_to(Label* L, int pos);
++ void next(Label* L);
++
++ class Trampoline {
++ public:
++ Trampoline() {
++ next_slot_ = 0;
++ free_slot_count_ = 0;
++ }
++ Trampoline(int start, int slot_count) {
++ next_slot_ = start;
++ free_slot_count_ = slot_count;
++ }
++ int take_slot() {
++ int trampoline_slot = kInvalidSlotPos;
++ if (free_slot_count_ <= 0) {
++ // We have run out of space on trampolines.
++ // Make sure we fail in debug mode, so we become aware of each case
++ // when this happens.
++ ASSERT(0);
++ // Internal exception will be caught.
++ } else {
++ trampoline_slot = next_slot_;
++ free_slot_count_--;
++ next_slot_ += kTrampolineSlotsSize;
++ }
++ return trampoline_slot;
++ }
++
++ private:
++ int next_slot_;
++ int free_slot_count_;
++ };
++
++ int32_t get_trampoline_entry();
++ int unbound_labels_count_;
++ // If trampoline is emitted, generated code is becoming large. As
++ // this is already a slow case which can possibly break our code
++ // generation for the extreme case, we use this information to
++ // trigger different mode of branch instruction generation, where we
++ // no longer use a single branch instruction.
++ bool trampoline_emitted_;
++ static const int kTrampolineSlotsSize = kInstrSize;
++ static const int kMaxCondBranchReach = (1 << (16 - 1)) - 1;
++ static const int kMaxBlockTrampolineSectionSize = 64 * kInstrSize;
++ static const int kInvalidSlotPos = -1;
++
++ Trampoline trampoline_;
++ bool internal_trampoline_exception_;
++
++ friend class RegExpMacroAssemblerPPC;
++ friend class RelocInfo;
++ friend class CodePatcher;
++ friend class BlockTrampolinePoolScope;
++
++ PositionsRecorder positions_recorder_;
++
++ bool emit_debug_code_;
++ bool predictable_code_size_;
++
++ friend class PositionsRecorder;
++ friend class EnsureSpace;
++};
++
++
++class EnsureSpace BASE_EMBEDDED {
++ public:
++ explicit EnsureSpace(Assembler* assembler) {
++ assembler->CheckBuffer();
++ }
++};
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_ASSEMBLER_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/assembler-ppc-inl.h.ppc
v8-3.14.5.10/src/ppc/assembler-ppc-inl.h
+--- v8-3.14.5.10/src/ppc/assembler-ppc-inl.h.ppc 2016-06-07 14:15:45.984393044 -0400
++++ v8-3.14.5.10/src/ppc/assembler-ppc-inl.h 2016-06-07 14:15:45.984393044 -0400
+@@ -0,0 +1,457 @@
++// Copyright (c) 1994-2006 Sun Microsystems Inc.
++// All Rights Reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions
++// are met:
++//
++// - Redistributions of source code must retain the above copyright notice,
++// this list of conditions and the following disclaimer.
++//
++// - Redistribution in binary form must reproduce the above copyright
++// notice, this list of conditions and the following disclaimer in the
++// documentation and/or other materials provided with the
++// distribution.
++//
++// - Neither the name of Sun Microsystems or the names of contributors may
++// be used to endorse or promote products derived from this software without
++// specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
++// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
++// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
++// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
++// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
++// OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// The original source code covered by the above license above has been modified
++// significantly by Google Inc.
++// Copyright 2012 the V8 project authors. All rights reserved.
++
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++
++#ifndef V8_PPC_ASSEMBLER_PPC_INL_H_
++#define V8_PPC_ASSEMBLER_PPC_INL_H_
++
++#include "ppc/assembler-ppc.h"
++
++#include "cpu.h"
++#include "debug.h"
++
++
++namespace v8 {
++namespace internal {
++
++
++int DwVfpRegister::ToAllocationIndex(DwVfpRegister reg) {
++ int index = reg.code() - 1; // d0 is skipped
++ ASSERT(index < kNumAllocatableRegisters);
++ ASSERT(!reg.is(kDoubleRegZero));
++ ASSERT(!reg.is(kScratchDoubleReg));
++ return index;
++}
++
++void RelocInfo::apply(intptr_t delta) {
++ if (RelocInfo::IsInternalReference(rmode_)) {
++ // absolute code pointer inside code object moves with the code object.
++ intptr_t* p = reinterpret_cast<intptr_t*>(pc_);
++ *p += delta; // relocate entry
++ CPU::FlushICache(p, sizeof(uintptr_t));
++ }
++ // We do not use pc relative addressing on PPC, so there is
++ // nothing else to do.
++}
++
++
++Address RelocInfo::target_address() {
++ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
++ return Assembler::target_address_at(pc_);
++}
++
++
++Address RelocInfo::target_address_address() {
++ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY
++ || rmode_ == EMBEDDED_OBJECT
++ || rmode_ == EXTERNAL_REFERENCE);
++
++ // Read the address of the word containing the target_address in an
++ // instruction stream.
++ // The only architecture-independent user of this function is the serializer.
++ // The serializer uses it to find out how many raw bytes of instruction to
++ // output before the next target.
++ // For an instruction like LIS/ADDIC where the target bits are mixed into the
++ // instruction bits, the size of the target will be zero, indicating that the
++ // serializer should not step forward in memory after a target is resolved
++ // and written. In this case the target_address_address function should
++ // return the end of the instructions to be patched, allowing the
++ // deserializer to deserialize the instructions as raw bytes and put them in
++ // place, ready to be patched with the target.
++
++ return reinterpret_cast<Address>(
++ pc_ + (Assembler::kInstructionsForPtrConstant *
++ Assembler::kInstrSize));
++}
++
++
++int RelocInfo::target_address_size() {
++ return Assembler::kSpecialTargetSize;
++}
++
++
++void RelocInfo::set_target_address(Address target, WriteBarrierMode mode) {
++ ASSERT(IsCodeTarget(rmode_) || rmode_ == RUNTIME_ENTRY);
++ Assembler::set_target_address_at(pc_, target);
++ if (mode == UPDATE_WRITE_BARRIER && host() != NULL &&
IsCodeTarget(rmode_)) {
++ Object* target_code = Code::GetCodeFromTargetAddress(target);
++ host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
++ host(), this, HeapObject::cast(target_code));
++ }
++}
++
++
++Address Assembler::target_address_from_return_address(Address pc) {
++ return pc - kCallTargetAddressOffset;
++}
++
++
++Object* RelocInfo::target_object() {
++ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
++ return reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
++}
++
++
++Handle<Object> RelocInfo::target_object_handle(Assembler* origin) {
++ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
++ return Handle<Object>(reinterpret_cast<Object**>(
++ Assembler::target_address_at(pc_)));
++}
++
++
++Object** RelocInfo::target_object_address() {
++ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
++ reconstructed_obj_ptr_ =
++ reinterpret_cast<Object*>(Assembler::target_address_at(pc_));
++ return &reconstructed_obj_ptr_;
++}
++
++
++void RelocInfo::set_target_object(Object* target, WriteBarrierMode mode) {
++ ASSERT(IsCodeTarget(rmode_) || rmode_ == EMBEDDED_OBJECT);
++ Assembler::set_target_address_at(pc_, reinterpret_cast<Address>(target));
++ if (mode == UPDATE_WRITE_BARRIER &&
++ host() != NULL &&
++ target->IsHeapObject()) {
++ host()->GetHeap()->incremental_marking()->RecordWrite(
++ host(), &Memory::Object_at(pc_), HeapObject::cast(target));
++ }
++}
++
++
++Address* RelocInfo::target_reference_address() {
++ ASSERT(rmode_ == EXTERNAL_REFERENCE);
++ reconstructed_adr_ptr_ = Assembler::target_address_at(pc_);
++ return &reconstructed_adr_ptr_;
++}
++
++
++Handle<JSGlobalPropertyCell> RelocInfo::target_cell_handle() {
++ ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL);
++ Address address = Memory::Address_at(pc_);
++ return Handle<JSGlobalPropertyCell>(
++ reinterpret_cast<JSGlobalPropertyCell**>(address));
++}
++
++
++JSGlobalPropertyCell* RelocInfo::target_cell() {
++ ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL);
++ return JSGlobalPropertyCell::FromValueAddress(Memory::Address_at(pc_));
++}
++
++
++void RelocInfo::set_target_cell(JSGlobalPropertyCell* cell,
++ WriteBarrierMode mode) {
++ ASSERT(rmode_ == RelocInfo::GLOBAL_PROPERTY_CELL);
++ Address address = cell->address() + JSGlobalPropertyCell::kValueOffset;
++ Memory::Address_at(pc_) = address;
++ if (mode == UPDATE_WRITE_BARRIER && host() != NULL) {
++ // TODO(1550) We are passing NULL as a slot because cell can never be on
++ // evacuation candidate.
++ host()->GetHeap()->incremental_marking()->RecordWrite(
++ host(), NULL, cell);
++ }
++}
++
++
++Address RelocInfo::call_address() {
++ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
++ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
++ // The pc_ offset of 0 assumes patched return sequence per
++ // BreakLocationIterator::SetDebugBreakAtReturn(), or debug break
++ // slot per BreakLocationIterator::SetDebugBreakAtSlot().
++ return Assembler::target_address_at(pc_);
++}
++
++
++void RelocInfo::set_call_address(Address target) {
++ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
++ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
++ Assembler::set_target_address_at(pc_, target);
++ if (host() != NULL) {
++ Object* target_code = Code::GetCodeFromTargetAddress(target);
++ host()->GetHeap()->incremental_marking()->RecordWriteIntoCode(
++ host(), this, HeapObject::cast(target_code));
++ }
++}
++
++
++Object* RelocInfo::call_object() {
++ return *call_object_address();
++}
++
++
++void RelocInfo::set_call_object(Object* target) {
++ *call_object_address() = target;
++}
++
++
++Object** RelocInfo::call_object_address() {
++ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
++ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
++ return reinterpret_cast<Object**>(pc_ + 2 * Assembler::kInstrSize);
++}
++
++
++bool RelocInfo::IsPatchedReturnSequence() {
++ //
++ // The patched return sequence is defined by
++ // BreakLocationIterator::SetDebugBreakAtReturn()
++ // FIXED_SEQUENCE
++
++ Instr instr0 = Assembler::instr_at(pc_);
++ Instr instr1 = Assembler::instr_at(pc_ + 1 * Assembler::kInstrSize);
++#if V8_TARGET_ARCH_PPC64
++ Instr instr3 = Assembler::instr_at(pc_ + (3 * Assembler::kInstrSize));
++ Instr instr4 = Assembler::instr_at(pc_ + (4 * Assembler::kInstrSize));
++ Instr binstr = Assembler::instr_at(pc_ + (7 * Assembler::kInstrSize));
++#else
++ Instr binstr = Assembler::instr_at(pc_ + 4 * Assembler::kInstrSize);
++#endif
++ bool patched_return = ((instr0 & kOpcodeMask) == ADDIS &&
++ (instr1 & kOpcodeMask) == ORI &&
++#if V8_TARGET_ARCH_PPC64
++ (instr3 & kOpcodeMask) == ORIS &&
++ (instr4 & kOpcodeMask) == ORI &&
++#endif
++ (binstr == 0x7d821008)); // twge r2, r2
++
++// printf("IsPatchedReturnSequence: %d\n", patched_return);
++ return patched_return;
++}
++
++
++bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
++ Instr current_instr = Assembler::instr_at(pc_);
++ return !Assembler::IsNop(current_instr, Assembler::DEBUG_BREAK_NOP);
++}
++
++
++void RelocInfo::Visit(ObjectVisitor* visitor) {
++ RelocInfo::Mode mode = rmode();
++ if (mode == RelocInfo::EMBEDDED_OBJECT) {
++ visitor->VisitEmbeddedPointer(this);
++ } else if (RelocInfo::IsCodeTarget(mode)) {
++ visitor->VisitCodeTarget(this);
++ } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
++ visitor->VisitGlobalPropertyCell(this);
++ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
++ visitor->VisitExternalReference(this);
++#ifdef ENABLE_DEBUGGER_SUPPORT
++ // TODO(isolates): Get a cached isolate below.
++ } else if (((RelocInfo::IsJSReturn(mode) &&
++ IsPatchedReturnSequence()) ||
++ (RelocInfo::IsDebugBreakSlot(mode) &&
++ IsPatchedDebugBreakSlotSequence())) &&
++ Isolate::Current()->debug()->has_break_points()) {
++ visitor->VisitDebugTarget(this);
++#endif
++ } else if (mode == RelocInfo::RUNTIME_ENTRY) {
++ visitor->VisitRuntimeEntry(this);
++ }
++}
++
++
++template<typename StaticVisitor>
++void RelocInfo::Visit(Heap* heap) {
++ RelocInfo::Mode mode = rmode();
++ if (mode == RelocInfo::EMBEDDED_OBJECT) {
++ StaticVisitor::VisitEmbeddedPointer(heap, this);
++ } else if (RelocInfo::IsCodeTarget(mode)) {
++ StaticVisitor::VisitCodeTarget(heap, this);
++ } else if (mode == RelocInfo::GLOBAL_PROPERTY_CELL) {
++ StaticVisitor::VisitGlobalPropertyCell(heap, this);
++ } else if (mode == RelocInfo::EXTERNAL_REFERENCE) {
++ StaticVisitor::VisitExternalReference(this);
++#ifdef ENABLE_DEBUGGER_SUPPORT
++ } else if (heap->isolate()->debug()->has_break_points() &&
++ ((RelocInfo::IsJSReturn(mode) &&
++ IsPatchedReturnSequence()) ||
++ (RelocInfo::IsDebugBreakSlot(mode) &&
++ IsPatchedDebugBreakSlotSequence()))) {
++ StaticVisitor::VisitDebugTarget(heap, this);
++#endif
++ } else if (mode == RelocInfo::RUNTIME_ENTRY) {
++ StaticVisitor::VisitRuntimeEntry(this);
++ }
++}
++
++Operand::Operand(intptr_t immediate, RelocInfo::Mode rmode) {
++ rm_ = no_reg;
++ imm_ = immediate;
++ rmode_ = rmode;
++}
++
++Operand::Operand(const ExternalReference& f) {
++ rm_ = no_reg;
++ imm_ = reinterpret_cast<intptr_t>(f.address());
++ rmode_ = RelocInfo::EXTERNAL_REFERENCE;
++}
++
++Operand::Operand(Smi* value) {
++ rm_ = no_reg;
++ imm_ = reinterpret_cast<intptr_t>(value);
++ rmode_ = RelocInfo::NONE;
++}
++
++Operand::Operand(Register rm) {
++ rm_ = rm;
++ rmode_ = RelocInfo::NONE; // PPC -why doesn't ARM do this?
++}
++
++void Assembler::CheckBuffer() {
++ if (buffer_space() <= kGap) {
++ GrowBuffer();
++ }
++}
++
++void Assembler::CheckTrampolinePoolQuick() {
++ if (pc_offset() >= next_buffer_check_) {
++ CheckTrampolinePool();
++ }
++}
++
++void Assembler::emit(Instr x) {
++ CheckBuffer();
++ *reinterpret_cast<Instr*>(pc_) = x;
++ pc_ += kInstrSize;
++ CheckTrampolinePoolQuick();
++}
++
++bool Operand::is_reg() const {
++ return rm_.is_valid();
++}
++
++
++// Fetch the 32bit value from the FIXED_SEQUENCE lis/ori
++Address Assembler::target_address_at(Address pc) {
++ Instr instr1 = instr_at(pc);
++ Instr instr2 = instr_at(pc + kInstrSize);
++#if V8_TARGET_ARCH_PPC64
++ Instr instr4 = instr_at(pc + (3*kInstrSize));
++ Instr instr5 = instr_at(pc + (4*kInstrSize));
++#endif
++ // Interpret 2 instructions generated by lis/ori
++ if (IsLis(instr1) && IsOri(instr2)) {
++#if V8_TARGET_ARCH_PPC64
++ // Assemble the 64 bit value.
++ uint64_t hi = (static_cast<uint32_t>((instr1 & kImm16Mask) << 16) |
++ static_cast<uint32_t>(instr2 & kImm16Mask));
++ uint64_t lo = (static_cast<uint32_t>((instr4 & kImm16Mask) << 16) |
++ static_cast<uint32_t>(instr5 & kImm16Mask));
++ return reinterpret_cast<Address>((hi << 32) | lo);
++#else
++ // Assemble the 32 bit value.
++ return reinterpret_cast<Address>(
++ ((instr1 & kImm16Mask) << 16) | (instr2 & kImm16Mask));
++#endif
++ }
++
++ PPCPORT_UNIMPLEMENTED();
++ return (Address)0;
++}
++
++
++// This sets the branch destination (which gets loaded at the call address).
++// This is for calls and branches within generated code. The serializer
++// has already deserialized the lis/ori instructions etc.
++// There is a FIXED_SEQUENCE assumption here
++void Assembler::deserialization_set_special_target_at(
++ Address instruction_payload, Address target) {
++ set_target_address_at(
++ instruction_payload - kInstructionsForPtrConstant * kInstrSize,
++ target);
++}
++
++// This code assumes the FIXED_SEQUENCE of lis/ori
++void Assembler::set_target_address_at(Address pc, Address target) {
++ Instr instr1 = instr_at(pc);
++ Instr instr2 = instr_at(pc + kInstrSize);
++ // Interpret 2 instructions generated by lis/ori
++ if (IsLis(instr1) && IsOri(instr2)) {
++#if V8_TARGET_ARCH_PPC64
++ Instr instr4 = instr_at(pc + (3*kInstrSize));
++ Instr instr5 = instr_at(pc + (4*kInstrSize));
++ // Needs to be fixed up when mov changes to handle 64-bit values.
++ uint32_t* p = reinterpret_cast<uint32_t*>(pc);
++ uintptr_t itarget = reinterpret_cast<uintptr_t>(target);
++
++ instr5 &= ~kImm16Mask;
++ instr5 |= itarget & kImm16Mask;
++ itarget = itarget >> 16;
++
++ instr4 &= ~kImm16Mask;
++ instr4 |= itarget & kImm16Mask;
++ itarget = itarget >> 16;
++
++ instr2 &= ~kImm16Mask;
++ instr2 |= itarget & kImm16Mask;
++ itarget = itarget >> 16;
++
++ instr1 &= ~kImm16Mask;
++ instr1 |= itarget & kImm16Mask;
++ itarget = itarget >> 16;
++
++ *p = instr1;
++ *(p+1) = instr2;
++ *(p+3) = instr4;
++ *(p+4) = instr5;
++ CPU::FlushICache(p, 20);
++#else
++ uint32_t* p = reinterpret_cast<uint32_t*>(pc);
++ uint32_t itarget = reinterpret_cast<uint32_t>(target);
++ int lo_word = itarget & kImm16Mask;
++ int hi_word = itarget >> 16;
++ instr1 &= ~kImm16Mask;
++ instr1 |= hi_word;
++ instr2 &= ~kImm16Mask;
++ instr2 |= lo_word;
++
++ *p = instr1;
++ *(p+1) = instr2;
++ CPU::FlushICache(p, 8);
++#endif
++ } else {
++ UNREACHABLE();
++ }
++}
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_ASSEMBLER_PPC_INL_H_
+diff -up v8-3.14.5.10/src/ppc/builtins-ppc.cc.ppc v8-3.14.5.10/src/ppc/builtins-ppc.cc
+--- v8-3.14.5.10/src/ppc/builtins-ppc.cc.ppc 2016-06-07 14:15:45.986393032 -0400
++++ v8-3.14.5.10/src/ppc/builtins-ppc.cc 2016-06-07 14:15:45.986393032 -0400
+@@ -0,0 +1,1910 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "codegen.h"
++#include "debug.h"
++#include "deoptimizer.h"
++#include "full-codegen.h"
++#include "runtime.h"
++
++namespace v8 {
++namespace internal {
++
++
++#define __ ACCESS_MASM(masm)
++
++void Builtins::Generate_Adaptor(MacroAssembler* masm,
++ CFunctionId id,
++ BuiltinExtraArguments extra_args) {
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments excluding receiver
++ // -- r4 : called function (only guaranteed when
++ // extra_args requires it)
++ // -- cp : context
++ // -- sp[0] : last argument
++ // -- ...
++ // -- sp[4 * (argc - 1)] : first argument (argc == r0)
++ // -- sp[4 * argc] : receiver
++ // -----------------------------------
++
++ // Insert extra arguments.
++ int num_extra_args = 0;
++ if (extra_args == NEEDS_CALLED_FUNCTION) {
++ num_extra_args = 1;
++ __ push(r4);
++ } else {
++ ASSERT(extra_args == NO_EXTRA_ARGUMENTS);
++ }
++
++ // JumpToExternalReference expects r0 to contain the number of arguments
++ // including the receiver and the extra arguments.
++ __ addi(r3, r3, Operand(num_extra_args + 1));
++ __ JumpToExternalReference(ExternalReference(id, masm->isolate()));
++}
++
++
++// Load the built-in InternalArray function from the current context.
++static void GenerateLoadInternalArrayFunction(MacroAssembler* masm,
++ Register result) {
++ // Load the native context.
++
++ __ LoadP(result,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ LoadP(result,
++ FieldMemOperand(result, GlobalObject::kNativeContextOffset));
++ // Load the InternalArray function from the native context.
++ __ LoadP(result,
++ MemOperand(result,
++ Context::SlotOffset(
++ Context::INTERNAL_ARRAY_FUNCTION_INDEX)));
++}
++
++
++// Load the built-in Array function from the current context.
++static void GenerateLoadArrayFunction(MacroAssembler* masm, Register result) {
++ // Load the native context.
++
++ __ LoadP(result,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ LoadP(result,
++ FieldMemOperand(result, GlobalObject::kNativeContextOffset));
++ // Load the Array function from the native context.
++ __ LoadP(result,
++ MemOperand(result,
++ Context::SlotOffset(Context::ARRAY_FUNCTION_INDEX)));
++}
++
++
++// Allocate an empty JSArray. The allocated array is put into the result
++// register. An elements backing store is allocated with size initial_capacity
++// and filled with the hole values.
++static void AllocateEmptyJSArray(MacroAssembler* masm,
++ Register array_function,
++ Register result,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* gc_required) {
++ const int initial_capacity = JSArray::kPreallocatedArrayElements;
++ STATIC_ASSERT(initial_capacity >= 0);
++ __ LoadInitialArrayMap(array_function, scratch2, scratch1, false);
++
++ // Allocate the JSArray object together with space for a fixed array with the
++ // requested elements.
++ int size = JSArray::kSize;
++ if (initial_capacity > 0) {
++ size += FixedArray::SizeFor(initial_capacity);
++ }
++ __ AllocateInNewSpace(size,
++ result,
++ scratch2,
++ scratch3,
++ gc_required,
++ TAG_OBJECT);
++
++ // Future optimization: defer tagging the result pointer for more
++ // efficient 64-bit memory accesses (due to alignment requirements
++ // on the memoperand offset).
++
++ // Allocated the JSArray. Now initialize the fields except for the elements
++ // array.
++ // result: JSObject
++ // scratch1: initial map
++ // scratch2: start of next object
++ __ StoreP(scratch1, FieldMemOperand(result, JSObject::kMapOffset), r0);
++ __ LoadRoot(scratch1, Heap::kEmptyFixedArrayRootIndex);
++ __ StoreP(scratch1, FieldMemOperand(result, JSArray::kPropertiesOffset), r0);
++ // Field JSArray::kElementsOffset is initialized later.
++ __ li(scratch3, Operand(0, RelocInfo::NONE));
++ __ StoreP(scratch3, FieldMemOperand(result, JSArray::kLengthOffset), r0);
++
++ if (initial_capacity == 0) {
++ __ StoreP(scratch1, FieldMemOperand(result, JSArray::kElementsOffset), r0);
++ return;
++ }
++
++ // Calculate the location of the elements array and set elements array member
++ // of the JSArray.
++ // result: JSObject
++ // scratch2: start of next object
++ __ addi(scratch1, result, Operand(JSArray::kSize));
++ __ StoreP(scratch1, FieldMemOperand(result, JSArray::kElementsOffset), r0);
++
++ // Clear the heap tag on the elements array.
++ __ subi(scratch1, scratch1, Operand(kHeapObjectTag));
++
++ // Initialize the FixedArray and fill it with holes. FixedArray length is
++ // stored as a smi.
++ // result: JSObject
++ // scratch1: elements array (untagged)
++ // scratch2: start of next object
++ __ LoadRoot(scratch3, Heap::kFixedArrayMapRootIndex);
++ STATIC_ASSERT(0 * kPointerSize == FixedArray::kMapOffset);
++ __ StoreP(scratch3, MemOperand(scratch1));
++ __ addi(scratch1, scratch1, Operand(kPointerSize));
++ __ LoadSmiLiteral(scratch3, Smi::FromInt(initial_capacity));
++ STATIC_ASSERT(1 * kPointerSize == FixedArray::kLengthOffset);
++ __ StoreP(scratch3, MemOperand(scratch1));
++ __ addi(scratch1, scratch1, Operand(kPointerSize));
++
++ // Fill the FixedArray with the hole value. Inline the code if short.
++ STATIC_ASSERT(2 * kPointerSize == FixedArray::kHeaderSize);
++ __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex);
++ static const int kLoopUnfoldLimit = 4;
++ if (initial_capacity <= kLoopUnfoldLimit) {
++ for (int i = 0; i < initial_capacity; i++) {
++ __ StoreP(scratch3, MemOperand(scratch1));
++ __ addi(scratch1, scratch1, Operand(kPointerSize));
++ }
++ } else {
++ Label loop, entry;
++ __ addi(scratch2, scratch1, Operand(initial_capacity * kPointerSize));
++ __ b(&entry);
++ __ bind(&loop);
++ __ StoreP(scratch3, MemOperand(scratch1));
++ __ addi(scratch1, scratch1, Operand(kPointerSize));
++ __ bind(&entry);
++ __ cmp(scratch1, scratch2);
++ __ blt(&loop);
++ }
++}
++
++// Allocate a JSArray with the number of elements stored in a register. The
++// register array_function holds the built-in Array function and the register
++// array_size holds the size of the array as a smi. The allocated array is put
++// into the result register and beginning and end of the FixedArray elements
++// storage is put into registers elements_array_storage and elements_array_end
++// (see below for when that is not the case). If the parameter fill_with_holes
++// is true the allocated elements backing store is filled with the hole values
++// otherwise it is left uninitialized. When the backing store is filled the
++// register elements_array_storage is scratched.
++static void AllocateJSArray(MacroAssembler* masm,
++ Register array_function, // Array function.
++ Register array_size, // As a smi, cannot be 0.
++ Register result,
++ Register elements_array_storage,
++ Register elements_array_end,
++ Register scratch1,
++ Register scratch2,
++ bool fill_with_hole,
++ Label* gc_required) {
++ // Load the initial map from the array function.
++ __ LoadInitialArrayMap(array_function, scratch2,
++ elements_array_storage, fill_with_hole);
++
++ if (FLAG_debug_code) { // Assert that array size is not zero.
++ __ cmpi(array_size, Operand::Zero());
++ __ Assert(ne, "array size is unexpectedly 0");
++ }
++
++ // Allocate the JSArray object together with space for a FixedArray with the
++ // requested number of elements. We omit the TAG_OBJECT flag and defer
++ // tagging the pointer until the end so that we can more efficiently perform
++ // aligned memory accesses.
++ __ li(elements_array_end,
++ Operand((JSArray::kSize + FixedArray::kHeaderSize) / kPointerSize));
++ __ SmiUntag(scratch1, array_size);
++ __ add(elements_array_end, elements_array_end, scratch1);
++ __ AllocateInNewSpace(
++ elements_array_end,
++ result,
++ scratch1,
++ scratch2,
++ gc_required,
++ static_cast<AllocationFlags>(SIZE_IN_WORDS));
++
++ // Allocated the JSArray. Now initialize the fields except for the elements
++ // array.
++ // result: JSObject (untagged)
++ // elements_array_storage: initial map
++ // array_size: size of array (smi)
++ __ StoreP(elements_array_storage, MemOperand(result, JSObject::kMapOffset));
++ __ LoadRoot(elements_array_storage, Heap::kEmptyFixedArrayRootIndex);
++ __ StoreP(elements_array_storage,
++ MemOperand(result, JSArray::kPropertiesOffset));
++ // Field JSArray::kElementsOffset is initialized later.
++ __ StoreP(array_size, MemOperand(result, JSArray::kLengthOffset));
++
++ // Calculate the location of the elements array and set elements array member
++ // of the JSArray.
++ // result: JSObject (untagged)
++ // array_size: size of array (smi)
++ __ addi(elements_array_storage, result,
++ Operand(JSArray::kSize + kHeapObjectTag));
++ __ StoreP(elements_array_storage,
++ MemOperand(result, JSArray::kElementsOffset));
++
++ // Clear the heap tag on the elements array.
++ STATIC_ASSERT(kSmiTag == 0);
++ __ subi(elements_array_storage,
++ elements_array_storage,
++ Operand(kHeapObjectTag));
++ // Initialize the fixed array and fill it with holes. FixedArray length is
++ // stored as a smi.
++ // result: JSObject (untagged)
++ // elements_array_storage: elements array (untagged)
++ // array_size: size of array (smi)
++ __ LoadRoot(scratch1, Heap::kFixedArrayMapRootIndex);
++ ASSERT_EQ(0 * kPointerSize, FixedArray::kMapOffset);
++ __ StoreP(scratch1, MemOperand(elements_array_storage));
++ __ addi(elements_array_storage, elements_array_storage,
++ Operand(kPointerSize));
++ STATIC_ASSERT(kSmiTag == 0);
++ ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset);
++ __ StoreP(array_size, MemOperand(elements_array_storage));
++ __ addi(elements_array_storage, elements_array_storage,
++ Operand(kPointerSize));
++
++ // Calculate elements array and elements array end.
++ // result: JSObject (untagged)
++ // elements_array_storage: elements array element storage
++ // array_size: smi-tagged size of elements array
++ __ SmiToPtrArrayOffset(scratch1, array_size);
++ __ add(elements_array_end, elements_array_storage, scratch1);
++
++ // Fill the allocated FixedArray with the hole value if requested.
++ // result: JSObject (untagged)
++ // elements_array_storage: elements array element storage
++ // elements_array_end: start of next object
++ if (fill_with_hole) {
++ Label loop, entry;
++ __ LoadRoot(scratch1, Heap::kTheHoleValueRootIndex);
++ __ b(&entry);
++ __ bind(&loop);
++ __ StoreP(scratch1, MemOperand(elements_array_storage));
++ __ addi(elements_array_storage, elements_array_storage,
++ Operand(kPointerSize));
++ __ bind(&entry);
++ __ cmp(elements_array_storage, elements_array_end);
++ __ blt(&loop);
++ }
++
++ // Tag object
++ __ addi(result, result, Operand(kHeapObjectTag));
++}
++
++// Create a new array for the built-in Array function. This function allocates
++// the JSArray object and the FixedArray elements array and initializes these.
++// If the Array cannot be constructed in native code the runtime is called. This
++// function assumes the following state:
++// r3: argc
++// r4: constructor (built-in Array function)
++// lr: return address
++// sp[0]: last argument
++// This function is used for both construct and normal calls of Array. The only
++// difference between handling a construct call and a normal call is that for a
++// construct call the constructor function in r1 needs to be preserved for
++// entering the generic code. In both cases argc in r0 needs to be preserved.
++// Both registers are preserved by this code so no need to differentiate between
++// construct call and normal call.
++static void ArrayNativeCode(MacroAssembler* masm,
++ Label* call_generic_code) {
++ Counters* counters = masm->isolate()->counters();
++ Label argc_one_or_more, argc_two_or_more, not_empty_array, empty_array,
++ has_non_smi_element, finish, cant_transition_map, not_double;
++
++ // Check for array construction with zero arguments or one.
++ __ cmpi(r3, Operand(0, RelocInfo::NONE));
++ __ bne(&argc_one_or_more);
++
++ // Handle construction of an empty array.
++ __ bind(&empty_array);
++ AllocateEmptyJSArray(masm,
++ r4,
++ r5,
++ r6,
++ r7,
++ r8,
++ call_generic_code);
++ __ IncrementCounter(counters->array_function_native(), 1, r6, r7);
++ // Set up return value, remove receiver from stack and return.
++ __ mr(r3, r5);
++ __ addi(sp, sp, Operand(kPointerSize));
++ __ blr();
++
++ // Check for one argument. Bail out if argument is not smi or if it is
++ // negative.
++ __ bind(&argc_one_or_more);
++ __ cmpi(r3, Operand(1));
++ __ bne(&argc_two_or_more);
++ STATIC_ASSERT(kSmiTag == 0);
++ __ LoadP(r5, MemOperand(sp)); // Get the argument from the stack.
++ __ cmpi(r5, Operand::Zero());
++ __ bne(¬_empty_array);
++ __ Drop(1); // Adjust stack.
++ __ li(r3, Operand::Zero()); // Treat this as a call with argc of zero.
++ __ b(&empty_array);
++
++ __ bind(¬_empty_array);
++ __ TestIfPositiveSmi(r5, r6);
++ __ bne(call_generic_code, cr0);
++
++ // Handle construction of an empty array of a certain size. Bail out if size
++ // is too large to actually allocate an elements array.
++ STATIC_ASSERT(kSmiTag == 0);
++ __ CmpSmiLiteral(r5, Smi::FromInt(JSObject::kInitialMaxFastElementArray), r0);
++ __ bge(call_generic_code);
++
++ // r3: argc
++ // r4: constructor
++ // r5: array_size (smi)
++ // sp[0]: argument
++ AllocateJSArray(masm,
++ r4,
++ r5,
++ r6,
++ r7,
++ r8,
++ r9,
++ r10,
++ true,
++ call_generic_code);
++ __ IncrementCounter(counters->array_function_native(), 1, r5, r7);
++ // Set up return value, remove receiver and argument from stack and return.
++ __ mr(r3, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ blr();
++
++ // Handle construction of an array from a list of arguments.
++ __ bind(&argc_two_or_more);
++ // Convet argc to a smi.
++ __ SmiTag(r5, r3);
++
++ // r3: argc
++ // r4: constructor
++ // r5: array_size (smi)
++ // sp[0]: last argument
++ AllocateJSArray(masm,
++ r4,
++ r5,
++ r6,
++ r7,
++ r8,
++ r9,
++ r10,
++ false,
++ call_generic_code);
++ __ IncrementCounter(counters->array_function_native(), 1, r5, r9);
++
++ // Fill arguments as array elements. Copy from the top of the stack (last
++ // element) to the array backing store filling it backwards. Note:
++ // elements_array_end points after the backing store therefore PreIndex is
++ // used when filling the backing store.
++ // r3: argc
++ // r6: JSArray
++ // r7: elements_array storage start (untagged)
++ // r8: elements_array_end (untagged)
++ // sp[0]: last argument
++ Label loop, entry;
++ __ mr(r10, sp);
++ __ b(&entry);
++ __ bind(&loop);
++ __ LoadP(r5, MemOperand(r10));
++ __ addi(r10, r10, Operand(kPointerSize));
++ if (FLAG_smi_only_arrays) {
++ __ JumpIfNotSmi(r5, &has_non_smi_element);
++ }
++ __ StorePU(r5, MemOperand(r8, -kPointerSize));
++ __ bind(&entry);
++ __ cmp(r7, r8);
++ __ blt(&loop);
++
++ __ bind(&finish);
++ __ mr(sp, r10);
++
++ // Remove caller arguments and receiver from the stack, setup return value and
++ // return.
++ // r3: argc
++ // r6: JSArray
++ // sp[0]: receiver
++ __ addi(sp, sp, Operand(kPointerSize));
++ __ mr(r3, r6);
++ __ blr();
++
++ __ bind(&has_non_smi_element);
++ // Double values are handled by the runtime.
++ __ CheckMap(
++ r5, r22, Heap::kHeapNumberMapRootIndex, ¬_double, DONT_DO_SMI_CHECK);
++ __ bind(&cant_transition_map);
++ __ UndoAllocationInNewSpace(r6, r7);
++ __ b(call_generic_code);
++
++ __ bind(¬_double);
++ // Transition FAST_SMI_ELEMENTS to FAST_ELEMENTS.
++ // r6: JSArray
++ __ LoadP(r5, FieldMemOperand(r6, HeapObject::kMapOffset));
++ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
++ FAST_ELEMENTS,
++ r5,
++ r22,
++ &cant_transition_map);
++ __ StoreP(r5, FieldMemOperand(r6, HeapObject::kMapOffset), r0);
++ __ RecordWriteField(r6,
++ HeapObject::kMapOffset,
++ r5,
++ r22,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ Label loop2;
++ __ subi(r10, r10, Operand(kPointerSize));
++ __ bind(&loop2);
++ __ LoadP(r5, MemOperand(r10));
++ __ addi(r10, r10, Operand(kPointerSize));
++ __ StorePU(r5, MemOperand(r8, -kPointerSize));
++ __ cmp(r7, r8);
++ __ blt(&loop2);
++ __ b(&finish);
++}
++
++
++void Builtins::Generate_InternalArrayCode(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments
++ // -- lr : return address
++ // -- sp[...]: constructor arguments
++ // -----------------------------------
++ Label generic_array_code, one_or_more_arguments, two_or_more_arguments;
++
++ // Get the InternalArray function.
++ GenerateLoadInternalArrayFunction(masm, r4);
++
++ if (FLAG_debug_code) {
++ // Initial map for the builtin InternalArray functions should be maps.
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kPrototypeOrInitialMapOffset));
++ STATIC_ASSERT(kSmiTagMask < 0x8000);
++ __ andi(r0, r5, Operand(kSmiTagMask));
++ __ Assert(ne, "Unexpected initial map for InternalArray function", cr0);
++ __ CompareObjectType(r5, r6, r7, MAP_TYPE);
++ __ Assert(eq, "Unexpected initial map for InternalArray function");
++ }
++
++ // Run the native code for the InternalArray function called as a normal
++ // function.
++ ArrayNativeCode(masm, &generic_array_code);
++
++ // Jump to the generic array code if the specialized code cannot handle the
++ // construction.
++ __ bind(&generic_array_code);
++
++ Handle<Code> array_code =
++ masm->isolate()->builtins()->InternalArrayCodeGeneric();
++ __ Jump(array_code, RelocInfo::CODE_TARGET);
++}
++
++
++void Builtins::Generate_ArrayCode(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments
++ // -- lr : return address
++ // -- sp[...]: constructor arguments
++ // -----------------------------------
++ Label generic_array_code, one_or_more_arguments, two_or_more_arguments;
++
++ // Get the Array function.
++ GenerateLoadArrayFunction(masm, r4);
++
++ if (FLAG_debug_code) {
++ // Initial map for the builtin Array functions should be maps.
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kPrototypeOrInitialMapOffset));
++ STATIC_ASSERT(kSmiTagMask < 0x8000);
++ __ andi(r0, r5, Operand(kSmiTagMask));
++ __ Assert(ne, "Unexpected initial map for Array function", cr0);
++ __ CompareObjectType(r5, r6, r7, MAP_TYPE);
++ __ Assert(eq, "Unexpected initial map for Array function");
++ }
++
++ // Run the native code for the Array function called as a normal function.
++ ArrayNativeCode(masm, &generic_array_code);
++
++ // Jump to the generic array code if the specialized code cannot handle
++ // the construction.
++ __ bind(&generic_array_code);
++
++ Handle<Code> array_code =
++ masm->isolate()->builtins()->ArrayCodeGeneric();
++ __ Jump(array_code, RelocInfo::CODE_TARGET);
++}
++
++
++void Builtins::Generate_ArrayConstructCode(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments
++ // -- r4 : constructor function
++ // -- lr : return address
++ // -- sp[...]: constructor arguments
++ // -----------------------------------
++ Label generic_constructor;
++
++ if (FLAG_debug_code) {
++ // The array construct code is only set for the builtin and internal
++ // Array functions which always have a map.
++ // Initial map for the builtin Array function should be a map.
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kPrototypeOrInitialMapOffset));
++ __ andi(r0, r5, Operand(kSmiTagMask));
++ __ Assert(ne, "Unexpected initial map for Array function", cr0);
++ __ CompareObjectType(r5, r6, r7, MAP_TYPE);
++ __ Assert(eq, "Unexpected initial map for Array function");
++ }
++
++ // Run the native code for the Array function called as a constructor.
++ ArrayNativeCode(masm, &generic_constructor);
++
++ // Jump to the generic construct code in case the specialized code cannot
++ // handle the construction.
++ __ bind(&generic_constructor);
++ Handle<Code> generic_construct_stub =
++ masm->isolate()->builtins()->JSConstructStubGeneric();
++ __ Jump(generic_construct_stub, RelocInfo::CODE_TARGET);
++}
++
++
++void Builtins::Generate_StringConstructCode(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments
++ // -- r4 : constructor function
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero based)
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++ Counters* counters = masm->isolate()->counters();
++ __ IncrementCounter(counters->string_ctor_calls(), 1, r5, r6);
++
++ Register function = r4;
++ if (FLAG_debug_code) {
++ __ LoadGlobalFunction(Context::STRING_FUNCTION_INDEX, r5);
++ __ cmp(function, r5);
++ __ Assert(eq, "Unexpected String function");
++ }
++
++ // Load the first arguments in r3 and get rid of the rest.
++ Label no_arguments;
++ __ cmpi(r3, Operand(0, RelocInfo::NONE));
++ __ beq(&no_arguments);
++ // First args = sp[(argc - 1) * 4].
++ __ subi(r3, r3, Operand(1));
++ __ ShiftLeftImm(r3, r3, Operand(kPointerSizeLog2));
++ __ add(sp, sp, r3);
++ __ LoadP(r3, MemOperand(sp));
++ // sp now point to args[0], drop args[0] + receiver.
++ __ Drop(2);
++
++ Register argument = r5;
++ Label not_cached, argument_is_string;
++ NumberToStringStub::GenerateLookupNumberStringCache(
++ masm,
++ r3, // Input.
++ argument, // Result.
++ r6, // Scratch.
++ r7, // Scratch.
++ r8, // Scratch.
++ false, // Is it a Smi?
++ ¬_cached);
++ __ IncrementCounter(counters->string_ctor_cached_number(), 1, r6, r7);
++ __ bind(&argument_is_string);
++
++ // ----------- S t a t e -------------
++ // -- r5 : argument converted to string
++ // -- r4 : constructor function
++ // -- lr : return address
++ // -----------------------------------
++
++ Label gc_required;
++ __ AllocateInNewSpace(JSValue::kSize,
++ r3, // Result.
++ r6, // Scratch.
++ r7, // Scratch.
++ &gc_required,
++ TAG_OBJECT);
++
++ // Initialising the String Object.
++ Register map = r6;
++ __ LoadGlobalFunctionInitialMap(function, map, r7);
++ if (FLAG_debug_code) {
++ __ lbz(r7, FieldMemOperand(map, Map::kInstanceSizeOffset));
++ __ cmpi(r7, Operand(JSValue::kSize >> kPointerSizeLog2));
++ __ Assert(eq, "Unexpected string wrapper instance size");
++ __ lbz(r7, FieldMemOperand(map, Map::kUnusedPropertyFieldsOffset));
++ __ cmpi(r7, Operand(0, RelocInfo::NONE));
++ __ Assert(eq, "Unexpected unused properties of string wrapper");
++ }
++ __ StoreP(map, FieldMemOperand(r3, HeapObject::kMapOffset), r0);
++
++ __ LoadRoot(r6, Heap::kEmptyFixedArrayRootIndex);
++ __ StoreP(r6, FieldMemOperand(r3, JSObject::kPropertiesOffset), r0);
++ __ StoreP(r6, FieldMemOperand(r3, JSObject::kElementsOffset), r0);
++
++ __ StoreP(argument, FieldMemOperand(r3, JSValue::kValueOffset), r0);
++
++ // Ensure the object is fully initialized.
++ STATIC_ASSERT(JSValue::kSize == 4 * kPointerSize);
++
++ __ Ret();
++
++ // The argument was not found in the number to string cache. Check
++ // if it's a string already before calling the conversion builtin.
++ Label convert_argument;
++ __ bind(¬_cached);
++ __ JumpIfSmi(r3, &convert_argument);
++
++ // Is it a String?
++ __ LoadP(r5, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ lbz(r6, FieldMemOperand(r5, Map::kInstanceTypeOffset));
++ STATIC_ASSERT(kNotStringTag != 0);
++ __ andi(r0, r6, Operand(kIsNotStringMask));
++ __ bne(&convert_argument, cr0);
++ __ mr(argument, r3);
++ __ IncrementCounter(counters->string_ctor_conversions(), 1, r6, r7);
++ __ b(&argument_is_string);
++
++ // Invoke the conversion builtin and put the result into r5.
++ __ bind(&convert_argument);
++ __ push(function); // Preserve the function.
++ __ IncrementCounter(counters->string_ctor_conversions(), 1, r6, r7);
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ push(r3);
++ __ InvokeBuiltin(Builtins::TO_STRING, CALL_FUNCTION);
++ }
++ __ pop(function);
++ __ mr(argument, r3);
++ __ b(&argument_is_string);
++
++ // Load the empty string into r5, remove the receiver from the
++ // stack, and jump back to the case where the argument is a string.
++ __ bind(&no_arguments);
++ __ LoadRoot(argument, Heap::kEmptyStringRootIndex);
++ __ Drop(1);
++ __ b(&argument_is_string);
++
++ // At this point the argument is already a string. Call runtime to
++ // create a string wrapper.
++ __ bind(&gc_required);
++ __ IncrementCounter(counters->string_ctor_gc_required(), 1, r6, r7);
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ push(argument);
++ __ CallRuntime(Runtime::kNewStringWrapper, 1);
++ }
++ __ Ret();
++}
++
++
++static void GenerateTailCallToSharedCode(MacroAssembler* masm) {
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++ __ LoadP(r5, FieldMemOperand(r5, SharedFunctionInfo::kCodeOffset));
++ __ addi(r5, r5, Operand(Code::kHeaderSize - kHeapObjectTag));
++ __ mtctr(r5);
++ __ bcr();
++}
++
++
++void Builtins::Generate_InRecompileQueue(MacroAssembler* masm) {
++ GenerateTailCallToSharedCode(masm);
++}
++
++
++void Builtins::Generate_ParallelRecompile(MacroAssembler* masm) {
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ // Push a copy of the function onto the stack.
++ __ push(r4);
++ // Push call kind information.
++ __ push(r8);
++
++ __ push(r4); // Function is also the parameter to the runtime call.
++ __ CallRuntime(Runtime::kParallelRecompile, 1);
++
++ // Restore call kind information.
++ __ pop(r8);
++ // Restore receiver.
++ __ pop(r4);
++
++ // Tear down internal frame.
++ }
++
++ GenerateTailCallToSharedCode(masm);
++}
++
++
++static void Generate_JSConstructStubHelper(MacroAssembler* masm,
++ bool is_api_function,
++ bool count_constructions) {
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments
++ // -- r4 : constructor function
++ // -- lr : return address
++ // -- sp[...]: constructor arguments
++ // -----------------------------------
++
++ // Should never count constructions for api objects.
++ ASSERT(!is_api_function || !count_constructions);
++
++ Isolate* isolate = masm->isolate();
++
++ // Enter a construct frame.
++ {
++ FrameScope scope(masm, StackFrame::CONSTRUCT);
++
++ // Preserve the two incoming parameters on the stack.
++ __ SmiTag(r3);
++ __ push(r3); // Smi-tagged arguments count.
++ __ push(r4); // Constructor function.
++
++ // Try to allocate the object without transitioning into C code. If any of
++ // the preconditions is not met, the code bails out to the runtime call.
++ Label rt_call, allocated;
++ if (FLAG_inline_new) {
++ Label undo_allocation;
++#ifdef ENABLE_DEBUGGER_SUPPORT
++ ExternalReference debug_step_in_fp =
++ ExternalReference::debug_step_in_fp_address(isolate);
++ __ mov(r5, Operand(debug_step_in_fp));
++ __ LoadP(r5, MemOperand(r5));
++ __ cmpi(r5, Operand::Zero());
++ __ bne(&rt_call);
++#endif
++
++ // Load the initial map and verify that it is in fact a map.
++ // r4: constructor function
++ __ LoadP(r5, FieldMemOperand(r4,
++ JSFunction::kPrototypeOrInitialMapOffset));
++ __ JumpIfSmi(r5, &rt_call);
++ __ CompareObjectType(r5, r6, r7, MAP_TYPE);
++ __ bne(&rt_call);
++
++ // Check that the constructor is not constructing a JSFunction (see
++ // comments in Runtime_NewObject in runtime.cc). In which case the
++ // initial map's instance type would be JS_FUNCTION_TYPE.
++ // r4: constructor function
++ // r5: initial map
++ __ CompareInstanceType(r5, r6, JS_FUNCTION_TYPE);
++ __ beq(&rt_call);
++
++ if (count_constructions) {
++ Label allocate;
++ // Decrease generous allocation count.
++ __ LoadP(r6, FieldMemOperand(r4,
++ JSFunction::kSharedFunctionInfoOffset));
++ MemOperand constructor_count =
++ FieldMemOperand(r6, SharedFunctionInfo::kConstructionCountOffset);
++ __ lbz(r7, constructor_count);
++ __ addi(r7, r7, Operand(-1));
++ __ stb(r7, constructor_count);
++ __ cmpi(r7, Operand::Zero());
++ __ bne(&allocate);
++
++ __ push(r4);
++ __ push(r5);
++
++ __ push(r4); // constructor
++ // The call will replace the stub, so the countdown is only done once.
++ __ CallRuntime(Runtime::kFinalizeInstanceSize, 1);
++
++ __ pop(r5);
++ __ pop(r4);
++
++ __ bind(&allocate);
++ }
++
++ // Now allocate the JSObject on the heap.
++ // r4: constructor function
++ // r5: initial map
++ __ lbz(r6, FieldMemOperand(r5, Map::kInstanceSizeOffset));
++ __ AllocateInNewSpace(r6, r7, r8, r9, &rt_call, SIZE_IN_WORDS);
++
++ // Allocated the JSObject, now initialize the fields. Map is set to
++ // initial map and properties and elements are set to empty fixed array.
++ // r4: constructor function
++ // r5: initial map
++ // r6: object size
++ // r7: JSObject (not tagged)
++ __ LoadRoot(r9, Heap::kEmptyFixedArrayRootIndex);
++ __ mr(r8, r7);
++ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
++ __ StoreP(r5, MemOperand(r8));
++ ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
++ __ StorePU(r9, MemOperand(r8, kPointerSize));
++ ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
++ __ StorePU(r9, MemOperand(r8, kPointerSize));
++ __ addi(r8, r8, Operand(kPointerSize));
++
++ // Fill all the in-object properties with the appropriate filler.
++ // r4: constructor function
++ // r5: initial map
++ // r6: object size (in words)
++ // r7: JSObject (not tagged)
++ // r8: First in-object property of JSObject (not tagged)
++ uint32_t byte;
++ __ ShiftLeftImm(r9, r6, Operand(kPointerSizeLog2));
++ __ add(r9, r7, r9); // End of object.
++ ASSERT_EQ(3 * kPointerSize, JSObject::kHeaderSize);
++ __ LoadRoot(r10, Heap::kUndefinedValueRootIndex);
++ if (count_constructions) {
++ __ lwz(r3, FieldMemOperand(r5, Map::kInstanceSizesOffset));
++ // Fetch Map::kPreAllocatedPropertyFieldsByte field from r3
++ // and multiply by kPointerSizeLog2
++ STATIC_ASSERT(Map::kPreAllocatedPropertyFieldsByte < 4);
++ byte = Map::kPreAllocatedPropertyFieldsByte;
++#if __BYTE_ORDER == __BIG_ENDIAN
++ byte = 3 - byte;
++#endif
++ __ ExtractBitRange(r3, r3,
++ ((byte + 1) * kBitsPerByte) - 1,
++ byte * kBitsPerByte);
++ __ ShiftLeftImm(r3, r3, Operand(kPointerSizeLog2));
++ __ add(r3, r8, r3);
++ // r3: offset of first field after pre-allocated fields
++ if (FLAG_debug_code) {
++ __ cmp(r3, r9);
++ __ Assert(le, "Unexpected number of pre-allocated property
fields.");
++ }
++ __ InitializeFieldsWithFiller(r8, r3, r10);
++ // To allow for truncation.
++ __ LoadRoot(r10, Heap::kOnePointerFillerMapRootIndex);
++ }
++ __ InitializeFieldsWithFiller(r8, r9, r10);
++
++ // Add the object tag to make the JSObject real, so that we can continue
++ // and jump into the continuation code at any time from now on. Any
++ // failures need to undo the allocation, so that the heap is in a
++ // consistent state and verifiable.
++ __ addi(r7, r7, Operand(kHeapObjectTag));
++
++ // Check if a non-empty properties array is needed. Continue with
++ // allocated object if not fall through to runtime call if it is.
++ // r4: constructor function
++ // r7: JSObject
++ // r8: start of next object (not tagged)
++ __ lbz(r6, FieldMemOperand(r5, Map::kUnusedPropertyFieldsOffset));
++ // The field instance sizes contains both pre-allocated property fields
++ // and in-object properties.
++ __ lwz(r3, FieldMemOperand(r5, Map::kInstanceSizesOffset));
++ // Fetch Map::kPreAllocatedPropertyFieldsByte field from r3
++ STATIC_ASSERT(Map::kPreAllocatedPropertyFieldsByte < 4);
++ byte = Map::kPreAllocatedPropertyFieldsByte;
++#if __BYTE_ORDER == __BIG_ENDIAN
++ byte = 3 - byte;
++#endif
++ __ ExtractBitRange(r9, r3,
++ ((byte + 1) * kBitsPerByte) - 1,
++ byte * kBitsPerByte);
++ __ add(r6, r6, r9);
++ STATIC_ASSERT(Map::kInObjectPropertiesByte < 4);
++ byte = Map::kInObjectPropertiesByte;
++#if __BYTE_ORDER == __BIG_ENDIAN
++ byte = 3 - byte;
++#endif
++ __ ExtractBitRange(r9, r3,
++ ((byte + 1) * kBitsPerByte) - 1,
++ byte * kBitsPerByte);
++ __ sub(r6, r6, r9); // roohack - sub order may be incorrect
++ __ cmpi(r6, Operand::Zero());
++
++ // Done if no extra properties are to be allocated.
++ __ beq(&allocated);
++ __ Assert(ge, "Property allocation count failed.");
++
++ // Scale the number of elements by pointer size and add the header for
++ // FixedArrays to the start of the next object calculation from above.
++ // r4: constructor
++ // r6: number of elements in properties array
++ // r7: JSObject
++ // r8: start of next object
++ __ addi(r3, r6, Operand(FixedArray::kHeaderSize / kPointerSize));
++ __ AllocateInNewSpace(
++ r3,
++ r8,
++ r9,
++ r5,
++ &undo_allocation,
++ static_cast<AllocationFlags>(RESULT_CONTAINS_TOP | SIZE_IN_WORDS));
++
++ // Initialize the FixedArray.
++ // r4: constructor
++ // r6: number of elements in properties array
++ // r7: JSObject
++ // r8: FixedArray (not tagged)
++ __ LoadRoot(r9, Heap::kFixedArrayMapRootIndex);
++ __ mr(r5, r8);
++ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
++ __ StoreP(r9, MemOperand(r5));
++ ASSERT_EQ(1 * kPointerSize, FixedArray::kLengthOffset);
++ __ SmiTag(r3, r6);
++ __ StorePU(r3, MemOperand(r5, kPointerSize));
++ __ addi(r5, r5, Operand(kPointerSize));
++
++ // Initialize the fields to undefined.
++ // r4: constructor function
++ // r5: First element of FixedArray (not tagged)
++ // r6: number of elements in properties array
++ // r7: JSObject
++ // r8: FixedArray (not tagged)
++ __ ShiftLeftImm(r9, r6, Operand(kPointerSizeLog2));
++ __ add(r9, r5, r9); // End of object.
++ ASSERT_EQ(2 * kPointerSize, FixedArray::kHeaderSize);
++ { Label loop, entry;
++ if (count_constructions) {
++ __ LoadRoot(r10, Heap::kUndefinedValueRootIndex);
++ } else if (FLAG_debug_code) {
++ __ LoadRoot(r11, Heap::kUndefinedValueRootIndex);
++ __ cmp(r10, r11);
++ __ Assert(eq, "Undefined value not loaded.");
++ }
++ __ b(&entry);
++ __ bind(&loop);
++ __ StoreP(r10, MemOperand(r5));
++ __ addi(r5, r5, Operand(kPointerSize));
++ __ bind(&entry);
++ __ cmp(r5, r9);
++ __ blt(&loop);
++ }
++
++ // Store the initialized FixedArray into the properties field of
++ // the JSObject
++ // r4: constructor function
++ // r7: JSObject
++ // r8: FixedArray (not tagged)
++ __ addi(r8, r8, Operand(kHeapObjectTag)); // Add the heap tag.
++ __ StoreP(r8, FieldMemOperand(r7, JSObject::kPropertiesOffset), r0);
++
++ // Continue with JSObject being successfully allocated
++ // r4: constructor function
++ // r7: JSObject
++ __ b(&allocated);
++
++ // Undo the setting of the new top so that the heap is verifiable. For
++ // example, the map's unused properties potentially do not match the
++ // allocated objects unused properties.
++ // r7: JSObject (previous new top)
++ __ bind(&undo_allocation);
++ __ UndoAllocationInNewSpace(r7, r8);
++ }
++
++ // Allocate the new receiver object using the runtime call.
++ // r4: constructor function
++ __ bind(&rt_call);
++ __ push(r4); // argument for Runtime_NewObject
++ __ CallRuntime(Runtime::kNewObject, 1);
++ __ mr(r7, r3);
++
++ // Receiver for constructor call allocated.
++ // r7: JSObject
++ __ bind(&allocated);
++ __ push(r7);
++ __ push(r7);
++
++ // Reload the number of arguments and the constructor from the stack.
++ // sp[0]: receiver
++ // sp[1]: receiver
++ // sp[2]: constructor function
++ // sp[3]: number of arguments (smi-tagged)
++ __ LoadP(r4, MemOperand(sp, 2 * kPointerSize));
++ __ LoadP(r6, MemOperand(sp, 3 * kPointerSize));
++
++ // Set up pointer to last argument.
++ __ addi(r5, fp, Operand(StandardFrameConstants::kCallerSPOffset));
++
++ // Set up number of arguments for function call below
++ __ SmiUntag(r3, r6);
++
++ // Copy arguments and receiver to the expression stack.
++ // r3: number of arguments
++ // r4: constructor function
++ // r5: address of last argument (caller sp)
++ // r6: number of arguments (smi-tagged)
++ // sp[0]: receiver
++ // sp[1]: receiver
++ // sp[2]: constructor function
++ // sp[3]: number of arguments (smi-tagged)
++ Label loop, no_args;
++ __ cmpi(r3, Operand::Zero());
++ __ beq(&no_args);
++ __ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
++ __ mtctr(r3);
++ __ bind(&loop);
++ __ subi(ip, ip, Operand(kPointerSize));
++ __ LoadPX(r0, MemOperand(r5, ip));
++ __ push(r0);
++ __ bdnz(&loop);
++ __ bind(&no_args);
++
++ // Call the function.
++ // r3: number of arguments
++ // r4: constructor function
++ if (is_api_function) {
++ __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++ Handle<Code> code =
++ masm->isolate()->builtins()->HandleApiCallConstruct();
++ ParameterCount expected(0);
++ __ InvokeCode(code, expected, expected,
++ RelocInfo::CODE_TARGET, CALL_FUNCTION, CALL_AS_METHOD);
++ } else {
++ ParameterCount actual(r3);
++ __ InvokeFunction(r4, actual, CALL_FUNCTION, // roohack
++ NullCallWrapper(), CALL_AS_METHOD);
++ }
++
++ // Store offset of return address for deoptimizer.
++ if (!is_api_function && !count_constructions) {
++
masm->isolate()->heap()->SetConstructStubDeoptPCOffset(masm->pc_offset());
++ }
++
++ // Restore context from the frame.
++ // r3: result
++ // sp[0]: receiver
++ // sp[1]: constructor function
++ // sp[2]: number of arguments (smi-tagged)
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++
++ // If the result is an object (in the ECMA sense), we should get rid
++ // of the receiver and use the result; see ECMA-262 section 13.2.2-7
++ // on page 74.
++ Label use_receiver, exit;
++
++ // If the result is a smi, it is *not* an object in the ECMA sense.
++ // r3: result
++ // sp[0]: receiver (newly allocated object)
++ // sp[1]: constructor function
++ // sp[2]: number of arguments (smi-tagged)
++ __ JumpIfSmi(r3, &use_receiver);
++
++ // If the type of the result (stored in its map) is less than
++ // FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense.
++ __ CompareObjectType(r3, r6, r6, FIRST_SPEC_OBJECT_TYPE);
++ __ bge(&exit);
++
++ // Throw away the result of the constructor invocation and use the
++ // on-stack receiver as the result.
++ __ bind(&use_receiver);
++ __ LoadP(r3, MemOperand(sp));
++
++ // Remove receiver from the stack, remove caller arguments, and
++ // return.
++ __ bind(&exit);
++ // r3: result
++ // sp[0]: receiver (newly allocated object)
++ // sp[1]: constructor function
++ // sp[2]: number of arguments (smi-tagged)
++ __ LoadP(r4, MemOperand(sp, 2 * kPointerSize));
++
++ // Leave construct frame.
++ }
++
++ __ SmiToPtrArrayOffset(r4, r4);
++ __ add(sp, sp, r4);
++ __ addi(sp, sp, Operand(kPointerSize));
++ __ IncrementCounter(isolate->counters()->constructed_objects(), 1, r4, r5);
++ __ blr();
++}
++
++
++void Builtins::Generate_JSConstructStubCountdown(MacroAssembler* masm) {
++ Generate_JSConstructStubHelper(masm, false, true);
++}
++
++
++void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) {
++ Generate_JSConstructStubHelper(masm, false, false);
++}
++
++
++void Builtins::Generate_JSConstructStubApi(MacroAssembler* masm) {
++ Generate_JSConstructStubHelper(masm, true, false);
++}
++
++
++static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
++ bool is_construct) {
++ // Called from Generate_JS_Entry
++ // r3: code entry
++ // r4: function
++ // r5: receiver
++ // r6: argc
++ // r7: argv
++ // r0,r8-r9, cp may be clobbered
++
++ // Clear the context before we push it when entering the internal frame.
++ __ li(cp, Operand(0, RelocInfo::NONE));
++
++ // Enter an internal frame.
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ // Set up the context from the function argument.
++ __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++
++ __ InitializeRootRegister();
++
++ // Push the function and the receiver onto the stack.
++ __ push(r4);
++ __ push(r5);
++
++ // Copy arguments to the stack in a loop.
++ // r4: function
++ // r6: argc
++ // r7: argv, i.e. points to first arg
++ Label loop, entry;
++ __ ShiftLeftImm(r0, r6, Operand(kPointerSizeLog2));
++ __ add(r5, r7, r0);
++ // r5 points past last arg.
++ __ b(&entry);
++ __ bind(&loop);
++ __ LoadP(r8, MemOperand(r7)); // read next parameter
++ __ addi(r7, r7, Operand(kPointerSize));
++ __ LoadP(r0, MemOperand(r8)); // dereference handle
++ __ push(r0); // push parameter
++ __ bind(&entry);
++ __ cmp(r7, r5);
++ __ bne(&loop);
++
++ // Initialize all JavaScript callee-saved registers, since they will be seen
++ // by the garbage collector as part of handlers.
++ __ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
++ __ mr(r14, r7);
++ __ mr(r15, r7);
++ __ mr(r16, r7);
++ __ mr(r22, r7); // hmmm, possibly should be reassigned to r17
++
++ // Invoke the code and pass argc as r3.
++ __ mr(r3, r6);
++ if (is_construct) {
++ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
++ __ CallStub(&stub);
++ } else {
++ ParameterCount actual(r3);
++ __ InvokeFunction(r4, actual, CALL_FUNCTION,
++ NullCallWrapper(), CALL_AS_METHOD);
++ }
++ // Exit the JS frame and remove the parameters (except function), and
++ // return.
++ }
++ __ blr();
++
++ // r3: result
++}
++
++
++void Builtins::Generate_JSEntryTrampoline(MacroAssembler* masm) {
++ Generate_JSEntryTrampolineHelper(masm, false);
++}
++
++
++void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
++ Generate_JSEntryTrampolineHelper(masm, true);
++}
++
++
++void Builtins::Generate_LazyCompile(MacroAssembler* masm) {
++ // Enter an internal frame.
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ // Preserve the function.
++ __ push(r4);
++ // Push call kind information.
++ __ push(r8);
++
++ // Push the function on the stack as the argument to the runtime function.
++ __ push(r4);
++ __ CallRuntime(Runtime::kLazyCompile, 1);
++ // Calculate the entry point.
++ __ addi(r5, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
++
++ // Restore call kind information.
++ __ pop(r8);
++ // Restore saved function.
++ __ pop(r4);
++
++ // Tear down internal frame.
++ }
++
++ // Do a tail-call of the compiled function.
++ __ Jump(r5);
++}
++
++
++void Builtins::Generate_LazyRecompile(MacroAssembler* masm) {
++ // Enter an internal frame.
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ // Preserve the function.
++ __ push(r4);
++ // Push call kind information.
++ __ push(r8);
++
++ // Push the function on the stack as the argument to the runtime function.
++ __ push(r4);
++ __ CallRuntime(Runtime::kLazyRecompile, 1);
++ // Calculate the entry point.
++ __ addi(r5, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
++
++ // Restore call kind information.
++ __ pop(r8);
++ // Restore saved function.
++ __ pop(r4);
++
++ // Tear down internal frame.
++ }
++
++ // Do a tail-call of the compiled function.
++ __ Jump(r5);
++}
++
++
++static void Generate_NotifyDeoptimizedHelper(MacroAssembler* masm,
++ Deoptimizer::BailoutType type) {
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ // Pass the function and deoptimization type to the runtime system.
++ __ LoadSmiLiteral(r3, Smi::FromInt(static_cast<int>(type)));
++ __ push(r3);
++ __ CallRuntime(Runtime::kNotifyDeoptimized, 1);
++ }
++
++ // Get the full codegen state from the stack and untag it -> r9.
++ __ LoadP(r9, MemOperand(sp, 0 * kPointerSize));
++ __ SmiUntag(r9);
++ // Switch on the state.
++ Label with_tos_register, unknown_state;
++ __ cmpi(r9, Operand(FullCodeGenerator::NO_REGISTERS));
++ __ bne(&with_tos_register);
++ __ addi(sp, sp, Operand(1 * kPointerSize)); // Remove state.
++ __ Ret();
++
++ __ bind(&with_tos_register);
++ __ LoadP(r3, MemOperand(sp, 1 * kPointerSize));
++ __ cmpi(r9, Operand(FullCodeGenerator::TOS_REG));
++ __ bne(&unknown_state);
++ __ addi(sp, sp, Operand(2 * kPointerSize)); // Remove state.
++ __ Ret();
++
++ __ bind(&unknown_state);
++ __ stop("no cases left");
++}
++
++
++void Builtins::Generate_NotifyDeoptimized(MacroAssembler* masm) {
++ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::EAGER);
++}
++
++
++void Builtins::Generate_NotifyLazyDeoptimized(MacroAssembler* masm) {
++ Generate_NotifyDeoptimizedHelper(masm, Deoptimizer::LAZY);
++}
++
++
++void Builtins::Generate_NotifyOSR(MacroAssembler* masm) {
++ // For now, we are relying on the fact that Runtime::NotifyOSR
++ // doesn't do any garbage collection which allows us to save/restore
++ // the registers without worrying about which of them contain
++ // pointers. This seems a bit fragile.
++ __ mflr(r0);
++ RegList saved_regs =
++ (kJSCallerSaved | kCalleeSaved | r0.bit() | fp.bit()) & ~sp.bit();
++ __ MultiPush(saved_regs);
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ CallRuntime(Runtime::kNotifyOSR, 0);
++ }
++ __ MultiPop(saved_regs);
++ __ mtlr(r0);
++ __ Ret();
++}
++
++
++void Builtins::Generate_OnStackReplacement(MacroAssembler* masm) {
++ // Lookup the function in the JavaScript frame and push it as an
++ // argument to the on-stack replacement function.
++ __ LoadP(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ push(r3);
++ __ CallRuntime(Runtime::kCompileForOnStackReplacement, 1);
++ }
++
++ // If the result was -1 it means that we couldn't optimize the
++ // function. Just return and continue in the unoptimized version.
++ Label skip;
++ __ CmpSmiLiteral(r3, Smi::FromInt(-1), r0);
++ __ bne(&skip);
++ __ Ret();
++
++ __ bind(&skip);
++ // Untag the AST id and push it on the stack.
++ __ SmiUntag(r3);
++ __ push(r3);
++
++ // Generate the code for doing the frame-to-frame translation using
++ // the deoptimizer infrastructure.
++ Deoptimizer::EntryGenerator generator(masm, Deoptimizer::OSR);
++ generator.Generate();
++}
++
++
++void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
++ // 1. Make sure we have at least one argument.
++ // r3: actual number of arguments
++ { Label done;
++ __ cmpi(r3, Operand::Zero());
++ __ bne(&done);
++ __ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
++ __ push(r5);
++ __ addi(r3, r3, Operand(1));
++ __ bind(&done);
++ }
++
++ // 2. Get the function to call (passed as receiver) from the stack, check
++ // if it is a function.
++ // r3: actual number of arguments
++ Label slow, non_function;
++ __ ShiftLeftImm(r4, r3, Operand(kPointerSizeLog2));
++ __ add(r4, sp, r4);
++ __ LoadP(r4, MemOperand(r4));
++ __ JumpIfSmi(r4, &non_function);
++ __ CompareObjectType(r4, r5, r5, JS_FUNCTION_TYPE);
++ __ bne(&slow);
++
++ // 3a. Patch the first argument if necessary when calling a function.
++ // r3: actual number of arguments
++ // r4: function
++ Label shift_arguments;
++ __ li(r7, Operand(0, RelocInfo::NONE)); // indicate regular JS_FUNCTION
++ { Label convert_to_object, use_global_receiver, patch_receiver;
++ // Change context eagerly in case we need the global receiver.
++ __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++
++ // Do not transform the receiver for strict mode functions.
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++ __ lwz(r6, FieldMemOperand(r5, SharedFunctionInfo::kCompilerHintsOffset));
++ __ TestBit(r6,
++#if V8_TARGET_ARCH_PPC64
++ SharedFunctionInfo::kStrictModeFunction,
++#else
++ SharedFunctionInfo::kStrictModeFunction + kSmiTagSize,
++#endif
++ r0);
++ __ bne(&shift_arguments, cr0);
++
++ // Do not transform the receiver for native (Compilerhints already in r6).
++ __ TestBit(r6,
++#if V8_TARGET_ARCH_PPC64
++ SharedFunctionInfo::kNative,
++#else
++ SharedFunctionInfo::kNative + kSmiTagSize,
++#endif
++ r0);
++ __ bne(&shift_arguments, cr0);
++
++ // Compute the receiver in non-strict mode.
++ __ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
++ __ add(r5, sp, ip);
++ __ LoadP(r5, MemOperand(r5, -kPointerSize));
++ // r3: actual number of arguments
++ // r4: function
++ // r5: first argument
++ __ JumpIfSmi(r5, &convert_to_object);
++
++ __ LoadRoot(r6, Heap::kUndefinedValueRootIndex);
++ __ cmp(r5, r6);
++ __ beq(&use_global_receiver);
++ __ LoadRoot(r6, Heap::kNullValueRootIndex);
++ __ cmp(r5, r6);
++ __ beq(&use_global_receiver);
++
++ STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
++ __ CompareObjectType(r5, r6, r6, FIRST_SPEC_OBJECT_TYPE);
++ __ bge(&shift_arguments);
++
++ __ bind(&convert_to_object);
++
++ {
++ // Enter an internal frame in order to preserve argument count.
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ SmiTag(r3);
++ __ push(r3);
++
++ __ push(r5);
++ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
++ __ mr(r5, r3);
++
++ __ pop(r3);
++ __ SmiUntag(r3);
++
++ // Exit the internal frame.
++ }
++
++ // Restore the function to r4, and the flag to r7.
++ __ ShiftLeftImm(r7, r3, Operand(kPointerSizeLog2));
++ __ add(r7, sp, r7);
++ __ LoadP(r4, MemOperand(r7));
++ __ li(r7, Operand(0, RelocInfo::NONE));
++ __ b(&patch_receiver);
++
++ // Use the global receiver object from the called function as the
++ // receiver.
++ __ bind(&use_global_receiver);
++ const int kGlobalIndex =
++ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
++ __ LoadP(r5, FieldMemOperand(cp, kGlobalIndex));
++ __ LoadP(r5, FieldMemOperand(r5, GlobalObject::kNativeContextOffset));
++ __ LoadP(r5, FieldMemOperand(r5, kGlobalIndex));
++ __ LoadP(r5, FieldMemOperand(r5, GlobalObject::kGlobalReceiverOffset));
++
++ __ bind(&patch_receiver);
++ __ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
++ __ add(r6, sp, ip);
++ __ StoreP(r5, MemOperand(r6, -kPointerSize));
++
++ __ b(&shift_arguments);
++ }
++
++ // 3b. Check for function proxy.
++ __ bind(&slow);
++ __ li(r7, Operand(1, RelocInfo::NONE)); // indicate function proxy
++ __ cmpi(r5, Operand(JS_FUNCTION_PROXY_TYPE));
++ __ beq(&shift_arguments);
++ __ bind(&non_function);
++ __ li(r7, Operand(2, RelocInfo::NONE)); // indicate non-function
++
++ // 3c. Patch the first argument when calling a non-function. The
++ // CALL_NON_FUNCTION builtin expects the non-function callee as
++ // receiver, so overwrite the first argument which will ultimately
++ // become the receiver.
++ // r3: actual number of arguments
++ // r4: function
++ // r7: call type (0: JS function, 1: function proxy, 2: non-function)
++ __ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
++ __ add(r5, sp, ip);
++ __ StoreP(r4, MemOperand(r5, -kPointerSize));
++
++ // 4. Shift arguments and return address one slot down on the stack
++ // (overwriting the original receiver). Adjust argument count to make
++ // the original first argument the new receiver.
++ // r3: actual number of arguments
++ // r4: function
++ // r7: call type (0: JS function, 1: function proxy, 2: non-function)
++ __ bind(&shift_arguments);
++ { Label loop;
++ // Calculate the copy start address (destination). Copy end address is sp.
++ __ ShiftLeftImm(ip, r3, Operand(kPointerSizeLog2));
++ __ add(r5, sp, ip);
++
++ __ bind(&loop);
++ __ LoadP(ip, MemOperand(r5, -kPointerSize));
++ __ StoreP(ip, MemOperand(r5));
++ __ subi(r5, r5, Operand(kPointerSize));
++ __ cmp(r5, sp);
++ __ bne(&loop);
++ // Adjust the actual number of arguments and remove the top element
++ // (which is a copy of the last argument).
++ __ subi(r3, r3, Operand(1));
++ __ pop();
++ }
++
++ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
++ // or a function proxy via CALL_FUNCTION_PROXY.
++ // r3: actual number of arguments
++ // r4: function
++ // r7: call type (0: JS function, 1: function proxy, 2: non-function)
++ { Label function, non_proxy;
++ __ cmpi(r7, Operand::Zero());
++ __ beq(&function);
++ // Expected number of arguments is 0 for CALL_NON_FUNCTION.
++ __ li(r5, Operand(0, RelocInfo::NONE));
++ __ SetCallKind(r8, CALL_AS_METHOD);
++ __ cmpi(r7, Operand(1));
++ __ bne(&non_proxy);
++
++ __ push(r4); // re-add proxy object as additional argument
++ __ addi(r3, r3, Operand(1));
++ __ GetBuiltinEntry(r6, Builtins::CALL_FUNCTION_PROXY);
++ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
++ RelocInfo::CODE_TARGET);
++
++ __ bind(&non_proxy);
++ __ GetBuiltinEntry(r6, Builtins::CALL_NON_FUNCTION);
++ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
++ RelocInfo::CODE_TARGET);
++ __ bind(&function);
++ }
++
++ // 5b. Get the code to call from the function and check that the number of
++ // expected arguments matches what we're providing. If so, jump
++ // (tail-call) to the code in register edx without checking arguments.
++ // r3: actual number of arguments
++ // r4: function
++ __ LoadP(r6, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++ __ LoadWordArith(r5,
++ FieldMemOperand(r6, SharedFunctionInfo::kFormalParameterCountOffset));
++#if !defined(V8_TARGET_ARCH_PPC64)
++ __ SmiUntag(r5);
++#endif
++ __ LoadP(r6, FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
++ __ SetCallKind(r8, CALL_AS_METHOD);
++ __ cmp(r5, r3); // Check formal and actual parameter counts.
++ Label skip;
++ __ beq(&skip);
++ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
++ RelocInfo::CODE_TARGET);
++
++ __ bind(&skip);
++ ParameterCount expected(0);
++ __ InvokeCode(r6, expected, expected, JUMP_FUNCTION,
++ NullCallWrapper(), CALL_AS_METHOD);
++}
++
++
++void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
++ const int kIndexOffset = -5 * kPointerSize;
++ const int kLimitOffset = -4 * kPointerSize;
++ const int kArgsOffset = 2 * kPointerSize;
++ const int kRecvOffset = 3 * kPointerSize;
++ const int kFunctionOffset = 4 * kPointerSize;
++
++ {
++ FrameScope frame_scope(masm, StackFrame::INTERNAL);
++
++ __ LoadP(r3, MemOperand(fp, kFunctionOffset)); // get the function
++ __ push(r3);
++ __ LoadP(r3, MemOperand(fp, kArgsOffset)); // get the args array
++ __ push(r3);
++ __ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
++
++ // Check the stack for overflow. We are not trying to catch
++ // interruptions (e.g. debug break and preemption) here, so the "real stack
++ // limit" is checked.
++ Label okay;
++ __ LoadRoot(r5, Heap::kRealStackLimitRootIndex);
++ // Make r5 the space we have left. The stack might already be overflowed
++ // here which will cause r5 to become negative.
++ __ sub(r5, sp, r5);
++ // Check if the arguments will overflow the stack.
++ __ SmiToPtrArrayOffset(r0, r3);
++ __ cmp(r5, r0);
++ __ bgt(&okay); // Signed comparison.
++
++ // Out of stack space.
++ __ LoadP(r4, MemOperand(fp, kFunctionOffset));
++ __ push(r4);
++ __ push(r3);
++ __ InvokeBuiltin(Builtins::APPLY_OVERFLOW, CALL_FUNCTION);
++ // End of stack check.
++
++ // Push current limit and index.
++ __ bind(&okay);
++ __ push(r3); // limit
++ __ li(r4, Operand(0, RelocInfo::NONE)); // initial index
++ __ push(r4);
++
++ // Get the receiver.
++ __ LoadP(r3, MemOperand(fp, kRecvOffset));
++
++ // Check that the function is a JS function (otherwise it must be a proxy).
++ Label push_receiver;
++ __ LoadP(r4, MemOperand(fp, kFunctionOffset));
++ __ CompareObjectType(r4, r5, r5, JS_FUNCTION_TYPE);
++ __ bne(&push_receiver);
++
++ // Change context eagerly to get the right global object if necessary.
++ __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++ // Load the shared function info while the function is still in r4.
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++
++ // Compute the receiver.
++ // Do not transform the receiver for strict mode functions.
++ Label call_to_object, use_global_receiver;
++ __ lwz(r5, FieldMemOperand(r5, SharedFunctionInfo::kCompilerHintsOffset));
++ __ TestBit(r5,
++#if V8_TARGET_ARCH_PPC64
++ SharedFunctionInfo::kStrictModeFunction,
++#else
++ SharedFunctionInfo::kStrictModeFunction + kSmiTagSize,
++#endif
++ r0);
++ __ bne(&push_receiver, cr0);
++
++ // Do not transform the receiver for strict mode functions.
++ __ TestBit(r5,
++#if V8_TARGET_ARCH_PPC64
++ SharedFunctionInfo::kNative,
++#else
++ SharedFunctionInfo::kNative + kSmiTagSize,
++#endif
++ r0);
++ __ bne(&push_receiver, cr0);
++
++ // Compute the receiver in non-strict mode.
++ __ JumpIfSmi(r3, &call_to_object);
++ __ LoadRoot(r4, Heap::kNullValueRootIndex);
++ __ cmp(r3, r4);
++ __ beq(&use_global_receiver);
++ __ LoadRoot(r4, Heap::kUndefinedValueRootIndex);
++ __ cmp(r3, r4);
++ __ beq(&use_global_receiver);
++
++ // Check if the receiver is already a JavaScript object.
++ // r3: receiver
++ STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
++ __ CompareObjectType(r3, r4, r4, FIRST_SPEC_OBJECT_TYPE);
++ __ bge(&push_receiver);
++
++ // Convert the receiver to a regular object.
++ // r3: receiver
++ __ bind(&call_to_object);
++ __ push(r3);
++ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
++ __ b(&push_receiver);
++
++ // Use the current global receiver object as the receiver.
++ __ bind(&use_global_receiver);
++ const int kGlobalOffset =
++ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
++ __ LoadP(r3, FieldMemOperand(cp, kGlobalOffset));
++ __ LoadP(r3, FieldMemOperand(r3, GlobalObject::kNativeContextOffset));
++ __ LoadP(r3, FieldMemOperand(r3, kGlobalOffset));
++ __ LoadP(r3, FieldMemOperand(r3, GlobalObject::kGlobalReceiverOffset));
++
++ // Push the receiver.
++ // r3: receiver
++ __ bind(&push_receiver);
++ __ push(r3);
++
++ // Copy all arguments from the array to the stack.
++ Label entry, loop;
++ __ LoadP(r3, MemOperand(fp, kIndexOffset));
++ __ b(&entry);
++
++ // Load the current argument from the arguments array and push it to the
++ // stack.
++ // r3: current argument index
++ __ bind(&loop);
++ __ LoadP(r4, MemOperand(fp, kArgsOffset));
++ __ push(r4);
++ __ push(r3);
++
++ // Call the runtime to access the property in the arguments array.
++ __ CallRuntime(Runtime::kGetProperty, 2);
++ __ push(r3);
++
++ // Use inline caching to access the arguments.
++ __ LoadP(r3, MemOperand(fp, kIndexOffset));
++ __ AddSmiLiteral(r3, r3, Smi::FromInt(1), r0);
++ __ StoreP(r3, MemOperand(fp, kIndexOffset));
++
++ // Test if the copy loop has finished copying all the elements from the
++ // arguments object.
++ __ bind(&entry);
++ __ LoadP(r4, MemOperand(fp, kLimitOffset));
++ __ cmp(r3, r4);
++ __ bne(&loop);
++
++ // Invoke the function.
++ Label call_proxy;
++ ParameterCount actual(r3);
++ __ SmiUntag(r3);
++ __ LoadP(r4, MemOperand(fp, kFunctionOffset));
++ __ CompareObjectType(r4, r5, r5, JS_FUNCTION_TYPE);
++ __ bne(&call_proxy);
++ __ InvokeFunction(r4, actual, CALL_FUNCTION,
++ NullCallWrapper(), CALL_AS_METHOD);
++
++ frame_scope.GenerateLeaveFrame();
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ blr();
++
++ // Invoke the function proxy.
++ __ bind(&call_proxy);
++ __ push(r4); // add function proxy as last argument
++ __ addi(r3, r3, Operand(1));
++ __ li(r5, Operand(0, RelocInfo::NONE));
++ __ SetCallKind(r8, CALL_AS_METHOD);
++ __ GetBuiltinEntry(r6, Builtins::CALL_FUNCTION_PROXY);
++ __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
++ RelocInfo::CODE_TARGET);
++
++ // Tear down the internal frame and remove function, receiver and args.
++ }
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ blr();
++}
++
++
++static void EnterArgumentsAdaptorFrame(MacroAssembler* masm) {
++ __ SmiTag(r3);
++ __ LoadSmiLiteral(r7, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
++ __ mflr(r0);
++ __ push(r0);
++ __ Push(fp, r7, r4, r3);
++ __ addi(fp, sp, Operand(3 * kPointerSize));
++}
++
++
++static void LeaveArgumentsAdaptorFrame(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : result being passed through
++ // -----------------------------------
++ // Get the number of arguments passed (as a smi), tear down the frame and
++ // then tear down the parameters.
++ __ LoadP(r4, MemOperand(fp, -3 * kPointerSize));
++ __ mr(sp, fp);
++ __ LoadP(fp, MemOperand(sp));
++ __ LoadP(r0, MemOperand(sp, kPointerSize));
++ __ mtlr(r0);
++ __ SmiToPtrArrayOffset(r0, r4);
++ __ add(sp, sp, r0);
++ __ addi(sp, sp, Operand(3 * kPointerSize)); // adjust for receiver + fp + lr
++}
++
++
++void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : actual number of arguments
++ // -- r4 : function (passed through to callee)
++ // -- r5 : expected number of arguments
++ // -- r6 : code entry to call
++ // -- r8 : call kind information
++ // -----------------------------------
++
++ Label invoke, dont_adapt_arguments;
++
++ Label enough, too_few;
++ __ cmp(r3, r5);
++ __ blt(&too_few);
++ __ cmpi(r5, Operand(SharedFunctionInfo::kDontAdaptArgumentsSentinel));
++ __ beq(&dont_adapt_arguments);
++
++ { // Enough parameters: actual >= expected
++ __ bind(&enough);
++ EnterArgumentsAdaptorFrame(masm);
++
++ // Calculate copy start address into r3 and copy end address into r5.
++ // r3: actual number of arguments as a smi
++ // r4: function
++ // r5: expected number of arguments
++ // r6: code entry to call
++ __ SmiToPtrArrayOffset(r3, r3);
++ __ add(r3, r3, fp);
++ // adjust for return address and receiver
++ __ addi(r3, r3, Operand(2 * kPointerSize));
++ __ ShiftLeftImm(r5, r5, Operand(kPointerSizeLog2));
++ __ sub(r5, r3, r5);
++
++ // Copy the arguments (including the receiver) to the new stack frame.
++ // r3: copy start address
++ // r4: function
++ // r5: copy end address
++ // r6: code entry to call
++
++ Label copy;
++ __ bind(©);
++ __ LoadP(ip, MemOperand(r3, 0));
++ __ push(ip);
++ __ cmp(r3, r5); // Compare before moving to next argument.
++ __ subi(r3, r3, Operand(kPointerSize));
++ __ bne(©);
++
++ __ b(&invoke);
++ }
++
++ { // Too few parameters: Actual < expected
++ __ bind(&too_few);
++ EnterArgumentsAdaptorFrame(masm);
++
++ // Calculate copy start address into r0 and copy end address is fp.
++ // r3: actual number of arguments as a smi
++ // r4: function
++ // r5: expected number of arguments
++ // r6: code entry to call
++ __ SmiToPtrArrayOffset(r3, r3);
++ __ add(r3, r3, fp);
++
++ // Copy the arguments (including the receiver) to the new stack frame.
++ // r3: copy start address
++ // r4: function
++ // r5: expected number of arguments
++ // r6: code entry to call
++ Label copy;
++ __ bind(©);
++ // Adjust load for return address and receiver.
++ __ LoadP(ip, MemOperand(r3, 2 * kPointerSize));
++ __ push(ip);
++ __ cmp(r3, fp); // Compare before moving to next argument.
++ __ subi(r3, r3, Operand(kPointerSize));
++ __ bne(©);
++
++ // Fill the remaining expected arguments with undefined.
++ // r4: function
++ // r5: expected number of arguments
++ // r6: code entry to call
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ ShiftLeftImm(r5, r5, Operand(kPointerSizeLog2));
++ __ sub(r5, fp, r5);
++ __ subi(r5, r5, Operand(4 * kPointerSize)); // Adjust for frame.
++
++ Label fill;
++ __ bind(&fill);
++ __ push(ip);
++ __ cmp(sp, r5);
++ __ bne(&fill);
++ }
++
++ // Call the entry point.
++ __ bind(&invoke);
++ __ Call(r6);
++
++ // Store offset of return address for deoptimizer.
++
masm->isolate()->heap()->SetArgumentsAdaptorDeoptPCOffset(masm->pc_offset());
++
++ // Exit frame and return.
++ LeaveArgumentsAdaptorFrame(masm);
++ __ blr();
++
++
++ // -------------------------------------------
++ // Dont adapt arguments.
++ // -------------------------------------------
++ __ bind(&dont_adapt_arguments);
++ __ Jump(r6);
++}
++
++
++#undef __
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/codegen-ppc.cc.ppc v8-3.14.5.10/src/ppc/codegen-ppc.cc
+--- v8-3.14.5.10/src/ppc/codegen-ppc.cc.ppc 2016-06-07 14:15:45.990393008 -0400
++++ v8-3.14.5.10/src/ppc/codegen-ppc.cc 2016-06-07 14:15:45.990393008 -0400
+@@ -0,0 +1,492 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "codegen.h"
++#include "macro-assembler.h"
++
++namespace v8 {
++namespace internal {
++
++#define __ ACCESS_MASM(masm)
++
++UnaryMathFunction CreateTranscendentalFunction(TranscendentalCache::Type type) {
++ switch (type) {
++ case TranscendentalCache::SIN: return &sin;
++ case TranscendentalCache::COS: return &cos;
++ case TranscendentalCache::TAN: return &tan;
++ case TranscendentalCache::LOG: return &log;
++ default: UNIMPLEMENTED();
++ }
++ return NULL;
++}
++
++
++UnaryMathFunction CreateSqrtFunction() {
++ return &sqrt;
++}
++
++// -------------------------------------------------------------------------
++// Platform-specific RuntimeCallHelper functions.
++
++void StubRuntimeCallHelper::BeforeCall(MacroAssembler* masm) const {
++ masm->EnterFrame(StackFrame::INTERNAL);
++ ASSERT(!masm->has_frame());
++ masm->set_has_frame(true);
++}
++
++
++void StubRuntimeCallHelper::AfterCall(MacroAssembler* masm) const {
++ masm->LeaveFrame(StackFrame::INTERNAL);
++ ASSERT(masm->has_frame());
++ masm->set_has_frame(false);
++}
++
++
++// -------------------------------------------------------------------------
++// Code generators
++
++void ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
++ MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -- r6 : target map, scratch for subsequent call
++ // -- r7 : scratch (elements)
++ // -----------------------------------
++ // Set transitioned map.
++ __ StoreP(r6, FieldMemOperand(r5, HeapObject::kMapOffset), r0);
++ __ RecordWriteField(r5,
++ HeapObject::kMapOffset,
++ r6,
++ r22,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++}
++
++
++void ElementsTransitionGenerator::GenerateSmiToDouble(
++ MacroAssembler* masm, Label* fail) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -- r6 : target map, scratch for subsequent call
++ // -- r7 : scratch (elements)
++ // -----------------------------------
++ Label loop, entry, convert_hole, gc_required, only_change_map, done;
++
++ // Check for empty arrays, which only require a map transition and no changes
++ // to the backing store.
++ __ LoadP(r7, FieldMemOperand(r5, JSObject::kElementsOffset));
++ __ CompareRoot(r7, Heap::kEmptyFixedArrayRootIndex);
++ __ beq(&only_change_map);
++
++ // Preserve lr and use r30 as a temporary register.
++ __ mflr(r0);
++ __ Push(r0, r30);
++
++ __ LoadP(r8, FieldMemOperand(r7, FixedArray::kLengthOffset));
++ // r7: source FixedArray
++ // r8: number of elements (smi-tagged)
++
++ // Allocate new FixedDoubleArray.
++ __ SmiToDoubleArrayOffset(r30, r8);
++ __ addi(r30, r30, Operand(FixedDoubleArray::kHeaderSize + kPointerSize));
++ __ AllocateInNewSpace(r30, r9, r10, r22, &gc_required, NO_ALLOCATION_FLAGS);
++ // r9: destination FixedDoubleArray, not tagged as heap object.
++
++ // Align the array conveniently for doubles.
++ // Store a filler value in the unused memory.
++ Label aligned, aligned_done;
++ __ andi(r0, r9, Operand(kDoubleAlignmentMask));
++ __ mov(ip, Operand(masm->isolate()->factory()->one_pointer_filler_map()));
++ __ beq(&aligned, cr0);
++ // Store at the beginning of the allocated memory and update the base pointer.
++ __ StoreP(ip, MemOperand(r9));
++ __ addi(r9, r9, Operand(kPointerSize));
++ __ b(&aligned_done);
++
++ __ bind(&aligned);
++ // Store the filler at the end of the allocated memory.
++ __ subi(r30, r30, Operand(kPointerSize));
++ __ StorePX(ip, MemOperand(r9, r30));
++
++ __ bind(&aligned_done);
++
++ // Set destination FixedDoubleArray's length and map.
++ __ LoadRoot(r22, Heap::kFixedDoubleArrayMapRootIndex);
++ __ StoreP(r8, MemOperand(r9, FixedDoubleArray::kLengthOffset));
++ // Update receiver's map.
++ __ StoreP(r22, MemOperand(r9, HeapObject::kMapOffset));
++
++ __ StoreP(r6, FieldMemOperand(r5, HeapObject::kMapOffset), r0);
++ __ RecordWriteField(r5,
++ HeapObject::kMapOffset,
++ r6,
++ r22,
++ kLRHasBeenSaved,
++ kDontSaveFPRegs,
++ OMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ // Replace receiver's backing store with newly created FixedDoubleArray.
++ __ addi(r6, r9, Operand(kHeapObjectTag));
++ __ StoreP(r6, FieldMemOperand(r5, JSObject::kElementsOffset), r0);
++ __ RecordWriteField(r5,
++ JSObject::kElementsOffset,
++ r6,
++ r22,
++ kLRHasBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++
++ // Prepare for conversion loop.
++ __ addi(r6, r7, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ addi(r10, r9, Operand(FixedDoubleArray::kHeaderSize));
++ __ SmiToDoubleArrayOffset(r9, r8);
++ __ add(r9, r10, r9);
++#if V8_TARGET_ARCH_PPC64
++ __ mov(r7, Operand(kHoleNanInt64));
++#else
++ __ mov(r7, Operand(kHoleNanLower32));
++ __ mov(r8, Operand(kHoleNanUpper32));
++#endif
++ // r6: begin of source FixedArray element fields, not tagged
++ // r7: kHoleNanLower32
++ // r8: kHoleNanUpper32
++ // r9: end of destination FixedDoubleArray, not tagged
++ // r10: begin of FixedDoubleArray element fields, not tagged
++
++ __ b(&entry);
++
++ __ bind(&only_change_map);
++ __ StoreP(r6, FieldMemOperand(r5, HeapObject::kMapOffset), r0);
++ __ RecordWriteField(r5,
++ HeapObject::kMapOffset,
++ r6,
++ r22,
++ kLRHasBeenSaved,
++ kDontSaveFPRegs,
++ OMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ __ b(&done);
++
++ // Call into runtime if GC is required.
++ __ bind(&gc_required);
++ __ Pop(r0, r30);
++ __ mtlr(r0);
++ __ b(fail);
++
++ // Convert and copy elements.
++ __ bind(&loop);
++ __ LoadP(r22, MemOperand(r6));
++ __ addi(r6, r6, Operand(kPointerSize));
++ // r22: current element
++ __ UntagAndJumpIfNotSmi(r22, r22, &convert_hole);
++
++ // Normal smi, convert to double and store.
++ FloatingPointHelper::ConvertIntToDouble(
++ masm, r22, d0);
++ __ stfd(d0, MemOperand(r10, 0));
++ __ addi(r10, r10, Operand(8));
++
++ __ b(&entry);
++
++ // Hole found, store the-hole NaN.
++ __ bind(&convert_hole);
++ if (FLAG_debug_code) {
++ // Restore a "smi-untagged" heap object.
++ __ LoadP(r22, MemOperand(r6, -kPointerSize));
++ __ CompareRoot(r22, Heap::kTheHoleValueRootIndex);
++ __ Assert(eq, "object found in smi-only array");
++ }
++#if V8_TARGET_ARCH_PPC64
++ __ std(r7, MemOperand(r10, 0));
++#else
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(r7, MemOperand(r10, 0));
++ __ stw(r8, MemOperand(r10, 4));
++#else
++ __ stw(r8, MemOperand(r10, 0));
++ __ stw(r7, MemOperand(r10, 4));
++#endif
++#endif
++ __ addi(r10, r10, Operand(8));
++
++ __ bind(&entry);
++ __ cmp(r10, r9);
++ __ blt(&loop);
++
++ __ Pop(r0, r30);
++ __ mtlr(r0);
++ __ bind(&done);
++}
++
++
++void ElementsTransitionGenerator::GenerateDoubleToObject(
++ MacroAssembler* masm, Label* fail) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -- r6 : target map, scratch for subsequent call
++ // -- r7 : scratch (elements)
++ // -----------------------------------
++ Label entry, loop, convert_hole, gc_required, only_change_map;
++
++ // Check for empty arrays, which only require a map transition and no changes
++ // to the backing store.
++ __ LoadP(r7, FieldMemOperand(r5, JSObject::kElementsOffset));
++ __ CompareRoot(r7, Heap::kEmptyFixedArrayRootIndex);
++ __ beq(&only_change_map);
++
++ __ Push(r6, r5, r4, r3);
++ __ LoadP(r8, FieldMemOperand(r7, FixedArray::kLengthOffset));
++ // r7: source FixedDoubleArray
++ // r8: number of elements (smi-tagged)
++
++ // Allocate new FixedArray.
++ __ li(r3, Operand(FixedDoubleArray::kHeaderSize));
++ __ SmiToPtrArrayOffset(r0, r8);
++ __ add(r3, r3, r0);
++ __ AllocateInNewSpace(r3, r9, r10, r22, &gc_required, NO_ALLOCATION_FLAGS);
++ // r9: destination FixedArray, not tagged as heap object
++ // Set destination FixedDoubleArray's length and map.
++ __ LoadRoot(r22, Heap::kFixedArrayMapRootIndex);
++ __ StoreP(r8, MemOperand(r9, FixedDoubleArray::kLengthOffset));
++ __ StoreP(r22, MemOperand(r9, HeapObject::kMapOffset));
++
++ // Prepare for conversion loop.
++ __ addi(r7, r7, Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag));
++ __ addi(r6, r9, Operand(FixedArray::kHeaderSize));
++ __ addi(r9, r9, Operand(kHeapObjectTag));
++ __ SmiToPtrArrayOffset(r8, r8);
++ __ add(r8, r6, r8);
++ __ LoadRoot(r10, Heap::kTheHoleValueRootIndex);
++ __ LoadRoot(r22, Heap::kHeapNumberMapRootIndex);
++ // Using offsetted addresses in r7 to fully take advantage of post-indexing.
++ // r6: begin of destination FixedArray element fields, not tagged
++ // r7: begin of source FixedDoubleArray element fields, not tagged
++ // r8: end of destination FixedArray, not tagged
++ // r9: destination FixedArray
++ // r10: the-hole pointer
++ // r22: heap number map
++ __ b(&entry);
++
++ // Call into runtime if GC is required.
++ __ bind(&gc_required);
++ __ Pop(r6, r5, r4, r3);
++ __ b(fail);
++
++ __ bind(&loop);
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(r4, MemOperand(r7, 4));
++#else
++ __ lwz(r4, MemOperand(r7));
++#endif
++ __ addi(r7, r7, Operand(8));
++ // r4: current element's upper 32 bit
++ // r7: address of next element's upper 32 bit
++ __ Cmpi(r4, Operand(kHoleNanUpper32), r0);
++ __ beq(&convert_hole);
++
++ // Non-hole double, copy value into a heap number.
++ __ AllocateHeapNumber(r5, r3, r4, r22, &gc_required);
++ // r5: new heap number
++#if V8_TARGET_ARCH_PPC64
++ __ ld(r3, MemOperand(r7, -8));
++ __ addi(r4, r5, Operand(-1)); // subtract tag for std
++ __ std(r3, MemOperand(r4, HeapNumber::kValueOffset));
++#else
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(r3, MemOperand(r7, -8));
++ __ lwz(r4, MemOperand(r7, -4));
++ __ stw(r3, FieldMemOperand(r5, HeapNumber::kValueOffset));
++ __ stw(r4, FieldMemOperand(r5, HeapNumber::kValueOffset+4));
++#else
++ __ lwz(r3, MemOperand(r7, -4));
++ __ lwz(r4, MemOperand(r7, -8));
++ __ stw(r3, FieldMemOperand(r5, HeapNumber::kValueOffset+4));
++ __ stw(r4, FieldMemOperand(r5, HeapNumber::kValueOffset));
++#endif
++#endif
++ __ mr(r3, r6);
++ __ StoreP(r5, MemOperand(r6));
++ __ addi(r6, r6, Operand(kPointerSize));
++ __ RecordWrite(r9,
++ r3,
++ r5,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ __ b(&entry);
++
++ // Replace the-hole NaN with the-hole pointer.
++ __ bind(&convert_hole);
++ __ StoreP(r10, MemOperand(r6));
++ __ addi(r6, r6, Operand(kPointerSize));
++
++ __ bind(&entry);
++ __ cmpl(r6, r8);
++ __ blt(&loop);
++
++ __ Pop(r6, r5, r4, r3);
++ // Replace receiver's backing store with newly created and filled FixedArray.
++ __ StoreP(r9, FieldMemOperand(r5, JSObject::kElementsOffset), r0);
++ __ RecordWriteField(r5,
++ JSObject::kElementsOffset,
++ r9,
++ r22,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++
++ __ bind(&only_change_map);
++ // Update receiver's map.
++ __ StoreP(r6, FieldMemOperand(r5, HeapObject::kMapOffset), r0);
++ __ RecordWriteField(r5,
++ HeapObject::kMapOffset,
++ r6,
++ r22,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ OMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++}
++
++
++// roohack - assume ip can be used as a scratch register below
++void StringCharLoadGenerator::Generate(MacroAssembler* masm,
++ Register string,
++ Register index,
++ Register result,
++ Label* call_runtime) {
++ // Fetch the instance type of the receiver into result register.
++ __ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
++ __ lbz(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
++
++ // We need special handling for indirect strings.
++ Label check_sequential;
++ __ andi(r0, result, Operand(kIsIndirectStringMask));
++ __ beq(&check_sequential, cr0);
++
++ // Dispatch on the indirect string shape: slice or cons.
++ Label cons_string;
++ __ mov(ip, Operand(kSlicedNotConsMask));
++ __ and_(r0, result, ip, SetRC);
++ __ beq(&cons_string, cr0);
++
++ // Handle slices.
++ Label indirect_string_loaded;
++ __ LoadP(result, FieldMemOperand(string, SlicedString::kOffsetOffset));
++ __ LoadP(string, FieldMemOperand(string, SlicedString::kParentOffset));
++ __ SmiUntag(ip, result);
++ __ add(index, index, ip);
++ __ b(&indirect_string_loaded);
++
++ // Handle cons strings.
++ // Check whether the right hand side is the empty string (i.e. if
++ // this is really a flat string in a cons string). If that is not
++ // the case we would rather go to the runtime system now to flatten
++ // the string.
++ __ bind(&cons_string);
++ __ LoadP(result, FieldMemOperand(string, ConsString::kSecondOffset));
++ __ CompareRoot(result, Heap::kEmptyStringRootIndex);
++ __ bne(call_runtime);
++ // Get the first of the two strings and load its instance type.
++ __ LoadP(string, FieldMemOperand(string, ConsString::kFirstOffset));
++
++ __ bind(&indirect_string_loaded);
++ __ LoadP(result, FieldMemOperand(string, HeapObject::kMapOffset));
++ __ lbz(result, FieldMemOperand(result, Map::kInstanceTypeOffset));
++
++ // Distinguish sequential and external strings. Only these two string
++ // representations can reach here (slices and flat cons strings have been
++ // reduced to the underlying sequential or external string).
++ Label external_string, check_encoding;
++ __ bind(&check_sequential);
++ STATIC_ASSERT(kSeqStringTag == 0);
++ __ andi(r0, result, Operand(kStringRepresentationMask));
++ __ bne(&external_string, cr0);
++
++ // Prepare sequential strings
++ STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
++ __ addi(string,
++ string,
++ Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
++ __ b(&check_encoding);
++
++ // Handle external strings.
++ __ bind(&external_string);
++ if (FLAG_debug_code) {
++ // Assert that we do not have a cons or slice (indirect strings) here.
++ // Sequential strings have already been ruled out.
++ __ andi(r0, result, Operand(kIsIndirectStringMask));
++ __ Assert(eq, "external string expected, but not found", cr0);
++ }
++ // Rule out short external strings.
++ STATIC_CHECK(kShortExternalStringTag != 0);
++ __ andi(r0, result, Operand(kShortExternalStringMask));
++ __ bne(call_runtime, cr0);
++ __ LoadP(string,
++ FieldMemOperand(string, ExternalString::kResourceDataOffset));
++
++ Label ascii, done;
++ __ bind(&check_encoding);
++ STATIC_ASSERT(kTwoByteStringTag == 0);
++ __ andi(r0, result, Operand(kStringEncodingMask));
++ __ bne(&ascii, cr0);
++ // Two-byte string.
++ __ ShiftLeftImm(result, index, Operand(1));
++ __ lhzx(result, MemOperand(string, result));
++ __ b(&done);
++ __ bind(&ascii);
++ // Ascii string.
++ __ lbzx(result, MemOperand(string, index));
++ __ bind(&done);
++}
++
++#undef __
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/codegen-ppc.h.ppc v8-3.14.5.10/src/ppc/codegen-ppc.h
+--- v8-3.14.5.10/src/ppc/codegen-ppc.h.ppc 2016-06-07 14:15:45.990393008 -0400
++++ v8-3.14.5.10/src/ppc/codegen-ppc.h 2016-06-07 14:15:45.990393008 -0400
+@@ -0,0 +1,96 @@
++// Copyright 2011 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_CODEGEN_PPC_H_
++#define V8_PPC_CODEGEN_PPC_H_
++
++#include "ast.h"
++#include "ic-inl.h"
++
++namespace v8 {
++namespace internal {
++
++// Forward declarations
++class CompilationInfo;
++
++enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
++
++// -------------------------------------------------------------------------
++// CodeGenerator
++
++class CodeGenerator: public AstVisitor {
++ public:
++ static bool MakeCode(CompilationInfo* info);
++
++ // Printing of AST, etc. as requested by flags.
++ static void MakeCodePrologue(CompilationInfo* info);
++
++ // Allocate and install the code.
++ static Handle<Code> MakeCodeEpilogue(MacroAssembler* masm,
++ Code::Flags flags,
++ CompilationInfo* info);
++
++ // Print the code after compiling it.
++ static void PrintCode(Handle<Code> code, CompilationInfo* info);
++
++ static bool ShouldGenerateLog(Expression* type);
++
++ static void SetFunctionInfo(Handle<JSFunction> fun,
++ FunctionLiteral* lit,
++ bool is_toplevel,
++ Handle<Script> script);
++
++ static bool RecordPositions(MacroAssembler* masm,
++ int pos,
++ bool right_here = false);
++
++ private:
++ DISALLOW_COPY_AND_ASSIGN(CodeGenerator);
++};
++
++
++class StringCharLoadGenerator : public AllStatic {
++ public:
++ // Generates the code for handling different string types and loading the
++ // indexed character into |result|. We expect |index| as untagged input and
++ // |result| as untagged output.
++ static void Generate(MacroAssembler* masm,
++ Register string,
++ Register index,
++ Register result,
++ Label* call_runtime);
++
++ private:
++ DISALLOW_COPY_AND_ASSIGN(StringCharLoadGenerator);
++};
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_CODEGEN_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/code-stubs-ppc.cc.ppc
v8-3.14.5.10/src/ppc/code-stubs-ppc.cc
+--- v8-3.14.5.10/src/ppc/code-stubs-ppc.cc.ppc 2016-06-07 14:15:45.989393014 -0400
++++ v8-3.14.5.10/src/ppc/code-stubs-ppc.cc 2016-06-07 14:15:45.989393014 -0400
+@@ -0,0 +1,7530 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "bootstrapper.h"
++#include "code-stubs.h"
++#include "regexp-macro-assembler.h"
++#include "ppc/regexp-macro-assembler-ppc.h"
++
++namespace v8 {
++namespace internal {
++
++
++#define __ ACCESS_MASM(masm)
++
++static void EmitIdenticalObjectComparison(MacroAssembler* masm,
++ Label* slow,
++ Condition cond,
++ bool never_nan_nan);
++static void EmitSmiNonsmiComparison(MacroAssembler* masm,
++ Register lhs,
++ Register rhs,
++ Label* lhs_not_nan,
++ Label* slow,
++ bool strict);
++static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
++ Register lhs,
++ Register rhs);
++
++
++// Check if the operand is a heap number.
++static void EmitCheckForHeapNumber(MacroAssembler* masm, Register operand,
++ Register scratch1, Register scratch2,
++ Label* not_a_heap_number) {
++ __ LoadP(scratch1, FieldMemOperand(operand, HeapObject::kMapOffset));
++ __ LoadRoot(scratch2, Heap::kHeapNumberMapRootIndex);
++ __ cmp(scratch1, scratch2);
++ __ bne(not_a_heap_number);
++}
++
++
++void ToNumberStub::Generate(MacroAssembler* masm) {
++ // The ToNumber stub takes one argument in eax.
++ Label check_heap_number, call_builtin;
++ __ JumpIfNotSmi(r3, &check_heap_number);
++ __ Ret();
++
++ __ bind(&check_heap_number);
++ EmitCheckForHeapNumber(masm, r3, r4, ip, &call_builtin);
++ __ Ret();
++
++ __ bind(&call_builtin);
++ __ push(r3);
++ __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION);
++}
++
++
++void FastNewClosureStub::Generate(MacroAssembler* masm) {
++ // Create a new closure from the given function info in new
++ // space. Set the context to the current context in cp.
++ Counters* counters = masm->isolate()->counters();
++
++ Label gc;
++
++ // Pop the function info from the stack.
++ __ pop(r6);
++
++ // Attempt to allocate new JSFunction in new space.
++ __ AllocateInNewSpace(JSFunction::kSize,
++ r3,
++ r4,
++ r5,
++ &gc,
++ TAG_OBJECT);
++
++ __ IncrementCounter(counters->fast_new_closure_total(), 1, r9, r10);
++
++ int map_index = (language_mode_ == CLASSIC_MODE)
++ ? Context::FUNCTION_MAP_INDEX
++ : Context::STRICT_MODE_FUNCTION_MAP_INDEX;
++
++ // Compute the function map in the current native context and set that
++ // as the map of the allocated object.
++ __ LoadP(r5,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ LoadP(r5, FieldMemOperand(r5, GlobalObject::kNativeContextOffset));
++ __ LoadP(r8, MemOperand(r5, Context::SlotOffset(map_index)));
++ __ StoreP(r8, FieldMemOperand(r3, HeapObject::kMapOffset), r0);
++
++ // Initialize the rest of the function. We don't have to update the
++ // write barrier because the allocated object is in new space.
++ __ LoadRoot(r4, Heap::kEmptyFixedArrayRootIndex);
++ __ LoadRoot(r8, Heap::kTheHoleValueRootIndex);
++ __ StoreP(r4, FieldMemOperand(r3, JSObject::kPropertiesOffset), r0);
++ __ StoreP(r4, FieldMemOperand(r3, JSObject::kElementsOffset), r0);
++ __ StoreP(r8, FieldMemOperand(r3, JSFunction::kPrototypeOrInitialMapOffset),
++ r0);
++ __ StoreP(r6, FieldMemOperand(r3, JSFunction::kSharedFunctionInfoOffset), r0);
++ __ StoreP(cp, FieldMemOperand(r3, JSFunction::kContextOffset), r0);
++ __ StoreP(r4, FieldMemOperand(r3, JSFunction::kLiteralsOffset), r0);
++
++ // Initialize the code pointer in the function to be the one
++ // found in the shared function info object.
++ // But first check if there is an optimized version for our context.
++ Label check_optimized;
++ Label install_unoptimized;
++ if (FLAG_cache_optimized_code) {
++ __ LoadP(r4,
++ FieldMemOperand(r6, SharedFunctionInfo::kOptimizedCodeMapOffset));
++ __ cmpi(r4, Operand::Zero());
++ __ bne(&check_optimized);
++ }
++ __ bind(&install_unoptimized);
++ __ LoadRoot(r7, Heap::kUndefinedValueRootIndex);
++ __ StoreP(r7, FieldMemOperand(r3, JSFunction::kNextFunctionLinkOffset), r0);
++ __ LoadP(r6, FieldMemOperand(r6, SharedFunctionInfo::kCodeOffset));
++ __ addi(r6, r6, Operand(Code::kHeaderSize - kHeapObjectTag));
++ __ StoreP(r6, FieldMemOperand(r3, JSFunction::kCodeEntryOffset), r0);
++
++ // Return result. The argument function info has been popped already.
++ __ Ret();
++
++ __ bind(&check_optimized);
++
++ __ IncrementCounter(counters->fast_new_closure_try_optimized(), 1, r9, r10);
++
++ // r5 holds native context, r4 points to fixed array of 3-element entries
++ // (native context, optimized code, literals).
++ // The optimized code map must never be empty, so check the first elements.
++ Label install_optimized;
++ // Speculatively move code object into r7
++ __ LoadP(r7, FieldMemOperand(r4, FixedArray::kHeaderSize + kPointerSize));
++ __ LoadP(r8, FieldMemOperand(r4, FixedArray::kHeaderSize));
++ __ cmp(r5, r8);
++ __ beq(&install_optimized);
++
++ // Iterate through the rest of map backwards. r7 holds an index as a Smi.
++ Label loop;
++ __ LoadP(r7, FieldMemOperand(r4, FixedArray::kLengthOffset));
++ __ bind(&loop);
++ // Do not double check first entry.
++
++ __ CmpSmiLiteral(r7, Smi::FromInt(SharedFunctionInfo::kEntryLength), r0);
++ __ beq(&install_unoptimized);
++ // Skip an entry.
++ __ SubSmiLiteral(r7, r7, Smi::FromInt(SharedFunctionInfo::kEntryLength), r0);
++ __ addi(r8, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(r9, r7);
++ __ LoadPX(r8, MemOperand(r8, r9));
++ __ cmp(r5, r8);
++ __ bne(&loop);
++ // Hit: fetch the optimized code.
++ // TODO(penguin): potential to use x-form for this sequence
++ __ addi(r8, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(r9, r7);
++ __ add(r8, r8, r9);
++ __ LoadPU(r7, MemOperand(r8, kPointerSize));
++
++ __ bind(&install_optimized);
++ __ IncrementCounter(counters->fast_new_closure_install_optimized(),
++ 1, r9, r10);
++
++ // TODO(fschneider): Idea: store proper code pointers in the map and either
++ // unmangle them on marking or do nothing as the whole map is discarded on
++ // major GC anyway.
++ __ addi(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag));
++ __ StoreP(r7, FieldMemOperand(r3, JSFunction::kCodeEntryOffset), r0);
++
++ // Now link a function into a list of optimized functions.
++ __ LoadP(r7, ContextOperand(r5, Context::OPTIMIZED_FUNCTIONS_LIST));
++
++ __ StoreP(r7, FieldMemOperand(r3, JSFunction::kNextFunctionLinkOffset), r0);
++ // No need for write barrier as JSFunction (eax) is in the new space.
++
++ __ StoreP(r3, ContextOperand(r5, Context::OPTIMIZED_FUNCTIONS_LIST), r0);
++ // Store JSFunction (eax) into edx before issuing write barrier as
++ // it clobbers all the registers passed.
++ __ mr(r7, r3);
++ __ RecordWriteContextSlot(
++ r5,
++ Context::SlotOffset(Context::OPTIMIZED_FUNCTIONS_LIST),
++ r7,
++ r4,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs);
++
++ // Return result. The argument function info has been popped already.
++ __ Ret();
++
++ // Create a new closure through the slower runtime call.
++ __ bind(&gc);
++ __ LoadRoot(r7, Heap::kFalseValueRootIndex);
++ __ Push(cp, r6, r7);
++ __ TailCallRuntime(Runtime::kNewClosure, 3, 1);
++}
++
++
++void FastNewContextStub::Generate(MacroAssembler* masm) {
++ // Try to allocate the context in new space.
++ Label gc;
++ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
++
++ // Attempt to allocate the context in new space.
++ __ AllocateInNewSpace(FixedArray::SizeFor(length),
++ r3,
++ r4,
++ r5,
++ &gc,
++ TAG_OBJECT);
++
++ // Load the function from the stack.
++ __ LoadP(r6, MemOperand(sp, 0));
++
++ // Set up the object header.
++ __ LoadRoot(r4, Heap::kFunctionContextMapRootIndex);
++ __ LoadSmiLiteral(r5, Smi::FromInt(length));
++ __ StoreP(r5, FieldMemOperand(r3, FixedArray::kLengthOffset), r0);
++ __ StoreP(r4, FieldMemOperand(r3, HeapObject::kMapOffset), r0);
++
++ // Set up the fixed slots, copy the global object from the previous context.
++ __ LoadP(r5,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ LoadSmiLiteral(r4, Smi::FromInt(0));
++ __ StoreP(r6, MemOperand(r3, Context::SlotOffset(Context::CLOSURE_INDEX)),
++ r0);
++ __ StoreP(cp, MemOperand(r3, Context::SlotOffset(Context::PREVIOUS_INDEX)),
++ r0);
++ __ StoreP(r4, MemOperand(r3, Context::SlotOffset(Context::EXTENSION_INDEX)),
++ r0);
++ __ StoreP(r5,
++ MemOperand(r3, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)),
++ r0);
++
++ // Initialize the rest of the slots to undefined.
++ __ LoadRoot(r4, Heap::kUndefinedValueRootIndex);
++ for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
++ __ StoreP(r4, MemOperand(r3, Context::SlotOffset(i)), r0);
++ }
++
++ // Remove the on-stack argument and return.
++ __ mr(cp, r3);
++ __ pop();
++ __ Ret();
++
++ // Need to collect. Call into runtime system.
++ __ bind(&gc);
++ __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
++}
++
++
++void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
++ // Stack layout on entry:
++ //
++ // [sp]: function.
++ // [sp + kPointerSize]: serialized scope info
++
++ // Try to allocate the context in new space.
++ Label gc;
++ int length = slots_ + Context::MIN_CONTEXT_SLOTS;
++ __ AllocateInNewSpace(FixedArray::SizeFor(length),
++ r3, r4, r5, &gc, TAG_OBJECT);
++
++ // Load the function from the stack.
++ __ LoadP(r6, MemOperand(sp, 0));
++
++ // Load the serialized scope info from the stack.
++ __ LoadP(r4, MemOperand(sp, 1 * kPointerSize));
++
++ // Set up the object header.
++ __ LoadRoot(r5, Heap::kBlockContextMapRootIndex);
++ __ StoreP(r5, FieldMemOperand(r3, HeapObject::kMapOffset), r0);
++ __ LoadSmiLiteral(r5, Smi::FromInt(length));
++ __ StoreP(r5, FieldMemOperand(r3, FixedArray::kLengthOffset), r0);
++
++ // If this block context is nested in the native context we get a smi
++ // sentinel instead of a function. The block context should get the
++ // canonical empty function of the native context as its closure which
++ // we still have to look up.
++ Label after_sentinel;
++ __ JumpIfNotSmi(r6, &after_sentinel);
++ if (FLAG_debug_code) {
++ const char* message = "Expected 0 as a Smi sentinel";
++ __ cmpi(r6, Operand::Zero());
++ __ Assert(eq, message);
++ }
++ __ LoadP(r6, GlobalObjectOperand());
++ __ LoadP(r6, FieldMemOperand(r6, GlobalObject::kNativeContextOffset));
++ __ LoadP(r6, ContextOperand(r6, Context::CLOSURE_INDEX));
++ __ bind(&after_sentinel);
++
++ // Set up the fixed slots, copy the global object from the previous context.
++ __ LoadP(r5, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
++ __ StoreP(r6, ContextOperand(r3, Context::CLOSURE_INDEX), r0);
++ __ StoreP(cp, ContextOperand(r3, Context::PREVIOUS_INDEX), r0);
++ __ StoreP(r4, ContextOperand(r3, Context::EXTENSION_INDEX), r0);
++ __ StoreP(r5, ContextOperand(r3, Context::GLOBAL_OBJECT_INDEX), r0);
++
++ // Initialize the rest of the slots to the hole value.
++ __ LoadRoot(r4, Heap::kTheHoleValueRootIndex);
++ for (int i = 0; i < slots_; i++) {
++ __ StoreP(r4, ContextOperand(r3, i + Context::MIN_CONTEXT_SLOTS), r0);
++ }
++
++ // Remove the on-stack argument and return.
++ __ mr(cp, r3);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ // Need to collect. Call into runtime system.
++ __ bind(&gc);
++ __ TailCallRuntime(Runtime::kPushBlockContext, 2, 1);
++}
++
++
++static void GenerateFastCloneShallowArrayCommon(
++ MacroAssembler* masm,
++ int length,
++ FastCloneShallowArrayStub::Mode mode,
++ Label* fail) {
++ // Registers on entry:
++ //
++ // r6: boilerplate literal array.
++ ASSERT(mode != FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS);
++
++ // All sizes here are multiples of kPointerSize.
++ int elements_size = 0;
++ if (length > 0) {
++ elements_size = mode == FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
++ ? FixedDoubleArray::SizeFor(length)
++ : FixedArray::SizeFor(length);
++ }
++ int size = JSArray::kSize + elements_size;
++
++ // Allocate both the JS array and the elements array in one big
++ // allocation. This avoids multiple limit checks.
++ __ AllocateInNewSpace(size,
++ r3,
++ r4,
++ r5,
++ fail,
++ TAG_OBJECT);
++
++ // Copy the JS array part.
++ for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
++ if ((i != JSArray::kElementsOffset) || (length == 0)) {
++ __ LoadP(r4, FieldMemOperand(r6, i));
++ __ StoreP(r4, FieldMemOperand(r3, i), r0);
++ }
++ }
++
++ if (length > 0) {
++ // Get hold of the elements array of the boilerplate and setup the
++ // elements pointer in the resulting object.
++ __ LoadP(r6, FieldMemOperand(r6, JSArray::kElementsOffset));
++ __ addi(r5, r3, Operand(JSArray::kSize));
++ __ StoreP(r5, FieldMemOperand(r3, JSArray::kElementsOffset), r0);
++
++ // Copy the elements array.
++ ASSERT((elements_size % kPointerSize) == 0);
++ __ CopyFields(r5, r6, r4.bit(), elements_size / kPointerSize);
++ }
++}
++
++void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
++ // Stack layout on entry:
++ //
++ // [sp]: constant elements.
++ // [sp + kPointerSize]: literal index.
++ // [sp + (2 * kPointerSize)]: literals array.
++
++ // Load boilerplate object into r3 and check if we need to create a
++ // boilerplate.
++ Label slow_case;
++ __ LoadP(r6, MemOperand(sp, 2 * kPointerSize));
++ __ LoadP(r3, MemOperand(sp, 1 * kPointerSize));
++ __ addi(r6, r6, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++
++ __ mr(r0, r3);
++ __ SmiToPtrArrayOffset(r3, r3);
++ __ LoadPX(r6, MemOperand(r6, r3));
++ __ mr(r3, r0);
++
++ __ CompareRoot(r6, Heap::kUndefinedValueRootIndex);
++ __ beq(&slow_case);
++
++ FastCloneShallowArrayStub::Mode mode = mode_;
++ if (mode == CLONE_ANY_ELEMENTS) {
++ Label double_elements, check_fast_elements;
++ __ LoadP(r3, FieldMemOperand(r6, JSArray::kElementsOffset));
++ __ LoadP(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ CompareRoot(r3, Heap::kFixedCOWArrayMapRootIndex);
++ __ bne(&check_fast_elements);
++ GenerateFastCloneShallowArrayCommon(masm, 0,
++ COPY_ON_WRITE_ELEMENTS, &slow_case);
++ // Return and remove the on-stack parameters.
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ Ret();
++
++ __ bind(&check_fast_elements);
++ __ CompareRoot(r3, Heap::kFixedArrayMapRootIndex);
++ __ bne(&double_elements);
++ GenerateFastCloneShallowArrayCommon(masm, length_,
++ CLONE_ELEMENTS, &slow_case);
++ // Return and remove the on-stack parameters.
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ Ret();
++
++ __ bind(&double_elements);
++ mode = CLONE_DOUBLE_ELEMENTS;
++ // Fall through to generate the code to handle double elements.
++ }
++
++ if (FLAG_debug_code) {
++ const char* message;
++ Heap::RootListIndex expected_map_index;
++ if (mode == CLONE_ELEMENTS) {
++ message = "Expected (writable) fixed array";
++ expected_map_index = Heap::kFixedArrayMapRootIndex;
++ } else if (mode == CLONE_DOUBLE_ELEMENTS) {
++ message = "Expected (writable) fixed double array";
++ expected_map_index = Heap::kFixedDoubleArrayMapRootIndex;
++ } else {
++ ASSERT(mode == COPY_ON_WRITE_ELEMENTS);
++ message = "Expected copy-on-write fixed array";
++ expected_map_index = Heap::kFixedCOWArrayMapRootIndex;
++ }
++ __ push(r6);
++ __ LoadP(r6, FieldMemOperand(r6, JSArray::kElementsOffset));
++ __ LoadP(r6, FieldMemOperand(r6, HeapObject::kMapOffset));
++ __ CompareRoot(r6, expected_map_index);
++ __ Assert(eq, message);
++ __ pop(r6);
++ }
++
++ GenerateFastCloneShallowArrayCommon(masm, length_, mode, &slow_case);
++
++ // Return and remove the on-stack parameters.
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ Ret();
++
++ __ bind(&slow_case);
++ __ TailCallRuntime(Runtime::kCreateArrayLiteralShallow, 3, 1);
++}
++
++
++void FastCloneShallowObjectStub::Generate(MacroAssembler* masm) {
++ // Stack layout on entry:
++ //
++ // [sp]: object literal flags.
++ // [sp + kPointerSize]: constant properties.
++ // [sp + (2 * kPointerSize)]: literal index.
++ // [sp + (3 * kPointerSize)]: literals array.
++
++ // Load boilerplate object into r3 and check if we need to create a
++ // boilerplate.
++ Label slow_case;
++ __ LoadP(r6, MemOperand(sp, 3 * kPointerSize));
++ __ LoadP(r3, MemOperand(sp, 2 * kPointerSize));
++ __ addi(r6, r6, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ mr(r0, r3);
++ __ SmiToPtrArrayOffset(r3, r3);
++ __ LoadPX(r6, MemOperand(r6, r3));
++ __ mr(r3, r0);
++
++ __ CompareRoot(r6, Heap::kUndefinedValueRootIndex);
++ __ beq(&slow_case);
++
++ // Check that the boilerplate contains only fast properties and we can
++ // statically determine the instance size.
++ int size = JSObject::kHeaderSize + length_ * kPointerSize;
++ __ LoadP(r3, FieldMemOperand(r6, HeapObject::kMapOffset));
++ __ lbz(r3, FieldMemOperand(r3, Map::kInstanceSizeOffset));
++ __ cmpi(r3, Operand(size >> kPointerSizeLog2));
++ __ bne(&slow_case);
++
++ // Allocate the JS object and copy header together with all in-object
++ // properties from the boilerplate.
++ __ AllocateInNewSpace(size, r3, r4, r5, &slow_case, TAG_OBJECT);
++ for (int i = 0; i < size; i += kPointerSize) {
++ __ LoadP(r4, FieldMemOperand(r6, i));
++ __ StoreP(r4, FieldMemOperand(r3, i), r0);
++ }
++
++ // Return and remove the on-stack parameters.
++ __ addi(sp, sp, Operand(4 * kPointerSize));
++ __ Ret();
++
++ __ bind(&slow_case);
++ __ TailCallRuntime(Runtime::kCreateObjectLiteralShallow, 4, 1);
++}
++
++
++// Takes a Smi and converts to an IEEE 64 bit floating point value in two
++// registers. The format is 1 sign bit, 11 exponent bits (biased 1023) and
++// 52 fraction bits (20 in the first word, 32 in the second). Zeros is a
++// scratch register. Destroys the source register. No GC occurs during this
++// stub so you don't have to set up the frame.
++class ConvertToDoubleStub : public CodeStub {
++ public:
++ ConvertToDoubleStub(Register result_reg_1,
++ Register result_reg_2,
++ Register source_reg,
++ Register scratch_reg)
++ : result1_(result_reg_1),
++ result2_(result_reg_2),
++ source_(source_reg),
++ zeros_(scratch_reg) { }
++
++ private:
++ Register result1_;
++ Register result2_;
++ Register source_;
++ Register zeros_;
++
++ // Minor key encoding in 16 bits.
++ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
++ class OpBits: public BitField<Token::Value, 2, 14> {};
++
++ Major MajorKey() { return ConvertToDouble; }
++ int MinorKey() {
++ // Encode the parameters in a unique 16 bit value.
++ return result1_.code() +
++ (result2_.code() << 4) +
++ (source_.code() << 8) +
++ (zeros_.code() << 12);
++ }
++
++ void Generate(MacroAssembler* masm);
++};
++
++void FloatingPointHelper::LoadSmis(MacroAssembler* masm,
++ Register scratch1,
++ Register scratch2) {
++ __ SmiToDoubleFPRegister(r3, d2, scratch1);
++ __ SmiToDoubleFPRegister(r4, d1, scratch1);
++}
++
++// needs cleanup for extra parameters that are unused
++void FloatingPointHelper::LoadOperands(
++ MacroAssembler* masm,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* slow) {
++ // Load right operand (r3) to d2
++ LoadNumber(masm, r3, d2, heap_number_map, scratch1, scratch2, slow);
++
++ // Load left operand (r4) to d1
++ LoadNumber(masm, r4, d1, heap_number_map, scratch1, scratch2, slow);
++}
++
++// needs cleanup for extra parameters that are unused
++// also needs a scratch double register instead of d3
++void FloatingPointHelper::LoadNumber(MacroAssembler* masm,
++ Register object,
++ DwVfpRegister dst,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* not_number) {
++ __ AssertRootValue(heap_number_map,
++ Heap::kHeapNumberMapRootIndex,
++ "HeapNumberMap register clobbered.");
++
++ Label is_smi, done;
++
++ // Smi-check
++ __ UntagAndJumpIfSmi(scratch1, object, &is_smi);
++ // Heap number check
++ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_number);
++
++ // Handle loading a double from a heap number
++ // Load the double from tagged HeapNumber to double register.
++ __ lfd(dst, FieldMemOperand(object, HeapNumber::kValueOffset));
++ __ b(&done);
++
++ // Handle loading a double from a smi.
++ __ bind(&is_smi);
++
++ // Convert untagged smi to double using FP instructions.
++ FloatingPointHelper::ConvertIntToDouble(masm, scratch1, dst);
++
++ __ bind(&done);
++}
++
++
++void FloatingPointHelper::ConvertNumberToInt32(MacroAssembler* masm,
++ Register object,
++ Register dst,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ DwVfpRegister double_scratch,
++ Label* not_number) {
++ __ AssertRootValue(heap_number_map,
++ Heap::kHeapNumberMapRootIndex,
++ "HeapNumberMap register clobbered.");
++ Label done;
++ Label not_in_int32_range;
++
++ __ UntagAndJumpIfSmi(dst, object, &done);
++ __ LoadP(scratch1, FieldMemOperand(object, HeapNumber::kMapOffset));
++ __ cmp(scratch1, heap_number_map);
++ __ bne(not_number);
++ __ ConvertToInt32(object,
++ dst,
++ scratch1,
++ scratch2,
++ double_scratch,
++ ¬_in_int32_range);
++ __ b(&done);
++
++ __ bind(¬_in_int32_range);
++ __ lwz(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
++ __ lwz(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset));
++
++ __ EmitOutOfInt32RangeTruncate(dst,
++ scratch1,
++ scratch2,
++ scratch3);
++ __ bind(&done);
++}
++
++
++void FloatingPointHelper::ConvertIntToDouble(MacroAssembler* masm,
++ Register src,
++ DwVfpRegister double_dst) {
++ ASSERT(!src.is(r0));
++
++ __ subi(sp, sp, Operand(8)); // reserve one temporary double on the stack
++
++ // sign-extend src to 64-bit and store it to temp double on the stack
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(r0, src);
++ __ std(r0, MemOperand(sp, 0));
++#else
++ __ srawi(r0, src, 31);
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(r0, MemOperand(sp, 4));
++ __ stw(src, MemOperand(sp, 0));
++#else
++ __ stw(r0, MemOperand(sp, 0));
++ __ stw(src, MemOperand(sp, 4));
++#endif
++#endif
++
++ // load into FPR
++ __ lfd(double_dst, MemOperand(sp, 0));
++
++ __ addi(sp, sp, Operand(8)); // restore stack
++
++ // convert to double
++ __ fcfid(double_dst, double_dst);
++}
++
++
++void FloatingPointHelper::ConvertUnsignedIntToDouble(MacroAssembler* masm,
++ Register src,
++ DwVfpRegister double_dst) {
++ ASSERT(!src.is(r0));
++
++ __ subi(sp, sp, Operand(8)); // reserve one temporary double on the stack
++
++ // zero-extend src to 64-bit and store it to temp double on the stack
++#if V8_TARGET_ARCH_PPC64
++ __ clrldi(r0, src, Operand(32));
++ __ std(r0, MemOperand(sp, 0));
++#else
++ __ li(r0, Operand::Zero());
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(r0, MemOperand(sp, 4));
++ __ stw(src, MemOperand(sp, 0));
++#else
++ __ stw(r0, MemOperand(sp, 0));
++ __ stw(src, MemOperand(sp, 4));
++#endif
++#endif
++
++ // load into FPR
++ __ lfd(double_dst, MemOperand(sp, 0));
++
++ __ addi(sp, sp, Operand(8)); // restore stack
++
++ // convert to double
++ __ fcfid(double_dst, double_dst);
++}
++
++void FloatingPointHelper::ConvertIntToFloat(MacroAssembler* masm,
++ const DwVfpRegister dst,
++ const Register src,
++ const Register int_scratch) {
++ __ subi(sp, sp, Operand(8)); // reserve one temporary double on the stack
++
++ // sign-extend src to 64-bit and store it to temp double on the stack
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(int_scratch, src);
++ __ std(int_scratch, MemOperand(sp, 0));
++#else
++ __ srawi(int_scratch, src, 31);
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(int_scratch, MemOperand(sp, 4));
++ __ stw(src, MemOperand(sp, 0));
++#else
++ __ stw(int_scratch, MemOperand(sp, 0));
++ __ stw(src, MemOperand(sp, 4));
++#endif
++#endif
++
++ // load sign-extended src into FPR
++ __ lfd(dst, MemOperand(sp, 0));
++
++ __ addi(sp, sp, Operand(8)); // restore stack
++
++ __ fcfid(dst, dst);
++ __ frsp(dst, dst);
++}
++
++void FloatingPointHelper::LoadNumberAsInt32Double(MacroAssembler* masm,
++ Register object,
++ DwVfpRegister double_dst,
++ DwVfpRegister double_scratch,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* not_int32) {
++ ASSERT(!scratch1.is(object) && !scratch2.is(object));
++ ASSERT(!scratch1.is(scratch2));
++ ASSERT(!heap_number_map.is(object) &&
++ !heap_number_map.is(scratch1) &&
++ !heap_number_map.is(scratch2));
++
++ Label done, obj_is_not_smi;
++
++ __ JumpIfNotSmi(object, &obj_is_not_smi);
++ __ SmiUntag(scratch1, object);
++ ConvertIntToDouble(masm, scratch1, double_dst);
++ __ b(&done);
++
++ __ bind(&obj_is_not_smi);
++ __ AssertRootValue(heap_number_map,
++ Heap::kHeapNumberMapRootIndex,
++ "HeapNumberMap register clobbered.");
++ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
++
++ // Load the double value.
++ __ lfd(double_dst, FieldMemOperand(object, HeapNumber::kValueOffset));
++
++ __ EmitVFPTruncate(kRoundToZero,
++ scratch1,
++ double_dst,
++ scratch2,
++ double_scratch,
++ kCheckForInexactConversion);
++
++ // Jump to not_int32 if the operation did not succeed.
++ __ bne(not_int32);
++
++ __ bind(&done);
++}
++
++
++void FloatingPointHelper::LoadNumberAsInt32(MacroAssembler* masm,
++ Register object,
++ Register dst,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ DwVfpRegister double_scratch0,
++ DwVfpRegister double_scratch1,
++ Label* not_int32) {
++ ASSERT(!dst.is(object));
++ ASSERT(!scratch1.is(object) && !scratch2.is(object) &&
!scratch3.is(object));
++ ASSERT(!scratch1.is(scratch2) &&
++ !scratch1.is(scratch3) &&
++ !scratch2.is(scratch3));
++
++ Label done;
++
++ __ UntagAndJumpIfSmi(dst, object, &done);
++
++ __ AssertRootValue(heap_number_map,
++ Heap::kHeapNumberMapRootIndex,
++ "HeapNumberMap register clobbered.");
++ __ JumpIfNotHeapNumber(object, heap_number_map, scratch1, not_int32);
++
++ // Load the double value.
++ __ lfd(double_scratch0, FieldMemOperand(object, HeapNumber::kValueOffset));
++
++ __ EmitVFPTruncate(kRoundToZero,
++ dst,
++ double_scratch0,
++ scratch1,
++ double_scratch1,
++ kCheckForInexactConversion);
++
++ // Jump to not_int32 if the operation did not succeed.
++ __ bne(not_int32);
++
++ __ bind(&done);
++}
++
++
++void FloatingPointHelper::DoubleIs32BitInteger(MacroAssembler* masm,
++ Register src1,
++ Register src2,
++ Register dst,
++ Register scratch,
++ Label* not_int32) {
++ // Get exponent alone in scratch.
++ STATIC_ASSERT(HeapNumber::kExponentMask == 0x7ff00000u);
++ __ ExtractBitMask(scratch, src1, HeapNumber::kExponentMask);
++
++ // Substract the bias from the exponent.
++ __ addi(scratch, scratch, Operand(-HeapNumber::kExponentBias));
++
++ // src1: higher (exponent) part of the double value.
++ // src2: lower (mantissa) part of the double value.
++ // scratch: unbiased exponent.
++
++ // Fast cases. Check for obvious non 32-bit integer values.
++ // Negative exponent cannot yield 32-bit integers.
++ __ cmpi(scratch, Operand::Zero());
++ __ blt(not_int32);
++ // Exponent greater than 31 cannot yield 32-bit integers.
++ // Also, a positive value with an exponent equal to 31 is outside of the
++ // signed 32-bit integer range.
++ // Another way to put it is that if (exponent - signbit) > 30 then the
++ // number cannot be represented as an int32.
++ Register tmp = dst;
++ __ ExtractSignBit32(tmp, src1);
++ __ sub(tmp, scratch, tmp);
++ __ cmpi(tmp, Operand(30));
++ __ bgt(not_int32);
++ // - Check whether bits [21:0] in the mantissa are not null.
++ __ TestBitRange(src2, 21, 0, r0);
++ __ bne(not_int32, cr0);
++
++ // Otherwise the exponent needs to be big enough to shift left all the
++ // non zero bits left. So we need the (30 - exponent) last bits of the
++ // 31 higher bits of the mantissa to be null.
++ // Because bits [21:0] are null, we can check instead that the
++ // (32 - exponent) last bits of the 32 higher bits of the mantissa are null.
++
++ // Get the 32 higher bits of the mantissa in dst.
++ STATIC_ASSERT(HeapNumber::kMantissaBitsInTopWord == 20);
++ STATIC_ASSERT(HeapNumber::kNonMantissaBitsInTopWord == 12);
++ __ ExtractBitRange(dst, src2, 31, HeapNumber::kMantissaBitsInTopWord);
++ __ slwi(src1, src1, Operand(HeapNumber::kNonMantissaBitsInTopWord));
++ __ orx(dst, dst, src1);
++
++ // Create the mask and test the lower bits (of the higher bits).
++ __ subfic(scratch, scratch, Operand(32));
++ __ li(src2, Operand(1));
++ __ ShiftLeft(src1, src2, scratch);
++ __ addi(src1, src1, Operand(-1));
++ __ and_(r0, dst, src1, SetRC);
++ __ bne(not_int32, cr0);
++}
++
++
++void FloatingPointHelper::CallCCodeForDoubleOperation(
++ MacroAssembler* masm,
++ Token::Value op,
++ Register heap_number_result,
++ Register scratch) {
++ // d1 - first arg, d2 - second arg
++ // d1 return value
++
++ // Assert that heap_number_result is callee-saved.
++ // PowerPC doesn't preserve r8.. need to handle this specially
++ // We currently always use r8 to pass it.
++ ASSERT(heap_number_result.is(r8));
++ __ push(r8);
++
++ // Push the current return address before the C call. Return will be
++ // through pop() below.
++ __ mflr(r0);
++ __ push(r0);
++ __ PrepareCallCFunction(0, 2, scratch);
++
++ {
++ AllowExternalCallThatCantCauseGC scope(masm);
++ __ CallCFunction(
++ ExternalReference::double_fp_operation(op, masm->isolate()), 0, 2);
++ }
++ // load saved r8 value, restore lr
++ __ pop(r0);
++ __ mtlr(r0);
++ __ pop(r8);
++
++ // Store answer in the overwritable heap number. Double returned in d1
++ __ stfd(d1, FieldMemOperand(heap_number_result, HeapNumber::kValueOffset));
++
++ // Place heap_number_result in r3 and return to the pushed return address.
++ __ mr(r3, heap_number_result);
++ __ blr();
++}
++
++// Handle the case where the lhs and rhs are the same object.
++// Equality is almost reflexive (everything but NaN), so this is a test
++// for "identity and not NaN".
++static void EmitIdenticalObjectComparison(MacroAssembler* masm,
++ Label* slow,
++ Condition cond,
++ bool never_nan_nan) {
++ Label not_identical;
++ Label heap_number, return_equal;
++ __ cmp(r3, r4);
++ __ bne(¬_identical);
++
++ // The two objects are identical. If we know that one of them isn't NaN then
++ // we now know they test equal.
++ if (cond != eq || !never_nan_nan) {
++ // Test for NaN. Sadly, we can't just compare to FACTORY->nan_value(),
++ // so we do the second best thing - test it ourselves.
++ // They are both equal and they are not both Smis so both of them are not
++ // Smis. If it's not a heap number, then return equal.
++ if (cond == lt || cond == gt) {
++ __ CompareObjectType(r3, r7, r7, FIRST_SPEC_OBJECT_TYPE);
++ __ bge(slow);
++ } else {
++ __ CompareObjectType(r3, r7, r7, HEAP_NUMBER_TYPE);
++ __ beq(&heap_number);
++ // Comparing JS objects with <=, >= is complicated.
++ if (cond != eq) {
++ __ cmpi(r7, Operand(FIRST_SPEC_OBJECT_TYPE));
++ __ bge(slow);
++ // Normally here we fall through to return_equal, but undefined is
++ // special: (undefined == undefined) == true, but
++ // (undefined <= undefined) == false! See ECMAScript 11.8.5.
++ if (cond == le || cond == ge) {
++ __ cmpi(r7, Operand(ODDBALL_TYPE));
++ __ bne(&return_equal);
++ __ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
++ __ cmp(r3, r5);
++ __ bne(&return_equal);
++ if (cond == le) {
++ // undefined <= undefined should fail.
++ __ li(r3, Operand(GREATER));
++ } else {
++ // undefined >= undefined should fail.
++ __ li(r3, Operand(LESS));
++ }
++ __ Ret();
++ }
++ }
++ }
++ }
++
++ __ bind(&return_equal);
++ if (cond == lt) {
++ __ li(r3, Operand(GREATER)); // Things aren't less than themselves.
++ } else if (cond == gt) {
++ __ li(r3, Operand(LESS)); // Things aren't greater than themselves.
++ } else {
++ __ li(r3, Operand(EQUAL)); // Things are <=, >=, ==, === themselves.
++ }
++ __ Ret();
++
++ if (cond != eq || !never_nan_nan) {
++ // For less and greater we don't have to check for NaN since the result of
++ // x < x is false regardless. For the others here is some code to check
++ // for NaN.
++ if (cond != lt && cond != gt) {
++ __ bind(&heap_number);
++ // It is a heap number, so return non-equal if it's NaN and equal if it's
++ // not NaN.
++
++ // The representation of NaN values has all exponent bits (52..62) set,
++ // and not all mantissa bits (0..51) clear.
++ // Read top bits of double representation (second word of value).
++ __ lwz(r5, FieldMemOperand(r3, HeapNumber::kExponentOffset));
++ // Test that exponent bits are all set.
++ STATIC_ASSERT(HeapNumber::kExponentMask == 0x7ff00000u);
++ __ ExtractBitMask(r6, r5, HeapNumber::kExponentMask);
++ __ cmpli(r6, Operand(0x7ff));
++ __ bne(&return_equal);
++
++ // Shift out flag and all exponent bits, retaining only mantissa.
++ __ slwi(r5, r5, Operand(HeapNumber::kNonMantissaBitsInTopWord));
++ // Or with all low-bits of mantissa.
++ __ lwz(r6, FieldMemOperand(r3, HeapNumber::kMantissaOffset));
++ __ orx(r3, r6, r5);
++ __ cmpi(r3, Operand::Zero());
++ // For equal we already have the right value in r3: Return zero (equal)
++ // if all bits in mantissa are zero (it's an Infinity) and non-zero if
++ // not (it's a NaN). For <= and >= we need to load r0 with the failing
++ // value if it's a NaN.
++ if (cond != eq) {
++ Label not_equal;
++ __ bne(¬_equal);
++ // All-zero means Infinity means equal.
++ __ Ret();
++ __ bind(¬_equal);
++ if (cond == le) {
++ __ li(r3, Operand(GREATER)); // NaN <= NaN should fail.
++ } else {
++ __ li(r3, Operand(LESS)); // NaN >= NaN should fail.
++ }
++ }
++ __ Ret();
++ }
++ // No fall through here.
++ }
++
++ __ bind(¬_identical);
++}
++
++
++// See comment at call site.
++static void EmitSmiNonsmiComparison(MacroAssembler* masm,
++ Register lhs,
++ Register rhs,
++ Label* lhs_not_nan,
++ Label* slow,
++ bool strict) {
++ ASSERT((lhs.is(r3) && rhs.is(r4)) ||
++ (lhs.is(r4) && rhs.is(r3)));
++
++ Label rhs_is_smi;
++ __ JumpIfSmi(rhs, &rhs_is_smi);
++
++ // Lhs is a Smi. Check whether the rhs is a heap number.
++ __ CompareObjectType(rhs, r6, r7, HEAP_NUMBER_TYPE);
++ if (strict) {
++ // If rhs is not a number and lhs is a Smi then strict equality cannot
++ // succeed. Return non-equal
++ // If rhs is r3 then there is already a non zero value in it.
++ Label skip;
++ __ beq(&skip);
++ if (!rhs.is(r3)) {
++ __ mov(r3, Operand(NOT_EQUAL));
++ }
++ __ Ret();
++ __ bind(&skip);
++ } else {
++ // Smi compared non-strictly with a non-Smi non-heap-number. Call
++ // the runtime.
++ __ bne(slow);
++ }
++
++ // Lhs is a smi, rhs is a number.
++ // Convert lhs to a double in d7.
++ __ SmiToDoubleFPRegister(lhs, d7, r10);
++ // Load the double from rhs, tagged HeapNumber r3, to d6.
++ __ lfd(d6, FieldMemOperand(rhs, HeapNumber::kValueOffset));
++
++ // We now have both loaded as doubles but we can skip the lhs nan check
++ // since it's a smi.
++ __ b(lhs_not_nan);
++
++ __ bind(&rhs_is_smi);
++ // Rhs is a smi. Check whether the non-smi lhs is a heap number.
++ __ CompareObjectType(lhs, r7, r7, HEAP_NUMBER_TYPE);
++ if (strict) {
++ // If lhs is not a number and rhs is a smi then strict equality cannot
++ // succeed. Return non-equal.
++ // If lhs is r3 then there is already a non zero value in it.
++ Label skip;
++ __ beq(&skip);
++ if (!lhs.is(r3)) {
++ __ mov(r3, Operand(NOT_EQUAL));
++ }
++ __ Ret();
++ __ bind(&skip);
++ } else {
++ // Smi compared non-strictly with a non-smi non-heap-number. Call
++ // the runtime.
++ __ bne(slow);
++ }
++
++ // Rhs is a smi, lhs is a heap number.
++ // Load the double from lhs, tagged HeapNumber r4, to d7.
++ __ lfd(d7, FieldMemOperand(lhs, HeapNumber::kValueOffset));
++ // Convert rhs to a double in d6.
++ __ SmiToDoubleFPRegister(rhs, d6, r10);
++ // Fall through to both_loaded_as_doubles.
++}
++
++// See comment at call site.
++static void EmitStrictTwoHeapObjectCompare(MacroAssembler* masm,
++ Register lhs,
++ Register rhs) {
++ ASSERT((lhs.is(r3) && rhs.is(r4)) ||
++ (lhs.is(r4) && rhs.is(r3)));
++
++ // If either operand is a JS object or an oddball value, then they are
++ // not equal since their pointers are different.
++ // There is no test for undetectability in strict equality.
++ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
++ Label first_non_object;
++ // Get the type of the first operand into r5 and compare it with
++ // FIRST_SPEC_OBJECT_TYPE.
++ __ CompareObjectType(rhs, r5, r5, FIRST_SPEC_OBJECT_TYPE);
++ __ blt(&first_non_object);
++
++ // Return non-zero (r3 is not zero)
++ Label return_not_equal;
++ __ bind(&return_not_equal);
++ __ Ret();
++
++ __ bind(&first_non_object);
++ // Check for oddballs: true, false, null, undefined.
++ __ cmpi(r5, Operand(ODDBALL_TYPE));
++ __ beq(&return_not_equal);
++
++ __ CompareObjectType(lhs, r6, r6, FIRST_SPEC_OBJECT_TYPE);
++ __ bge(&return_not_equal);
++
++ // Check for oddballs: true, false, null, undefined.
++ __ cmpi(r6, Operand(ODDBALL_TYPE));
++ __ beq(&return_not_equal);
++
++ // Now that we have the types we might as well check for symbol-symbol.
++ // Ensure that no non-strings have the symbol bit set.
++ STATIC_ASSERT(LAST_TYPE < kNotStringTag + kIsSymbolMask);
++ STATIC_ASSERT(kSymbolTag != 0);
++ __ and_(r5, r5, r6);
++ __ andi(r0, r5, Operand(kIsSymbolMask));
++ __ bne(&return_not_equal, cr0);
++}
++
++
++static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
++ Register lhs,
++ Register rhs,
++ Label* both_loaded_as_doubles,
++ Label* not_heap_numbers,
++ Label* slow) {
++ ASSERT((lhs.is(r3) && rhs.is(r4)) ||
++ (lhs.is(r4) && rhs.is(r3)));
++
++ __ CompareObjectType(rhs, r6, r5, HEAP_NUMBER_TYPE);
++ __ bne(not_heap_numbers);
++ __ LoadP(r5, FieldMemOperand(lhs, HeapObject::kMapOffset));
++ __ cmp(r5, r6);
++ __ bne(slow); // First was a heap number, second wasn't. Go slow case.
++
++ // Both are heap numbers. Load them up then jump to the code we have
++ // for that.
++ __ lfd(d6, FieldMemOperand(rhs, HeapNumber::kValueOffset));
++ __ lfd(d7, FieldMemOperand(lhs, HeapNumber::kValueOffset));
++
++ __ b(both_loaded_as_doubles);
++}
++
++
++// Fast negative check for symbol-to-symbol equality.
++static void EmitCheckForSymbolsOrObjects(MacroAssembler* masm,
++ Register lhs,
++ Register rhs,
++ Label* possible_strings,
++ Label* not_both_strings) {
++ ASSERT((lhs.is(r3) && rhs.is(r4)) ||
++ (lhs.is(r4) && rhs.is(r3)));
++
++ // r5 is object type of rhs.
++ // Ensure that no non-strings have the symbol bit set.
++ Label object_test;
++ STATIC_ASSERT(kSymbolTag != 0);
++ __ andi(r0, r5, Operand(kIsNotStringMask));
++ __ bne(&object_test, cr0);
++ __ andi(r0, r5, Operand(kIsSymbolMask));
++ __ beq(possible_strings, cr0);
++ __ CompareObjectType(lhs, r6, r6, FIRST_NONSTRING_TYPE);
++ __ bge(not_both_strings);
++ __ andi(r0, r6, Operand(kIsSymbolMask));
++ __ beq(possible_strings, cr0);
++
++ // Both are symbols. We already checked they weren't the same pointer
++ // so they are not equal.
++ __ li(r3, Operand(NOT_EQUAL));
++ __ Ret();
++
++ __ bind(&object_test);
++ __ cmpi(r5, Operand(FIRST_SPEC_OBJECT_TYPE));
++ __ blt(not_both_strings);
++ __ CompareObjectType(lhs, r5, r6, FIRST_SPEC_OBJECT_TYPE);
++ __ blt(not_both_strings);
++ // If both objects are undetectable, they are equal. Otherwise, they
++ // are not equal, since they are different objects and an object is not
++ // equal to undefined.
++ __ LoadP(r6, FieldMemOperand(rhs, HeapObject::kMapOffset));
++ __ lbz(r5, FieldMemOperand(r5, Map::kBitFieldOffset));
++ __ lbz(r6, FieldMemOperand(r6, Map::kBitFieldOffset));
++ __ and_(r3, r5, r6);
++ __ andi(r3, r3, Operand(1 << Map::kIsUndetectable));
++ __ xori(r3, r3, Operand(1 << Map::kIsUndetectable));
++ __ Ret();
++}
++
++
++void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
++ Register object,
++ Register result,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ bool object_is_smi,
++ Label* not_found) {
++ // Use of registers. Register result is used as a temporary.
++ Register number_string_cache = result;
++ Register mask = scratch3;
++
++ // Load the number string cache.
++ __ LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex);
++
++ // Make the hash mask from the length of the number string cache. It
++ // contains two elements (number and string) for each cache entry.
++ __ LoadP(mask, FieldMemOperand(number_string_cache,
++ FixedArray::kLengthOffset));
++ // Divide length by two (length is a smi).
++ __ ShiftRightArithImm(mask, mask, kSmiTagSize + kSmiShiftSize + 1);
++ __ subi(mask, mask, Operand(1)); // Make mask.
++
++ // Calculate the entry in the number string cache. The hash value in the
++ // number string cache for smis is just the smi value, and the hash for
++ // doubles is the xor of the upper and lower words. See
++ // Heap::GetNumberStringCache.
++ Isolate* isolate = masm->isolate();
++ Label is_smi;
++ Label load_result_from_cache;
++ if (!object_is_smi) {
++ __ JumpIfSmi(object, &is_smi);
++
++ __ CheckMap(object,
++ scratch1,
++ Heap::kHeapNumberMapRootIndex,
++ not_found,
++ DONT_DO_SMI_CHECK);
++
++ STATIC_ASSERT(8 == kDoubleSize);
++ __ lwz(scratch1, FieldMemOperand(object, HeapNumber::kExponentOffset));
++ __ lwz(scratch2, FieldMemOperand(object, HeapNumber::kMantissaOffset));
++ __ xor_(scratch1, scratch1, scratch2);
++ __ and_(scratch1, scratch1, mask);
++
++ // Calculate address of entry in string cache: each entry consists
++ // of two pointer sized fields.
++ __ ShiftLeftImm(scratch1, scratch1, Operand(kPointerSizeLog2 + 1));
++ __ add(scratch1, number_string_cache, scratch1);
++
++ Register probe = mask;
++ __ LoadP(probe, FieldMemOperand(scratch1, FixedArray::kHeaderSize));
++ __ JumpIfSmi(probe, not_found);
++ __ lfd(d0, FieldMemOperand(object, HeapNumber::kValueOffset));
++ __ lfd(d1, FieldMemOperand(probe, HeapNumber::kValueOffset));
++ __ fcmpu(d0, d1);
++ __ bne(not_found); // The cache did not contain this value.
++ __ b(&load_result_from_cache);
++ }
++
++ __ bind(&is_smi);
++ Register scratch = scratch1;
++ __ SmiUntag(scratch, object);
++ __ and_(scratch, mask, scratch);
++ // Calculate address of entry in string cache: each entry consists
++ // of two pointer sized fields.
++ __ ShiftLeftImm(scratch, scratch, Operand(kPointerSizeLog2 + 1));
++ __ add(scratch, number_string_cache, scratch);
++
++ // Check if the entry is the smi we are looking for.
++ Register probe = mask;
++ __ LoadP(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize));
++ __ cmp(object, probe);
++ __ bne(not_found);
++
++ // Get the result from the cache.
++ __ bind(&load_result_from_cache);
++ __ LoadP(result,
++ FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize));
++ __ IncrementCounter(isolate->counters()->number_to_string_native(),
++ 1,
++ scratch1,
++ scratch2);
++}
++
++
++void NumberToStringStub::Generate(MacroAssembler* masm) {
++ Label runtime;
++
++ __ LoadP(r4, MemOperand(sp, 0));
++
++ // Generate code to lookup number in the number string cache.
++ GenerateLookupNumberStringCache(masm, r4, r3, r5, r6, r7, false, &runtime);
++ __ addi(sp, sp, Operand(1 * kPointerSize));
++ __ Ret();
++
++ __ bind(&runtime);
++ // Handle number to string in the runtime system if not found in the cache.
++ __ TailCallRuntime(Runtime::kNumberToStringSkipCache, 1, 1);
++}
++
++
++// On entry lhs_ and rhs_ are the values to be compared.
++// On exit r3 is 0, positive or negative to indicate the result of
++// the comparison.
++void CompareStub::Generate(MacroAssembler* masm) {
++ ASSERT((lhs_.is(r3) && rhs_.is(r4)) ||
++ (lhs_.is(r4) && rhs_.is(r3)));
++
++ Label slow; // Call builtin.
++ Label not_smis, both_loaded_as_doubles, lhs_not_nan;
++
++ if (include_smi_compare_) {
++ Label not_two_smis, smi_done;
++ __ orx(r5, r4, r3);
++ __ JumpIfNotSmi(r5, ¬_two_smis);
++ __ SmiUntag(r4);
++ __ SmiUntag(r3);
++ __ sub(r3, r4, r3);
++ __ Ret();
++ __ bind(¬_two_smis);
++ } else if (FLAG_debug_code) {
++ __ orx(r5, r4, r3);
++ STATIC_ASSERT(kSmiTagMask < 0x8000);
++ __ andi(r0, r5, Operand(kSmiTagMask));
++ __ Assert(ne, "CompareStub: unexpected smi operands.", cr0);
++ }
++
++ // NOTICE! This code is only reached after a smi-fast-case check, so
++ // it is certain that at least one operand isn't a smi.
++
++ // Handle the case where the objects are identical. Either returns the answer
++ // or goes to slow. Only falls through if the objects were not identical.
++ EmitIdenticalObjectComparison(masm, &slow, cc_, never_nan_nan_);
++
++ // If either is a Smi (we know that not both are), then they can only
++ // be strictly equal if the other is a HeapNumber.
++ STATIC_ASSERT(kSmiTag == 0);
++ ASSERT_EQ(0, Smi::FromInt(0));
++ __ and_(r5, lhs_, rhs_);
++ __ JumpIfNotSmi(r5, ¬_smis);
++ // One operand is a smi. EmitSmiNonsmiComparison generates code that can:
++ // 1) Return the answer.
++ // 2) Go to slow.
++ // 3) Fall through to both_loaded_as_doubles.
++ // 4) Jump to lhs_not_nan.
++ // In cases 3 and 4 we have found out we were dealing with a number-number
++ // comparison. The double values of the numbers have been loaded
++ // into d7 and d6.
++ EmitSmiNonsmiComparison(masm, lhs_, rhs_, &lhs_not_nan, &slow, strict_);
++
++ __ bind(&both_loaded_as_doubles);
++ // The arguments have been converted to doubles and stored in d6 and d7
++ Isolate* isolate = masm->isolate();
++ __ bind(&lhs_not_nan);
++ Label no_nan;
++ __ fcmpu(d7, d6);
++
++ Label nan, equal, less_than;
++ __ bunordered(&nan);
++ __ beq(&equal);
++ __ blt(&less_than);
++ __ li(r3, Operand(GREATER));
++ __ Ret();
++ __ bind(&equal);
++ __ li(r3, Operand(EQUAL));
++ __ Ret();
++ __ bind(&less_than);
++ __ li(r3, Operand(LESS));
++ __ Ret();
++
++ __ bind(&nan);
++ // If one of the sides was a NaN then the v flag is set. Load r3 with
++ // whatever it takes to make the comparison fail, since comparisons with NaN
++ // always fail.
++ if (cc_ == lt || cc_ == le) {
++ __ li(r3, Operand(GREATER));
++ } else {
++ __ li(r3, Operand(LESS));
++ }
++ __ Ret();
++
++ __ bind(¬_smis);
++ // At this point we know we are dealing with two different objects,
++ // and neither of them is a Smi. The objects are in rhs_ and lhs_.
++ if (strict_) {
++ // This returns non-equal for some object types, or falls through if it
++ // was not lucky.
++ EmitStrictTwoHeapObjectCompare(masm, lhs_, rhs_);
++ }
++
++ Label check_for_symbols;
++ Label flat_string_check;
++ // Check for heap-number-heap-number comparison. Can jump to slow case,
++ // or load both doubles into r3, r4, r5, r6 and jump to the code that handles
++ // that case. If the inputs are not doubles then jumps to check_for_symbols.
++ // In this case r5 will contain the type of rhs_. Never falls through.
++ EmitCheckForTwoHeapNumbers(masm,
++ lhs_,
++ rhs_,
++ &both_loaded_as_doubles,
++ &check_for_symbols,
++ &flat_string_check);
++
++ __ bind(&check_for_symbols);
++ // In the strict case the EmitStrictTwoHeapObjectCompare already took care of
++ // symbols.
++ if (cc_ == eq && !strict_) {
++ // Returns an answer for two symbols or two detectable objects.
++ // Otherwise jumps to string case or not both strings case.
++ // Assumes that r5 is the type of rhs_ on entry.
++ EmitCheckForSymbolsOrObjects(masm, lhs_, rhs_, &flat_string_check, &slow);
++ }
++
++ // Check for both being sequential ASCII strings, and inline if that is the
++ // case.
++ __ bind(&flat_string_check);
++
++ __ JumpIfNonSmisNotBothSequentialAsciiStrings(lhs_, rhs_, r5, r6, &slow);
++
++ __ IncrementCounter(isolate->counters()->string_compare_native(), 1, r5, r6);
++ if (cc_ == eq) {
++ StringCompareStub::GenerateFlatAsciiStringEquals(masm,
++ lhs_,
++ rhs_,
++ r5,
++ r6);
++ } else {
++ StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
++ lhs_,
++ rhs_,
++ r5,
++ r6,
++ r7);
++ }
++ // Never falls through to here.
++
++ __ bind(&slow);
++
++ __ Push(lhs_, rhs_);
++ // Figure out which native to call and setup the arguments.
++ Builtins::JavaScript native;
++ if (cc_ == eq) {
++ native = strict_ ? Builtins::STRICT_EQUALS : Builtins::EQUALS;
++ } else {
++ native = Builtins::COMPARE;
++ int ncr; // NaN compare result
++ if (cc_ == lt || cc_ == le) {
++ ncr = GREATER;
++ } else {
++ ASSERT(cc_ == gt || cc_ == ge); // remaining cases
++ ncr = LESS;
++ }
++ __ LoadSmiLiteral(r3, Smi::FromInt(ncr));
++ __ push(r3);
++ }
++
++ // Call the native; it returns -1 (less), 0 (equal), or 1 (greater)
++ // tagged as a small integer.
++ __ InvokeBuiltin(native, JUMP_FUNCTION);
++}
++
++
++// The stub expects its argument in the tos_ register and returns its result in
++// it, too: zero for false, and a non-zero value for true.
++void ToBooleanStub::Generate(MacroAssembler* masm) {
++ // This stub overrides SometimesSetsUpAFrame() to return false. That means
++ // we cannot call anything that could cause a GC from this stub.
++ Label patch;
++ const Register map = r22.is(tos_) ? r10 : r22;
++
++ // undefined -> false.
++ CheckOddball(masm, UNDEFINED, Heap::kUndefinedValueRootIndex, false);
++
++ // Boolean -> its value.
++ CheckOddball(masm, BOOLEAN, Heap::kFalseValueRootIndex, false);
++ CheckOddball(masm, BOOLEAN, Heap::kTrueValueRootIndex, true);
++
++ // 'null' -> false.
++ CheckOddball(masm, NULL_TYPE, Heap::kNullValueRootIndex, false);
++
++ if (types_.Contains(SMI)) {
++ // Smis: 0 -> false, all other -> true
++ Label not_smi;
++ __ JumpIfNotSmi(tos_, ¬_smi);
++ // tos_ contains the correct return value already
++ __ Ret();
++ __ bind(¬_smi);
++ } else if (types_.NeedsMap()) {
++ // If we need a map later and have a Smi -> patch.
++ __ JumpIfSmi(tos_, &patch);
++ }
++
++ if (types_.NeedsMap()) {
++ __ LoadP(map, FieldMemOperand(tos_, HeapObject::kMapOffset));
++
++ if (types_.CanBeUndetectable()) {
++ Label not_undetectable;
++ __ lbz(ip, FieldMemOperand(map, Map::kBitFieldOffset));
++ STATIC_ASSERT((1 << Map::kIsUndetectable) < 0x8000);
++ __ andi(r0, ip, Operand(1 << Map::kIsUndetectable));
++ __ beq(¬_undetectable, cr0);
++ // Undetectable -> false.
++ __ li(tos_, Operand(0, RelocInfo::NONE));
++ __ Ret();
++ __ bind(¬_undetectable);
++ }
++ }
++
++ if (types_.Contains(SPEC_OBJECT)) {
++ // Spec object -> true.
++ Label not_js_object;
++ __ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
++ // tos_ contains the correct non-zero return value already.
++ __ blt(¬_js_object);
++ __ Ret();
++ __ bind(¬_js_object);
++ }
++
++ if (types_.Contains(STRING)) {
++ // String value -> false iff empty.
++ Label not_string;
++ __ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
++ __ bge(¬_string);
++ __ LoadP(tos_, FieldMemOperand(tos_, String::kLengthOffset));
++ __ Ret(); // the string length is OK as the return value
++ __ bind(¬_string);
++ }
++
++ if (types_.Contains(HEAP_NUMBER)) {
++ // Heap number -> false iff +0, -0, or NaN.
++ Label not_heap_number, nan_or_zero;
++ __ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
++ __ bne(¬_heap_number);
++
++ __ lfd(d1, FieldMemOperand(tos_, HeapNumber::kValueOffset));
++ __ li(r0, Operand::Zero());
++ __ push(r0);
++#if !V8_TARGET_ARCH_PPC64
++ __ push(r0);
++#endif
++ __ lfd(d2, MemOperand(sp, 0));
++ __ addi(sp, sp, Operand(8));
++ __ fcmpu(d1, d2);
++ // "tos_" is a register, and contains a non zero value by default.
++ // Hence we only need to overwrite "tos_" with zero to return false for
++ // FP_ZERO or FP_NAN cases. Otherwise, by default it returns true.
++ __ bunordered(&nan_or_zero);
++ __ beq(&nan_or_zero);
++ __ Ret();
++
++ __ bind(&nan_or_zero);
++ __ li(tos_, Operand::Zero());
++ __ Ret();
++
++ __ bind(¬_heap_number);
++ }
++
++ __ bind(&patch);
++ GenerateTypeTransition(masm);
++}
++
++
++void ToBooleanStub::CheckOddball(MacroAssembler* masm,
++ Type type,
++ Heap::RootListIndex value,
++ bool result) {
++ if (types_.Contains(type)) {
++ // If we see an expected oddball, return its ToBoolean value tos_.
++ Label different_value;
++ __ LoadRoot(ip, value);
++ __ cmp(tos_, ip);
++ __ bne(&different_value);
++ // The value of a root is never NULL, so we can avoid loading a non-null
++ // value into tos_ when we want to return 'true'.
++ if (!result) {
++ __ li(tos_, Operand(0, RelocInfo::NONE));
++ }
++ // Intel has some logic here not present on ARM
++ // unclear if it's needed or not
++ __ Ret();
++ __ bind(&different_value);
++ }
++}
++
++
++void ToBooleanStub::GenerateTypeTransition(MacroAssembler* masm) {
++ if (!tos_.is(r6)) {
++ __ mr(r6, tos_);
++ }
++ __ LoadSmiLiteral(r5, Smi::FromInt(tos_.code()));
++ __ LoadSmiLiteral(r4, Smi::FromInt(types_.ToByte()));
++ __ Push(r6, r5, r4);
++ // Patch the caller to an appropriate specialized stub and return the
++ // operation result to the caller of the stub.
++ __ TailCallExternalReference(
++ ExternalReference(IC_Utility(IC::kToBoolean_Patch), masm->isolate()),
++ 3,
++ 1);
++}
++
++
++void StoreBufferOverflowStub::Generate(MacroAssembler* masm) {
++ // We don't allow a GC during a store buffer overflow so there is no need to
++ // store the registers in any particular way, but we do have to store and
++ // restore them.
++ __ mflr(r0);
++ __ MultiPush(kJSCallerSaved | r0.bit());
++ if (save_doubles_ == kSaveFPRegs) {
++ const int kNumRegs = DwVfpRegister::kNumVolatileRegisters;
++ __ subi(sp, sp, Operand(kDoubleSize * kNumRegs));
++ for (int i = 0; i < kNumRegs; i++) {
++ DwVfpRegister reg = DwVfpRegister::from_code(i);
++ __ stfd(reg, MemOperand(sp, i * kDoubleSize));
++ }
++ }
++ const int argument_count = 1;
++ const int fp_argument_count = 0;
++ const Register scratch = r4;
++
++ AllowExternalCallThatCantCauseGC scope(masm);
++ __ PrepareCallCFunction(argument_count, fp_argument_count, scratch);
++ __ mov(r3, Operand(ExternalReference::isolate_address()));
++ __ CallCFunction(
++ ExternalReference::store_buffer_overflow_function(masm->isolate()),
++ argument_count);
++ if (save_doubles_ == kSaveFPRegs) {
++ const int kNumRegs = DwVfpRegister::kNumVolatileRegisters;
++ for (int i = 0; i < kNumRegs; i++) {
++ DwVfpRegister reg = DwVfpRegister::from_code(i);
++ __ lfd(reg, MemOperand(sp, i * kDoubleSize));
++ }
++ __ addi(sp, sp, Operand(kDoubleSize * kNumRegs));
++ }
++ __ MultiPop(kJSCallerSaved | r0.bit());
++ __ mtlr(r0);
++ __ Ret();
++}
++
++
++void UnaryOpStub::PrintName(StringStream* stream) {
++ const char* op_name = Token::Name(op_);
++ const char* overwrite_name = NULL; // Make g++ happy.
++ switch (mode_) {
++ case UNARY_NO_OVERWRITE: overwrite_name = "Alloc"; break;
++ case UNARY_OVERWRITE: overwrite_name = "Overwrite"; break;
++ }
++ stream->Add("UnaryOpStub_%s_%s_%s",
++ op_name,
++ overwrite_name,
++ UnaryOpIC::GetName(operand_type_));
++}
++
++
++// TODO(svenpanne): Use virtual functions instead of switch.
++void UnaryOpStub::Generate(MacroAssembler* masm) {
++ switch (operand_type_) {
++ case UnaryOpIC::UNINITIALIZED:
++ GenerateTypeTransition(masm);
++ break;
++ case UnaryOpIC::SMI:
++ GenerateSmiStub(masm);
++ break;
++ case UnaryOpIC::HEAP_NUMBER:
++ GenerateHeapNumberStub(masm);
++ break;
++ case UnaryOpIC::GENERIC:
++ GenerateGenericStub(masm);
++ break;
++ }
++}
++
++
++void UnaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
++ __ mr(r6, r3); // the operand
++ __ LoadSmiLiteral(r5, Smi::FromInt(op_));
++ __ LoadSmiLiteral(r4, Smi::FromInt(mode_));
++ __ LoadSmiLiteral(r3, Smi::FromInt(operand_type_));
++ __ Push(r6, r5, r4, r3);
++
++ __ TailCallExternalReference(
++ ExternalReference(IC_Utility(IC::kUnaryOp_Patch), masm->isolate()), 4, 1);
++}
++
++
++// TODO(svenpanne): Use virtual functions instead of switch.
++void UnaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
++ switch (op_) {
++ case Token::SUB:
++ GenerateSmiStubSub(masm);
++ break;
++ case Token::BIT_NOT:
++ GenerateSmiStubBitNot(masm);
++ break;
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void UnaryOpStub::GenerateSmiStubSub(MacroAssembler* masm) {
++ Label non_smi, slow;
++ GenerateSmiCodeSub(masm, &non_smi, &slow);
++ __ bind(&non_smi);
++ __ bind(&slow);
++ GenerateTypeTransition(masm);
++}
++
++
++void UnaryOpStub::GenerateSmiStubBitNot(MacroAssembler* masm) {
++ Label non_smi;
++ GenerateSmiCodeBitNot(masm, &non_smi);
++ __ bind(&non_smi);
++ GenerateTypeTransition(masm);
++}
++
++
++void UnaryOpStub::GenerateSmiCodeSub(MacroAssembler* masm,
++ Label* non_smi,
++ Label* slow) {
++ __ JumpIfNotSmi(r3, non_smi);
++
++ // The result of negating zero or the smallest negative smi is not a smi.
++ __ TestBitRange(r3, kBitsPerPointer - 2, 0, r0);
++ __ beq(slow, cr0);
++
++ // Return '- value'.
++ __ neg(r3, r3);
++ __ Ret();
++}
++
++
++void UnaryOpStub::GenerateSmiCodeBitNot(MacroAssembler* masm,
++ Label* non_smi) {
++ __ JumpIfNotSmi(r3, non_smi);
++
++ // Flip bits and revert inverted smi-tag.
++ ASSERT(kSmiTagMask == 1);
++ __ notx(r3, r3);
++ __ ClearRightImm(r3, r3, Operand(kSmiTagSize + kSmiShiftSize));
++ __ Ret();
++}
++
++
++// TODO(svenpanne): Use virtual functions instead of switch.
++void UnaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
++ switch (op_) {
++ case Token::SUB:
++ GenerateHeapNumberStubSub(masm);
++ break;
++ case Token::BIT_NOT:
++ GenerateHeapNumberStubBitNot(masm);
++ break;
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void UnaryOpStub::GenerateHeapNumberStubSub(MacroAssembler* masm) {
++ Label non_smi, slow, call_builtin;
++ GenerateSmiCodeSub(masm, &non_smi, &call_builtin);
++ __ bind(&non_smi);
++ GenerateHeapNumberCodeSub(masm, &slow);
++ __ bind(&slow);
++ GenerateTypeTransition(masm);
++ __ bind(&call_builtin);
++ GenerateGenericCodeFallback(masm);
++}
++
++
++void UnaryOpStub::GenerateHeapNumberStubBitNot(MacroAssembler* masm) {
++ Label non_smi, slow;
++ GenerateSmiCodeBitNot(masm, &non_smi);
++ __ bind(&non_smi);
++ GenerateHeapNumberCodeBitNot(masm, &slow);
++ __ bind(&slow);
++ GenerateTypeTransition(masm);
++}
++
++void UnaryOpStub::GenerateHeapNumberCodeSub(MacroAssembler* masm,
++ Label* slow) {
++ EmitCheckForHeapNumber(masm, r3, r4, r9, slow);
++ // r3 is a heap number. Get a new heap number in r4.
++ if (mode_ == UNARY_OVERWRITE) {
++ __ lwz(r5, FieldMemOperand(r3, HeapNumber::kExponentOffset));
++ __ xoris(r5, r5, Operand(HeapNumber::kSignMask >> 16)); // Flip sign.
++ __ stw(r5, FieldMemOperand(r3, HeapNumber::kExponentOffset));
++ } else {
++ Label slow_allocate_heapnumber, heapnumber_allocated;
++ __ AllocateHeapNumber(r4, r5, r6, r9, &slow_allocate_heapnumber);
++ __ b(&heapnumber_allocated);
++
++ __ bind(&slow_allocate_heapnumber);
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ push(r3);
++ __ CallRuntime(Runtime::kNumberAlloc, 0);
++ __ mr(r4, r3);
++ __ pop(r3);
++ }
++
++ __ bind(&heapnumber_allocated);
++ __ lwz(r6, FieldMemOperand(r3, HeapNumber::kMantissaOffset));
++ __ lwz(r5, FieldMemOperand(r3, HeapNumber::kExponentOffset));
++ __ stw(r6, FieldMemOperand(r4, HeapNumber::kMantissaOffset));
++ __ mov(r0, Operand(HeapNumber::kSignMask));
++ __ xor_(r5, r5, r0);
++ __ stw(r5, FieldMemOperand(r4, HeapNumber::kExponentOffset));
++ __ mr(r3, r4);
++ }
++ __ Ret();
++}
++
++
++void UnaryOpStub::GenerateHeapNumberCodeBitNot(
++ MacroAssembler* masm, Label* slow) {
++ Label impossible;
++
++ EmitCheckForHeapNumber(masm, r3, r4, r9, slow);
++ // Convert the heap number in r3 to an untagged integer in r4.
++ __ ConvertToInt32(r3, r4, r5, r6, d0, slow);
++
++ // Do the bitwise operation and check if the result fits in a smi.
++ __ notx(r4, r4);
++
++#if !V8_TARGET_ARCH_PPC64
++ Label try_float;
++ __ JumpIfNotSmiCandidate(r4, r5, &try_float);
++#endif
++
++ // Tag the result as a smi and we're done.
++ __ SmiTag(r3, r4);
++ __ Ret();
++
++#if !V8_TARGET_ARCH_PPC64
++ // Try to store the result in a heap number.
++ __ bind(&try_float);
++ if (mode_ == UNARY_NO_OVERWRITE) {
++ Label slow_allocate_heapnumber, heapnumber_allocated;
++ // Allocate a new heap number without zapping r0, which we need if it fails.
++ __ AllocateHeapNumber(r5, r6, r7, r9, &slow_allocate_heapnumber);
++ __ b(&heapnumber_allocated);
++
++ __ bind(&slow_allocate_heapnumber);
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ push(r3); // Push the heap number, not the untagged int32.
++ __ CallRuntime(Runtime::kNumberAlloc, 0);
++ __ mr(r5, r3); // Move the new heap number into r5.
++ // Get the heap number into r3, now that the new heap number is in r5.
++ __ pop(r3);
++ }
++
++ // Convert the heap number in r3 to an untagged integer in r4.
++ // This can't go slow-case because it's the same number we already
++ // converted once again.
++ __ ConvertToInt32(r3, r4, r6, r7, d0, &impossible);
++ __ notx(r4, r4);
++
++ __ bind(&heapnumber_allocated);
++ __ mr(r3, r5); // Move newly allocated heap number to r0.
++ }
++
++ // Convert the int32 in r4 to the heap number in r3.
++ FloatingPointHelper::ConvertIntToDouble(
++ masm, r4, d0);
++ __ stfd(d0, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ __ Ret();
++
++ __ bind(&impossible);
++ if (FLAG_debug_code) {
++ __ stop("Incorrect assumption in bit-not stub");
++ }
++#endif
++}
++
++
++// TODO(svenpanne): Use virtual functions instead of switch.
++void UnaryOpStub::GenerateGenericStub(MacroAssembler* masm) {
++ switch (op_) {
++ case Token::SUB:
++ GenerateGenericStubSub(masm);
++ break;
++ case Token::BIT_NOT:
++ GenerateGenericStubBitNot(masm);
++ break;
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void UnaryOpStub::GenerateGenericStubSub(MacroAssembler* masm) {
++ Label non_smi, slow;
++ GenerateSmiCodeSub(masm, &non_smi, &slow);
++ __ bind(&non_smi);
++ GenerateHeapNumberCodeSub(masm, &slow);
++ __ bind(&slow);
++ GenerateGenericCodeFallback(masm);
++}
++
++
++void UnaryOpStub::GenerateGenericStubBitNot(MacroAssembler* masm) {
++ Label non_smi, slow;
++ GenerateSmiCodeBitNot(masm, &non_smi);
++ __ bind(&non_smi);
++ GenerateHeapNumberCodeBitNot(masm, &slow);
++ __ bind(&slow);
++ GenerateGenericCodeFallback(masm);
++}
++
++
++void UnaryOpStub::GenerateGenericCodeFallback(MacroAssembler* masm) {
++ // Handle the slow case by jumping to the JavaScript builtin.
++ __ push(r3);
++ switch (op_) {
++ case Token::SUB:
++ __ InvokeBuiltin(Builtins::UNARY_MINUS, JUMP_FUNCTION);
++ break;
++ case Token::BIT_NOT:
++ __ InvokeBuiltin(Builtins::BIT_NOT, JUMP_FUNCTION);
++ break;
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void BinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
++ Label get_result;
++
++ __ Push(r4, r3);
++
++ __ LoadSmiLiteral(r5, Smi::FromInt(MinorKey()));
++ __ LoadSmiLiteral(r4, Smi::FromInt(op_));
++ __ LoadSmiLiteral(r3, Smi::FromInt(operands_type_));
++ __ Push(r5, r4, r3);
++
++ __ TailCallExternalReference(
++ ExternalReference(IC_Utility(IC::kBinaryOp_Patch),
++ masm->isolate()),
++ 5,
++ 1);
++}
++
++
++void BinaryOpStub::GenerateTypeTransitionWithSavedArgs(
++ MacroAssembler* masm) {
++ UNIMPLEMENTED();
++}
++
++
++void BinaryOpStub::Generate(MacroAssembler* masm) {
++ // Explicitly allow generation of nested stubs. It is safe here because
++ // generation code does not use any raw pointers.
++ AllowStubCallsScope allow_stub_calls(masm, true);
++
++ switch (operands_type_) {
++ case BinaryOpIC::UNINITIALIZED:
++ GenerateTypeTransition(masm);
++ break;
++ case BinaryOpIC::SMI:
++ GenerateSmiStub(masm);
++ break;
++ case BinaryOpIC::INT32:
++ GenerateInt32Stub(masm);
++ break;
++ case BinaryOpIC::HEAP_NUMBER:
++ GenerateHeapNumberStub(masm);
++ break;
++ case BinaryOpIC::ODDBALL:
++ GenerateOddballStub(masm);
++ break;
++ case BinaryOpIC::BOTH_STRING:
++ GenerateBothStringStub(masm);
++ break;
++ case BinaryOpIC::STRING:
++ GenerateStringStub(masm);
++ break;
++ case BinaryOpIC::GENERIC:
++ GenerateGeneric(masm);
++ break;
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void BinaryOpStub::PrintName(StringStream* stream) {
++ const char* op_name = Token::Name(op_);
++ const char* overwrite_name;
++ switch (mode_) {
++ case NO_OVERWRITE: overwrite_name = "Alloc"; break;
++ case OVERWRITE_RIGHT: overwrite_name = "OverwriteRight"; break;
++ case OVERWRITE_LEFT: overwrite_name = "OverwriteLeft"; break;
++ default: overwrite_name = "UnknownOverwrite"; break;
++ }
++ stream->Add("BinaryOpStub_%s_%s_%s",
++ op_name,
++ overwrite_name,
++ BinaryOpIC::GetName(operands_type_));
++}
++
++
++void BinaryOpStub::GenerateSmiSmiOperation(MacroAssembler* masm) {
++ Register left = r4;
++ Register right = r3;
++ Register scratch1 = r10;
++ Register scratch2 = r22;
++
++ ASSERT(right.is(r3));
++ STATIC_ASSERT(kSmiTag == 0);
++
++ Label not_smi_result;
++ switch (op_) {
++ case Token::ADD: {
++ Label undo_add, add_no_overflow;
++ // C = A+B; C overflows if A/B have same sign and C has diff sign than A
++ __ xor_(r0, left, right);
++ __ mr(scratch1, right);
++ __ add(right, left, right); // Add optimistically.
++ __ TestSignBit(r0, r0);
++ __ bne(&add_no_overflow, cr0);
++ __ xor_(r0, right, scratch1);
++ __ TestSignBit(r0, r0);
++ __ bne(&undo_add, cr0);
++ __ bind(&add_no_overflow);
++ __ Ret();
++ __ bind(&undo_add);
++ __ mr(right, scratch1); // Revert optimistic add.
++ break;
++ }
++ case Token::SUB: {
++ Label undo_sub, sub_no_overflow;
++ // C = A-B; C overflows if A/B have diff signs and C has diff sign than A
++ __ xor_(r0, left, right);
++ __ mr(scratch1, right);
++ __ sub(right, left, right); // Subtract optimistically.
++ __ TestSignBit(r0, r0);
++ __ beq(&sub_no_overflow, cr0);
++ __ xor_(r0, right, left);
++ __ TestSignBit(r0, r0);
++ __ bne(&undo_sub, cr0);
++ __ bind(&sub_no_overflow);
++ __ Ret();
++ __ bind(&undo_sub);
++ __ mr(right, scratch1); // Revert optimistic subtract.
++ break;
++ }
++ case Token::MUL: {
++ Label mul_zero, mul_neg_zero;
++#if V8_TARGET_ARCH_PPC64
++ // Remove tag from both operands.
++ __ SmiUntag(ip, right);
++ __ SmiUntag(r0, left);
++ // Do multiplication
++ // scratch1 = product (untagged)
++ // scratch2 = sign-extended higher 32 bits of product.
++ __ Mul(scratch1, r0, ip);
++ // Check for overflowing the smi range - no overflow if higher 33 bits of
++ // the result are identical.
++ __ TestIfInt32(scratch1, scratch2, ip);
++ __ bne(¬_smi_result);
++#else
++ // Remove tag from one of the operands. This way the multiplication result
++ // will be a smi if it fits the smi range.
++ __ SmiUntag(ip, right);
++ // Do multiplication
++ // scratch1 = lower 32 bits of product.
++ // scratch2 = higher 32 bits of product.
++ __ mullw(scratch1, left, ip);
++ __ mulhw(scratch2, left, ip);
++ // Check for overflowing the smi range - no overflow if higher 33 bits of
++ // the result are identical.
++ __ TestIfInt32(scratch2, scratch1, ip);
++ __ bne(¬_smi_result);
++#endif
++ // Go slow on zero result to handle -0.
++ __ cmpi(scratch1, Operand::Zero());
++ __ beq(&mul_zero);
++#if V8_TARGET_ARCH_PPC64
++ __ SmiTag(right, scratch1);
++#else
++ __ mr(right, scratch1);
++#endif
++ __ Ret();
++ __ bind(&mul_zero);
++ // We need -0 if we were multiplying a negative number with 0 to get 0.
++ // We know one of them was zero.
++ __ add(scratch2, right, left);
++ __ cmpi(scratch2, Operand::Zero());
++ __ blt(&mul_neg_zero);
++ __ LoadSmiLiteral(right, Smi::FromInt(0));
++ __ Ret(); // Return smi 0 if the non-zero one was positive.
++ __ bind(&mul_neg_zero);
++ // We fall through here if we multiplied a negative number with 0, because
++ // that would mean we should produce -0.
++ break;
++ }
++ case Token::DIV: {
++ Label check_neg_zero;
++ __ SmiUntag(ip, left);
++ __ SmiUntag(scratch2, right, SetRC);
++ __ Div(scratch1, ip, scratch2);
++ // Check for zero on the right hand side.
++ __ beq(¬_smi_result, cr0);
++ // Not Smi if remainder is non-zero.
++ __ Mul(scratch2, scratch2, scratch1);
++ __ cmp(ip, scratch2);
++ __ bne(¬_smi_result);
++ // If the result is 0, we need to check for the -0 case.
++ __ SmiTag(scratch2, scratch1, SetRC);
++ __ beq(&check_neg_zero, cr0);
++ // Check for Smi overflow
++ __ xor_(scratch1, scratch2, scratch1, SetRC);
++ __ blt(¬_smi_result, cr0);
++ __ mr(right, scratch2);
++ __ Ret();
++
++ // If divisor (right) is negative, we must produce -0.
++ __ bind(&check_neg_zero);
++ __ cmpi(right, Operand::Zero());
++ __ blt(¬_smi_result);
++ __ mr(right, scratch2);
++ __ Ret();
++ break;
++ }
++ case Token::MOD: {
++ Label check_neg_zero;
++ __ SmiUntag(ip, left);
++ __ SmiUntag(scratch2, right, SetRC);
++ __ Div(scratch1, ip, scratch2);
++ // Check for zero on the right hand side.
++ __ beq(¬_smi_result, cr0);
++ __ Mul(scratch1, scratch2, scratch1);
++ __ sub(scratch1, ip, scratch1, LeaveOE, SetRC);
++ // If the result is 0, we need to check for the -0 case.
++ __ beq(&check_neg_zero, cr0);
++#if !V8_TARGET_ARCH_PPC64
++ // Check that the signed result fits in a Smi.
++ __ JumpIfNotSmiCandidate(scratch1, scratch2, ¬_smi_result);
++#endif
++ __ SmiTag(right, scratch1);
++ __ Ret();
++
++ // If dividend (left) is negative, we must produce -0.
++ __ bind(&check_neg_zero);
++ __ cmpi(left, Operand::Zero());
++ __ blt(¬_smi_result);
++ __ LoadSmiLiteral(right, Smi::FromInt(0));
++ __ Ret();
++ break;
++ }
++ case Token::BIT_OR:
++ __ orx(right, left, right);
++ __ Ret();
++ break;
++ case Token::BIT_AND:
++ __ and_(right, left, right);
++ __ Ret();
++ break;
++ case Token::BIT_XOR:
++ __ xor_(right, left, right);
++ __ Ret();
++ break;
++ case Token::SAR:
++ // Remove tags from right operand.
++ __ GetLeastBitsFromSmi(scratch1, right, 5);
++ __ ShiftRightArith(right, left, scratch1);
++ // Smi tag result.
++ __ ClearRightImm(right, right, Operand(kSmiTagSize + kSmiShiftSize));
++ __ Ret();
++ break;
++ case Token::SHR:
++ // Remove tags from operands. We can't do this on a 31 bit number
++ // because then the 0s get shifted into bit 30 instead of bit 31.
++ __ SmiUntag(scratch1, left);
++ __ GetLeastBitsFromSmi(scratch2, right, 5);
++ __ srw(scratch1, scratch1, scratch2);
++ // Unsigned shift is not allowed to produce a negative number.
++ __ JumpIfNotUnsignedSmiCandidate(scratch1, r0, ¬_smi_result);
++ // Smi tag result.
++ __ SmiTag(right, scratch1);
++ __ Ret();
++ break;
++ case Token::SHL:
++ // Remove tags from operands.
++ __ SmiUntag(scratch1, left);
++ __ GetLeastBitsFromSmi(scratch2, right, 5);
++ __ ShiftLeft(scratch1, scratch1, scratch2);
++#if !V8_TARGET_ARCH_PPC64
++ // Check that the signed result fits in a Smi.
++ __ JumpIfNotSmiCandidate(scratch1, scratch2, ¬_smi_result);
++#endif
++ __ SmiTag(right, scratch1);
++ __ Ret();
++ break;
++ default:
++ UNREACHABLE();
++ }
++ __ bind(¬_smi_result);
++}
++
++
++void BinaryOpStub::GenerateFPOperation(MacroAssembler* masm,
++ bool smi_operands,
++ Label* not_numbers,
++ Label* gc_required) {
++ Register left = r4;
++ Register right = r3;
++ Register scratch1 = r10;
++ Register scratch2 = r22;
++ Register scratch3 = r7;
++
++ ASSERT(smi_operands || (not_numbers != NULL));
++ if (smi_operands) {
++ __ AssertSmi(left);
++ __ AssertSmi(right);
++ }
++
++ Register heap_number_map = r9;
++ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
++
++ switch (op_) {
++ case Token::ADD:
++ case Token::SUB:
++ case Token::MUL:
++ case Token::DIV:
++ case Token::MOD: {
++ // Load left and right operands into d1 and d2
++ // Allocate new heap number for result.
++ Register result = r8;
++ GenerateHeapResultAllocation(
++ masm, result, heap_number_map, scratch1, scratch2, gc_required);
++
++ // Load the operands.
++ if (smi_operands) {
++ FloatingPointHelper::LoadSmis(masm, scratch1, scratch2);
++ } else {
++ FloatingPointHelper::LoadOperands(masm,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ not_numbers);
++ }
++
++ // Calculate the result.
++ // Using FP registers:
++ // d1: Left value
++ // d2: Right value
++ switch (op_) {
++ case Token::ADD:
++ __ fadd(d1, d1, d2);
++ break;
++ case Token::SUB:
++ __ fsub(d1, d1, d2);
++ break;
++ case Token::MUL:
++ __ fmul(d1, d1, d2);
++ break;
++ case Token::DIV:
++ __ fdiv(d1, d1, d2);
++ break;
++ case Token::MOD:
++ // Call the C function to handle the double operation.
++ FloatingPointHelper::CallCCodeForDoubleOperation(masm,
++ op_,
++ result,
++ scratch1);
++ if (FLAG_debug_code) {
++ __ stop("Unreachable code.");
++ }
++ break;
++ default:
++ UNREACHABLE();
++ }
++ __ stfd(d1, FieldMemOperand(result, HeapNumber::kValueOffset));
++ __ mr(r3, result);
++ __ Ret();
++ break;
++ }
++ case Token::BIT_OR:
++ case Token::BIT_XOR:
++ case Token::BIT_AND:
++ case Token::SAR:
++ case Token::SHR:
++ case Token::SHL: {
++ if (smi_operands) {
++ __ SmiUntag(r6, left);
++ __ SmiUntag(r5, right);
++ } else {
++ // Convert operands to 32-bit integers. Right in r5 and left in r6.
++ FloatingPointHelper::ConvertNumberToInt32(masm,
++ left,
++ r6,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ scratch3,
++ d0,
++ not_numbers);
++ FloatingPointHelper::ConvertNumberToInt32(masm,
++ right,
++ r5,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ scratch3,
++ d0,
++ not_numbers);
++ }
++
++ Label result_not_a_smi;
++ switch (op_) {
++ case Token::BIT_OR:
++ __ orx(r5, r6, r5);
++ break;
++ case Token::BIT_XOR:
++ __ xor_(r5, r6, r5);
++ break;
++ case Token::BIT_AND:
++ __ and_(r5, r6, r5);
++ break;
++ case Token::SAR:
++ // Use only the 5 least significant bits of the shift count.
++ __ GetLeastBitsFromInt32(r5, r5, 5);
++ __ sraw(r5, r6, r5);
++ break;
++ case Token::SHR:
++ {
++ // Use only the 5 least significant bits of the shift count.
++ __ GetLeastBitsFromInt32(r5, r5, 5);
++ // SHR is special because it is required to produce a positive answer.
++ // The code below for writing into heap numbers isn't capable of
++ // writing the register as an unsigned int so we go to slow case if we
++ // hit this case.
++#if V8_TARGET_ARCH_PPC64
++ const Condition cond = ne;
++ __ srw(r5, r6, r5);
++ __ TestSignBit32(r5, r0);
++#else
++ const Condition cond = lt;
++ __ srw(r5, r6, r5, SetRC);
++#endif
++ __ b(cond, &result_not_a_smi, cr0);
++ break;
++ }
++ case Token::SHL:
++ // Use only the 5 least significant bits of the shift count.
++ __ GetLeastBitsFromInt32(r5, r5, 5);
++ __ ShiftLeft(r5, r6, r5);
++ break;
++ default:
++ UNREACHABLE();
++ }
++
++#if !V8_TARGET_ARCH_PPC64
++ // Check that the *signed* result fits in a smi.
++ __ JumpIfNotSmiCandidate(r5, r6, &result_not_a_smi);
++#endif
++ __ SmiTag(r3, r5);
++ __ Ret();
++
++ // Allocate new heap number for result.
++ __ bind(&result_not_a_smi);
++ Register result = r8;
++ if (smi_operands) {
++ __ AllocateHeapNumber(
++ result, scratch1, scratch2, heap_number_map, gc_required);
++ } else {
++ GenerateHeapResultAllocation(
++ masm, result, heap_number_map, scratch1, scratch2, gc_required);
++ }
++
++ // r5: Answer as signed int32.
++ // r8: Heap number to write answer into.
++
++ // Nothing can go wrong now, so move the heap number to r3, which is the
++ // result.
++ __ mr(r3, r8);
++
++ // Convert the int32 in r5 to the heap number in r3. As
++ // mentioned above SHR needs to always produce a positive result.
++ if (op_ == Token::SHR) {
++ FloatingPointHelper::ConvertUnsignedIntToDouble(
++ masm, r5, d0);
++ } else {
++ FloatingPointHelper::ConvertIntToDouble(
++ masm, r5, d0);
++ }
++ __ stfd(d0, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ __ Ret();
++ break;
++ }
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++// Generate the smi code. If the operation on smis are successful this return is
++// generated. If the result is not a smi and heap number allocation is not
++// requested the code falls through. If number allocation is requested but a
++// heap number cannot be allocated the code jumps to the lable gc_required.
++void BinaryOpStub::GenerateSmiCode(
++ MacroAssembler* masm,
++ Label* use_runtime,
++ Label* gc_required,
++ SmiCodeGenerateHeapNumberResults allow_heapnumber_results) {
++ Label not_smis;
++
++ Register left = r4;
++ Register right = r3;
++ Register scratch1 = r10;
++
++ // Perform combined smi check on both operands.
++ __ orx(scratch1, left, right);
++ STATIC_ASSERT(kSmiTag == 0);
++ __ JumpIfNotSmi(scratch1, ¬_smis);
++
++ // If the smi-smi operation results in a smi return is generated.
++ GenerateSmiSmiOperation(masm);
++
++ // If heap number results are possible generate the result in an allocated
++ // heap number.
++ if (allow_heapnumber_results == ALLOW_HEAPNUMBER_RESULTS) {
++ GenerateFPOperation(masm, true, use_runtime, gc_required);
++ }
++ __ bind(¬_smis);
++}
++
++
++void BinaryOpStub::GenerateSmiStub(MacroAssembler* masm) {
++ Label not_smis, call_runtime;
++
++ if (result_type_ == BinaryOpIC::UNINITIALIZED ||
++ result_type_ == BinaryOpIC::SMI) {
++ // Only allow smi results.
++ GenerateSmiCode(masm, &call_runtime, NULL, NO_HEAPNUMBER_RESULTS);
++ } else {
++ // Allow heap number result and don't make a transition if a heap number
++ // cannot be allocated.
++ GenerateSmiCode(masm,
++ &call_runtime,
++ &call_runtime,
++ ALLOW_HEAPNUMBER_RESULTS);
++ }
++
++ // Code falls through if the result is not returned as either a smi or heap
++ // number.
++ GenerateTypeTransition(masm);
++
++ __ bind(&call_runtime);
++ GenerateCallRuntime(masm);
++}
++
++
++void BinaryOpStub::GenerateStringStub(MacroAssembler* masm) {
++ ASSERT(operands_type_ == BinaryOpIC::STRING);
++ ASSERT(op_ == Token::ADD);
++ // Try to add arguments as strings, otherwise, transition to the generic
++ // BinaryOpIC type.
++ GenerateAddStrings(masm);
++ GenerateTypeTransition(masm);
++}
++
++
++void BinaryOpStub::GenerateBothStringStub(MacroAssembler* masm) {
++ Label call_runtime;
++ ASSERT(operands_type_ == BinaryOpIC::BOTH_STRING);
++ ASSERT(op_ == Token::ADD);
++ // If both arguments are strings, call the string add stub.
++ // Otherwise, do a transition.
++
++ // Registers containing left and right operands respectively.
++ Register left = r4;
++ Register right = r3;
++
++ // Test if left operand is a string.
++ __ JumpIfSmi(left, &call_runtime);
++ __ CompareObjectType(left, r5, r5, FIRST_NONSTRING_TYPE);
++ __ bge(&call_runtime);
++
++ // Test if right operand is a string.
++ __ JumpIfSmi(right, &call_runtime);
++ __ CompareObjectType(right, r5, r5, FIRST_NONSTRING_TYPE);
++ __ bge(&call_runtime);
++
++ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
++ GenerateRegisterArgsPush(masm);
++ __ TailCallStub(&string_add_stub);
++
++ __ bind(&call_runtime);
++ GenerateTypeTransition(masm);
++}
++
++
++void BinaryOpStub::GenerateInt32Stub(MacroAssembler* masm) {
++ ASSERT(operands_type_ == BinaryOpIC::INT32);
++
++ Register left = r4;
++ Register right = r3;
++ Register scratch1 = r10;
++ Register scratch2 = r11;
++ DwVfpRegister double_scratch0 = d0;
++ DwVfpRegister double_scratch1 = d1;
++
++ Register heap_number_result = no_reg;
++ Register heap_number_map = r9;
++ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
++
++ Label call_runtime;
++ // Labels for type transition, used for wrong input or output types.
++ // Both label are currently actually bound to the same position. We use two
++ // different label to differentiate the cause leading to type transition.
++ Label transition;
++
++ // Smi-smi fast case.
++ Label skip;
++ __ orx(scratch1, left, right);
++ __ JumpIfNotSmi(scratch1, &skip);
++ GenerateSmiSmiOperation(masm);
++ // Fall through if the result is not a smi.
++ __ bind(&skip);
++
++ switch (op_) {
++ case Token::ADD:
++ case Token::SUB:
++ case Token::MUL:
++ case Token::DIV:
++ case Token::MOD: {
++ // Load both operands and check that they are 32-bit integer.
++ // Jump to type transition if they are not. The registers r3 and r4 (right
++ // and left) are preserved for the runtime call.
++ FloatingPointHelper::LoadNumberAsInt32Double(masm,
++ right,
++ d2,
++ d8,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ &transition);
++ FloatingPointHelper::LoadNumberAsInt32Double(masm,
++ left,
++ d1,
++ d8,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ &transition);
++
++ Label return_heap_number;
++ switch (op_) {
++ case Token::ADD:
++ __ fadd(d1, d1, d2);
++ break;
++ case Token::SUB:
++ __ fsub(d1, d1, d2);
++ break;
++ case Token::MUL:
++ __ fmul(d1, d1, d2);
++ break;
++ case Token::DIV:
++ __ fdiv(d1, d1, d2);
++ break;
++ case Token::MOD: {
++ Label pop_and_call_runtime;
++
++ // Allocate a heap number to store the result.
++ heap_number_result = r8;
++ GenerateHeapResultAllocation(masm,
++ heap_number_result,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ &pop_and_call_runtime);
++
++ // Call the C function to handle the double operation.
++ FloatingPointHelper::CallCCodeForDoubleOperation(
++ masm, op_, heap_number_result, scratch1);
++ if (FLAG_debug_code) {
++ __ stop("Unreachable code.");
++ }
++
++ __ bind(&pop_and_call_runtime);
++ __ b(&call_runtime);
++ break;
++ }
++ default:
++ UNREACHABLE();
++ }
++
++ if (op_ != Token::DIV) {
++ // These operations produce an integer result.
++ // Try to return a smi if we can.
++ // Otherwise return a heap number if allowed, or jump to type
++ // transition.
++
++ __ EmitVFPTruncate(kRoundToZero,
++ scratch1,
++ d1,
++ scratch2,
++ d8);
++
++ // result does not fit in a 32-bit integer.
++ Label *not_int32 = ((result_type_ <= BinaryOpIC::INT32) ?
++ &transition : &return_heap_number);
++ __ bne(not_int32);
++
++#if !V8_TARGET_ARCH_PPC64
++ // Check if the result fits in a smi.
++ // If not try to return a heap number.
++ __ JumpIfNotSmiCandidate(scratch1, scratch2, &return_heap_number);
++#endif
++ // Check for minus zero. Return heap number for minus zero.
++ Label not_zero;
++ __ cmpi(scratch1, Operand::Zero());
++ __ bne(¬_zero);
++
++ __ subi(sp, sp, Operand(8));
++ __ stfd(d1, MemOperand(sp, 0));
++#if V8_TARGET_ARCH_PPC64
++ __ ld(scratch2, MemOperand(sp, 0));
++#else
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(scratch2, MemOperand(sp, 4));
++#else
++ __ lwz(scratch2, MemOperand(sp, 0));
++#endif
++#endif
++ __ addi(sp, sp, Operand(8));
++
++ __ TestSignBit(scratch2, r0);
++ __ bne(&return_heap_number, cr0);
++ __ bind(¬_zero);
++
++ // Tag the result and return.
++ __ SmiTag(r3, scratch1);
++ __ Ret();
++ } else {
++ // DIV just falls through to allocating a heap number.
++ }
++
++ __ bind(&return_heap_number);
++ // Return a heap number, or fall through to type transition or runtime
++ // call if we can't.
++ if (result_type_ >= ((op_ == Token::DIV) ? BinaryOpIC::HEAP_NUMBER
++ : BinaryOpIC::INT32)) {
++ heap_number_result = r8;
++ GenerateHeapResultAllocation(masm,
++ heap_number_result,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ &call_runtime);
++ __ stfd(d1, FieldMemOperand(heap_number_result,
++ HeapNumber::kValueOffset));
++ __ mr(r3, heap_number_result);
++ __ Ret();
++ }
++
++ // A DIV operation expecting an integer result falls through
++ // to type transition.
++ break;
++ }
++
++ case Token::BIT_OR:
++ case Token::BIT_XOR:
++ case Token::BIT_AND:
++ case Token::SAR:
++ case Token::SHR:
++ case Token::SHL: {
++ Label return_heap_number;
++ Register scratch3 = r8;
++ // Convert operands to 32-bit integers. Right in r5 and left in r6. The
++ // registers r3 and r4 (right and left) are preserved for the runtime
++ // call.
++ FloatingPointHelper::LoadNumberAsInt32(masm,
++ left,
++ r6,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ scratch3,
++ double_scratch0,
++ double_scratch1,
++ &transition);
++ FloatingPointHelper::LoadNumberAsInt32(masm,
++ right,
++ r5,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ scratch3,
++ double_scratch0,
++ double_scratch1,
++ &transition);
++
++ // The ECMA-262 standard specifies that, for shift operations, only the
++ // 5 least significant bits of the shift value should be used.
++ switch (op_) {
++ case Token::BIT_OR:
++ __ orx(r5, r6, r5);
++ break;
++ case Token::BIT_XOR:
++ __ xor_(r5, r6, r5);
++ break;
++ case Token::BIT_AND:
++ __ and_(r5, r6, r5);
++ break;
++ case Token::SAR:
++ __ GetLeastBitsFromInt32(r5, r5, 5);
++ __ sraw(r5, r6, r5);
++ break;
++ case Token::SHR:
++ {
++ __ GetLeastBitsFromInt32(r5, r5, 5);
++ // SHR is special because it is required to produce a positive answer.
++ // We only get a negative result if the shift value (r5) is 0.
++ // This result cannot be respresented as a signed 32-bit integer, try
++ // to return a heap number if we can.
++#if V8_TARGET_ARCH_PPC64
++ const Condition cond = ne;
++ __ srw(r5, r6, r5);
++ __ TestSignBit32(r5, r0);
++#else
++ const Condition cond = lt;
++ __ srw(r5, r6, r5, SetRC);
++#endif
++ __ b(cond, ((result_type_ <= BinaryOpIC::INT32)
++ ? &transition
++ : &return_heap_number), cr0);
++ break;
++ }
++ case Token::SHL:
++ __ andi(r5, r5, Operand(0x1f));
++ __ ShiftLeft(r5, r6, r5);
++ break;
++ default:
++ UNREACHABLE();
++ }
++
++#if !V8_TARGET_ARCH_PPC64
++ // Check if the result fits in a smi.
++ // If not try to return a heap number. (We know the result is an int32.)
++ __ JumpIfNotSmiCandidate(r5, scratch1, &return_heap_number);
++#endif
++ // Tag the result and return.
++ __ SmiTag(r3, r5);
++ __ Ret();
++
++ __ bind(&return_heap_number);
++ heap_number_result = r8;
++ GenerateHeapResultAllocation(masm,
++ heap_number_result,
++ heap_number_map,
++ scratch1,
++ scratch2,
++ &call_runtime);
++
++ if (op_ != Token::SHR) {
++ // Convert the result to a floating point value.
++ FloatingPointHelper::ConvertIntToDouble(masm, r5, double_scratch0);
++ } else {
++ // The result must be interpreted as an unsigned 32-bit integer.
++ FloatingPointHelper::ConvertUnsignedIntToDouble(masm, r5,
++ double_scratch0);
++ }
++
++ // Store the result.
++ __ stfd(double_scratch0, FieldMemOperand(heap_number_result,
++ HeapNumber::kValueOffset));
++ __ mr(r3, heap_number_result);
++ __ Ret();
++
++ break;
++ }
++
++ default:
++ UNREACHABLE();
++ }
++
++ // We never expect DIV to yield an integer result, so we always generate
++ // type transition code for DIV operations expecting an integer result: the
++ // code will fall through to this type transition.
++ if (transition.is_linked() ||
++ ((op_ == Token::DIV) && (result_type_ <= BinaryOpIC::INT32))) {
++ __ bind(&transition);
++ GenerateTypeTransition(masm);
++ }
++
++ __ bind(&call_runtime);
++ GenerateCallRuntime(masm);
++}
++
++
++void BinaryOpStub::GenerateOddballStub(MacroAssembler* masm) {
++ Label call_runtime;
++
++ if (op_ == Token::ADD) {
++ // Handle string addition here, because it is the only operation
++ // that does not do a ToNumber conversion on the operands.
++ GenerateAddStrings(masm);
++ }
++
++ // Convert oddball arguments to numbers.
++ Label check, done;
++ __ CompareRoot(r4, Heap::kUndefinedValueRootIndex);
++ __ bne(&check);
++ if (Token::IsBitOp(op_)) {
++ __ LoadSmiLiteral(r4, Smi::FromInt(0));
++ } else {
++ __ LoadRoot(r4, Heap::kNanValueRootIndex);
++ }
++ __ b(&done);
++ __ bind(&check);
++ __ CompareRoot(r3, Heap::kUndefinedValueRootIndex);
++ __ bne(&done);
++ if (Token::IsBitOp(op_)) {
++ __ LoadSmiLiteral(r3, Smi::FromInt(0));
++ } else {
++ __ LoadRoot(r3, Heap::kNanValueRootIndex);
++ }
++ __ bind(&done);
++
++ GenerateHeapNumberStub(masm);
++}
++
++
++void BinaryOpStub::GenerateHeapNumberStub(MacroAssembler* masm) {
++ Label call_runtime;
++ GenerateFPOperation(masm, false, &call_runtime, &call_runtime);
++
++ __ bind(&call_runtime);
++ GenerateCallRuntime(masm);
++}
++
++
++void BinaryOpStub::GenerateGeneric(MacroAssembler* masm) {
++ Label call_runtime, call_string_add_or_runtime;
++
++ GenerateSmiCode(masm, &call_runtime, &call_runtime,
ALLOW_HEAPNUMBER_RESULTS);
++
++ GenerateFPOperation(masm, false, &call_string_add_or_runtime, &call_runtime);
++
++ __ bind(&call_string_add_or_runtime);
++ if (op_ == Token::ADD) {
++ GenerateAddStrings(masm);
++ }
++
++ __ bind(&call_runtime);
++ GenerateCallRuntime(masm);
++}
++
++
++void BinaryOpStub::GenerateAddStrings(MacroAssembler* masm) {
++ ASSERT(op_ == Token::ADD);
++ Label left_not_string, call_runtime;
++
++ Register left = r4;
++ Register right = r3;
++
++ // Check if left argument is a string.
++ __ JumpIfSmi(left, &left_not_string);
++ __ CompareObjectType(left, r5, r5, FIRST_NONSTRING_TYPE);
++ __ bge(&left_not_string);
++
++ StringAddStub string_add_left_stub(NO_STRING_CHECK_LEFT_IN_STUB);
++ GenerateRegisterArgsPush(masm);
++ __ TailCallStub(&string_add_left_stub);
++
++ // Left operand is not a string, test right.
++ __ bind(&left_not_string);
++ __ JumpIfSmi(right, &call_runtime);
++ __ CompareObjectType(right, r5, r5, FIRST_NONSTRING_TYPE);
++ __ bge(&call_runtime);
++
++ StringAddStub string_add_right_stub(NO_STRING_CHECK_RIGHT_IN_STUB);
++ GenerateRegisterArgsPush(masm);
++ __ TailCallStub(&string_add_right_stub);
++
++ // At least one argument is not a string.
++ __ bind(&call_runtime);
++}
++
++
++void BinaryOpStub::GenerateCallRuntime(MacroAssembler* masm) {
++ GenerateRegisterArgsPush(masm);
++ switch (op_) {
++ case Token::ADD:
++ __ InvokeBuiltin(Builtins::ADD, JUMP_FUNCTION);
++ break;
++ case Token::SUB:
++ __ InvokeBuiltin(Builtins::SUB, JUMP_FUNCTION);
++ break;
++ case Token::MUL:
++ __ InvokeBuiltin(Builtins::MUL, JUMP_FUNCTION);
++ break;
++ case Token::DIV:
++ __ InvokeBuiltin(Builtins::DIV, JUMP_FUNCTION);
++ break;
++ case Token::MOD:
++ __ InvokeBuiltin(Builtins::MOD, JUMP_FUNCTION);
++ break;
++ case Token::BIT_OR:
++ __ InvokeBuiltin(Builtins::BIT_OR, JUMP_FUNCTION);
++ break;
++ case Token::BIT_AND:
++ __ InvokeBuiltin(Builtins::BIT_AND, JUMP_FUNCTION);
++ break;
++ case Token::BIT_XOR:
++ __ InvokeBuiltin(Builtins::BIT_XOR, JUMP_FUNCTION);
++ break;
++ case Token::SAR:
++ __ InvokeBuiltin(Builtins::SAR, JUMP_FUNCTION);
++ break;
++ case Token::SHR:
++ __ InvokeBuiltin(Builtins::SHR, JUMP_FUNCTION);
++ break;
++ case Token::SHL:
++ __ InvokeBuiltin(Builtins::SHL, JUMP_FUNCTION);
++ break;
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void BinaryOpStub::GenerateHeapResultAllocation(MacroAssembler* masm,
++ Register result,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required) {
++ // Code below will scratch result if allocation fails. To keep both arguments
++ // intact for the runtime call result cannot be one of these.
++ ASSERT(!result.is(r3) && !result.is(r4));
++
++ if (mode_ == OVERWRITE_LEFT || mode_ == OVERWRITE_RIGHT) {
++ Label skip_allocation, allocated;
++ Register overwritable_operand = mode_ == OVERWRITE_LEFT ? r4 : r3;
++ // If the overwritable operand is already an object, we skip the
++ // allocation of a heap number.
++ __ JumpIfNotSmi(overwritable_operand, &skip_allocation);
++ // Allocate a heap number for the result.
++ __ AllocateHeapNumber(
++ result, scratch1, scratch2, heap_number_map, gc_required);
++ __ b(&allocated);
++ __ bind(&skip_allocation);
++ // Use object holding the overwritable operand for result.
++ __ mr(result, overwritable_operand);
++ __ bind(&allocated);
++ } else {
++ ASSERT(mode_ == NO_OVERWRITE);
++ __ AllocateHeapNumber(
++ result, scratch1, scratch2, heap_number_map, gc_required);
++ }
++}
++
++
++void BinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
++ __ Push(r4, r3);
++}
++
++
++void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
++ // Untagged case: double input in d2, double result goes
++ // into d2.
++ // Tagged case: tagged input on top of stack and in r3,
++ // tagged result (heap number) goes into r3.
++
++ Label input_not_smi;
++ Label loaded;
++ Label calculate;
++ Label invalid_cache;
++ const Register scratch0 = r22;
++ const Register scratch1 = r10;
++ const Register cache_entry = r3;
++ const bool tagged = (argument_type_ == TAGGED);
++
++ if (tagged) {
++ // Argument is a number and is on stack and in r3.
++ // Load argument and check if it is a smi.
++ __ JumpIfNotSmi(r3, &input_not_smi);
++
++ // Input is a smi. Convert to double and load the low and high words
++ // of the double into r5, r6.
++ __ SmiToDoubleFPRegister(r3, d6, scratch0);
++ __ subi(sp, sp, Operand(8));
++ __ stfd(d6, MemOperand(sp, 0));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(r5, MemOperand(sp));
++ __ lwz(r6, MemOperand(sp, 4));
++#else
++ __ lwz(r5, MemOperand(sp, 4));
++ __ lwz(r6, MemOperand(sp));
++#endif
++ __ addi(sp, sp, Operand(8));
++ __ b(&loaded);
++
++ __ bind(&input_not_smi);
++ // Check if input is a HeapNumber.
++ __ CheckMap(r3,
++ r4,
++ Heap::kHeapNumberMapRootIndex,
++ &calculate,
++ DONT_DO_SMI_CHECK);
++ // Input is a HeapNumber. Load it to a double register and store the
++ // low and high words into r5, r6.
++ __ lwz(r6, FieldMemOperand(r3, HeapNumber::kExponentOffset));
++ __ lwz(r5, FieldMemOperand(r3, HeapNumber::kMantissaOffset));
++ } else {
++ // Input is untagged double in d2. Output goes to d2.
++ __ subi(sp, sp, Operand(8));
++ __ stfd(d2, MemOperand(sp, 0));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(r5, MemOperand(sp, 4));
++ __ lwz(r6, MemOperand(sp));
++#else
++ __ lwz(r5, MemOperand(sp));
++ __ lwz(r6, MemOperand(sp, 4));
++#endif
++ __ addi(sp, sp, Operand(8));
++ }
++ __ bind(&loaded);
++ // r5 = low 32 bits of double value
++ // r6 = high 32 bits of double value
++ // Compute hash (the shifts are arithmetic):
++ // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize -
1);
++ __ xor_(r4, r5, r6);
++ __ srawi(scratch0, r4, 16);
++ __ xor_(r4, r4, scratch0);
++ __ srawi(scratch0, r4, 8);
++ __ xor_(r4, r4, scratch0);
++ ASSERT(IsPowerOf2(TranscendentalCache::SubCache::kCacheSize));
++ __ andi(r4, r4, Operand(TranscendentalCache::SubCache::kCacheSize - 1));
++
++ // r5 = low 32 bits of double value.
++ // r6 = high 32 bits of double value.
++ // r4 = TranscendentalCache::hash(double value).
++ Isolate* isolate = masm->isolate();
++ ExternalReference cache_array =
++ ExternalReference::transcendental_cache_array_address(isolate);
++ __ mov(cache_entry, Operand(cache_array));
++ // cache_entry points to cache array.
++ int cache_array_index
++ = type_ * sizeof(isolate->transcendental_cache()->caches_[0]);
++ __ LoadP(cache_entry, MemOperand(cache_entry, cache_array_index), r0);
++ // r3 points to the cache for the type type_.
++ // If NULL, the cache hasn't been initialized yet, so go through runtime.
++ __ cmpi(cache_entry, Operand(0, RelocInfo::NONE));
++ __ beq(&invalid_cache);
++
++#ifdef DEBUG
++ // Check that the layout of cache elements match expectations.
++ { TranscendentalCache::SubCache::Element test_elem[2];
++ char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
++ char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
++ char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
++ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
++ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
++ // Two uint_32's and a pointer.
++#if V8_TARGET_ARCH_PPC64
++ CHECK_EQ(16, static_cast<int>(elem2_start - elem_start));
++#else
++ CHECK_EQ(12, static_cast<int>(elem2_start - elem_start));
++#endif
++ CHECK_EQ(0, static_cast<int>(elem_in0 - elem_start));
++ CHECK_EQ(kIntSize, static_cast<int>(elem_in1 - elem_start));
++ CHECK_EQ(2 * kIntSize, static_cast<int>(elem_out - elem_start));
++ }
++#endif
++
++#if V8_TARGET_ARCH_PPC64
++ // Find the address of the r4'th entry in the cache, i.e., &r3[r4*16].
++ __ ShiftLeftImm(scratch0, r4, Operand(4));
++#else
++ // Find the address of the r4'th entry in the cache, i.e., &r3[r4*12].
++ __ ShiftLeftImm(scratch0, r4, Operand(1));
++ __ add(r4, r4, scratch0);
++ __ ShiftLeftImm(scratch0, r4, Operand(2));
++#endif
++ __ add(cache_entry, cache_entry, scratch0);
++ // Check if cache matches: Double value is stored in uint32_t[2] array.
++ __ lwz(r7, MemOperand(cache_entry, 0));
++ __ lwz(r8, MemOperand(cache_entry, 4));
++ __ LoadP(r9, MemOperand(cache_entry, 8));
++ __ cmp(r5, r7);
++ __ bne(&calculate);
++ __ cmp(r6, r8);
++ __ bne(&calculate);
++ // Cache hit. Load result, cleanup and return.
++ Counters* counters = masm->isolate()->counters();
++ __ IncrementCounter(
++ counters->transcendental_cache_hit(), 1, scratch0, scratch1);
++ if (tagged) {
++ // Pop input value from stack and load result into r3.
++ __ pop();
++ __ mr(r3, r9);
++ } else {
++ // Load result into d2.
++ __ lfd(d2, FieldMemOperand(r9, HeapNumber::kValueOffset));
++ }
++ __ Ret();
++
++ __ bind(&calculate);
++ __ IncrementCounter(
++ counters->transcendental_cache_miss(), 1, scratch0, scratch1);
++ if (tagged) {
++ __ bind(&invalid_cache);
++ ExternalReference runtime_function =
++ ExternalReference(RuntimeFunction(), masm->isolate());
++ __ TailCallExternalReference(runtime_function, 1, 1);
++ } else {
++ Label no_update;
++ Label skip_cache;
++
++ // Call C function to calculate the result and update the cache.
++ // r3: precalculated cache entry address.
++ // r5 and r6: parts of the double value.
++ // Store r3, r5 and r6 on stack for later before calling C function.
++ __ Push(r6, r5, cache_entry);
++ GenerateCallCFunction(masm, scratch0);
++ __ GetCFunctionDoubleResult(d2);
++
++ // Try to update the cache. If we cannot allocate a
++ // heap number, we return the result without updating.
++ __ Pop(r6, r5, cache_entry);
++ __ LoadRoot(r8, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r9, scratch0, scratch1, r8, &no_update);
++ __ stfd(d2, FieldMemOperand(r9, HeapNumber::kValueOffset));
++ __ stw(r5, MemOperand(cache_entry, 0));
++ __ stw(r6, MemOperand(cache_entry, 4));
++ __ StoreP(r9, MemOperand(cache_entry, 8));
++ __ Ret();
++
++ __ bind(&invalid_cache);
++ // The cache is invalid. Call runtime which will recreate the
++ // cache.
++ __ LoadRoot(r8, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r3, scratch0, scratch1, r8, &skip_cache);
++ __ stfd(d2, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ push(r3);
++ __ CallRuntime(RuntimeFunction(), 1);
++ }
++ __ lfd(d2, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ __ Ret();
++
++ __ bind(&skip_cache);
++ // Call C function to calculate the result and answer directly
++ // without updating the cache.
++ GenerateCallCFunction(masm, scratch0);
++ __ GetCFunctionDoubleResult(d2);
++ __ bind(&no_update);
++
++ // We return the value in d2 without adding it to the cache, but
++ // we cause a scavenging GC so that future allocations will succeed.
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ // Allocate an aligned object larger than a HeapNumber.
++ ASSERT(2 * kDoubleSize >= HeapNumber::kSize);
++ __ LoadSmiLiteral(scratch0, Smi::FromInt(2 * kDoubleSize));
++ __ push(scratch0);
++ __ CallRuntimeSaveDoubles(Runtime::kAllocateInNewSpace);
++ }
++ __ Ret();
++ }
++}
++
++
++void TranscendentalCacheStub::GenerateCallCFunction(MacroAssembler* masm,
++ Register scratch) {
++ Isolate* isolate = masm->isolate();
++
++ __ mflr(r0);
++ __ push(r0);
++ __ PrepareCallCFunction(0, 1, scratch);
++ __ fmr(d1, d2);
++ AllowExternalCallThatCantCauseGC scope(masm);
++ switch (type_) {
++ case TranscendentalCache::SIN:
++ __ CallCFunction(ExternalReference::math_sin_double_function(isolate),
++ 0, 1);
++ break;
++ case TranscendentalCache::COS:
++ __ CallCFunction(ExternalReference::math_cos_double_function(isolate),
++ 0, 1);
++ break;
++ case TranscendentalCache::TAN:
++ __ CallCFunction(ExternalReference::math_tan_double_function(isolate),
++ 0, 1);
++ break;
++ case TranscendentalCache::LOG:
++ __ CallCFunction(ExternalReference::math_log_double_function(isolate),
++ 0, 1);
++ break;
++ default:
++ UNIMPLEMENTED();
++ break;
++ }
++ __ pop(r0);
++ __ mtlr(r0);
++}
++
++
++Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
++ switch (type_) {
++ // Add more cases when necessary.
++ case TranscendentalCache::SIN: return Runtime::kMath_sin;
++ case TranscendentalCache::COS: return Runtime::kMath_cos;
++ case TranscendentalCache::TAN: return Runtime::kMath_tan;
++ case TranscendentalCache::LOG: return Runtime::kMath_log;
++ default:
++ UNIMPLEMENTED();
++ return Runtime::kAbort;
++ }
++}
++
++
++void StackCheckStub::Generate(MacroAssembler* masm) {
++ __ TailCallRuntime(Runtime::kStackGuard, 0, 1);
++}
++
++
++void InterruptStub::Generate(MacroAssembler* masm) {
++ __ TailCallRuntime(Runtime::kInterrupt, 0, 1);
++}
++
++
++void MathPowStub::Generate(MacroAssembler* masm) {
++ const Register base = r4;
++ const Register exponent = r5;
++ const Register heapnumbermap = r8;
++ const Register heapnumber = r3;
++ const DoubleRegister double_base = d1;
++ const DoubleRegister double_exponent = d2;
++ const DoubleRegister double_result = d3;
++ const DoubleRegister double_scratch = d0;
++ const Register scratch = r22;
++ const Register scratch2 = r10;
++
++ Label call_runtime, done, int_exponent;
++ if (exponent_type_ == ON_STACK) {
++ Label base_is_smi, unpack_exponent;
++ // The exponent and base are supplied as arguments on the stack.
++ // This can only happen if the stub is called from non-optimized code.
++ // Load input parameters from stack to double registers.
++ __ LoadP(base, MemOperand(sp, 1 * kPointerSize));
++ __ LoadP(exponent, MemOperand(sp, 0 * kPointerSize));
++
++ __ LoadRoot(heapnumbermap, Heap::kHeapNumberMapRootIndex);
++
++ __ UntagAndJumpIfSmi(scratch, base, &base_is_smi);
++ __ LoadP(scratch, FieldMemOperand(base, JSObject::kMapOffset));
++ __ cmp(scratch, heapnumbermap);
++ __ bne(&call_runtime);
++
++ __ lfd(double_base, FieldMemOperand(base, HeapNumber::kValueOffset));
++ __ b(&unpack_exponent);
++
++ __ bind(&base_is_smi);
++ FloatingPointHelper::ConvertIntToDouble(masm, scratch, double_base);
++ __ bind(&unpack_exponent);
++
++ __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent);
++ __ LoadP(scratch, FieldMemOperand(exponent, JSObject::kMapOffset));
++ __ cmp(scratch, heapnumbermap);
++ __ bne(&call_runtime);
++
++ __ lfd(double_exponent,
++ FieldMemOperand(exponent, HeapNumber::kValueOffset));
++ } else if (exponent_type_ == TAGGED) {
++ // Base is already in double_base.
++ __ UntagAndJumpIfSmi(scratch, exponent, &int_exponent);
++
++ __ lfd(double_exponent,
++ FieldMemOperand(exponent, HeapNumber::kValueOffset));
++ }
++
++ if (exponent_type_ != INTEGER) {
++ // Detect integer exponents stored as double.
++ __ EmitVFPTruncate(kRoundToZero,
++ scratch,
++ double_exponent,
++ scratch2,
++ double_scratch,
++ kCheckForInexactConversion);
++ __ beq(&int_exponent);
++
++ if (exponent_type_ == ON_STACK) {
++ // Detect square root case. Crankshaft detects constant +/-0.5 at
++ // compile time and uses DoMathPowHalf instead. We then skip this check
++ // for non-constant cases of +/-0.5 as these hardly occur.
++ Label not_plus_half, not_minus_inf1, not_minus_inf2;
++
++ // Test for 0.5.
++ __ LoadDoubleLiteral(double_scratch, 0.5, scratch);
++ __ fcmpu(double_exponent, double_scratch);
++ __ bne(¬_plus_half);
++
++ // Calculates square root of base. Check for the special case of
++ // Math.pow(-Infinity, 0.5) == Infinity (ECMA spec, 15.8.2.13).
++ __ LoadDoubleLiteral(double_scratch, -V8_INFINITY, scratch);
++ __ fcmpu(double_base, double_scratch);
++ __ bne(¬_minus_inf1);
++ __ fneg(double_result, double_scratch);
++ __ b(&done);
++ __ bind(¬_minus_inf1);
++
++ // Add +0 to convert -0 to +0.
++ __ fadd(double_scratch, double_base, kDoubleRegZero);
++ __ fsqrt(double_result, double_scratch);
++ __ b(&done);
++
++ __ bind(¬_plus_half);
++ __ LoadDoubleLiteral(double_scratch, -0.5, scratch);
++ __ fcmpu(double_exponent, double_scratch);
++ __ bne(&call_runtime);
++
++ // Calculates square root of base. Check for the special case of
++ // Math.pow(-Infinity, -0.5) == 0 (ECMA spec, 15.8.2.13).
++ __ LoadDoubleLiteral(double_scratch, -V8_INFINITY, scratch);
++ __ fcmpu(double_base, double_scratch);
++ __ bne(¬_minus_inf2);
++ __ fmr(double_result, kDoubleRegZero);
++ __ b(&done);
++ __ bind(¬_minus_inf2);
++
++ // Add +0 to convert -0 to +0.
++ __ fadd(double_scratch, double_base, kDoubleRegZero);
++ __ LoadDoubleLiteral(double_result, 1.0, scratch);
++ __ fsqrt(double_scratch, double_scratch);
++ __ fdiv(double_result, double_result, double_scratch);
++ __ b(&done);
++ }
++
++ __ mflr(r0);
++ __ push(r0);
++ {
++ AllowExternalCallThatCantCauseGC scope(masm);
++ __ PrepareCallCFunction(0, 2, scratch);
++ __ SetCallCDoubleArguments(double_base, double_exponent);
++ __ CallCFunction(
++ ExternalReference::power_double_double_function(masm->isolate()),
++ 0, 2);
++ }
++ __ pop(r0);
++ __ mtlr(r0);
++ __ GetCFunctionDoubleResult(double_result);
++ __ b(&done);
++ }
++
++ // Calculate power with integer exponent.
++ __ bind(&int_exponent);
++
++ // Get two copies of exponent in the registers scratch and exponent.
++ if (exponent_type_ == INTEGER) {
++ __ mr(scratch, exponent);
++ } else {
++ // Exponent has previously been stored into scratch as untagged integer.
++ __ mr(exponent, scratch);
++ }
++ __ fmr(double_scratch, double_base); // Back up base.
++ __ li(scratch2, Operand(1));
++ FloatingPointHelper::ConvertIntToDouble(masm, scratch2, double_result);
++
++ // Get absolute value of exponent.
++ Label positive_exponent;
++ __ cmpi(scratch, Operand::Zero());
++ __ bge(&positive_exponent);
++ __ neg(scratch, scratch);
++ __ bind(&positive_exponent);
++
++ Label while_true, no_carry, loop_end;
++ __ bind(&while_true);
++ __ andi(scratch2, scratch, Operand(1));
++ __ beq(&no_carry, cr0);
++ __ fmul(double_result, double_result, double_scratch);
++ __ bind(&no_carry);
++ __ ShiftRightArithImm(scratch, scratch, 1, SetRC);
++ __ beq(&loop_end, cr0);
++ __ fmul(double_scratch, double_scratch, double_scratch);
++ __ b(&while_true);
++ __ bind(&loop_end);
++
++ __ cmpi(exponent, Operand::Zero());
++ __ bge(&done);
++
++ __ li(scratch2, Operand(1));
++ FloatingPointHelper::ConvertIntToDouble(masm, scratch2, double_scratch);
++ __ fdiv(double_result, double_scratch, double_result);
++ // Test whether result is zero. Bail out to check for subnormal result.
++ // Due to subnormals, x^-y == (1/x)^y does not hold in all cases.
++ __ fcmpu(double_result, kDoubleRegZero);
++ __ bne(&done);
++ // double_exponent may not containe the exponent value if the input was a
++ // smi. We set it with exponent value before bailing out.
++ FloatingPointHelper::ConvertIntToDouble(masm, exponent, double_exponent);
++
++ // Returning or bailing out.
++ Counters* counters = masm->isolate()->counters();
++ if (exponent_type_ == ON_STACK) {
++ // The arguments are still on the stack.
++ __ bind(&call_runtime);
++ __ TailCallRuntime(Runtime::kMath_pow_cfunction, 2, 1);
++
++ // The stub is called from non-optimized code, which expects the result
++ // as heap number in exponent.
++ __ bind(&done);
++ __ AllocateHeapNumber(
++ heapnumber, scratch, scratch2, heapnumbermap, &call_runtime);
++ __ stfd(double_result,
++ FieldMemOperand(heapnumber, HeapNumber::kValueOffset));
++ ASSERT(heapnumber.is(r3));
++ __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2);
++ __ Ret(2);
++ } else {
++ __ mflr(r0);
++ __ push(r0);
++ {
++ AllowExternalCallThatCantCauseGC scope(masm);
++ __ PrepareCallCFunction(0, 2, scratch);
++ __ SetCallCDoubleArguments(double_base, double_exponent);
++ __ CallCFunction(
++ ExternalReference::power_double_double_function(masm->isolate()),
++ 0, 2);
++ }
++ __ pop(r0);
++ __ mtlr(r0);
++ __ GetCFunctionDoubleResult(double_result);
++
++ __ bind(&done);
++ __ IncrementCounter(counters->math_pow(), 1, scratch, scratch2);
++ __ Ret();
++ }
++}
++
++
++bool CEntryStub::NeedsImmovableCode() {
++ return true;
++}
++
++
++bool CEntryStub::IsPregenerated() {
++ return (!save_doubles_ || ISOLATE->fp_stubs_generated()) &&
++ result_size_ == 1;
++}
++
++
++void CodeStub::GenerateStubsAheadOfTime() {
++ CEntryStub::GenerateAheadOfTime();
++ StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime();
++ RecordWriteStub::GenerateFixedRegStubsAheadOfTime();
++}
++
++
++void CodeStub::GenerateFPStubs() {
++ CEntryStub save_doubles(1, kSaveFPRegs);
++ Handle<Code> code = save_doubles.GetCode();
++ code->set_is_pregenerated(true);
++ StoreBufferOverflowStub stub(kSaveFPRegs);
++ stub.GetCode()->set_is_pregenerated(true);
++ code->GetIsolate()->set_fp_stubs_generated(true);
++}
++
++
++void CEntryStub::GenerateAheadOfTime() {
++ CEntryStub stub(1, kDontSaveFPRegs);
++ Handle<Code> code = stub.GetCode();
++ code->set_is_pregenerated(true);
++}
++
++
++void CEntryStub::GenerateCore(MacroAssembler* masm,
++ Label* throw_normal_exception,
++ Label* throw_termination_exception,
++ Label* throw_out_of_memory_exception,
++ bool do_gc,
++ bool always_allocate) {
++ // r3: result parameter for PerformGC, if any
++ // r14: number of arguments including receiver (C callee-saved)
++ // r15: pointer to builtin function (C callee-saved)
++ // r16: pointer to the first argument (C callee-saved)
++ Isolate* isolate = masm->isolate();
++ Register isolate_reg = no_reg;
++
++ if (do_gc) {
++ // Passing r3.
++ __ PrepareCallCFunction(1, 0, r4);
++ __ CallCFunction(ExternalReference::perform_gc_function(isolate),
++ 1, 0);
++ }
++
++ ExternalReference scope_depth =
++ ExternalReference::heap_always_allocate_scope_depth(isolate);
++ if (always_allocate) {
++ __ mov(r3, Operand(scope_depth));
++ __ lwz(r4, MemOperand(r3));
++ __ addi(r4, r4, Operand(1));
++ __ stw(r4, MemOperand(r3));
++ }
++
++ // PPC LINUX ABI:
++ // The #if below used to be !USE_SIMULATOR but needed
++ // to change to support nativesim=true builds
++#if defined(V8_HOST_ARCH_PPC64) || defined(V8_HOST_ARCH_PPC)
++ // Call C built-in on native hardware.
++#if defined(V8_TARGET_ARCH_PPC64)
++
++#if !ABI_RETURNS_OBJECT_PAIRS_IN_REGS
++ if (result_size_ < 2) {
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ __ mr(r3, r14);
++#else
++ // r3 = argc << 32 (for alignment), r4 = argv
++ __ ShiftLeftImm(r3, r14, Operand(32));
++#endif
++ __ mr(r4, r16);
++ isolate_reg = r5;
++ } else {
++ ASSERT_EQ(2, result_size_);
++ // The return value is 16-byte non-scalar value.
++ // Use frame storage reserved by calling function to pass return
++ // buffer as implicit first argument.
++ __ addi(r3, sp, Operand((kStackFrameExtraParamSlot + 1) * kPointerSize));
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ __ mr(r4, r14);
++#else
++ // r4 = argc << 32 (for alignment), r5 = argv
++ __ ShiftLeftImm(r4, r14, Operand(32));
++#endif
++ __ mr(r5, r16);
++ isolate_reg = r6;
++ }
++#else
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ __ mr(r3, r14);
++#else
++ // r3 = argc << 32 (for alignment), r4 = argv
++ __ ShiftLeftImm(r3, r14, Operand(32));
++#endif
++ __ mr(r4, r16);
++ isolate_reg = r5;
++#endif
++
++#elif defined(_AIX) // 32-bit AIX
++ // r3 = argc, r4 = argv
++ __ mr(r3, r14);
++ __ mr(r4, r16);
++ isolate_reg = r5;
++#else // 32-bit linux
++ // Use frame storage reserved by calling function
++ // PPC passes C++ objects by reference not value
++ // This builds an object in the stack frame
++ __ addi(r3, sp, Operand((kStackFrameExtraParamSlot + 1) * kPointerSize));
++ __ StoreP(r14, MemOperand(r3));
++ __ StoreP(r16, MemOperand(r3, kPointerSize));
++ isolate_reg = r4;
++#endif
++#else // Simulated
++ // Call C built-in using simulator.
++ // r3 = argc, r4 = argv
++#if defined(V8_TARGET_ARCH_PPC64) && __BYTE_ORDER == __BIG_ENDIAN
++ __ ShiftLeftImm(r3, r14, Operand(32));
++#else
++ __ mr(r3, r14);
++#endif
++ __ mr(r4, r16);
++ isolate_reg = r5;
++#endif
++
++ __ mov(isolate_reg, Operand(ExternalReference::isolate_address()));
++
++#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++ // Native AIX/PPC64 Linux use a function descriptor.
++ __ LoadP(ToRegister(2), MemOperand(r15, kPointerSize)); // TOC
++ __ LoadP(ip, MemOperand(r15, 0)); // Instruction address
++ Register target = ip;
++#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++ Register target = ip;
++ __ Move(ip, r15);
++#else
++ Register target = r15;
++#endif
++
++ // To let the GC traverse the return address of the exit frames, we need to
++ // know where the return address is. The CEntryStub is unmovable, so
++ // we can store the address on the stack to be able to find it again and
++ // we never have to restore it, because it will not change.
++ // Compute the return address in lr to return to after the jump below. Pc is
++ // already at '+ 8' from the current instruction but return is after three
++ // instructions so add another 4 to pc to get the return address.
++ { Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
++ Label here;
++ __ b(&here, SetLK);
++ __ bind(&here);
++ __ mflr(r8);
++
++// Constant used below is dependent on size of Call() macro instructions
++ __ addi(r0, r8, Operand(20));
++
++ __ StoreP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kPointerSize));
++ __ Call(target);
++ }
++
++ if (always_allocate) {
++ // It's okay to clobber r5 and r6 here. Don't mess with r3 and r4
++ // though (contain the result).
++ __ mov(r5, Operand(scope_depth));
++ __ lwz(r6, MemOperand(r5));
++ __ subi(r6, r6, Operand(1));
++ __ stw(r6, MemOperand(r5));
++ }
++
++ // check for failure result
++ Label failure_returned;
++ STATIC_ASSERT(((kFailureTag + 1) & kFailureTagMask) == 0);
++#if defined(V8_TARGET_ARCH_PPC64) && !ABI_RETURNS_OBJECT_PAIRS_IN_REGS
++ // If return value is on the stack, pop it to registers.
++ if (result_size_ > 1) {
++ ASSERT_EQ(2, result_size_);
++ __ LoadP(r4, MemOperand(r3, kPointerSize));
++ __ LoadP(r3, MemOperand(r3));
++ }
++#endif
++ // Lower 2 bits of r5 are 0 iff r3 has failure tag.
++ __ addi(r5, r3, Operand(1));
++ STATIC_ASSERT(kFailureTagMask < 0x8000);
++ __ andi(r0, r5, Operand(kFailureTagMask));
++ __ beq(&failure_returned, cr0);
++
++ // Exit C frame and return.
++ // r3:r4: result
++ // sp: stack pointer
++ // fp: frame pointer
++ // Callee-saved register r14 still holds argc.
++ __ LeaveExitFrame(save_doubles_, r14);
++ __ blr();
++
++ // check if we should retry or throw exception
++ Label retry;
++ __ bind(&failure_returned);
++ STATIC_ASSERT(Failure::RETRY_AFTER_GC == 0);
++ __ andi(r0, r3, Operand(((1 << kFailureTypeTagSize) - 1) <<
kFailureTagSize));
++ __ beq(&retry, cr0);
++
++ // Special handling of out of memory exceptions.
++ Failure* out_of_memory = Failure::OutOfMemoryException();
++ __ cmpi(r3, Operand(reinterpret_cast<intptr_t>(out_of_memory)));
++ __ beq(throw_out_of_memory_exception);
++
++ // Retrieve the pending exception and clear the variable.
++ __ mov(r6, Operand(isolate->factory()->the_hole_value()));
++ __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
++ isolate)));
++ __ LoadP(r3, MemOperand(ip));
++ __ StoreP(r6, MemOperand(ip));
++
++ // Special handling of termination exceptions which are uncatchable
++ // by javascript code.
++ __ mov(r6, Operand(isolate->factory()->termination_exception()));
++ __ cmp(r3, r6);
++ __ beq(throw_termination_exception);
++
++ // Handle normal exception.
++ __ b(throw_normal_exception);
++
++ __ bind(&retry); // pass last failure (r3) as parameter (r3) when retrying
++}
++
++
++void CEntryStub::Generate(MacroAssembler* masm) {
++ // Called from JavaScript; parameters are on stack as if calling JS function
++ // r3: number of arguments including receiver
++ // r4: pointer to builtin function
++ // fp: frame pointer (restored after C call)
++ // sp: stack pointer (restored as callee's sp after C call)
++ // cp: current context (C callee-saved)
++
++ // Result returned in r3 or r3+r4 by default.
++
++ // NOTE: Invocations of builtins may return failure objects
++ // instead of a proper result. The builtin entry handles
++ // this by performing a garbage collection and retrying the
++ // builtin once.
++
++ // Compute the argv pointer in a callee-saved register.
++ __ ShiftLeftImm(r16, r3, Operand(kPointerSizeLog2));
++ __ add(r16, r16, sp);
++ __ subi(r16, r16, Operand(kPointerSize));
++
++ // Enter the exit frame that transitions from JavaScript to C++.
++ FrameScope scope(masm, StackFrame::MANUAL);
++
++ // Need at least one extra slot for return address location.
++ int arg_stack_space = 1;
++
++ // PPC LINUX ABI:
++ // The #if immediately below was !USE_SIMULATOR, but needed
++ // to change to support nativesim=true builds
++#if defined(V8_HOST_ARCH_PPC64) || defined(V8_HOST_ARCH_PPC)
++#if defined(V8_TARGET_ARCH_PPC64) && !ABI_RETURNS_OBJECT_PAIRS_IN_REGS
++ // Pass buffer for return value on stack if necessary
++ if (result_size_ > 1) {
++ ASSERT_EQ(2, result_size_);
++ arg_stack_space += 2;
++ }
++#elif !defined(_AIX)
++ // 32-bit linux
++ // Pass C++ objects by reference not value
++ arg_stack_space += 2;
++#endif
++#endif
++
++ __ EnterExitFrame(save_doubles_, arg_stack_space);
++
++ // Set up argc and the builtin function in callee-saved registers.
++ __ mr(r14, r3);
++ __ mr(r15, r4);
++
++ // r14: number of arguments (C callee-saved)
++ // r15: pointer to builtin function (C callee-saved)
++ // r16: pointer to first argument (C callee-saved)
++
++ Label throw_normal_exception;
++ Label throw_termination_exception;
++ Label throw_out_of_memory_exception;
++
++ // Call into the runtime system.
++ GenerateCore(masm,
++ &throw_normal_exception,
++ &throw_termination_exception,
++ &throw_out_of_memory_exception,
++ false,
++ false);
++
++ // Do space-specific GC and retry runtime call.
++ GenerateCore(masm,
++ &throw_normal_exception,
++ &throw_termination_exception,
++ &throw_out_of_memory_exception,
++ true,
++ false);
++
++ // Do full GC and retry runtime call one final time.
++ Failure* failure = Failure::InternalError();
++ __ mov(r3, Operand(reinterpret_cast<intptr_t>(failure)));
++ GenerateCore(masm,
++ &throw_normal_exception,
++ &throw_termination_exception,
++ &throw_out_of_memory_exception,
++ true,
++ true);
++
++ __ bind(&throw_out_of_memory_exception);
++ // Set external caught exception to false.
++ Isolate* isolate = masm->isolate();
++ ExternalReference external_caught(Isolate::kExternalCaughtExceptionAddress,
++ isolate);
++ __ li(r3, Operand(false, RelocInfo::NONE));
++ __ mov(r5, Operand(external_caught));
++ __ StoreP(r3, MemOperand(r5));
++
++ // Set pending exception and r0 to out of memory exception.
++ Failure* out_of_memory = Failure::OutOfMemoryException();
++ __ mov(r3, Operand(reinterpret_cast<intptr_t>(out_of_memory)));
++ __ mov(r5, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
++ isolate)));
++ __ StoreP(r3, MemOperand(r5));
++ // Fall through to the next label.
++
++ __ bind(&throw_termination_exception);
++ __ ThrowUncatchable(r3);
++
++ __ bind(&throw_normal_exception);
++ __ Throw(r3);
++}
++
++
++void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
++ // r3: code entry
++ // r4: function
++ // r5: receiver
++ // r6: argc
++ // [sp+0]: argv
++
++ Label invoke, handler_entry, exit;
++
++ // Called from C
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++
++ // PPC LINUX ABI:
++ // preserve LR in pre-reserved slot in caller's frame
++ __ mflr(r0);
++ __ StoreP(r0, MemOperand(sp, kStackFrameLRSlot * kPointerSize));
++
++ // Save callee saved registers on the stack.
++ __ MultiPush(kCalleeSaved);
++
++ // Floating point regs FPR0 - FRP13 are volatile
++ // FPR14-FPR31 are non-volatile, but sub-calls will save them for us
++
++// int offset_to_argv = kPointerSize * 22; // matches (22*4) above
++// __ lwz(r7, MemOperand(sp, offset_to_argv));
++
++ // Push a frame with special values setup to mark it as an entry frame.
++ // r3: code entry
++ // r4: function
++ // r5: receiver
++ // r6: argc
++ // r7: argv
++ Isolate* isolate = masm->isolate();
++ __ li(r0, Operand(-1)); // Push a bad frame pointer to fail if it is used.
++ __ push(r0);
++ int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
++ __ LoadSmiLiteral(r0, Smi::FromInt(marker));
++ __ push(r0);
++ __ push(r0);
++ // Save copies of the top frame descriptor on the stack.
++ __ mov(r8, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate)));
++ __ LoadP(r0, MemOperand(r8));
++ __ push(r0);
++
++ // Set up frame pointer for the frame to be pushed.
++ __ addi(fp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
++
++ // If this is the outermost JS call, set js_entry_sp value.
++ Label non_outermost_js;
++ ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate);
++ __ mov(r8, Operand(ExternalReference(js_entry_sp)));
++ __ LoadP(r9, MemOperand(r8));
++ __ cmpi(r9, Operand::Zero());
++ __ bne(&non_outermost_js);
++ __ StoreP(fp, MemOperand(r8));
++ __ LoadSmiLiteral(ip, Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME));
++ Label cont;
++ __ b(&cont);
++ __ bind(&non_outermost_js);
++ __ LoadSmiLiteral(ip, Smi::FromInt(StackFrame::INNER_JSENTRY_FRAME));
++ __ bind(&cont);
++ __ push(ip); // frame-type
++
++ // Jump to a faked try block that does the invoke, with a faked catch
++ // block that sets the pending exception.
++ __ b(&invoke);
++
++ __ bind(&handler_entry);
++ handler_offset_ = handler_entry.pos();
++ // Caught exception: Store result (exception) in the pending exception
++ // field in the JSEnv and return a failure sentinel. Coming in here the
++ // fp will be invalid because the PushTryHandler below sets it to 0 to
++ // signal the existence of the JSEntry frame.
++ __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
++ isolate)));
++
++ __ StoreP(r3, MemOperand(ip));
++ __ mov(r3, Operand(reinterpret_cast<intptr_t>(Failure::Exception())));
++ __ b(&exit);
++
++ // Invoke: Link this frame into the handler chain. There's only one
++ // handler block in this code object, so its index is 0.
++ __ bind(&invoke);
++ // Must preserve r0-r4, r5-r7 are available. (needs update for PPC)
++ __ PushTryHandler(StackHandler::JS_ENTRY, 0);
++ // If an exception not caught by another handler occurs, this handler
++ // returns control to the code after the b(&invoke) above, which
++ // restores all kCalleeSaved registers (including cp and fp) to their
++ // saved values before returning a failure to C.
++
++ // Clear any pending exceptions.
++ __ mov(r8, Operand(isolate->factory()->the_hole_value()));
++ __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
++ isolate)));
++ __ StoreP(r8, MemOperand(ip));
++
++ // Invoke the function by calling through JS entry trampoline builtin.
++ // Notice that we cannot store a reference to the trampoline code directly in
++ // this stub, because runtime stubs are not traversed when doing GC.
++
++ // Expected registers by Builtins::JSEntryTrampoline
++ // r3: code entry
++ // r4: function
++ // r5: receiver
++ // r6: argc
++ // r7: argv
++ if (is_construct) {
++ ExternalReference construct_entry(Builtins::kJSConstructEntryTrampoline,
++ isolate);
++ __ mov(ip, Operand(construct_entry));
++ } else {
++ ExternalReference entry(Builtins::kJSEntryTrampoline, isolate);
++ __ mov(ip, Operand(entry));
++ }
++ __ LoadP(ip, MemOperand(ip)); // deref address
++
++ // Branch and link to JSEntryTrampoline.
++ // the address points to the start of the code object, skip the header
++ __ addi(r0, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
++ __ mtlr(r0);
++ __ bclr(BA, SetLK); // make the call
++
++ // Unlink this frame from the handler chain.
++ __ PopTryHandler();
++
++ __ bind(&exit); // r3 holds result
++ // Check if the current stack frame is marked as the outermost JS frame.
++ Label non_outermost_js_2;
++ __ pop(r8);
++ __ CmpSmiLiteral(r8, Smi::FromInt(StackFrame::OUTERMOST_JSENTRY_FRAME), r0);
++ __ bne(&non_outermost_js_2);
++ __ mov(r9, Operand::Zero());
++ __ mov(r8, Operand(ExternalReference(js_entry_sp)));
++ __ StoreP(r9, MemOperand(r8));
++ __ bind(&non_outermost_js_2);
++
++ // Restore the top frame descriptors from the stack.
++ __ pop(r6);
++ __ mov(ip,
++ Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate)));
++ __ StoreP(r6, MemOperand(ip));
++
++ // Reset the stack to the callee saved registers.
++ __ addi(sp, sp, Operand(-EntryFrameConstants::kCallerFPOffset));
++
++ // Restore callee-saved registers and return.
++#ifdef DEBUG
++ if (FLAG_debug_code) {
++ Label here;
++ __ b(&here, SetLK);
++ __ bind(&here);
++ }
++#endif
++
++ __ MultiPop(kCalleeSaved);
++
++ __ LoadP(r0, MemOperand(sp, kStackFrameLRSlot * kPointerSize));
++ __ mtctr(r0);
++ __ bcr();
++}
++
++
++// Uses registers r3 to r7.
++// Expected input (depending on whether args are in registers or on the stack):
++// * object: r3 or at sp + 1 * kPointerSize.
++// * function: r4 or at sp.
++//
++// An inlined call site may have been generated before calling this stub.
++// In this case the offset to the inline site to patch is passed on the stack,
++// in the safepoint slot for register r7.
++// (See LCodeGen::DoInstanceOfKnownGlobal)
++void InstanceofStub::Generate(MacroAssembler* masm) {
++ // Call site inlining and patching implies arguments in registers.
++ ASSERT(HasArgsInRegisters() || !HasCallSiteInlineCheck());
++ // ReturnTrueFalse is only implemented for inlined call sites.
++ ASSERT(!ReturnTrueFalseObject() || HasCallSiteInlineCheck());
++
++ // Fixed register usage throughout the stub:
++ const Register object = r3; // Object (lhs).
++ Register map = r6; // Map of the object.
++ const Register function = r4; // Function (rhs).
++ const Register prototype = r7; // Prototype of the function.
++ const Register inline_site = r9;
++ const Register scratch = r5;
++ const Register scratch2 = r8;
++ Register scratch3 = no_reg;
++
++#if V8_TARGET_ARCH_PPC64
++ const int32_t kDeltaToLoadBoolResult = 9 * Assembler::kInstrSize;
++#else
++ const int32_t kDeltaToLoadBoolResult = 5 * Assembler::kInstrSize;
++#endif
++
++ Label slow, loop, is_instance, is_not_instance, not_js_object;
++
++ if (!HasArgsInRegisters()) {
++ __ LoadP(object, MemOperand(sp, 1 * kPointerSize));
++ __ LoadP(function, MemOperand(sp, 0));
++ }
++
++ // Check that the left hand is a JS object and load map.
++ __ JumpIfSmi(object, ¬_js_object);
++ __ IsObjectJSObjectType(object, map, scratch, ¬_js_object);
++
++ // If there is a call site cache don't look in the global cache, but do the
++ // real lookup and update the call site cache.
++ if (!HasCallSiteInlineCheck()) {
++ Label miss;
++ __ CompareRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
++ __ bne(&miss);
++ __ CompareRoot(map, Heap::kInstanceofCacheMapRootIndex);
++ __ bne(&miss);
++ __ LoadRoot(r3, Heap::kInstanceofCacheAnswerRootIndex);
++ __ Ret(HasArgsInRegisters() ? 0 : 2);
++
++ __ bind(&miss);
++ }
++
++ // Get the prototype of the function.
++ __ TryGetFunctionPrototype(function, prototype, scratch, &slow, true);
++
++ // Check that the function prototype is a JS object.
++ __ JumpIfSmi(prototype, &slow);
++ __ IsObjectJSObjectType(prototype, scratch, scratch, &slow);
++
++ // Update the global instanceof or call site inlined cache with the current
++ // map and function. The cached answer will be set when it is known below.
++ if (!HasCallSiteInlineCheck()) {
++ __ StoreRoot(function, Heap::kInstanceofCacheFunctionRootIndex);
++ __ StoreRoot(map, Heap::kInstanceofCacheMapRootIndex);
++ } else {
++ ASSERT(HasArgsInRegisters());
++ // Patch the (relocated) inlined map check.
++
++ // The offset was stored in r7 safepoint slot.
++ // (See LCodeGen::DoDeferredLInstanceOfKnownGlobal)
++ __ LoadFromSafepointRegisterSlot(scratch, r7);
++ __ mflr(inline_site);
++ __ sub(inline_site, inline_site, scratch);
++ // Get the map location in scratch and patch it.
++ __ GetRelocatedValueLocation(inline_site, scratch, scratch2);
++ __ StoreP(map, FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset),
++ r0);
++ }
++
++ // Register mapping: r6 is object map and r7 is function prototype.
++ // Get prototype of object into r5.
++ __ LoadP(scratch, FieldMemOperand(map, Map::kPrototypeOffset));
++
++ // We don't need map any more. Use it as a scratch register.
++ scratch3 = map;
++ map = no_reg;
++
++ // Loop through the prototype chain looking for the function prototype.
++ __ LoadRoot(scratch3, Heap::kNullValueRootIndex);
++ __ bind(&loop);
++ __ cmp(scratch, prototype);
++ __ beq(&is_instance);
++ __ cmp(scratch, scratch3);
++ __ beq(&is_not_instance);
++ __ LoadP(scratch, FieldMemOperand(scratch, HeapObject::kMapOffset));
++ __ LoadP(scratch, FieldMemOperand(scratch, Map::kPrototypeOffset));
++ __ b(&loop);
++
++ __ bind(&is_instance);
++ if (!HasCallSiteInlineCheck()) {
++ __ LoadSmiLiteral(r3, Smi::FromInt(0));
++ __ StoreRoot(r3, Heap::kInstanceofCacheAnswerRootIndex);
++ } else {
++ // Patch the call site to return true.
++ __ LoadRoot(r3, Heap::kTrueValueRootIndex);
++ __ addi(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
++ // Get the boolean result location in scratch and patch it.
++ __ PatchRelocatedValue(inline_site, scratch, r3);
++
++ if (!ReturnTrueFalseObject()) {
++ __ LoadSmiLiteral(r3, Smi::FromInt(0));
++ }
++ }
++ __ Ret(HasArgsInRegisters() ? 0 : 2);
++
++ __ bind(&is_not_instance);
++ if (!HasCallSiteInlineCheck()) {
++ __ LoadSmiLiteral(r3, Smi::FromInt(1));
++ __ StoreRoot(r3, Heap::kInstanceofCacheAnswerRootIndex);
++ } else {
++ // Patch the call site to return false.
++ __ LoadRoot(r3, Heap::kFalseValueRootIndex);
++ __ addi(inline_site, inline_site, Operand(kDeltaToLoadBoolResult));
++ // Get the boolean result location in scratch and patch it.
++ __ PatchRelocatedValue(inline_site, scratch, r3);
++
++ if (!ReturnTrueFalseObject()) {
++ __ LoadSmiLiteral(r3, Smi::FromInt(1));
++ }
++ }
++ __ Ret(HasArgsInRegisters() ? 0 : 2);
++
++ Label object_not_null, object_not_null_or_smi;
++ __ bind(¬_js_object);
++ // Before null, smi and string value checks, check that the rhs is a function
++ // as for a non-function rhs an exception needs to be thrown.
++ __ JumpIfSmi(function, &slow);
++ __ CompareObjectType(function, scratch3, scratch, JS_FUNCTION_TYPE);
++ __ bne(&slow);
++
++ // Null is not instance of anything.
++ __ Cmpi(scratch, Operand(masm->isolate()->factory()->null_value()), r0);
++ __ bne(&object_not_null);
++ __ LoadSmiLiteral(r3, Smi::FromInt(1));
++ __ Ret(HasArgsInRegisters() ? 0 : 2);
++
++ __ bind(&object_not_null);
++ // Smi values are not instances of anything.
++ __ JumpIfNotSmi(object, &object_not_null_or_smi);
++ __ LoadSmiLiteral(r3, Smi::FromInt(1));
++ __ Ret(HasArgsInRegisters() ? 0 : 2);
++
++ __ bind(&object_not_null_or_smi);
++ // String values are not instances of anything.
++ __ IsObjectJSStringType(object, scratch, &slow);
++ __ LoadSmiLiteral(r3, Smi::FromInt(1));
++ __ Ret(HasArgsInRegisters() ? 0 : 2);
++
++ // Slow-case. Tail call builtin.
++ __ bind(&slow);
++ if (!ReturnTrueFalseObject()) {
++ if (HasArgsInRegisters()) {
++ __ Push(r3, r4);
++ }
++ __ InvokeBuiltin(Builtins::INSTANCE_OF, JUMP_FUNCTION);
++ } else {
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ Push(r3, r4);
++ __ InvokeBuiltin(Builtins::INSTANCE_OF, CALL_FUNCTION);
++ }
++ Label true_value, done;
++ __ cmpi(r3, Operand::Zero());
++ __ beq(&true_value);
++
++ __ LoadRoot(r3, Heap::kFalseValueRootIndex);
++ __ b(&done);
++
++ __ bind(&true_value);
++ __ LoadRoot(r3, Heap::kTrueValueRootIndex);
++
++ __ bind(&done);
++ __ Ret(HasArgsInRegisters() ? 0 : 2);
++ }
++}
++
++
++Register InstanceofStub::left() { return r3; }
++
++
++Register InstanceofStub::right() { return r4; }
++
++
++void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
++ // The displacement is the offset of the last parameter (if any)
++ // relative to the frame pointer.
++ const int kDisplacement =
++ StandardFrameConstants::kCallerSPOffset - kPointerSize;
++
++ // Check that the key is a smi.
++ Label slow;
++ __ JumpIfNotSmi(r4, &slow);
++
++ // Check if the calling frame is an arguments adaptor frame.
++ Label adaptor;
++ __ LoadP(r5, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++ __ LoadP(r6, MemOperand(r5, StandardFrameConstants::kContextOffset));
++ STATIC_ASSERT(StackFrame::ARGUMENTS_ADAPTOR < 0x3fffu);
++ __ CmpSmiLiteral(r6, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++ __ beq(&adaptor);
++
++ // Check index against formal parameters count limit passed in
++ // through register r3. Use unsigned comparison to get negative
++ // check for free.
++ __ cmpl(r4, r3);
++ __ bge(&slow);
++
++ // Read the argument from the stack and return it.
++ __ sub(r6, r3, r4);
++ __ SmiToPtrArrayOffset(r6, r6);
++ __ add(r6, fp, r6);
++ __ LoadP(r3, MemOperand(r6, kDisplacement));
++ __ blr();
++
++ // Arguments adaptor case: Check index against actual arguments
++ // limit found in the arguments adaptor frame. Use unsigned
++ // comparison to get negative check for free.
++ __ bind(&adaptor);
++ __ LoadP(r3, MemOperand(r5, ArgumentsAdaptorFrameConstants::kLengthOffset));
++ __ cmpl(r4, r3);
++ __ bge(&slow);
++
++ // Read the argument from the adaptor frame and return it.
++ __ sub(r6, r3, r4);
++ __ SmiToPtrArrayOffset(r6, r6);
++ __ add(r6, r5, r6);
++ __ LoadP(r3, MemOperand(r6, kDisplacement));
++ __ blr();
++
++ // Slow-case: Handle non-smi or out-of-bounds access to arguments
++ // by calling the runtime system.
++ __ bind(&slow);
++ __ push(r4);
++ __ TailCallRuntime(Runtime::kGetArgumentsProperty, 1, 1);
++}
++
++
++void ArgumentsAccessStub::GenerateNewNonStrictSlow(MacroAssembler* masm) {
++ // sp[0] : number of parameters
++ // sp[1] : receiver displacement
++ // sp[2] : function
++
++ // Check if the calling frame is an arguments adaptor frame.
++ Label runtime;
++ __ LoadP(r6, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++ __ LoadP(r5, MemOperand(r6, StandardFrameConstants::kContextOffset));
++ STATIC_ASSERT(StackFrame::ARGUMENTS_ADAPTOR < 0x3fffu);
++ __ CmpSmiLiteral(r5, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++ __ bne(&runtime);
++
++ // Patch the arguments.length and the parameters pointer in the current frame.
++ __ LoadP(r5, MemOperand(r6, ArgumentsAdaptorFrameConstants::kLengthOffset));
++ __ StoreP(r5, MemOperand(sp, 0 * kPointerSize));
++ __ SmiToPtrArrayOffset(r5, r5);
++ __ add(r6, r6, r5);
++ __ addi(r6, r6, Operand(StandardFrameConstants::kCallerSPOffset));
++ __ StoreP(r6, MemOperand(sp, 1 * kPointerSize));
++
++ __ bind(&runtime);
++ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
++}
++
++
++void ArgumentsAccessStub::GenerateNewNonStrictFast(MacroAssembler* masm) {
++ // Stack layout:
++ // sp[0] : number of parameters (tagged)
++ // sp[1] : address of receiver argument
++ // sp[2] : function
++ // Registers used over whole function:
++ // r9 : allocated object (tagged)
++ // r22 : mapped parameter count (tagged)
++
++ __ LoadP(r4, MemOperand(sp, 0 * kPointerSize));
++ // r4 = parameter count (tagged)
++
++ // Check if the calling frame is an arguments adaptor frame.
++ Label runtime;
++ Label adaptor_frame, try_allocate;
++ __ LoadP(r6, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++ __ LoadP(r5, MemOperand(r6, StandardFrameConstants::kContextOffset));
++ STATIC_ASSERT(StackFrame::ARGUMENTS_ADAPTOR < 0x3fffu);
++ __ CmpSmiLiteral(r5, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++ __ beq(&adaptor_frame);
++
++ // No adaptor, parameter count = argument count.
++ __ mr(r5, r4);
++ __ b(&try_allocate);
++
++ // We have an adaptor frame. Patch the parameters pointer.
++ __ bind(&adaptor_frame);
++ __ LoadP(r5, MemOperand(r6, ArgumentsAdaptorFrameConstants::kLengthOffset));
++ __ SmiToPtrArrayOffset(r7, r5);
++ __ add(r6, r6, r7);
++ __ addi(r6, r6, Operand(StandardFrameConstants::kCallerSPOffset));
++ __ StoreP(r6, MemOperand(sp, 1 * kPointerSize));
++
++ // r4 = parameter count (tagged)
++ // r5 = argument count (tagged)
++ // Compute the mapped parameter count = min(r4, r5) in r4.
++ Label skip;
++ __ cmp(r4, r5);
++ __ blt(&skip);
++ __ mr(r4, r5);
++ __ bind(&skip);
++
++ __ bind(&try_allocate);
++
++ // Compute the sizes of backing store, parameter map, and arguments object.
++ // 1. Parameter map, has 2 extra words containing context and backing store.
++ const int kParameterMapHeaderSize =
++ FixedArray::kHeaderSize + 2 * kPointerSize;
++ // If there are no mapped parameters, we do not need the parameter_map.
++ Label skip2, skip3;
++ __ CmpSmiLiteral(r4, Smi::FromInt(0), r0);
++ __ bne(&skip2);
++ __ li(r22, Operand::Zero());
++ __ b(&skip3);
++ __ bind(&skip2);
++ __ SmiToPtrArrayOffset(r22, r4);
++ __ addi(r22, r22, Operand(kParameterMapHeaderSize));
++ __ bind(&skip3);
++
++ // 2. Backing store.
++ __ SmiToPtrArrayOffset(r7, r5);
++ __ add(r22, r22, r7);
++ __ addi(r22, r22, Operand(FixedArray::kHeaderSize));
++
++ // 3. Arguments object.
++ __ addi(r22, r22, Operand(Heap::kArgumentsObjectSize));
++
++ // Do the allocation of all three objects in one go.
++ __ AllocateInNewSpace(r22, r3, r6, r7, &runtime, TAG_OBJECT);
++
++ // r3 = address of new object(s) (tagged)
++ // r5 = argument count (tagged)
++ // Get the arguments boilerplate from the current native context into r4.
++ const int kNormalOffset =
++ Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
++ const int kAliasedOffset =
++ Context::SlotOffset(Context::ALIASED_ARGUMENTS_BOILERPLATE_INDEX);
++
++ __ LoadP(r7, MemOperand(r20,
++ Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ LoadP(r7, FieldMemOperand(r7, GlobalObject::kNativeContextOffset));
++ Label skip4, skip5;
++ __ cmpi(r4, Operand::Zero());
++ __ bne(&skip4);
++ __ LoadP(r7, MemOperand(r7, kNormalOffset));
++ __ b(&skip5);
++ __ bind(&skip4);
++ __ LoadP(r7, MemOperand(r7, kAliasedOffset));
++ __ bind(&skip5);
++
++ // r3 = address of new object (tagged)
++ // r4 = mapped parameter count (tagged)
++ // r5 = argument count (tagged)
++ // r7 = address of boilerplate object (tagged)
++ // Copy the JS object part.
++ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
++ __ LoadP(r6, FieldMemOperand(r7, i));
++ __ StoreP(r6, FieldMemOperand(r3, i), r0);
++ }
++
++ // Set up the callee in-object property.
++ STATIC_ASSERT(Heap::kArgumentsCalleeIndex == 1);
++ __ LoadP(r6, MemOperand(sp, 2 * kPointerSize));
++ const int kCalleeOffset = JSObject::kHeaderSize +
++ Heap::kArgumentsCalleeIndex * kPointerSize;
++ __ StoreP(r6, FieldMemOperand(r3, kCalleeOffset), r0);
++
++ // Use the length (smi tagged) and set that as an in-object property too.
++ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
++ const int kLengthOffset = JSObject::kHeaderSize +
++ Heap::kArgumentsLengthIndex * kPointerSize;
++ __ StoreP(r5, FieldMemOperand(r3, kLengthOffset), r0);
++
++ // Set up the elements pointer in the allocated arguments object.
++ // If we allocated a parameter map, r7 will point there, otherwise
++ // it will point to the backing store.
++ __ addi(r7, r3, Operand(Heap::kArgumentsObjectSize));
++ __ StoreP(r7, FieldMemOperand(r3, JSObject::kElementsOffset), r0);
++
++ // r3 = address of new object (tagged)
++ // r4 = mapped parameter count (tagged)
++ // r5 = argument count (tagged)
++ // r7 = address of parameter map or backing store (tagged)
++ // Initialize parameter map. If there are no mapped arguments, we're done.
++ Label skip_parameter_map, skip6;
++ __ CmpSmiLiteral(r4, Smi::FromInt(0), r0);
++ __ bne(&skip6);
++ // Move backing store address to r6, because it is
++ // expected there when filling in the unmapped arguments.
++ __ mr(r6, r7);
++ __ b(&skip_parameter_map);
++ __ bind(&skip6);
++
++ __ LoadRoot(r9, Heap::kNonStrictArgumentsElementsMapRootIndex);
++ __ StoreP(r9, FieldMemOperand(r7, FixedArray::kMapOffset), r0);
++ __ AddSmiLiteral(r9, r4, Smi::FromInt(2), r0);
++ __ StoreP(r9, FieldMemOperand(r7, FixedArray::kLengthOffset), r0);
++ __ StoreP(r20, FieldMemOperand(r7,
++ FixedArray::kHeaderSize + 0 * kPointerSize),
++ r0);
++ __ SmiToPtrArrayOffset(r9, r4);
++ __ add(r9, r7, r9);
++ __ addi(r9, r9, Operand(kParameterMapHeaderSize));
++ __ StoreP(r9, FieldMemOperand(r7,
++ FixedArray::kHeaderSize + 1 * kPointerSize),
++ r0);
++
++ // Copy the parameter slots and the holes in the arguments.
++ // We need to fill in mapped_parameter_count slots. They index the context,
++ // where parameters are stored in reverse order, at
++ // MIN_CONTEXT_SLOTS .. MIN_CONTEXT_SLOTS+parameter_count-1
++ // The mapped parameter thus need to get indices
++ // MIN_CONTEXT_SLOTS+parameter_count-1 ..
++ // MIN_CONTEXT_SLOTS+parameter_count-mapped_parameter_count
++ // We loop from right to left.
++ Label parameters_loop, parameters_test;
++ __ mr(r9, r4);
++ __ LoadP(r22, MemOperand(sp, 0 * kPointerSize));
++ __ AddSmiLiteral(r22, r22, Smi::FromInt(Context::MIN_CONTEXT_SLOTS), r0);
++ __ sub(r22, r22, r4);
++ __ LoadRoot(r10, Heap::kTheHoleValueRootIndex);
++ __ SmiToPtrArrayOffset(r6, r9);
++ __ add(r6, r7, r6);
++ __ addi(r6, r6, Operand(kParameterMapHeaderSize));
++
++ // r9 = loop variable (tagged)
++ // r4 = mapping index (tagged)
++ // r6 = address of backing store (tagged)
++ // r7 = address of parameter map (tagged)
++ // r8 = temporary scratch (a.o., for address calculation)
++ // r10 = the hole value
++ __ b(¶meters_test);
++
++ __ bind(¶meters_loop);
++ __ SubSmiLiteral(r9, r9, Smi::FromInt(1), r0);
++ __ SmiToPtrArrayOffset(r8, r9);
++ __ addi(r8, r8, Operand(kParameterMapHeaderSize - kHeapObjectTag));
++ __ StorePX(r22, MemOperand(r8, r7));
++ __ subi(r8, r8, Operand(kParameterMapHeaderSize - FixedArray::kHeaderSize));
++ __ StorePX(r10, MemOperand(r8, r6));
++ __ AddSmiLiteral(r22, r22, Smi::FromInt(1), r0);
++ __ bind(¶meters_test);
++ __ CmpSmiLiteral(r9, Smi::FromInt(0), r0);
++ __ bne(¶meters_loop);
++
++ __ bind(&skip_parameter_map);
++ // r5 = argument count (tagged)
++ // r6 = address of backing store (tagged)
++ // r8 = scratch
++ // Copy arguments header and remaining slots (if there are any).
++ __ LoadRoot(r8, Heap::kFixedArrayMapRootIndex);
++ __ StoreP(r8, FieldMemOperand(r6, FixedArray::kMapOffset), r0);
++ __ StoreP(r5, FieldMemOperand(r6, FixedArray::kLengthOffset), r0);
++
++ Label arguments_loop, arguments_test;
++ __ mr(r22, r4);
++ __ LoadP(r7, MemOperand(sp, 1 * kPointerSize));
++ __ SmiToPtrArrayOffset(r8, r22);
++ __ sub(r7, r7, r8);
++ __ b(&arguments_test);
++
++ __ bind(&arguments_loop);
++ __ subi(r7, r7, Operand(kPointerSize));
++ __ LoadP(r9, MemOperand(r7, 0));
++ __ SmiToPtrArrayOffset(r8, r22);
++ __ add(r8, r6, r8);
++ __ StoreP(r9, FieldMemOperand(r8, FixedArray::kHeaderSize), r0);
++ __ AddSmiLiteral(r22, r22, Smi::FromInt(1), r0);
++
++ __ bind(&arguments_test);
++ __ cmp(r22, r5);
++ __ blt(&arguments_loop);
++
++ // Return and remove the on-stack parameters.
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ Ret();
++
++ // Do the runtime call to allocate the arguments object.
++ // r5 = argument count (tagged)
++ __ bind(&runtime);
++ __ StoreP(r5, MemOperand(sp, 0 * kPointerSize)); // Patch argument count.
++ __ TailCallRuntime(Runtime::kNewArgumentsFast, 3, 1);
++}
++
++void ArgumentsAccessStub::GenerateNewStrict(MacroAssembler* masm) {
++ // sp[0] : number of parameters
++ // sp[4] : receiver displacement
++ // sp[8] : function
++ // Check if the calling frame is an arguments adaptor frame.
++ Label adaptor_frame, try_allocate, runtime;
++ __ LoadP(r5, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++ __ LoadP(r6, MemOperand(r5, StandardFrameConstants::kContextOffset));
++ STATIC_ASSERT(StackFrame::ARGUMENTS_ADAPTOR < 0x3fffu);
++ __ CmpSmiLiteral(r6, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++ __ beq(&adaptor_frame);
++
++ // Get the length from the frame.
++ __ LoadP(r4, MemOperand(sp, 0));
++ __ b(&try_allocate);
++
++ // Patch the arguments.length and the parameters pointer.
++ __ bind(&adaptor_frame);
++ __ LoadP(r4, MemOperand(r5, ArgumentsAdaptorFrameConstants::kLengthOffset));
++ __ StoreP(r4, MemOperand(sp, 0));
++ __ SmiToPtrArrayOffset(r6, r4);
++ __ add(r6, r5, r6);
++ __ addi(r6, r6, Operand(StandardFrameConstants::kCallerSPOffset));
++ __ StoreP(r6, MemOperand(sp, 1 * kPointerSize));
++
++ // Try the new space allocation. Start out with computing the size
++ // of the arguments object and the elements array in words.
++ Label add_arguments_object;
++ __ bind(&try_allocate);
++ __ cmpi(r4, Operand(0, RelocInfo::NONE));
++ __ beq(&add_arguments_object);
++ __ SmiUntag(r4);
++ __ addi(r4, r4, Operand(FixedArray::kHeaderSize / kPointerSize));
++ __ bind(&add_arguments_object);
++ __ addi(r4, r4, Operand(Heap::kArgumentsObjectSizeStrict / kPointerSize));
++
++ // Do the allocation of both objects in one go.
++ __ AllocateInNewSpace(r4,
++ r3,
++ r5,
++ r6,
++ &runtime,
++ static_cast<AllocationFlags>(TAG_OBJECT |
++ SIZE_IN_WORDS));
++
++ // Get the arguments boilerplate from the current native context.
++ __ LoadP(r7,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ LoadP(r7, FieldMemOperand(r7, GlobalObject::kNativeContextOffset));
++ __ LoadP(r7, MemOperand(r7, Context::SlotOffset(
++ Context::STRICT_MODE_ARGUMENTS_BOILERPLATE_INDEX)));
++
++ // Copy the JS object part.
++ __ CopyFields(r3, r7, r6.bit(), JSObject::kHeaderSize / kPointerSize);
++
++ // Get the length (smi tagged) and set that as an in-object property too.
++ STATIC_ASSERT(Heap::kArgumentsLengthIndex == 0);
++ __ LoadP(r4, MemOperand(sp, 0 * kPointerSize));
++ __ StoreP(r4, FieldMemOperand(r3, JSObject::kHeaderSize +
++ Heap::kArgumentsLengthIndex * kPointerSize),
++ r0);
++
++ // If there are no actual arguments, we're done.
++ Label done;
++ __ cmpi(r4, Operand(0, RelocInfo::NONE));
++ __ beq(&done);
++
++ // Get the parameters pointer from the stack.
++ __ LoadP(r5, MemOperand(sp, 1 * kPointerSize));
++
++ // Set up the elements pointer in the allocated arguments object and
++ // initialize the header in the elements fixed array.
++ __ addi(r7, r3, Operand(Heap::kArgumentsObjectSizeStrict));
++ __ StoreP(r7, FieldMemOperand(r3, JSObject::kElementsOffset), r0);
++ __ LoadRoot(r6, Heap::kFixedArrayMapRootIndex);
++ __ StoreP(r6, FieldMemOperand(r7, FixedArray::kMapOffset), r0);
++ __ StoreP(r4, FieldMemOperand(r7, FixedArray::kLengthOffset), r0);
++ // Untag the length for the loop.
++ __ SmiUntag(r4);
++
++ // Copy the fixed array slots.
++ Label loop;
++ // Set up r7 to point to the first array slot.
++ __ addi(r7, r7, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ bind(&loop);
++ // Pre-decrement r5 with kPointerSize on each iteration.
++ // Pre-decrement in order to skip receiver.
++ __ LoadPU(r6, MemOperand(r5, -kPointerSize));
++ // Post-increment r7 with kPointerSize on each iteration.
++ __ StoreP(r6, MemOperand(r7));
++ __ addi(r7, r7, Operand(kPointerSize));
++ __ subi(r4, r4, Operand(1));
++ __ cmpi(r4, Operand(0, RelocInfo::NONE));
++ __ bne(&loop);
++
++ // Return and remove the on-stack parameters.
++ __ bind(&done);
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ Ret();
++
++ // Do the runtime call to allocate the arguments object.
++ __ bind(&runtime);
++ __ TailCallRuntime(Runtime::kNewStrictArgumentsFast, 3, 1);
++}
++
++
++void RegExpExecStub::Generate(MacroAssembler* masm) {
++ // Just jump directly to runtime if native RegExp is not selected at compile
++ // time or if regexp entry in generated code is turned off runtime switch or
++ // at compilation.
++#ifdef V8_INTERPRETED_REGEXP
++ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
++#else // V8_INTERPRETED_REGEXP
++
++ // Stack frame on entry.
++ // sp[0]: last_match_info (expected JSArray)
++ // sp[4]: previous index
++ // sp[8]: subject string
++ // sp[12]: JSRegExp object
++
++ const int kLastMatchInfoOffset = 0 * kPointerSize;
++ const int kPreviousIndexOffset = 1 * kPointerSize;
++ const int kSubjectOffset = 2 * kPointerSize;
++ const int kJSRegExpOffset = 3 * kPointerSize;
++
++ Label runtime, invoke_regexp, br_over, encoding_type_UC16;
++
++ // Allocation of registers for this function. These are in callee save
++ // registers and will be preserved by the call to the native RegExp code, as
++ // this code is called using the normal C calling convention. When calling
++ // directly from generated code the native RegExp code will not do a GC and
++ // therefore the content of these registers are safe to use after the call.
++ Register subject = r26;
++ Register regexp_data = r27;
++ Register last_match_info_elements = r28;
++ Register code = r29;
++
++ // Ensure register assigments are consistent with callee save masks
++ ASSERT(subject.bit() & (kCalleeSaved & kRegExpCalleeSaved));
++ ASSERT(regexp_data.bit() & (kCalleeSaved & kRegExpCalleeSaved));
++ ASSERT(last_match_info_elements.bit() & (kCalleeSaved & kRegExpCalleeSaved));
++ ASSERT(code.bit() & (kCalleeSaved & kRegExpCalleeSaved));
++
++ // Ensure that a RegExp stack is allocated.
++ Isolate* isolate = masm->isolate();
++ ExternalReference address_of_regexp_stack_memory_address =
++ ExternalReference::address_of_regexp_stack_memory_address(isolate);
++ ExternalReference address_of_regexp_stack_memory_size =
++ ExternalReference::address_of_regexp_stack_memory_size(isolate);
++ __ mov(r3, Operand(address_of_regexp_stack_memory_size));
++ __ LoadP(r3, MemOperand(r3, 0));
++ __ cmpi(r3, Operand::Zero());
++ __ beq(&runtime);
++
++ // Check that the first argument is a JSRegExp object.
++ __ LoadP(r3, MemOperand(sp, kJSRegExpOffset));
++ STATIC_ASSERT(kSmiTag == 0);
++ __ JumpIfSmi(r3, &runtime);
++ __ CompareObjectType(r3, r4, r4, JS_REGEXP_TYPE);
++ __ bne(&runtime);
++
++ // Check that the RegExp has been compiled (data contains a fixed array).
++ __ LoadP(regexp_data, FieldMemOperand(r3, JSRegExp::kDataOffset));
++ if (FLAG_debug_code) {
++ STATIC_ASSERT(kSmiTagMask == 1);
++ __ andi(r0, regexp_data, Operand(kSmiTagMask));
++ __ Check(ne, "Unexpected type for RegExp data, FixedArray expected",
cr0);
++ __ CompareObjectType(regexp_data, r3, r3, FIXED_ARRAY_TYPE);
++ __ Check(eq, "Unexpected type for RegExp data, FixedArray expected");
++ }
++
++ // regexp_data: RegExp data (FixedArray)
++ // Check the type of the RegExp. Only continue if type is JSRegExp::IRREGEXP.
++ __ LoadP(r3, FieldMemOperand(regexp_data, JSRegExp::kDataTagOffset));
++ // ASSERT(Smi::FromInt(JSRegExp::IRREGEXP) < (char *)0xffffu);
++ __ CmpSmiLiteral(r3, Smi::FromInt(JSRegExp::IRREGEXP), r0);
++ __ bne(&runtime);
++
++ // regexp_data: RegExp data (FixedArray)
++ // Check that the number of captures fit in the static offsets vector buffer.
++ __ LoadP(r5,
++ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
++ // Calculate number of capture registers (number_of_captures + 1) * 2.
++ __ SmiToShortArrayOffset(r5, r5);
++ __ addi(r5, r5, Operand(2));
++ // Check that the static offsets vector buffer is large enough.
++ // STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize < 0xffffu);
++ __ cmpli(r5, Operand(Isolate::kJSRegexpStaticOffsetsVectorSize));
++ __ bgt(&runtime);
++
++ // r5: Number of capture registers
++ // regexp_data: RegExp data (FixedArray)
++ // Check that the second argument is a string.
++ __ LoadP(subject, MemOperand(sp, kSubjectOffset));
++ __ JumpIfSmi(subject, &runtime);
++ Condition is_string = masm->IsObjectStringType(subject, r3);
++ __ b(NegateCondition(is_string), &runtime, cr0);
++ // Get the length of the string to r6.
++ __ LoadP(r6, FieldMemOperand(subject, String::kLengthOffset));
++
++ // r5: Number of capture registers
++ // r6: Length of subject string as a smi
++ // subject: Subject string
++ // regexp_data: RegExp data (FixedArray)
++ // Check that the third argument is a positive smi less than the subject
++ // string length. A negative value will be greater (unsigned comparison).
++ __ LoadP(r3, MemOperand(sp, kPreviousIndexOffset));
++ __ JumpIfNotSmi(r3, &runtime);
++ __ cmpl(r6, r3);
++ __ ble(&runtime);
++
++ // r5: Number of capture registers
++ // subject: Subject string
++ // regexp_data: RegExp data (FixedArray)
++ // Check that the fourth object is a JSArray object.
++ __ LoadP(r3, MemOperand(sp, kLastMatchInfoOffset));
++ __ JumpIfSmi(r3, &runtime);
++ __ CompareObjectType(r3, r4, r4, JS_ARRAY_TYPE);
++ __ bne(&runtime);
++ // Check that the JSArray is in fast case.
++ __ LoadP(last_match_info_elements,
++ FieldMemOperand(r3, JSArray::kElementsOffset));
++ __ LoadP(r3, FieldMemOperand(last_match_info_elements,
++ HeapObject::kMapOffset));
++ __ CompareRoot(r3, Heap::kFixedArrayMapRootIndex);
++ __ bne(&runtime);
++ // Check that the last match info has space for the capture registers and the
++ // additional information.
++ __ LoadP(r3,
++ FieldMemOperand(last_match_info_elements, FixedArray::kLengthOffset));
++ __ addi(r5, r5, Operand(RegExpImpl::kLastMatchOverhead));
++ __ SmiUntag(r0, r3);
++ __ cmp(r5, r0);
++ __ bgt(&runtime);
++
++ // Reset offset for possibly sliced string.
++ __ li(r11, Operand::Zero());
++ // subject: Subject string
++ // regexp_data: RegExp data (FixedArray)
++ // Check the representation and encoding of the subject string.
++ Label seq_string;
++ __ LoadP(r3, FieldMemOperand(subject, HeapObject::kMapOffset));
++ __ lbz(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
++ // First check for flat string. None of the following string type tests will
++ // succeed if subject is not a string or a short external string.
++ STATIC_ASSERT((kIsNotStringMask |
++ kStringRepresentationMask |
++ kShortExternalStringMask) == 0x93);
++ __ andi(r4, r3, Operand(kIsNotStringMask |
++ kStringRepresentationMask |
++ kShortExternalStringMask));
++ STATIC_ASSERT((kStringTag | kSeqStringTag) == 0);
++ __ beq(&seq_string, cr0);
++
++ // subject: Subject string
++ // regexp_data: RegExp data (FixedArray)
++ // r4: whether subject is a string and if yes, its string representation
++ // Check for flat cons string or sliced string.
++ // A flat cons string is a cons string where the second part is the empty
++ // string. In that case the subject string is just the first part of the cons
++ // string. Also in this case the first part of the cons string is known to be
++ // a sequential string or an external string.
++ // In the case of a sliced string its offset has to be taken into account.
++ Label cons_string, external_string, check_encoding;
++ STATIC_ASSERT(kConsStringTag < kExternalStringTag);
++ STATIC_ASSERT(kSlicedStringTag > kExternalStringTag);
++ STATIC_ASSERT(kIsNotStringMask > kExternalStringTag);
++ STATIC_ASSERT(kShortExternalStringTag > kExternalStringTag);
++ STATIC_ASSERT(kExternalStringTag < 0xffffu);
++ __ cmpi(r4, Operand(kExternalStringTag));
++ __ blt(&cons_string);
++ __ beq(&external_string);
++
++ // Catch non-string subject or short external string.
++ STATIC_ASSERT(kNotStringTag != 0 && kShortExternalStringTag !=0);
++ STATIC_ASSERT((kNotStringTag | kShortExternalStringTag) < 0xffffu);
++ __ andi(r0, r4, Operand(kIsNotStringMask | kShortExternalStringMask));
++ __ bne(&runtime, cr0);
++
++ // String is sliced.
++ __ LoadP(r11, FieldMemOperand(subject, SlicedString::kOffsetOffset));
++ __ SmiUntag(r11);
++ __ LoadP(subject, FieldMemOperand(subject, SlicedString::kParentOffset));
++ // r11: offset of sliced string, smi-tagged.
++ __ b(&check_encoding);
++ // String is a cons string, check whether it is flat.
++ __ bind(&cons_string);
++ __ LoadP(r3, FieldMemOperand(subject, ConsString::kSecondOffset));
++ __ CompareRoot(r3, Heap::kEmptyStringRootIndex);
++ __ bne(&runtime);
++ __ LoadP(subject, FieldMemOperand(subject, ConsString::kFirstOffset));
++ // Is first part of cons or parent of slice a flat string?
++ __ bind(&check_encoding);
++ __ LoadP(r3, FieldMemOperand(subject, HeapObject::kMapOffset));
++ __ lbz(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
++ STATIC_ASSERT(kSeqStringTag == 0);
++ STATIC_ASSERT(kStringRepresentationMask == 3);
++ __ andi(r0, r3, Operand(kStringRepresentationMask));
++ __ bne(&external_string, cr0);
++
++ __ bind(&seq_string);
++ // subject: Subject string
++ // regexp_data: RegExp data (FixedArray)
++ // r3: Instance type of subject string
++ STATIC_ASSERT(4 == kAsciiStringTag);
++ STATIC_ASSERT(kTwoByteStringTag == 0);
++ // Find the code object based on the assumptions above.
++ STATIC_ASSERT(kStringEncodingMask == 4);
++ __ ExtractBitMask(r6, r3, kStringEncodingMask, SetRC);
++ __ beq(&encoding_type_UC16, cr0);
++ __ LoadP(code, FieldMemOperand(regexp_data, JSRegExp::kDataAsciiCodeOffset));
++ __ b(&br_over);
++ __ bind(&encoding_type_UC16);
++ __ LoadP(code, FieldMemOperand(regexp_data, JSRegExp::kDataUC16CodeOffset));
++ __ bind(&br_over);
++
++ // Check that the irregexp code has been generated for the actual string
++ // encoding. If it has, the field contains a code object otherwise it contains
++ // a smi (code flushing support).
++ __ JumpIfSmi(code, &runtime);
++
++ // r6: encoding of subject string (1 if ASCII, 0 if two_byte);
++ // code: Address of generated regexp code
++ // subject: Subject string
++ // regexp_data: RegExp data (FixedArray)
++ // Load used arguments before starting to push arguments for call to native
++ // RegExp code to avoid handling changing stack height.
++ __ LoadP(r4, MemOperand(sp, kPreviousIndexOffset));
++ __ SmiUntag(r4);
++
++ // r4: previous index
++ // r6: encoding of subject string (1 if ASCII, 0 if two_byte);
++ // code: Address of generated regexp code
++ // subject: Subject string
++ // regexp_data: RegExp data (FixedArray)
++ // All checks done. Now push arguments for native regexp code.
++ __ IncrementCounter(isolate->counters()->regexp_entry_native(), 1, r3, r5);
++
++ // Isolates: note we add an additional parameter here (isolate pointer).
++ const int kRegExpExecuteArguments = 10;
++ const int kParameterRegisters = 8;
++ __ EnterExitFrame(false, kRegExpExecuteArguments - kParameterRegisters);
++
++ // Stack pointer now points to cell where return address is to be written.
++ // Arguments are before that on the stack or in registers.
++
++ // Argument 10 (in stack parameter area): Pass current isolate address.
++ __ mov(r3, Operand(ExternalReference::isolate_address()));
++ __ StoreP(r3, MemOperand(sp, (kStackFrameExtraParamSlot + 1) * kPointerSize));
++
++ // Argument 9 is a dummy that reserves the space used for
++ // the return address added by the ExitFrame in native calls.
++
++ // Argument 8 (r10): Indicate that this is a direct call from JavaScript.
++ __ li(r10, Operand(1));
++
++ // Argument 7 (r9): Start (high end) of backtracking stack memory area.
++ __ mov(r3, Operand(address_of_regexp_stack_memory_address));
++ __ LoadP(r3, MemOperand(r3, 0));
++ __ mov(r5, Operand(address_of_regexp_stack_memory_size));
++ __ LoadP(r5, MemOperand(r5, 0));
++ __ add(r9, r3, r5);
++
++ // Argument 6 (r8): Set the number of capture registers to zero to force
++ // global egexps to behave as non-global. This does not affect non-global
++ // regexps.
++ __ li(r8, Operand::Zero());
++
++ // Argument 5 (r7): static offsets vector buffer.
++ __ mov(r7,
++ Operand(ExternalReference::address_of_static_offsets_vector(isolate)));
++
++ // For arguments 4 (r6) and 3 (r5) get string length, calculate start of
++ // string data and calculate the shift of the index (0 for ASCII and 1 for
++ // two byte).
++ __ addi(r22, subject, Operand(SeqString::kHeaderSize - kHeapObjectTag));
++ __ xori(r6, r6, Operand(1));
++ // Load the length from the original subject string from the previous stack
++ // frame. Therefore we have to use fp, which points exactly to two pointer
++ // sizes below the previous sp. (Because creating a new stack frame pushes
++ // the previous fp onto the stack and moves up sp by 2 * kPointerSize.)
++ __ LoadP(subject, MemOperand(fp, kSubjectOffset + 2 * kPointerSize));
++ // If slice offset is not 0, load the length from the original sliced string.
++ // Argument 4, r6: End of string data
++ // Argument 3, r5: Start of string data
++ // Prepare start and end index of the input.
++ __ ShiftLeft(r11, r11, r6);
++ __ add(r11, r22, r11);
++ __ ShiftLeft(r5, r4, r6);
++ __ add(r5, r11, r5);
++
++ __ LoadP(r22, FieldMemOperand(subject, String::kLengthOffset));
++ __ SmiUntag(r22);
++ __ ShiftLeft(r6, r22, r6);
++ __ add(r6, r11, r6);
++
++ // Argument 2 (r4): Previous index.
++ // Already there
++
++ // Argument 1 (r3): Subject string.
++ __ mr(r3, subject);
++
++ // Locate the code entry and call it.
++ __ addi(code, code, Operand(Code::kHeaderSize - kHeapObjectTag));
++
++
++#if ABI_USES_FUNCTION_DESCRIPTORS && defined(USE_SIMULATOR)
++ // Even Simulated AIX/PPC64 Linux uses a function descriptor for the
++ // RegExp routine. Extract the instruction address here since
++ // DirectCEntryStub::GenerateCall will not do it for calls out to
++ // what it thinks is C code compiled for the simulator/host
++ // platform.
++ __ LoadP(code, MemOperand(code, 0)); // Instruction address
++#endif
++
++ DirectCEntryStub stub;
++ stub.GenerateCall(masm, code);
++
++ __ LeaveExitFrame(false, no_reg);
++
++ // r3: result
++ // subject: subject string (callee saved)
++ // regexp_data: RegExp data (callee saved)
++ // last_match_info_elements: Last match info elements (callee saved)
++
++ // Check the result.
++ Label success;
++
++ __ cmpi(r3, Operand(1));
++ // We expect exactly one result since we force the called regexp to behave
++ // as non-global.
++ __ beq(&success);
++ Label failure;
++ __ cmpi(r3, Operand(NativeRegExpMacroAssembler::FAILURE));
++ __ beq(&failure);
++ __ cmpi(r3, Operand(NativeRegExpMacroAssembler::EXCEPTION));
++ // If not exception it can only be retry. Handle that in the runtime system.
++ __ bne(&runtime);
++ // Result must now be exception. If there is no pending exception already a
++ // stack overflow (on the backtrack stack) was detected in RegExp code but
++ // haven't created the exception yet. Handle that in the runtime system.
++ // TODO(592): Rerunning the RegExp to get the stack overflow exception.
++ __ mov(r4, Operand(isolate->factory()->the_hole_value()));
++ __ mov(r5, Operand(ExternalReference(Isolate::kPendingExceptionAddress,
++ isolate)));
++ __ LoadP(r3, MemOperand(r5, 0));
++ __ cmp(r3, r4);
++ __ beq(&runtime);
++
++ __ StoreP(r4, MemOperand(r5, 0)); // Clear pending exception.
++
++ // Check if the exception is a termination. If so, throw as uncatchable.
++ __ CompareRoot(r3, Heap::kTerminationExceptionRootIndex);
++
++ Label termination_exception;
++ __ beq(&termination_exception);
++
++ __ Throw(r3);
++
++ __ bind(&termination_exception);
++ __ ThrowUncatchable(r3);
++
++ __ bind(&failure);
++ // For failure and exception return null.
++ __ mov(r3, Operand(masm->isolate()->factory()->null_value()));
++ __ addi(sp, sp, Operand(4 * kPointerSize));
++ __ Ret();
++
++ // Process the result from the native regexp code.
++ __ bind(&success);
++ __ LoadP(r4,
++ FieldMemOperand(regexp_data, JSRegExp::kIrregexpCaptureCountOffset));
++ // Calculate number of capture registers (number_of_captures + 1) * 2.
++ __ SmiToShortArrayOffset(r4, r4);
++ __ addi(r4, r4, Operand(2));
++
++ // r4: number of capture registers
++ // r26: subject string
++ // Store the capture count.
++ __ SmiTag(r5, r4);
++ __ StoreP(r5, FieldMemOperand(last_match_info_elements,
++ RegExpImpl::kLastCaptureCountOffset), r0);
++ // Store last subject and last input.
++ __ StoreP(subject,
++ FieldMemOperand(last_match_info_elements,
++ RegExpImpl::kLastSubjectOffset), r0);
++ __ mr(r5, subject);
++ __ RecordWriteField(last_match_info_elements,
++ RegExpImpl::kLastSubjectOffset,
++ r5,
++ r10,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs);
++ __ StoreP(subject,
++ FieldMemOperand(last_match_info_elements,
++ RegExpImpl::kLastInputOffset), r0);
++ __ RecordWriteField(last_match_info_elements,
++ RegExpImpl::kLastInputOffset,
++ subject,
++ r10,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs);
++
++ // Get the static offsets vector filled by the native regexp code.
++ ExternalReference address_of_static_offsets_vector =
++ ExternalReference::address_of_static_offsets_vector(isolate);
++ __ mov(r5, Operand(address_of_static_offsets_vector));
++
++ // r4: number of capture registers
++ // r5: offsets vector
++ Label next_capture;
++ // Capture register counter starts from number of capture registers and
++ // counts down until wraping after zero.
++ __ addi(r3,
++ last_match_info_elements,
++ Operand(RegExpImpl::kFirstCaptureOffset - kHeapObjectTag -
++ kPointerSize));
++ __ addi(r5, r5, Operand(-kIntSize)); // bias down for lwzu
++ __ mtctr(r4);
++ __ bind(&next_capture);
++ // Read the value from the static offsets vector buffer.
++ __ lwzu(r6, MemOperand(r5, kIntSize));
++ // Store the smi value in the last match info.
++ __ SmiTag(r6);
++ __ StorePU(r6, MemOperand(r3, kPointerSize));
++ __ bdnz(&next_capture);
++
++ // Return last match info.
++ __ LoadP(r3, MemOperand(sp, kLastMatchInfoOffset));
++ __ addi(sp, sp, Operand(4 * kPointerSize));
++ __ Ret();
++
++ // External string. Short external strings have already been ruled out.
++ // r3: scratch
++ __ bind(&external_string);
++ __ LoadP(r3, FieldMemOperand(subject, HeapObject::kMapOffset));
++ __ lbz(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
++ if (FLAG_debug_code) {
++ // Assert that we do not have a cons or slice (indirect strings) here.
++ // Sequential strings have already been ruled out.
++ STATIC_ASSERT(kIsIndirectStringMask == 1);
++ __ andi(r0, r3, Operand(kIsIndirectStringMask));
++ __ Assert(eq, "external string expected, but not found", cr0);
++ }
++ __ LoadP(subject,
++ FieldMemOperand(subject, ExternalString::kResourceDataOffset));
++ // Move the pointer so that offset-wise, it looks like a sequential string.
++ STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
++ __ subi(subject,
++ subject,
++ Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
++ __ b(&seq_string);
++
++ // Do the runtime call to execute the regexp.
++ __ bind(&runtime);
++ __ TailCallRuntime(Runtime::kRegExpExec, 4, 1);
++#endif // V8_INTERPRETED_REGEXP
++}
++
++
++void RegExpConstructResultStub::Generate(MacroAssembler* masm) {
++ const int kMaxInlineLength = 100;
++ Label slowcase;
++ Label done;
++ Factory* factory = masm->isolate()->factory();
++
++ __ LoadP(r4, MemOperand(sp, kPointerSize * 2));
++ __ JumpIfNotSmi(r4, &slowcase);
++ __ CmplSmiLiteral(r4, Smi::FromInt(kMaxInlineLength), r0);
++ __ bgt(&slowcase);
++ // Allocate RegExpResult followed by FixedArray with size in ebx.
++ // JSArray: [Map][empty properties][Elements][Length-smi][index][input]
++ // Elements: [Map][Length][..elements..]
++ // Size of JSArray with two in-object properties and the header of a
++ // FixedArray.
++ int objects_size =
++ (JSRegExpResult::kSize + FixedArray::kHeaderSize) / kPointerSize;
++ __ SmiUntag(r8, r4);
++ __ addi(r5, r8, Operand(objects_size));
++ // Future optimization: defer tagging the result pointer for more
++ // efficient 64-bit memory accesses (due to alignment requirements
++ // on the memoperand offset).
++ __ AllocateInNewSpace(
++ r5, // In: Size, in words.
++ r3, // Out: Start of allocation (tagged).
++ r6, // Scratch register.
++ r7, // Scratch register.
++ &slowcase,
++ static_cast<AllocationFlags>(TAG_OBJECT | SIZE_IN_WORDS));
++ // r3: Start of allocated area, object-tagged.
++ // r4: Number of elements in array, as smi.
++ // r8: Number of elements, untagged.
++
++ // Set JSArray map to global.regexp_result_map().
++ // Set empty properties FixedArray.
++ // Set elements to point to FixedArray allocated right after the JSArray.
++ // Interleave operations for better latency.
++ __ LoadP(r5, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
++ __ addi(r6, r3, Operand(JSRegExpResult::kSize));
++ __ mov(r7, Operand(factory->empty_fixed_array()));
++ __ LoadP(r5, FieldMemOperand(r5, GlobalObject::kNativeContextOffset));
++ __ StoreP(r6, FieldMemOperand(r3, JSObject::kElementsOffset), r0);
++ __ LoadP(r5, ContextOperand(r5, Context::REGEXP_RESULT_MAP_INDEX));
++ __ StoreP(r7, FieldMemOperand(r3, JSObject::kPropertiesOffset), r0);
++ __ StoreP(r5, FieldMemOperand(r3, HeapObject::kMapOffset), r0);
++
++ // Set input, index and length fields from arguments.
++ __ LoadP(r4, MemOperand(sp, kPointerSize * 0));
++ __ LoadP(r5, MemOperand(sp, kPointerSize * 1));
++ __ LoadP(r9, MemOperand(sp, kPointerSize * 2));
++ __ StoreP(r4, FieldMemOperand(r3, JSRegExpResult::kInputOffset), r0);
++ __ StoreP(r5, FieldMemOperand(r3, JSRegExpResult::kIndexOffset), r0);
++ __ StoreP(r9, FieldMemOperand(r3, JSArray::kLengthOffset), r0);
++
++ // Fill out the elements FixedArray.
++ // r3: JSArray, tagged.
++ // r6: FixedArray, tagged.
++ // r8: Number of elements in array, untagged.
++
++ // Set map.
++ __ mov(r5, Operand(factory->fixed_array_map()));
++ __ StoreP(r5, FieldMemOperand(r6, HeapObject::kMapOffset), r0);
++ // Set FixedArray length.
++ __ SmiTag(r9, r8);
++ __ StoreP(r9, FieldMemOperand(r6, FixedArray::kLengthOffset), r0);
++ // Fill contents of fixed-array with undefined.
++ __ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
++ __ addi(r6, r6, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ // Fill fixed array elements with undefined.
++ // r3: JSArray, tagged.
++ // r5: undefined.
++ // r6: Start of elements in FixedArray.
++ // r8: Number of elements to fill.
++ Label loop;
++ __ cmpi(r8, Operand::Zero());
++ __ bind(&loop);
++ __ ble(&done); // Jump if r8 is negative or zero.
++ __ subi(r8, r8, Operand(1));
++ __ ShiftLeftImm(ip, r8, Operand(kPointerSizeLog2));
++ __ StorePX(r5, MemOperand(ip, r6));
++ __ cmpi(r8, Operand::Zero());
++ __ b(&loop);
++
++ __ bind(&done);
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ Ret();
++
++ __ bind(&slowcase);
++ __ TailCallRuntime(Runtime::kRegExpConstructResult, 3, 1);
++}
++
++
++static void GenerateRecordCallTarget(MacroAssembler* masm) {
++ // Cache the called function in a global property cell. Cache states
++ // are uninitialized, monomorphic (indicated by a JSFunction), and
++ // megamorphic.
++ // r4 : the function to call
++ // r5 : cache cell for call target
++ Label initialize, done;
++ const Register scratch = r6;
++
++ ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()),
++ masm->isolate()->heap()->undefined_value());
++ ASSERT_EQ(*TypeFeedbackCells::UninitializedSentinel(masm->isolate()),
++ masm->isolate()->heap()->the_hole_value());
++
++ // Load the cache state into scratch.
++ __ LoadP(scratch, FieldMemOperand(r5, JSGlobalPropertyCell::kValueOffset));
++
++ // A monomorphic cache hit or an already megamorphic state: invoke the
++ // function without changing the state.
++ __ cmp(scratch, r4);
++ __ beq(&done);
++ __ CompareRoot(scratch, Heap::kUndefinedValueRootIndex);
++ __ beq(&done);
++
++ // A monomorphic miss (i.e, here the cache is not uninitialized) goes
++ // megamorphic.
++ __ CompareRoot(scratch, Heap::kTheHoleValueRootIndex);
++ __ beq(&initialize);
++ // MegamorphicSentinel is an immortal immovable object (undefined) so no
++ // write-barrier is needed.
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ StoreP(ip, FieldMemOperand(r5, JSGlobalPropertyCell::kValueOffset), r0);
++ __ b(&done);
++
++ // An uninitialized cache is patched with the function.
++ __ bind(&initialize);
++ __ StoreP(r4, FieldMemOperand(r5, JSGlobalPropertyCell::kValueOffset), r0);
++ // No need for a write barrier here - cells are rescanned.
++
++ __ bind(&done);
++}
++
++
++void CallFunctionStub::Generate(MacroAssembler* masm) {
++ // r4 : the function to call
++ // r5 : cache cell for call target
++ Label slow, non_function;
++
++ // The receiver might implicitly be the global object. This is
++ // indicated by passing the hole as the receiver to the call
++ // function stub.
++ if (ReceiverMightBeImplicit()) {
++ Label call;
++ // Get the receiver from the stack.
++ // function, receiver [, arguments]
++ __ LoadP(r7, MemOperand(sp, argc_ * kPointerSize), r0);
++ // Call as function is indicated with the hole.
++ __ CompareRoot(r7, Heap::kTheHoleValueRootIndex);
++ __ bne(&call);
++ // Patch the receiver on the stack with the global receiver object.
++ __ LoadP(r6,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ LoadP(r6, FieldMemOperand(r6, GlobalObject::kGlobalReceiverOffset));
++ __ StoreP(r6, MemOperand(sp, argc_ * kPointerSize), r0);
++ __ bind(&call);
++ }
++
++ // Check that the function is really a JavaScript function.
++ // r4: pushed function (to be verified)
++ __ JumpIfSmi(r4, &non_function);
++ // Get the map of the function object.
++ __ CompareObjectType(r4, r6, r6, JS_FUNCTION_TYPE);
++ __ bne(&slow);
++
++ if (RecordCallTarget()) {
++ GenerateRecordCallTarget(masm);
++ }
++
++ // Fast-case: Invoke the function now.
++ // r4: pushed function
++ ParameterCount actual(argc_);
++
++ if (ReceiverMightBeImplicit()) {
++ Label call_as_function;
++ __ CompareRoot(r7, Heap::kTheHoleValueRootIndex);
++ __ beq(&call_as_function);
++ __ InvokeFunction(r4,
++ actual,
++ JUMP_FUNCTION,
++ NullCallWrapper(),
++ CALL_AS_METHOD);
++ __ bind(&call_as_function);
++ }
++ __ InvokeFunction(r4,
++ actual,
++ JUMP_FUNCTION,
++ NullCallWrapper(),
++ CALL_AS_FUNCTION);
++
++ // Slow-case: Non-function called.
++ __ bind(&slow);
++ if (RecordCallTarget()) {
++ // If there is a call target cache, mark it megamorphic in the
++ // non-function case. MegamorphicSentinel is an immortal immovable
++ // object (undefined) so no write barrier is needed.
++ ASSERT_EQ(*TypeFeedbackCells::MegamorphicSentinel(masm->isolate()),
++ masm->isolate()->heap()->undefined_value());
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ StoreP(ip, FieldMemOperand(r5, JSGlobalPropertyCell::kValueOffset), r0);
++ }
++ // Check for function proxy.
++ STATIC_ASSERT(JS_FUNCTION_PROXY_TYPE < 0xffffu);
++ __ cmpi(r6, Operand(JS_FUNCTION_PROXY_TYPE));
++ __ bne(&non_function);
++ __ push(r4); // put proxy as additional argument
++ __ li(r3, Operand(argc_ + 1));
++ __ li(r5, Operand::Zero());
++ __ GetBuiltinEntry(r6, Builtins::CALL_FUNCTION_PROXY);
++ __ SetCallKind(r8, CALL_AS_METHOD);
++ {
++ Handle<Code> adaptor =
++ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
++ __ Jump(adaptor, RelocInfo::CODE_TARGET);
++ }
++
++ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
++ // of the original receiver from the call site).
++ __ bind(&non_function);
++ __ StoreP(r4, MemOperand(sp, argc_ * kPointerSize), r0);
++ __ li(r3, Operand(argc_)); // Set up the number of arguments.
++ __ li(r5, Operand::Zero());
++ __ GetBuiltinEntry(r6, Builtins::CALL_NON_FUNCTION);
++ __ SetCallKind(r8, CALL_AS_METHOD);
++ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
++ RelocInfo::CODE_TARGET);
++}
++
++
++void CallConstructStub::Generate(MacroAssembler* masm) {
++ // r3 : number of arguments
++ // r4 : the function to call
++ // r5 : cache cell for call target
++ Label slow, non_function_call;
++
++ // Check that the function is not a smi.
++ __ JumpIfSmi(r4, &non_function_call);
++ // Check that the function is a JSFunction.
++ __ CompareObjectType(r4, r6, r6, JS_FUNCTION_TYPE);
++ __ bne(&slow);
++
++ if (RecordCallTarget()) {
++ GenerateRecordCallTarget(masm);
++ }
++
++ // Jump to the function-specific construct stub.
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++ __ LoadP(r5, FieldMemOperand(r5, SharedFunctionInfo::kConstructStubOffset));
++ __ addi(r0, r5, Operand(Code::kHeaderSize - kHeapObjectTag));
++ __ Jump(r0);
++
++ // r3: number of arguments
++ // r4: called object
++ // r6: object type
++ Label do_call;
++ __ bind(&slow);
++ STATIC_ASSERT(JS_FUNCTION_PROXY_TYPE < 0xffffu);
++ __ cmpi(r6, Operand(JS_FUNCTION_PROXY_TYPE));
++ __ bne(&non_function_call);
++ __ GetBuiltinEntry(r6, Builtins::CALL_FUNCTION_PROXY_AS_CONSTRUCTOR);
++ __ b(&do_call);
++
++ __ bind(&non_function_call);
++ __ GetBuiltinEntry(r6, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
++ __ bind(&do_call);
++ // Set expected number of arguments to zero (not changing r3).
++ __ li(r5, Operand::Zero());
++ __ SetCallKind(r8, CALL_AS_METHOD);
++ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
++ RelocInfo::CODE_TARGET);
++}
++
++
++// Unfortunately you have to run without snapshots to see most of these
++// names in the profile since most compare stubs end up in the snapshot.
++void CompareStub::PrintName(StringStream* stream) {
++ ASSERT((lhs_.is(r3) && rhs_.is(r4)) ||
++ (lhs_.is(r4) && rhs_.is(r3)));
++ const char* cc_name;
++ switch (cc_) {
++ case lt: cc_name = "LT"; break;
++ case gt: cc_name = "GT"; break;
++ case le: cc_name = "LE"; break;
++ case ge: cc_name = "GE"; break;
++ case eq: cc_name = "EQ"; break;
++ case ne: cc_name = "NE"; break;
++ default: cc_name = "UnknownCondition"; break;
++ }
++ bool is_equality = cc_ == eq || cc_ == ne;
++ stream->Add("CompareStub_%s", cc_name);
++ stream->Add(lhs_.is(r3) ? "_r3" : "_r4");
++ stream->Add(rhs_.is(r3) ? "_r3" : "_r4");
++ if (strict_ && is_equality) stream->Add("_STRICT");
++ if (never_nan_nan_ && is_equality) stream->Add("_NO_NAN");
++ if (!include_number_compare_) stream->Add("_NO_NUMBER");
++ if (!include_smi_compare_) stream->Add("_NO_SMI");
++}
++
++
++int CompareStub::MinorKey() {
++ // Encode the three parameters in a unique 16 bit value. To avoid duplicate
++ // stubs the never NaN NaN condition is only taken into account if the
++ // condition is equals.
++ ASSERT(static_cast<unsigned>(cc_) < (1 << 12));
++ ASSERT((lhs_.is(r3) && rhs_.is(r4)) ||
++ (lhs_.is(r4) && rhs_.is(r3)));
++ return ConditionField::encode(static_cast<unsigned>(cc_))
++ | RegisterField::encode(lhs_.is(r3))
++ | StrictField::encode(strict_)
++ | NeverNanNanField::encode(cc_ == eq ? never_nan_nan_ : false)
++ | IncludeNumberCompareField::encode(include_number_compare_)
++ | IncludeSmiCompareField::encode(include_smi_compare_);
++}
++
++
++// StringCharCodeAtGenerator
++void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) {
++ Label flat_string;
++ Label ascii_string;
++ Label got_char_code;
++ Label sliced_string;
++
++ // If the receiver is a smi trigger the non-string case.
++ __ JumpIfSmi(object_, receiver_not_string_);
++
++ // Fetch the instance type of the receiver into result register.
++ __ LoadP(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
++ __ lbz(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
++ // If the receiver is not a string trigger the non-string case.
++ __ andi(r0, result_, Operand(kIsNotStringMask));
++ __ bne(receiver_not_string_, cr0);
++
++ // If the index is non-smi trigger the non-smi case.
++ __ JumpIfNotSmi(index_, &index_not_smi_);
++ __ bind(&got_smi_index_);
++
++ // Check for index out of range.
++ __ LoadP(ip, FieldMemOperand(object_, String::kLengthOffset));
++ __ cmpl(ip, index_);
++ __ ble(index_out_of_range_);
++
++ __ SmiUntag(index_);
++
++ StringCharLoadGenerator::Generate(masm,
++ object_,
++ index_,
++ result_,
++ &call_runtime_);
++
++ __ SmiTag(result_, result_);
++ __ bind(&exit_);
++}
++
++
++void StringCharCodeAtGenerator::GenerateSlow(
++ MacroAssembler* masm,
++ const RuntimeCallHelper& call_helper) {
++ __ Abort("Unexpected fallthrough to CharCodeAt slow case");
++
++ // Index is not a smi.
++ __ bind(&index_not_smi_);
++ // If index is a heap number, try converting it to an integer.
++ __ CheckMap(index_,
++ result_,
++ Heap::kHeapNumberMapRootIndex,
++ index_not_number_,
++ DONT_DO_SMI_CHECK);
++ call_helper.BeforeCall(masm);
++ __ push(object_);
++ __ push(index_); // Consumed by runtime conversion function.
++ if (index_flags_ == STRING_INDEX_IS_NUMBER) {
++ __ CallRuntime(Runtime::kNumberToIntegerMapMinusZero, 1);
++ } else {
++ ASSERT(index_flags_ == STRING_INDEX_IS_ARRAY_INDEX);
++ // NumberToSmi discards numbers that are not exact integers.
++ __ CallRuntime(Runtime::kNumberToSmi, 1);
++ }
++ // Save the conversion result before the pop instructions below
++ // have a chance to overwrite it.
++ __ Move(index_, r3);
++ __ pop(object_);
++ // Reload the instance type.
++ __ LoadP(result_, FieldMemOperand(object_, HeapObject::kMapOffset));
++ __ lbz(result_, FieldMemOperand(result_, Map::kInstanceTypeOffset));
++ call_helper.AfterCall(masm);
++ // If index is still not a smi, it must be out of range.
++ __ JumpIfNotSmi(index_, index_out_of_range_);
++ // Otherwise, return to the fast path.
++ __ b(&got_smi_index_);
++
++ // Call runtime. We get here when the receiver is a string and the
++ // index is a number, but the code of getting the actual character
++ // is too complex (e.g., when the string needs to be flattened).
++ __ bind(&call_runtime_);
++ call_helper.BeforeCall(masm);
++ __ SmiTag(index_);
++ __ Push(object_, index_);
++ __ CallRuntime(Runtime::kStringCharCodeAt, 2);
++ __ Move(result_, r3);
++ call_helper.AfterCall(masm);
++ __ b(&exit_);
++
++ __ Abort("Unexpected fallthrough from CharCodeAt slow case");
++}
++
++
++// -------------------------------------------------------------------------
++// StringCharFromCodeGenerator
++
++ void StringCharFromCodeGenerator::GenerateFast(MacroAssembler* masm) {
++ // Fast case of Heap::LookupSingleCharacterStringFromCode.
++ ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
++ __ LoadSmiLiteral(r0, Smi::FromInt(~String::kMaxAsciiCharCode));
++ __ ori(r0, r0, Operand(kSmiTagMask));
++ __ and_(r0, code_, r0);
++ __ cmpi(r0, Operand::Zero());
++ __ bne(&slow_case_);
++
++ __ LoadRoot(result_, Heap::kSingleCharacterStringCacheRootIndex);
++ // At this point code register contains smi tagged ASCII char code.
++ __ mr(r0, code_);
++ __ SmiToPtrArrayOffset(code_, code_);
++ __ add(result_, result_, code_);
++ __ mr(code_, r0);
++ __ LoadP(result_, FieldMemOperand(result_, FixedArray::kHeaderSize));
++ __ CompareRoot(result_, Heap::kUndefinedValueRootIndex);
++ __ beq(&slow_case_);
++ __ bind(&exit_);
++}
++
++
++void StringCharFromCodeGenerator::GenerateSlow(
++ MacroAssembler* masm,
++ const RuntimeCallHelper& call_helper) {
++ __ Abort("Unexpected fallthrough to CharFromCode slow case");
++
++ __ bind(&slow_case_);
++ call_helper.BeforeCall(masm);
++ __ push(code_);
++ __ CallRuntime(Runtime::kCharFromCode, 1);
++ __ Move(result_, r3);
++ call_helper.AfterCall(masm);
++ __ b(&exit_);
++
++ __ Abort("Unexpected fallthrough from CharFromCode slow case");
++}
++
++
++// -------------------------------------------------------------------------
++// StringCharAtGenerator
++
++void StringCharAtGenerator::GenerateFast(MacroAssembler* masm) {
++ char_code_at_generator_.GenerateFast(masm);
++ char_from_code_generator_.GenerateFast(masm);
++}
++
++
++void StringCharAtGenerator::GenerateSlow(
++ MacroAssembler* masm,
++ const RuntimeCallHelper& call_helper) {
++ char_code_at_generator_.GenerateSlow(masm, call_helper);
++ char_from_code_generator_.GenerateSlow(masm, call_helper);
++}
++
++
++void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
++ Register dest,
++ Register src,
++ Register count,
++ Register scratch,
++ bool ascii) {
++ Label loop;
++ __ bind(&loop);
++ // This loop just copies one character at a time, as it is only used for very
++ // short strings.
++ if (ascii) {
++ __ lbz(scratch, MemOperand(src));
++ __ stb(scratch, MemOperand(dest));
++ __ addi(src, src, Operand(1));
++ __ addi(dest, dest, Operand(1));
++ } else {
++ __ lhz(scratch, MemOperand(src));
++ __ sth(scratch, MemOperand(dest));
++ __ addi(src, src, Operand(2));
++ __ addi(dest, dest, Operand(2));
++ }
++ __ subi(count, count, Operand(1));
++ __ cmpi(count, Operand::Zero());
++ __ bgt(&loop);
++}
++
++
++enum CopyCharactersFlags {
++ COPY_ASCII = 1,
++ DEST_ALWAYS_ALIGNED = 2
++};
++
++
++// roohack - optimization opportunity here, stringcopy is important
++// and the current version below is very dumb
++void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
++ Register dest,
++ Register src,
++ Register count,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Register scratch5,
++ int flags) {
++ bool ascii = (flags & COPY_ASCII) != 0;
++ bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
++
++ if (dest_always_aligned && FLAG_debug_code) {
++ // Check that destination is actually word aligned if the flag says
++ // that it is.
++ __ andi(r0, dest, Operand(kPointerAlignmentMask));
++ __ Check(eq, "Destination of copy not aligned.", cr0);
++ }
++
++ // Nothing to do for zero characters.
++ Label done;
++ if (!ascii) { // for non-ascii, double the length
++ __ add(count, count, count);
++ }
++ __ cmpi(count, Operand(0, RelocInfo::NONE));
++ __ beq(&done);
++
++ // Assume that you cannot read (or write) unaligned.
++ Label byte_loop;
++ __ add(count, dest, count);
++ Register limit = count; // Read until src equals this.
++ // Copy bytes from src to dst until dst hits limit.
++ __ bind(&byte_loop);
++ __ cmp(dest, limit);
++ __ bge(&done);
++ __ lbz(scratch1, MemOperand(src));
++ __ addi(src, src, Operand(1));
++ __ stb(scratch1, MemOperand(dest));
++ __ addi(dest, dest, Operand(1));
++ __ b(&byte_loop);
++
++ __ bind(&done);
++}
++
++
++void StringHelper::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
++ Register c1,
++ Register c2,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Register scratch5,
++ Label* not_found) {
++ // Register scratch3 is the general scratch register in this function.
++ Register scratch = scratch3;
++
++ // Make sure that both characters are not digits as such strings has a
++ // different hash algorithm. Don't try to look for these in the symbol table.
++ Label not_array_index;
++ __ subi(scratch, c1, Operand(static_cast<intptr_t>('0')));
++ __ cmpli(scratch, Operand(static_cast<intptr_t>('9' - '0')));
++ __ bgt(¬_array_index);
++ __ subi(scratch, c2, Operand(static_cast<intptr_t>('0')));
++ __ cmpli(scratch, Operand(static_cast<intptr_t>('9' - '0')));
++ __ bgt(¬_array_index);
++
++ // If check failed combine both characters into single halfword.
++ // This is required by the contract of the method: code at the
++ // not_found branch expects this combination in c1 register
++#if __BYTE_ORDER == __BIG_ENDIAN
++ __ ShiftLeftImm(c1, c1, Operand(kBitsPerByte));
++ __ orx(c1, c1, c2);
++#else
++ __ ShiftLeftImm(r0, c2, Operand(kBitsPerByte));
++ __ orx(c1, c1, r0);
++#endif
++ __ b(not_found);
++
++ __ bind(¬_array_index);
++ // Calculate the two character string hash.
++ Register hash = scratch1;
++ StringHelper::GenerateHashInit(masm, hash, c1, scratch);
++ StringHelper::GenerateHashAddCharacter(masm, hash, c2, scratch);
++ StringHelper::GenerateHashGetHash(masm, hash, scratch);
++
++ // Collect the two characters in a register.
++ Register chars = c1;
++#if __BYTE_ORDER == __BIG_ENDIAN
++ __ ShiftLeftImm(c1, c1, Operand(kBitsPerByte));
++ __ orx(chars, c1, c2);
++#else
++ __ ShiftLeftImm(r0, c2, Operand(kBitsPerByte));
++ __ orx(chars, c1, r0);
++#endif
++
++ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
++ // hash: hash of two character string.
++
++ // Load symbol table
++ // Load address of first element of the symbol table.
++ Register symbol_table = c2;
++ __ LoadRoot(symbol_table, Heap::kSymbolTableRootIndex);
++
++ Register undefined = scratch4;
++ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
++
++ // Calculate capacity mask from the symbol table capacity.
++ Register mask = scratch2;
++ __ LoadP(mask, FieldMemOperand(symbol_table, SymbolTable::kCapacityOffset));
++ __ SmiUntag(mask);
++ __ subi(mask, mask, Operand(1));
++
++ // Calculate untagged address of the first element of the symbol table.
++ Register first_symbol_table_element = symbol_table;
++ __ addi(first_symbol_table_element, symbol_table,
++ Operand(SymbolTable::kElementsStartOffset - kHeapObjectTag));
++
++ // Registers
++ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
++ // hash: hash of two character string
++ // mask: capacity mask
++ // first_symbol_table_element: address of the first element of
++ // the symbol table
++ // undefined: the undefined object
++ // scratch: -
++
++ // Perform a number of probes in the symbol table.
++ const int kProbes = 4;
++ Label found_in_symbol_table;
++ Label next_probe[kProbes];
++ Register candidate = scratch5; // Scratch register contains candidate.
++ for (int i = 0; i < kProbes; i++) {
++ // Calculate entry in symbol table.
++ if (i > 0) {
++ __ addi(candidate, hash, Operand(SymbolTable::GetProbeOffset(i)));
++ } else {
++ __ mr(candidate, hash);
++ }
++
++ __ and_(candidate, candidate, mask);
++
++ // Load the entry from the symble table.
++ STATIC_ASSERT(SymbolTable::kEntrySize == 1);
++ __ ShiftLeftImm(scratch, candidate, Operand(kPointerSizeLog2));
++ __ LoadPX(candidate, MemOperand(scratch, first_symbol_table_element));
++
++ // If entry is undefined no string with this hash can be found.
++ Label is_string;
++ __ CompareObjectType(candidate, scratch, scratch, ODDBALL_TYPE);
++ __ bne(&is_string);
++
++ __ cmp(undefined, candidate);
++ __ beq(not_found);
++ // Must be the hole (deleted entry).
++ if (FLAG_debug_code) {
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(ip, candidate);
++ __ Assert(eq, "oddball in symbol table is not undefined or the hole");
++ }
++ __ b(&next_probe[i]);
++
++ __ bind(&is_string);
++
++ // Check that the candidate is a non-external ASCII string. The instance
++ // type is still in the scratch register from the CompareObjectType
++ // operation.
++ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch, &next_probe[i]);
++
++ // If length is not 2 the string is not a candidate.
++ __ LoadP(scratch, FieldMemOperand(candidate, String::kLengthOffset));
++ __ CmpSmiLiteral(scratch, Smi::FromInt(2), r0);
++ __ bne(&next_probe[i]);
++
++ // Check if the two characters match.
++ __ lhz(scratch, FieldMemOperand(candidate, SeqAsciiString::kHeaderSize));
++ __ cmp(chars, scratch);
++ __ beq(&found_in_symbol_table);
++ __ bind(&next_probe[i]);
++ }
++
++ // No matching 2 character string found by probing.
++ __ b(not_found);
++
++ // Scratch register contains result when we fall through to here.
++ Register result = candidate;
++ __ bind(&found_in_symbol_table);
++ __ mr(r3, result);
++}
++
++
++void StringHelper::GenerateHashInit(MacroAssembler* masm,
++ Register hash,
++ Register character,
++ Register scratch) {
++ // hash = character + (character << 10);
++ __ LoadRoot(hash, Heap::kHashSeedRootIndex);
++ // Untag smi seed and add the character.
++ __ SmiUntag(scratch, hash);
++ __ add(hash, character, scratch);
++ // hash += hash << 10;
++ __ slwi(scratch, hash, Operand(10));
++ __ add(hash, hash, scratch);
++ // hash ^= hash >> 6;
++ __ srwi(scratch, hash, Operand(6));
++ __ xor_(hash, hash, scratch);
++}
++
++
++void StringHelper::GenerateHashAddCharacter(MacroAssembler* masm,
++ Register hash,
++ Register character,
++ Register scratch) {
++ // hash += character;
++ __ add(hash, hash, character);
++ // hash += hash << 10;
++ __ slwi(scratch, hash, Operand(10));
++ __ add(hash, hash, scratch);
++ // hash ^= hash >> 6;
++ __ srwi(scratch, hash, Operand(6));
++ __ xor_(hash, hash, scratch);
++}
++
++
++void StringHelper::GenerateHashGetHash(MacroAssembler* masm,
++ Register hash,
++ Register scratch) {
++ // hash += hash << 3;
++ __ slwi(scratch, hash, Operand(3));
++ __ add(hash, hash, scratch);
++ // hash ^= hash >> 11;
++ __ srwi(scratch, hash, Operand(11));
++ __ xor_(hash, hash, scratch);
++ // hash += hash << 15;
++ __ slwi(scratch, hash, Operand(15));
++ __ add(hash, hash, scratch);
++
++ __ mov(scratch, Operand(String::kHashBitMask));
++ __ and_(hash, hash, scratch, SetRC);
++
++ // if (hash == 0) hash = 27;
++ Label done;
++ __ bne(&done, cr0);
++ __ li(hash, Operand(StringHasher::kZeroHash));
++ __ bind(&done);
++}
++
++
++void SubStringStub::Generate(MacroAssembler* masm) {
++ Label runtime;
++
++ // Stack frame on entry.
++ // lr: return address
++ // sp[0]: to
++ // sp[4]: from
++ // sp[8]: string
++
++ // This stub is called from the native-call %_SubString(...), so
++ // nothing can be assumed about the arguments. It is tested that:
++ // "string" is a sequential string,
++ // both "from" and "to" are smis, and
++ // 0 <= from <= to <= string.length.
++ // If any of these assumptions fail, we call the runtime system.
++
++ const int kToOffset = 0 * kPointerSize;
++ const int kFromOffset = 1 * kPointerSize;
++ const int kStringOffset = 2 * kPointerSize;
++
++ __ LoadP(r5, MemOperand(sp, kToOffset));
++ __ LoadP(r6, MemOperand(sp, kFromOffset));
++
++ // If either to or from had the smi tag bit set, then fail to generic runtime
++ __ JumpIfNotSmi(r5, &runtime);
++ __ JumpIfNotSmi(r6, &runtime);
++ __ SmiUntag(r5);
++ __ SmiUntag(r6, SetRC);
++ // Both r5 and r6 are untagged integers.
++
++ // We want to bailout to runtime here if From is negative.
++ __ blt(&runtime, cr0); // From < 0.
++
++ __ cmpl(r6, r5);
++ __ bgt(&runtime); // Fail if from > to.
++ __ sub(r5, r5, r6);
++
++ // Make sure first argument is a string.
++ __ LoadP(r3, MemOperand(sp, kStringOffset));
++ __ JumpIfSmi(r3, &runtime);
++ Condition is_string = masm->IsObjectStringType(r3, r4);
++ __ b(NegateCondition(is_string), &runtime, cr0);
++
++ // Short-cut for the case of trivial substring.
++ Label return_r3;
++ // r3: original string
++ // r5: result string length
++ __ LoadP(r7, FieldMemOperand(r3, String::kLengthOffset));
++ __ SmiUntag(r0, r7);
++ __ cmpl(r5, r0);
++ // Return original string.
++ __ beq(&return_r3);
++ // Longer than original string's length or negative: unsafe arguments.
++ __ bgt(&runtime);
++ // Shorter than original string's length: an actual substring.
++
++ // Deal with different string types: update the index if necessary
++ // and put the underlying string into r8.
++ // r3: original string
++ // r4: instance type
++ // r5: length
++ // r6: from index (untagged)
++ Label underlying_unpacked, sliced_string, seq_or_external_string;
++ // If the string is not indirect, it can only be sequential or external.
++ STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
++ STATIC_ASSERT(kIsIndirectStringMask != 0);
++ __ andi(r0, r4, Operand(kIsIndirectStringMask));
++ __ beq(&seq_or_external_string, cr0);
++
++ __ andi(r0, r4, Operand(kSlicedNotConsMask));
++ __ bne(&sliced_string, cr0);
++ // Cons string. Check whether it is flat, then fetch first part.
++ __ LoadP(r8, FieldMemOperand(r3, ConsString::kSecondOffset));
++ __ CompareRoot(r8, Heap::kEmptyStringRootIndex);
++ __ bne(&runtime);
++ __ LoadP(r8, FieldMemOperand(r3, ConsString::kFirstOffset));
++ // Update instance type.
++ __ LoadP(r4, FieldMemOperand(r8, HeapObject::kMapOffset));
++ __ lbz(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
++ __ b(&underlying_unpacked);
++
++ __ bind(&sliced_string);
++ // Sliced string. Fetch parent and correct start index by offset.
++ __ LoadP(r8, FieldMemOperand(r3, SlicedString::kParentOffset));
++ __ LoadP(r7, FieldMemOperand(r3, SlicedString::kOffsetOffset));
++ __ SmiUntag(r4, r7);
++ __ add(r6, r6, r4); // Add offset to index.
++ // Update instance type.
++ __ LoadP(r4, FieldMemOperand(r8, HeapObject::kMapOffset));
++ __ lbz(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
++ __ b(&underlying_unpacked);
++
++ __ bind(&seq_or_external_string);
++ // Sequential or external string. Just move string to the expected register.
++ __ mr(r8, r3);
++
++ __ bind(&underlying_unpacked);
++
++ if (FLAG_string_slices) {
++ Label copy_routine;
++ // r8: underlying subject string
++ // r4: instance type of underlying subject string
++ // r5: length
++ // r6: adjusted start index (untagged)
++ __ cmpi(r5, Operand(SlicedString::kMinLength));
++ // Short slice. Copy instead of slicing.
++ __ blt(©_routine);
++ // Allocate new sliced string. At this point we do not reload the instance
++ // type including the string encoding because we simply rely on the info
++ // provided by the original string. It does not matter if the original
++ // string's encoding is wrong because we always have to recheck encoding of
++ // the newly created string's parent anyways due to externalized strings.
++ Label two_byte_slice, set_slice_header;
++ STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0);
++ STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
++ __ andi(r0, r4, Operand(kStringEncodingMask));
++ __ beq(&two_byte_slice, cr0);
++ __ AllocateAsciiSlicedString(r3, r5, r9, r10, &runtime);
++ __ b(&set_slice_header);
++ __ bind(&two_byte_slice);
++ __ AllocateTwoByteSlicedString(r3, r5, r9, r10, &runtime);
++ __ bind(&set_slice_header);
++ __ SmiTag(r6);
++ __ StoreP(r8, FieldMemOperand(r3, SlicedString::kParentOffset), r0);
++ __ StoreP(r6, FieldMemOperand(r3, SlicedString::kOffsetOffset), r0);
++ __ b(&return_r3);
++
++ __ bind(©_routine);
++ }
++
++ // r8: underlying subject string
++ // r4: instance type of underlying subject string
++ // r5: length
++ // r6: adjusted start index (untagged)
++ Label two_byte_sequential, sequential_string, allocate_result;
++ STATIC_ASSERT(kExternalStringTag != 0);
++ STATIC_ASSERT(kSeqStringTag == 0);
++ __ andi(r0, r4, Operand(kExternalStringTag));
++ __ beq(&sequential_string, cr0);
++
++ // Handle external string.
++ // Rule out short external strings.
++ STATIC_CHECK(kShortExternalStringTag != 0);
++ __ andi(r0, r4, Operand(kShortExternalStringTag));
++ __ bne(&runtime, cr0);
++ __ LoadP(r8, FieldMemOperand(r8, ExternalString::kResourceDataOffset));
++ // r8 already points to the first character of underlying string.
++ __ b(&allocate_result);
++
++ __ bind(&sequential_string);
++ // Locate first character of underlying subject string.
++ STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqAsciiString::kHeaderSize);
++ __ addi(r8, r8, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++
++ __ bind(&allocate_result);
++ // Sequential acii string. Allocate the result.
++ STATIC_ASSERT((kAsciiStringTag & kStringEncodingMask) != 0);
++ __ andi(r0, r4, Operand(kStringEncodingMask));
++ __ beq(&two_byte_sequential, cr0);
++
++ // Allocate and copy the resulting ASCII string.
++ __ AllocateAsciiString(r3, r5, r7, r9, r10, &runtime);
++
++ // Locate first character of substring to copy.
++ __ add(r8, r8, r6);
++ // Locate first character of result.
++ __ addi(r4, r3, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++
++ // r3: result string
++ // r4: first character of result string
++ // r5: result string length
++ // r8: first character of substring to copy
++ STATIC_ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0);
++ StringHelper::GenerateCopyCharactersLong(masm, r4, r8, r5, r6, r7, r9,
++ r10, r22, COPY_ASCII | DEST_ALWAYS_ALIGNED);
++ __ b(&return_r3);
++
++ // Allocate and copy the resulting two-byte string.
++ __ bind(&two_byte_sequential);
++ __ AllocateTwoByteString(r3, r5, r7, r9, r10, &runtime);
++
++ // Locate first character of substring to copy.
++ __ ShiftLeftImm(r4, r6, Operand(1));
++ __ add(r8, r8, r4);
++ // Locate first character of result.
++ __ addi(r4, r3, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
++
++ // r3: result string.
++ // r4: first character of result.
++ // r5: result length.
++ // r8: first character of substring to copy.
++ STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
++ StringHelper::GenerateCopyCharactersLong(
++ masm, r4, r8, r5, r6, r7, r9, r10, r22, DEST_ALWAYS_ALIGNED);
++
++ __ bind(&return_r3);
++ Counters* counters = masm->isolate()->counters();
++ __ IncrementCounter(counters->sub_string_native(), 1, r6, r7);
++ __ addi(sp, sp, Operand(3 * kPointerSize));
++ __ Ret();
++
++ // Just jump to runtime to create the sub string.
++ __ bind(&runtime);
++ __ TailCallRuntime(Runtime::kSubString, 3, 1);
++}
++
++
++void StringCompareStub::GenerateFlatAsciiStringEquals(MacroAssembler* masm,
++ Register left,
++ Register right,
++ Register scratch1,
++ Register scratch2) {
++ Register length = scratch1;
++
++ // Compare lengths.
++ Label strings_not_equal, check_zero_length;
++ __ LoadP(length, FieldMemOperand(left, String::kLengthOffset));
++ __ LoadP(scratch2, FieldMemOperand(right, String::kLengthOffset));
++ __ cmp(length, scratch2);
++ __ beq(&check_zero_length);
++ __ bind(&strings_not_equal);
++ __ LoadSmiLiteral(r3, Smi::FromInt(NOT_EQUAL));
++ __ Ret();
++
++ // Check if the length is zero.
++ Label compare_chars;
++ __ bind(&check_zero_length);
++ STATIC_ASSERT(kSmiTag == 0);
++ __ cmpi(length, Operand::Zero());
++ __ bne(&compare_chars);
++ __ LoadSmiLiteral(r3, Smi::FromInt(EQUAL));
++ __ Ret();
++
++ // Compare characters.
++ __ bind(&compare_chars);
++ GenerateAsciiCharsCompareLoop(masm,
++ left, right, length, scratch2,
++ &strings_not_equal);
++
++ // Characters are equal.
++ __ LoadSmiLiteral(r3, Smi::FromInt(EQUAL));
++ __ Ret();
++}
++
++
++void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
++ Register left,
++ Register right,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3) {
++ Label skip, result_not_equal, compare_lengths;
++ // Find minimum length and length difference.
++ __ LoadP(scratch1, FieldMemOperand(left, String::kLengthOffset));
++ __ LoadP(scratch2, FieldMemOperand(right, String::kLengthOffset));
++ __ sub(scratch3, scratch1, scratch2, LeaveOE, SetRC);
++ Register length_delta = scratch3;
++ __ ble(&skip, cr0);
++ __ mr(scratch1, scratch2);
++ __ bind(&skip);
++ Register min_length = scratch1;
++ STATIC_ASSERT(kSmiTag == 0);
++ __ cmpi(min_length, Operand::Zero());
++ __ beq(&compare_lengths);
++
++ // Compare loop.
++ GenerateAsciiCharsCompareLoop(masm,
++ left, right, min_length, scratch2,
++ &result_not_equal);
++
++ // Compare lengths - strings up to min-length are equal.
++ __ bind(&compare_lengths);
++ ASSERT(Smi::FromInt(EQUAL) == static_cast<Smi*>(0));
++ // Use length_delta as result if it's zero.
++ __ mr(r3, length_delta);
++ __ cmpi(r3, Operand::Zero());
++ __ bind(&result_not_equal);
++ // Conditionally update the result based either on length_delta or
++ // the last comparion performed in the loop above.
++ Label less_equal, equal;
++ __ ble(&less_equal);
++ __ LoadSmiLiteral(r3, Smi::FromInt(GREATER));
++ __ Ret();
++ __ bind(&less_equal);
++ __ beq(&equal);
++ __ LoadSmiLiteral(r3, Smi::FromInt(LESS));
++ __ bind(&equal);
++ __ Ret();
++}
++
++
++void StringCompareStub::GenerateAsciiCharsCompareLoop(
++ MacroAssembler* masm,
++ Register left,
++ Register right,
++ Register length,
++ Register scratch1,
++ Label* chars_not_equal) {
++ // Change index to run from -length to -1 by adding length to string
++ // start. This means that loop ends when index reaches zero, which
++ // doesn't need an additional compare.
++ __ SmiUntag(length);
++ __ addi(scratch1, length,
++ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ __ add(left, left, scratch1);
++ __ add(right, right, scratch1);
++ __ subfic(length, length, Operand::Zero());
++ Register index = length; // index = -length;
++
++ // Compare loop.
++ Label loop;
++ __ bind(&loop);
++ __ lbzx(scratch1, MemOperand(left, index));
++ __ lbzx(r0, MemOperand(right, index));
++ __ cmp(scratch1, r0);
++ __ bne(chars_not_equal);
++ __ addi(index, index, Operand(1));
++ __ cmpi(index, Operand::Zero());
++ __ bne(&loop);
++}
++
++
++void StringCompareStub::Generate(MacroAssembler* masm) {
++ Label runtime;
++
++ Counters* counters = masm->isolate()->counters();
++
++ // Stack frame on entry.
++ // sp[0]: right string
++ // sp[4]: left string
++ __ LoadP(r3, MemOperand(sp)); // Load right in r3, left in r4.
++ __ LoadP(r4, MemOperand(sp, kPointerSize));
++
++ Label not_same;
++ __ cmp(r3, r4);
++ __ bne(¬_same);
++ STATIC_ASSERT(EQUAL == 0);
++ STATIC_ASSERT(kSmiTag == 0);
++ __ LoadSmiLiteral(r3, Smi::FromInt(EQUAL));
++ __ IncrementCounter(counters->string_compare_native(), 1, r4, r5);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ __ bind(¬_same);
++
++ // Check that both objects are sequential ASCII strings.
++ __ JumpIfNotBothSequentialAsciiStrings(r4, r3, r5, r6, &runtime);
++
++ // Compare flat ASCII strings natively. Remove arguments from stack first.
++ __ IncrementCounter(counters->string_compare_native(), 1, r5, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ GenerateCompareFlatAsciiStrings(masm, r4, r3, r5, r6, r7);
++
++ // Call the runtime; it returns -1 (less), 0 (equal), or 1 (greater)
++ // tagged as a small integer.
++ __ bind(&runtime);
++ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
++}
++
++
++void StringAddStub::Generate(MacroAssembler* masm) {
++ Label call_runtime, call_builtin;
++ Builtins::JavaScript builtin_id = Builtins::ADD;
++
++ Counters* counters = masm->isolate()->counters();
++
++ // Stack on entry:
++ // sp[0]: second argument (right).
++ // sp[4]: first argument (left).
++
++ // Load the two arguments.
++ __ LoadP(r3, MemOperand(sp, 1 * kPointerSize)); // First argument.
++ __ LoadP(r4, MemOperand(sp, 0 * kPointerSize)); // Second argument.
++
++ // Make sure that both arguments are strings if not known in advance.
++ if (flags_ == NO_STRING_ADD_FLAGS) {
++ __ JumpIfEitherSmi(r3, r4, &call_runtime);
++ // Load instance types.
++ __ LoadP(r7, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadP(r8, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ lbz(r7, FieldMemOperand(r7, Map::kInstanceTypeOffset));
++ __ lbz(r8, FieldMemOperand(r8, Map::kInstanceTypeOffset));
++ STATIC_ASSERT(kStringTag == 0);
++ // If either is not a string, go to runtime.
++ __ andi(r0, r7, Operand(kIsNotStringMask));
++ __ bne(&call_runtime, cr0);
++ __ andi(r0, r8, Operand(kIsNotStringMask));
++ __ bne(&call_runtime, cr0);
++ } else {
++ // Here at least one of the arguments is definitely a string.
++ // We convert the one that is not known to be a string.
++ if ((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) == 0) {
++ ASSERT((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) != 0);
++ GenerateConvertArgument(
++ masm, 1 * kPointerSize, r3, r5, r6, r7, r8, &call_builtin);
++ builtin_id = Builtins::STRING_ADD_RIGHT;
++ } else if ((flags_ & NO_STRING_CHECK_RIGHT_IN_STUB) == 0) {
++ ASSERT((flags_ & NO_STRING_CHECK_LEFT_IN_STUB) != 0);
++ GenerateConvertArgument(
++ masm, 0 * kPointerSize, r4, r5, r6, r7, r8, &call_builtin);
++ builtin_id = Builtins::STRING_ADD_LEFT;
++ }
++ }
++
++ // Both arguments are strings.
++ // r3: first string
++ // r4: second string
++ // r7: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
++ // r8: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
++ {
++ Label first_not_empty, return_second, strings_not_empty;
++ // Check if either of the strings are empty. In that case return the other.
++ __ LoadP(r5, FieldMemOperand(r3, String::kLengthOffset));
++ __ LoadP(r6, FieldMemOperand(r4, String::kLengthOffset));
++ STATIC_ASSERT(kSmiTag == 0);
++ // Test if first string is empty.
++ __ CmpSmiLiteral(r5, Smi::FromInt(0), r0);
++ __ bne(&first_not_empty);
++ __ mr(r3, r4); // If first is empty, return second.
++ __ b(&return_second);
++ STATIC_ASSERT(kSmiTag == 0);
++ __ bind(&first_not_empty);
++ // Else test if second string is empty.
++ __ CmpSmiLiteral(r6, Smi::FromInt(0), r0);
++ __ bne(&strings_not_empty); // If either string was empty, return r3.
++
++ __ bind(&return_second);
++ __ IncrementCounter(counters->string_add_native(), 1, r5, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ __ bind(&strings_not_empty);
++ }
++
++ __ SmiUntag(r5);
++ __ SmiUntag(r6);
++ // Both strings are non-empty.
++ // r3: first string
++ // r4: second string
++ // r5: length of first string
++ // r6: length of second string
++ // r7: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
++ // r8: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
++ // Look at the length of the result of adding the two strings.
++ Label string_add_flat_result, longer_than_two;
++ // Adding two lengths can't overflow.
++ STATIC_ASSERT(String::kMaxLength < String::kMaxLength * 2);
++ __ add(r9, r5, r6);
++ // Use the symbol table when adding two one character strings, as it
++ // helps later optimizations to return a symbol here.
++ __ cmpi(r9, Operand(2));
++ __ bne(&longer_than_two);
++
++ // Check that both strings are non-external ASCII strings.
++ if (flags_ != NO_STRING_ADD_FLAGS) {
++ __ LoadP(r7, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadP(r8, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ lbz(r7, FieldMemOperand(r7, Map::kInstanceTypeOffset));
++ __ lbz(r8, FieldMemOperand(r8, Map::kInstanceTypeOffset));
++ }
++ __ JumpIfBothInstanceTypesAreNotSequentialAscii(r7, r8, r9, r10,
++ &call_runtime);
++
++ // Get the two characters forming the sub string.
++ __ lbz(r5, FieldMemOperand(r3, SeqAsciiString::kHeaderSize));
++ __ lbz(r6, FieldMemOperand(r4, SeqAsciiString::kHeaderSize));
++
++ // Try to lookup two character string in symbol table. If it is not found
++ // just allocate a new one.
++ Label make_two_character_string;
++ StringHelper::GenerateTwoCharacterSymbolTableProbe(
++ masm, r5, r6, r9, r10, r7, r8, r22, &make_two_character_string);
++ __ IncrementCounter(counters->string_add_native(), 1, r5, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ __ bind(&make_two_character_string);
++ // Resulting string has length 2 and first chars of two strings
++ // are combined into single halfword in r5 register.
++ // So we can fill resulting string without two loops by a single
++ // halfword store instruction
++ __ li(r9, Operand(2));
++ __ AllocateAsciiString(r3, r9, r7, r8, r22, &call_runtime);
++ __ sth(r5, FieldMemOperand(r3, SeqAsciiString::kHeaderSize));
++ __ IncrementCounter(counters->string_add_native(), 1, r5, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ __ bind(&longer_than_two);
++ // Check if resulting string will be flat.
++ __ cmpi(r9, Operand(ConsString::kMinLength));
++ __ blt(&string_add_flat_result);
++ // Handle exceptionally long strings in the runtime system.
++ STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
++ ASSERT(IsPowerOf2(String::kMaxLength + 1));
++ // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
++ __ mov(r10, Operand(String::kMaxLength + 1));
++ __ cmpl(r9, r10);
++ __ bge(&call_runtime);
++
++ // If result is not supposed to be flat, allocate a cons string object.
++ // If both strings are ASCII the result is an ASCII cons string.
++ if (flags_ != NO_STRING_ADD_FLAGS) {
++ __ LoadP(r7, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadP(r8, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ lbz(r7, FieldMemOperand(r7, Map::kInstanceTypeOffset));
++ __ lbz(r8, FieldMemOperand(r8, Map::kInstanceTypeOffset));
++ }
++ Label non_ascii, allocated, ascii_data;
++ STATIC_ASSERT(kTwoByteStringTag == 0);
++ __ andi(r0, r7, Operand(kStringEncodingMask));
++ __ beq(&non_ascii, cr0);
++ __ andi(r0, r8, Operand(kStringEncodingMask));
++ __ beq(&non_ascii, cr0);
++
++ // Allocate an ASCII cons string.
++ __ bind(&ascii_data);
++ __ AllocateAsciiConsString(r10, r9, r7, r8, &call_runtime);
++ __ bind(&allocated);
++ // Fill the fields of the cons string.
++ __ StoreP(r3, FieldMemOperand(r10, ConsString::kFirstOffset), r0);
++ __ StoreP(r4, FieldMemOperand(r10, ConsString::kSecondOffset), r0);
++ __ mr(r3, r10);
++ __ IncrementCounter(counters->string_add_native(), 1, r5, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ __ bind(&non_ascii);
++ // At least one of the strings is two-byte. Check whether it happens
++ // to contain only ASCII characters.
++ // r7: first instance type.
++ // r8: second instance type.
++ __ andi(r0, r7, Operand(kAsciiDataHintMask));
++ __ bne(&ascii_data, cr0);
++ __ andi(r0, r8, Operand(kAsciiDataHintMask));
++ __ bne(&ascii_data, cr0);
++ __ xor_(r7, r7, r8);
++ STATIC_ASSERT(kAsciiStringTag != 0 && kAsciiDataHintTag != 0);
++ __ andi(r7, r7, Operand(kAsciiStringTag | kAsciiDataHintTag));
++ __ cmpi(r7, Operand(kAsciiStringTag | kAsciiDataHintTag));
++ __ beq(&ascii_data);
++
++ // Allocate a two byte cons string.
++ __ AllocateTwoByteConsString(r10, r9, r7, r8, &call_runtime);
++ __ b(&allocated);
++
++ // We cannot encounter sliced strings or cons strings here since:
++ STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
++ // Handle creating a flat result from either external or sequential strings.
++ // Locate the first characters' locations.
++ // r3: first string
++ // r4: second string
++ // r5: length of first string
++ // r6: length of second string
++ // r7: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
++ // r8: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
++ // r9: sum of lengths.
++ Label first_prepared, second_prepared, external_string1, external_string2;
++ __ bind(&string_add_flat_result);
++ if (flags_ != NO_STRING_ADD_FLAGS) {
++ __ LoadP(r7, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadP(r8, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ lbz(r7, FieldMemOperand(r7, Map::kInstanceTypeOffset));
++ __ lbz(r8, FieldMemOperand(r8, Map::kInstanceTypeOffset));
++ }
++
++ // Check whether both strings have same encoding
++ __ xor_(r10, r7, r8);
++ __ andi(r0, r10, Operand(kStringEncodingMask));
++ __ bne(&call_runtime, cr0);
++
++ STATIC_ASSERT(kSeqStringTag == 0);
++ __ andi(r0, r7, Operand(kStringRepresentationMask));
++ __ bne(&external_string1, cr0);
++ STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
++ __ addi(r10, r3, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ __ b(&first_prepared);
++ // External string: rule out short external string and load string resource.
++ STATIC_ASSERT(kShortExternalStringTag != 0);
++ __ bind(&external_string1);
++ __ andi(r0, r7, Operand(kShortExternalStringMask));
++ __ bne(&call_runtime, cr0);
++ __ LoadP(r10, FieldMemOperand(r3, ExternalString::kResourceDataOffset));
++ __ bind(&first_prepared);
++
++ STATIC_ASSERT(kSeqStringTag == 0);
++ __ andi(r0, r8, Operand(kStringRepresentationMask));
++ __ bne(&external_string2, cr0);
++ STATIC_ASSERT(SeqAsciiString::kHeaderSize == SeqTwoByteString::kHeaderSize);
++ __ addi(r4, r4, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ __ b(&second_prepared);
++ // External string: rule out short external string and load string resource.
++ STATIC_ASSERT(kShortExternalStringTag != 0);
++ __ bind(&external_string2);
++ __ andi(r0, r8, Operand(kShortExternalStringMask));
++ __ bne(&call_runtime, cr0);
++ __ LoadP(r4, FieldMemOperand(r4, ExternalString::kResourceDataOffset));
++ __ bind(&second_prepared);
++
++ Label non_ascii_string_add_flat_result;
++ // r10: first character of first string
++ // r4: first character of second string
++ // r5: length of first string.
++ // r6: length of second string.
++ // r9: sum of lengths.
++ // Both strings have the same encoding.
++ STATIC_ASSERT(kTwoByteStringTag == 0);
++ __ andi(r0, r8, Operand(kStringEncodingMask));
++ __ beq(&non_ascii_string_add_flat_result, cr0);
++
++ __ AllocateAsciiString(r3, r9, r7, r8, r22, &call_runtime);
++ __ addi(r9, r3, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ // r3: result string.
++ // r10: first character of first string.
++ // r4: first character of second string.
++ // r5: length of first string.
++ // r6: length of second string.
++ // r9: first character of result.
++ StringHelper::GenerateCopyCharacters(masm, r9, r10, r5, r7, true);
++ // r9: next character of result.
++ StringHelper::GenerateCopyCharacters(masm, r9, r4, r6, r7, true);
++ __ IncrementCounter(counters->string_add_native(), 1, r5, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ __ bind(&non_ascii_string_add_flat_result);
++ __ AllocateTwoByteString(r3, r9, r7, r8, r22, &call_runtime);
++ __ addi(r9, r3, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
++ // r3: result string.
++ // r10: first character of first string.
++ // r4: first character of second string.
++ // r5: length of first string.
++ // r6: length of second string.
++ // r9: first character of result.
++ StringHelper::GenerateCopyCharacters(masm, r9, r10, r5, r7, false);
++ // r9: next character of result.
++ StringHelper::GenerateCopyCharacters(masm, r9, r4, r6, r7, false);
++ __ IncrementCounter(counters->string_add_native(), 1, r5, r6);
++ __ addi(sp, sp, Operand(2 * kPointerSize));
++ __ Ret();
++
++ // Just jump to runtime to add the two strings.
++ __ bind(&call_runtime);
++ __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
++
++ if (call_builtin.is_linked()) {
++ __ bind(&call_builtin);
++ __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
++ }
++}
++
++
++void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
++ int stack_offset,
++ Register arg,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Label* slow) {
++ // First check if the argument is already a string.
++ Label not_string, done;
++ __ JumpIfSmi(arg, ¬_string);
++ __ CompareObjectType(arg, scratch1, scratch1, FIRST_NONSTRING_TYPE);
++ __ blt(&done);
++
++ // Check the number to string cache.
++ Label not_cached;
++ __ bind(¬_string);
++ // Puts the cached result into scratch1.
++ NumberToStringStub::GenerateLookupNumberStringCache(masm,
++ arg,
++ scratch1,
++ scratch2,
++ scratch3,
++ scratch4,
++ false,
++ ¬_cached);
++ __ mr(arg, scratch1);
++ __ StoreP(arg, MemOperand(sp, stack_offset));
++ __ b(&done);
++
++ // Check if the argument is a safe string wrapper.
++ __ bind(¬_cached);
++ __ JumpIfSmi(arg, slow);
++ __ CompareObjectType(
++ arg, scratch1, scratch2, JS_VALUE_TYPE); // map -> scratch1.
++ __ bne(slow);
++ __ lbz(scratch2, FieldMemOperand(scratch1, Map::kBitField2Offset));
++ __ andi(scratch2,
++ scratch2, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
++ __ cmpi(scratch2,
++ Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
++ __ bne(slow);
++ __ LoadP(arg, FieldMemOperand(arg, JSValue::kValueOffset));
++ __ StoreP(arg, MemOperand(sp, stack_offset));
++
++ __ bind(&done);
++}
++
++
++void ICCompareStub::GenerateSmis(MacroAssembler* masm) {
++ ASSERT(state_ == CompareIC::SMIS);
++ Label miss;
++ __ orx(r5, r4, r3);
++ __ JumpIfNotSmi(r5, &miss);
++
++ if (GetCondition() == eq) {
++ // For equality we do not care about the sign of the result.
++ // __ sub(r3, r3, r4, SetCC);
++ __ sub(r3, r3, r4);
++ } else {
++ // Untag before subtracting to avoid handling overflow.
++ __ SmiUntag(r4);
++ __ SmiUntag(r3);
++ __ sub(r3, r4, r3);
++ }
++ __ Ret();
++
++ __ bind(&miss);
++ GenerateMiss(masm);
++}
++
++
++void ICCompareStub::GenerateHeapNumbers(MacroAssembler* masm) {
++ ASSERT(state_ == CompareIC::HEAP_NUMBERS);
++ Label generic_stub;
++ Label unordered, maybe_undefined1, maybe_undefined2;
++ Label miss;
++ Label equal, less_than;
++
++ __ and_(r5, r4, r3);
++ __ JumpIfSmi(r5, &generic_stub);
++
++ __ CompareObjectType(r3, r5, r5, HEAP_NUMBER_TYPE);
++ __ bne(&maybe_undefined1);
++ __ CompareObjectType(r4, r5, r5, HEAP_NUMBER_TYPE);
++ __ bne(&maybe_undefined2);
++
++ // Inlining the double comparison and falling back to the general compare
++ // stub if NaN is involved
++
++ // Load left and right operand
++ // likely we can combine the constants to remove the sub
++ __ lfd(d0, FieldMemOperand(r4, HeapNumber::kValueOffset));
++ __ lfd(d1, FieldMemOperand(r3, HeapNumber::kValueOffset));
++
++ // Compare operands
++ __ fcmpu(d0, d1);
++
++ // Don't base result on status bits when a NaN is involved.
++ __ bunordered(&unordered);
++
++ // Return a result of -1, 0, or 1, based on status bits.
++ __ beq(&equal);
++ __ blt(&less_than);
++ // assume greater than
++ __ li(r3, Operand(GREATER));
++ __ Ret();
++ __ bind(&equal);
++ __ li(r3, Operand(EQUAL));
++ __ Ret();
++ __ bind(&less_than);
++ __ li(r3, Operand(LESS));
++ __ Ret();
++
++ __ bind(&unordered);
++
++ CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS, r4, r3);
++ __ bind(&generic_stub);
++ __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
++
++ __ bind(&maybe_undefined1);
++ if (Token::IsOrderedRelationalCompareOp(op_)) {
++ __ CompareRoot(r3, Heap::kUndefinedValueRootIndex);
++ __ bne(&miss);
++ __ CompareObjectType(r4, r5, r5, HEAP_NUMBER_TYPE);
++ __ bne(&maybe_undefined2);
++ __ b(&unordered);
++ }
++
++ __ bind(&maybe_undefined2);
++ if (Token::IsOrderedRelationalCompareOp(op_)) {
++ __ CompareRoot(r4, Heap::kUndefinedValueRootIndex);
++ __ beq(&unordered);
++ }
++
++ __ bind(&miss);
++ GenerateMiss(masm);
++}
++
++
++void ICCompareStub::GenerateSymbols(MacroAssembler* masm) {
++ ASSERT(state_ == CompareIC::SYMBOLS);
++ Label miss, not_equal;
++
++ // Registers containing left and right operands respectively.
++ Register left = r4;
++ Register right = r3;
++ Register tmp1 = r5;
++ Register tmp2 = r6;
++
++ // Check that both operands are heap objects.
++ __ JumpIfEitherSmi(left, right, &miss);
++
++ // Check that both operands are symbols.
++ __ LoadP(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
++ __ LoadP(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
++ __ lbz(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
++ __ lbz(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
++ STATIC_ASSERT(kSymbolTag != 0);
++ __ and_(tmp1, tmp1, tmp2);
++ __ andi(r0, tmp1, Operand(kIsSymbolMask));
++ __ beq(&miss, cr0);
++
++ // Symbols are compared by identity.
++ __ cmp(left, right);
++ __ bne(¬_equal);
++ // Make sure r3 is non-zero. At this point input operands are
++ // guaranteed to be non-zero.
++ ASSERT(right.is(r3));
++ STATIC_ASSERT(EQUAL == 0);
++ STATIC_ASSERT(kSmiTag == 0);
++ __ LoadSmiLiteral(r3, Smi::FromInt(EQUAL));
++ __ bind(¬_equal);
++ __ Ret();
++
++ __ bind(&miss);
++ GenerateMiss(masm);
++}
++
++
++void ICCompareStub::GenerateStrings(MacroAssembler* masm) {
++ ASSERT(state_ == CompareIC::STRINGS);
++ Label miss, not_identical, is_symbol;
++
++ bool equality = Token::IsEqualityOp(op_);
++
++ // Registers containing left and right operands respectively.
++ Register left = r4;
++ Register right = r3;
++ Register tmp1 = r5;
++ Register tmp2 = r6;
++ Register tmp3 = r7;
++ Register tmp4 = r8;
++
++ // Check that both operands are heap objects.
++ __ JumpIfEitherSmi(left, right, &miss);
++
++ // Check that both operands are strings. This leaves the instance
++ // types loaded in tmp1 and tmp2.
++ __ LoadP(tmp1, FieldMemOperand(left, HeapObject::kMapOffset));
++ __ LoadP(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
++ __ lbz(tmp1, FieldMemOperand(tmp1, Map::kInstanceTypeOffset));
++ __ lbz(tmp2, FieldMemOperand(tmp2, Map::kInstanceTypeOffset));
++ STATIC_ASSERT(kNotStringTag != 0);
++ __ orx(tmp3, tmp1, tmp2);
++ __ andi(r0, tmp3, Operand(kIsNotStringMask));
++ __ bne(&miss, cr0);
++
++ // Fast check for identical strings.
++ __ cmp(left, right);
++ STATIC_ASSERT(EQUAL == 0);
++ STATIC_ASSERT(kSmiTag == 0);
++ __ bne(¬_identical);
++ __ LoadSmiLiteral(r3, Smi::FromInt(EQUAL));
++ __ Ret();
++ __ bind(¬_identical);
++
++ // Handle not identical strings.
++
++ // Check that both strings are symbols. If they are, we're done
++ // because we already know they are not identical.
++ if (equality) {
++ ASSERT(GetCondition() == eq);
++ STATIC_ASSERT(kSymbolTag != 0);
++ __ and_(tmp3, tmp1, tmp2);
++ __ andi(r0, tmp3, Operand(kIsSymbolMask));
++ __ beq(&is_symbol, cr0);
++ // Make sure r3 is non-zero. At this point input operands are
++ // guaranteed to be non-zero.
++ ASSERT(right.is(r3));
++ __ Ret();
++ __ bind(&is_symbol);
++ }
++
++ // Check that both strings are sequential ASCII.
++ Label runtime;
++ __ JumpIfBothInstanceTypesAreNotSequentialAscii(
++ tmp1, tmp2, tmp3, tmp4, &runtime);
++
++ // Compare flat ASCII strings. Returns when done.
++ if (equality) {
++ StringCompareStub::GenerateFlatAsciiStringEquals(
++ masm, left, right, tmp1, tmp2);
++ } else {
++ StringCompareStub::GenerateCompareFlatAsciiStrings(
++ masm, left, right, tmp1, tmp2, tmp3);
++ }
++
++ // Handle more complex cases in runtime.
++ __ bind(&runtime);
++ __ Push(left, right);
++ if (equality) {
++ __ TailCallRuntime(Runtime::kStringEquals, 2, 1);
++ } else {
++ __ TailCallRuntime(Runtime::kStringCompare, 2, 1);
++ }
++
++ __ bind(&miss);
++ GenerateMiss(masm);
++}
++
++
++void ICCompareStub::GenerateObjects(MacroAssembler* masm) {
++ ASSERT(state_ == CompareIC::OBJECTS);
++ Label miss;
++ __ and_(r5, r4, r3);
++ __ JumpIfSmi(r5, &miss);
++
++ __ CompareObjectType(r3, r5, r5, JS_OBJECT_TYPE);
++ __ bne(&miss);
++ __ CompareObjectType(r4, r5, r5, JS_OBJECT_TYPE);
++ __ bne(&miss);
++
++ ASSERT(GetCondition() == eq);
++ __ sub(r3, r3, r4);
++ __ Ret();
++
++ __ bind(&miss);
++ GenerateMiss(masm);
++}
++
++
++void ICCompareStub::GenerateKnownObjects(MacroAssembler* masm) {
++ Label miss;
++ __ and_(r5, r4, r3);
++ __ JumpIfSmi(r5, &miss);
++ __ LoadP(r5, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadP(r6, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ Cmpi(r5, Operand(known_map_), r0);
++ __ bne(&miss);
++ __ Cmpi(r6, Operand(known_map_), r0);
++ __ bne(&miss);
++
++ __ sub(r3, r3, r4);
++ __ Ret();
++
++ __ bind(&miss);
++ GenerateMiss(masm);
++}
++
++
++
++void ICCompareStub::GenerateMiss(MacroAssembler* masm) {
++ {
++ // Call the runtime system in a fresh internal frame.
++ ExternalReference miss =
++ ExternalReference(IC_Utility(IC::kCompareIC_Miss), masm->isolate());
++
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ Push(r4, r3);
++ __ mflr(r0);
++ __ push(r0);
++ __ Push(r4, r3);
++ __ LoadSmiLiteral(ip, Smi::FromInt(op_));
++ __ push(ip);
++ __ CallExternalReference(miss, 3);
++ // Compute the entry point of the rewritten stub.
++ __ addi(r5, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
++ // Restore registers.
++ __ pop(r0);
++ __ mtlr(r0);
++ __ pop(r3);
++ __ pop(r4);
++ }
++
++ __ Jump(r5);
++}
++
++// This stub is paired with DirectCEntryStub::GenerateCall
++void DirectCEntryStub::Generate(MacroAssembler* masm) {
++ // Retrieve return address
++ __ LoadP(r0, MemOperand(sp, kStackFrameExtraParamSlot * kPointerSize));
++ __ Jump(r0);
++}
++
++
++void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
++ ExternalReference function) {
++ __ mov(r6, Operand(function));
++ GenerateCall(masm, r6);
++}
++
++
++void DirectCEntryStub::GenerateCall(MacroAssembler* masm,
++ Register target) {
++ Register scratch = r11;
++#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++ Register dest = ip;
++ // Native AIX/PPC64 Linux use a function descriptor.
++ __ LoadP(ToRegister(2), MemOperand(target, kPointerSize)); // TOC
++ __ LoadP(ip, MemOperand(target, 0)); // Instruction address
++#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++ Register dest = ip;
++ __ Move(ip, target);
++#else
++ Register dest = target;
++#endif
++
++ __ mov(r0, Operand(reinterpret_cast<intptr_t>(GetCode().location()),
++ RelocInfo::CODE_TARGET));
++
++ // Block the trampoline pool through the whole function to make sure the
++ // number of generated instructions is constant.
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
++
++ // Push return address (accessible to GC through exit frame pc).
++ Label start, here;
++ __ bind(&start);
++ __ b(&here, SetLK);
++ __ bind(&here);
++ __ mflr(scratch);
++ __ mtlr(r0); // from above, so we know where to return
++ __ addi(scratch, scratch, Operand(6 * Assembler::kInstrSize));
++ __ StoreP(scratch, MemOperand(sp, kStackFrameExtraParamSlot * kPointerSize));
++ __ Jump(dest); // Call the C++ function.
++ ASSERT_EQ(Assembler::kInstrSize +
++ (6 * Assembler::kInstrSize),
++ masm->SizeOfCodeGeneratedSince(&start));
++}
++
++
++void StringDictionaryLookupStub::GenerateNegativeLookup(MacroAssembler* masm,
++ Label* miss,
++ Label* done,
++ Register receiver,
++ Register properties,
++ Handle<String> name,
++ Register scratch0) {
++ // If names of slots in range from 1 to kProbes - 1 for the hash value are
++ // not equal to the name and kProbes-th slot is not used (its name is the
++ // undefined value), it guarantees the hash table doesn't contain the
++ // property. It's true even if some slots represent deleted properties
++ // (their names are the hole value).
++ for (int i = 0; i < kInlinedProbes; i++) {
++ // scratch0 points to properties hash.
++ // Compute the masked index: (hash + i + i * i) & mask.
++ Register index = scratch0;
++ // Capacity is smi 2^n.
++ __ LoadP(index, FieldMemOperand(properties, kCapacityOffset));
++ __ subi(index, index, Operand(1));
++ __ LoadSmiLiteral(ip, Smi::FromInt(name->Hash() +
++ StringDictionary::GetProbeOffset(i)));
++ __ and_(index, index, ip);
++
++ // Scale the index by multiplying by the entry size.
++ ASSERT(StringDictionary::kEntrySize == 3);
++ __ ShiftLeftImm(ip, index, Operand(1));
++ __ add(index, index, ip); // index *= 3.
++
++ Register entity_name = scratch0;
++ // Having undefined at this place means the name is not contained.
++ Register tmp = properties;
++ __ SmiToPtrArrayOffset(ip, index);
++ __ add(tmp, properties, ip);
++ __ LoadP(entity_name, FieldMemOperand(tmp, kElementsStartOffset));
++
++ ASSERT(!tmp.is(entity_name));
++ __ LoadRoot(tmp, Heap::kUndefinedValueRootIndex);
++ __ cmp(entity_name, tmp);
++ __ beq(done);
++
++ if (i != kInlinedProbes - 1) {
++ // Load the hole ready for use below:
++ __ LoadRoot(tmp, Heap::kTheHoleValueRootIndex);
++
++ // Stop if found the property.
++ __ Cmpi(entity_name, Operand(Handle<String>(name)), r0);
++ __ beq(miss);
++
++ Label the_hole;
++ __ cmp(entity_name, tmp);
++ __ beq(&the_hole);
++
++ // Check if the entry name is not a symbol.
++ __ LoadP(entity_name, FieldMemOperand(entity_name,
++ HeapObject::kMapOffset));
++ __ lbz(entity_name,
++ FieldMemOperand(entity_name, Map::kInstanceTypeOffset));
++ __ andi(r0, entity_name, Operand(kIsSymbolMask));
++ __ beq(miss, cr0);
++
++ __ bind(&the_hole);
++
++ // Restore the properties.
++ __ LoadP(properties,
++ FieldMemOperand(receiver, JSObject::kPropertiesOffset));
++ }
++ }
++
++ const int spill_mask =
++ (r0.bit() | r9.bit() | r8.bit() | r7.bit() | r6.bit() |
++ r5.bit() | r4.bit() | r3.bit());
++
++ __ mflr(r0);
++ __ MultiPush(spill_mask);
++
++ __ LoadP(r3, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
++ __ mov(r4, Operand(Handle<String>(name)));
++ StringDictionaryLookupStub stub(NEGATIVE_LOOKUP);
++ __ CallStub(&stub);
++ __ cmpi(r3, Operand::Zero());
++
++ __ MultiPop(spill_mask); // MultiPop does not touch condition flags
++ __ mtlr(r0);
++
++ __ beq(done);
++ __ bne(miss);
++}
++
++
++// Probe the string dictionary in the |elements| register. Jump to the
++// |done| label if a property with the given name is found. Jump to
++// the |miss| label otherwise.
++// If lookup was successful |scratch2| will be equal to elements + 4 * index.
++void StringDictionaryLookupStub::GeneratePositiveLookup(MacroAssembler* masm,
++ Label* miss,
++ Label* done,
++ Register elements,
++ Register name,
++ Register scratch1,
++ Register scratch2) {
++ ASSERT(!elements.is(scratch1));
++ ASSERT(!elements.is(scratch2));
++ ASSERT(!name.is(scratch1));
++ ASSERT(!name.is(scratch2));
++
++ // Assert that name contains a string.
++ __ AssertString(name);
++
++ // Compute the capacity mask.
++ __ LoadP(scratch1, FieldMemOperand(elements, kCapacityOffset));
++ __ SmiUntag(scratch1); // convert smi to int
++ __ subi(scratch1, scratch1, Operand(1));
++
++ // Generate an unrolled loop that performs a few probes before
++ // giving up. Measurements done on Gmail indicate that 2 probes
++ // cover ~93% of loads from dictionaries.
++ for (int i = 0; i < kInlinedProbes; i++) {
++ // Compute the masked index: (hash + i + i * i) & mask.
++ __ lwz(scratch2, FieldMemOperand(name, String::kHashFieldOffset));
++ if (i > 0) {
++ // Add the probe offset (i + i * i) left shifted to avoid right shifting
++ // the hash in a separate instruction. The value hash + i + i * i is right
++ // shifted in the following and instruction.
++ ASSERT(StringDictionary::GetProbeOffset(i) <
++ 1 << (32 - String::kHashFieldSlot));
++ __ addi(scratch2, scratch2, Operand(
++ StringDictionary::GetProbeOffset(i) << String::kHashShift));
++ }
++ __ srwi(scratch2, scratch2, Operand(String::kHashShift));
++ __ and_(scratch2, scratch1, scratch2);
++
++ // Scale the index by multiplying by the element size.
++ ASSERT(StringDictionary::kEntrySize == 3);
++ // scratch2 = scratch2 * 3.
++ __ ShiftLeftImm(ip, scratch2, Operand(1));
++ __ add(scratch2, scratch2, ip);
++
++ // Check if the key is identical to the name.
++ __ ShiftLeftImm(ip, scratch2, Operand(kPointerSizeLog2));
++ __ add(scratch2, elements, ip);
++ __ LoadP(ip, FieldMemOperand(scratch2, kElementsStartOffset));
++ __ cmp(name, ip);
++ __ beq(done);
++ }
++
++ const int spill_mask =
++ (r0.bit() | r9.bit() | r8.bit() | r7.bit() |
++ r6.bit() | r5.bit() | r4.bit() | r3.bit()) &
++ ~(scratch1.bit() | scratch2.bit());
++
++ __ mflr(r0);
++ __ MultiPush(spill_mask);
++ if (name.is(r3)) {
++ ASSERT(!elements.is(r4));
++ __ mr(r4, name);
++ __ mr(r3, elements);
++ } else {
++ __ mr(r3, elements);
++ __ mr(r4, name);
++ }
++ StringDictionaryLookupStub stub(POSITIVE_LOOKUP);
++ __ CallStub(&stub);
++ __ cmpi(r3, Operand::Zero());
++ __ mr(scratch2, r5);
++ __ MultiPop(spill_mask);
++ __ mtlr(r0);
++
++ __ bne(done);
++ __ beq(miss);
++}
++
++
++void StringDictionaryLookupStub::Generate(MacroAssembler* masm) {
++ // This stub overrides SometimesSetsUpAFrame() to return false. That means
++ // we cannot call anything that could cause a GC from this stub.
++ // Registers:
++ // result: StringDictionary to probe
++ // r4: key
++ // : StringDictionary to probe.
++ // index_: will hold an index of entry if lookup is successful.
++ // might alias with result_.
++ // Returns:
++ // result_ is zero if lookup failed, non zero otherwise.
++
++ Register result = r3;
++ Register dictionary = r3;
++ Register key = r4;
++ Register index = r5;
++ Register mask = r6;
++ Register hash = r7;
++ Register undefined = r8;
++ Register entry_key = r9;
++ Register scratch = r9;
++
++ Label in_dictionary, maybe_in_dictionary, not_in_dictionary;
++
++ __ LoadP(mask, FieldMemOperand(dictionary, kCapacityOffset));
++ __ SmiUntag(mask);
++ __ subi(mask, mask, Operand(1));
++
++ __ lwz(hash, FieldMemOperand(key, String::kHashFieldOffset));
++
++ __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
++
++ for (int i = kInlinedProbes; i < kTotalProbes; i++) {
++ // Compute the masked index: (hash + i + i * i) & mask.
++ // Capacity is smi 2^n.
++ if (i > 0) {
++ // Add the probe offset (i + i * i) left shifted to avoid right shifting
++ // the hash in a separate instruction. The value hash + i + i * i is right
++ // shifted in the following and instruction.
++ ASSERT(StringDictionary::GetProbeOffset(i) <
++ 1 << (32 - String::kHashFieldSlot));
++ __ addi(index, hash, Operand(
++ StringDictionary::GetProbeOffset(i) << String::kHashShift));
++ } else {
++ __ mr(index, hash);
++ }
++ __ srwi(r0, index, Operand(String::kHashShift));
++ __ and_(index, mask, r0);
++
++ // Scale the index by multiplying by the entry size.
++ ASSERT(StringDictionary::kEntrySize == 3);
++ __ ShiftLeftImm(scratch, index, Operand(1));
++ __ add(index, index, scratch); // index *= 3.
++
++ ASSERT_EQ(kSmiTagSize, 1);
++ __ ShiftLeftImm(scratch, index, Operand(kPointerSizeLog2));
++ __ add(index, dictionary, scratch);
++ __ LoadP(entry_key, FieldMemOperand(index, kElementsStartOffset));
++
++ // Having undefined at this place means the name is not contained.
++ __ cmp(entry_key, undefined);
++ __ beq(¬_in_dictionary);
++
++ // Stop if found the property.
++ __ cmp(entry_key, key);
++ __ beq(&in_dictionary);
++
++ if (i != kTotalProbes - 1 && mode_ == NEGATIVE_LOOKUP) {
++ // Check if the entry name is not a symbol.
++ __ LoadP(entry_key, FieldMemOperand(entry_key, HeapObject::kMapOffset));
++ __ lbz(entry_key,
++ FieldMemOperand(entry_key, Map::kInstanceTypeOffset));
++ __ andi(r0, entry_key, Operand(kIsSymbolMask));
++ __ beq(&maybe_in_dictionary, cr0);
++ }
++ }
++
++ __ bind(&maybe_in_dictionary);
++ // If we are doing negative lookup then probing failure should be
++ // treated as a lookup success. For positive lookup probing failure
++ // should be treated as lookup failure.
++ if (mode_ == POSITIVE_LOOKUP) {
++ __ li(result, Operand::Zero());
++ __ Ret();
++ }
++
++ __ bind(&in_dictionary);
++ __ li(result, Operand(1));
++ __ Ret();
++
++ __ bind(¬_in_dictionary);
++ __ li(result, Operand::Zero());
++ __ Ret();
++}
++
++
++struct AheadOfTimeWriteBarrierStubList {
++ Register object, value, address;
++ RememberedSetAction action;
++};
++
++#define REG(Name) { kRegister_ ## Name ## _Code }
++
++static const AheadOfTimeWriteBarrierStubList kAheadOfTime[] = {
++ // Used in RegExpExecStub.
++ { REG(r28), REG(r26), REG(r10), EMIT_REMEMBERED_SET },
++ { REG(r28), REG(r5), REG(r10), EMIT_REMEMBERED_SET },
++ // Used in CompileArrayPushCall.
++ // Also used in StoreIC::GenerateNormal via GenerateDictionaryStore.
++ // Also used in KeyedStoreIC::GenerateGeneric.
++ { REG(r6), REG(r7), REG(r8), EMIT_REMEMBERED_SET },
++ // Used in CompileStoreGlobal.
++ { REG(r7), REG(r4), REG(r5), OMIT_REMEMBERED_SET },
++ // Used in StoreStubCompiler::CompileStoreField via GenerateStoreField.
++ { REG(r4), REG(r5), REG(r6), EMIT_REMEMBERED_SET },
++ { REG(r6), REG(r5), REG(r4), EMIT_REMEMBERED_SET },
++ // Used in KeyedStoreStubCompiler::CompileStoreField via GenerateStoreField.
++ { REG(r5), REG(r4), REG(r6), EMIT_REMEMBERED_SET },
++ { REG(r6), REG(r4), REG(r5), EMIT_REMEMBERED_SET },
++ // KeyedStoreStubCompiler::GenerateStoreFastElement.
++ { REG(r6), REG(r5), REG(r7), EMIT_REMEMBERED_SET },
++ { REG(r5), REG(r6), REG(r7), EMIT_REMEMBERED_SET },
++ // ElementsTransitionGenerator::GenerateMapChangeElementTransition
++ // and ElementsTransitionGenerator::GenerateSmiToDouble
++ // and ElementsTransitionGenerator::GenerateDoubleToObject
++ { REG(r5), REG(r6), REG(r22), EMIT_REMEMBERED_SET },
++ { REG(r5), REG(r6), REG(r22), OMIT_REMEMBERED_SET },
++ // ElementsTransitionGenerator::GenerateDoubleToObject
++ { REG(r9), REG(r5), REG(r3), EMIT_REMEMBERED_SET },
++ { REG(r5), REG(r9), REG(r22), EMIT_REMEMBERED_SET },
++ // StoreArrayLiteralElementStub::Generate
++ { REG(r8), REG(r3), REG(r9), EMIT_REMEMBERED_SET },
++ // FastNewClosureStub::Generate
++ { REG(r5), REG(r7), REG(r4), EMIT_REMEMBERED_SET },
++ // Null termination.
++ { REG(no_reg), REG(no_reg), REG(no_reg), EMIT_REMEMBERED_SET}
++};
++
++#undef REG
++
++
++bool RecordWriteStub::IsPregenerated() {
++ for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
++ !entry->object.is(no_reg);
++ entry++) {
++ if (object_.is(entry->object) &&
++ value_.is(entry->value) &&
++ address_.is(entry->address) &&
++ remembered_set_action_ == entry->action &&
++ save_fp_regs_mode_ == kDontSaveFPRegs) {
++ return true;
++ }
++ }
++ return false;
++}
++
++
++bool StoreBufferOverflowStub::IsPregenerated() {
++ return save_doubles_ == kDontSaveFPRegs || ISOLATE->fp_stubs_generated();
++}
++
++
++void StoreBufferOverflowStub::GenerateFixedRegStubsAheadOfTime() {
++ StoreBufferOverflowStub stub1(kDontSaveFPRegs);
++ stub1.GetCode()->set_is_pregenerated(true);
++}
++
++
++void RecordWriteStub::GenerateFixedRegStubsAheadOfTime() {
++ for (const AheadOfTimeWriteBarrierStubList* entry = kAheadOfTime;
++ !entry->object.is(no_reg);
++ entry++) {
++ RecordWriteStub stub(entry->object,
++ entry->value,
++ entry->address,
++ entry->action,
++ kDontSaveFPRegs);
++ stub.GetCode()->set_is_pregenerated(true);
++ }
++}
++
++
++bool CodeStub::CanUseFPRegisters() {
++ return true;
++}
++
++
++// Takes the input in 3 registers: address_ value_ and object_. A pointer to
++// the value has just been written into the object, now this stub makes sure
++// we keep the GC informed. The word in the object where the value has been
++// written is in the address register.
++void RecordWriteStub::Generate(MacroAssembler* masm) {
++ Label skip_to_incremental_noncompacting;
++ Label skip_to_incremental_compacting;
++ const int crBit = Assembler::encode_crbit(cr2, CR_LT);
++
++ // The first two branch instructions are generated with labels so as to
++ // get the offset fixed up correctly by the bind(Label*) call. We patch
++ // it back and forth between branch condition True and False
++ // when we start and stop incremental heap marking.
++ // See RecordWriteStub::Patch for details.
++
++ // Clear the bit, branch on True for NOP action initially
++ __ crxor(crBit, crBit, crBit);
++ __ blt(&skip_to_incremental_noncompacting, cr2);
++ __ blt(&skip_to_incremental_compacting, cr2);
++
++ if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
++ __ RememberedSetHelper(object_,
++ address_,
++ value_,
++ save_fp_regs_mode_,
++ MacroAssembler::kReturnAtEnd);
++ }
++ __ Ret();
++
++ __ bind(&skip_to_incremental_noncompacting);
++ GenerateIncremental(masm, INCREMENTAL);
++
++ __ bind(&skip_to_incremental_compacting);
++ GenerateIncremental(masm, INCREMENTAL_COMPACTION);
++
++ // Initial mode of the stub is expected to be STORE_BUFFER_ONLY.
++ // Will be checked in IncrementalMarking::ActivateGeneratedStub.
++ // patching not required on PPC as the initial path is effectively NOP
++}
++
++
++void RecordWriteStub::GenerateIncremental(MacroAssembler* masm, Mode mode) {
++ regs_.Save(masm);
++
++ if (remembered_set_action_ == EMIT_REMEMBERED_SET) {
++ Label dont_need_remembered_set;
++
++ __ LoadP(regs_.scratch0(), MemOperand(regs_.address(), 0));
++ __ JumpIfNotInNewSpace(regs_.scratch0(), // Value.
++ regs_.scratch0(),
++ &dont_need_remembered_set);
++
++ __ CheckPageFlag(regs_.object(),
++ regs_.scratch0(),
++ 1 << MemoryChunk::SCAN_ON_SCAVENGE,
++ ne,
++ &dont_need_remembered_set);
++
++ // First notify the incremental marker if necessary, then update the
++ // remembered set.
++ CheckNeedsToInformIncrementalMarker(
++ masm, kUpdateRememberedSetOnNoNeedToInformIncrementalMarker, mode);
++ InformIncrementalMarker(masm, mode);
++ regs_.Restore(masm);
++ __ RememberedSetHelper(object_,
++ address_,
++ value_,
++ save_fp_regs_mode_,
++ MacroAssembler::kReturnAtEnd);
++
++ __ bind(&dont_need_remembered_set);
++ }
++
++ CheckNeedsToInformIncrementalMarker(
++ masm, kReturnOnNoNeedToInformIncrementalMarker, mode);
++ InformIncrementalMarker(masm, mode);
++ regs_.Restore(masm);
++ __ Ret();
++}
++
++
++void RecordWriteStub::InformIncrementalMarker(MacroAssembler* masm, Mode mode) {
++ regs_.SaveCallerSaveRegisters(masm, save_fp_regs_mode_);
++ int argument_count = 3;
++ __ PrepareCallCFunction(argument_count, regs_.scratch0());
++ Register address =
++ r3.is(regs_.address()) ? regs_.scratch0() : regs_.address();
++ ASSERT(!address.is(regs_.object()));
++ ASSERT(!address.is(r3));
++ __ mr(address, regs_.address());
++ __ mr(r3, regs_.object());
++ if (mode == INCREMENTAL_COMPACTION) {
++ __ mr(r4, address);
++ } else {
++ ASSERT(mode == INCREMENTAL);
++ __ LoadP(r4, MemOperand(address, 0));
++ }
++ __ mov(r5, Operand(ExternalReference::isolate_address()));
++
++ AllowExternalCallThatCantCauseGC scope(masm);
++ if (mode == INCREMENTAL_COMPACTION) {
++ __ CallCFunction(
++ ExternalReference::incremental_evacuation_record_write_function(
++ masm->isolate()),
++ argument_count);
++ } else {
++ ASSERT(mode == INCREMENTAL);
++ __ CallCFunction(
++ ExternalReference::incremental_marking_record_write_function(
++ masm->isolate()),
++ argument_count);
++ }
++ regs_.RestoreCallerSaveRegisters(masm, save_fp_regs_mode_);
++}
++
++
++void RecordWriteStub::CheckNeedsToInformIncrementalMarker(
++ MacroAssembler* masm,
++ OnNoNeedToInformIncrementalMarker on_no_need,
++ Mode mode) {
++ Label on_black;
++ Label need_incremental;
++ Label need_incremental_pop_scratch;
++
++ ASSERT((~Page::kPageAlignmentMask & 0xffff) == 0);
++ __ lis(r0, Operand((~Page::kPageAlignmentMask >> 16)));
++ __ and_(regs_.scratch0(), regs_.object(), r0);
++ __ LoadP(regs_.scratch1(),
++ MemOperand(regs_.scratch0(),
++ MemoryChunk::kWriteBarrierCounterOffset));
++ __ subi(regs_.scratch1(), regs_.scratch1(), Operand(1));
++ __ StoreP(regs_.scratch1(),
++ MemOperand(regs_.scratch0(),
++ MemoryChunk::kWriteBarrierCounterOffset));
++ __ cmpi(regs_.scratch1(), Operand::Zero()); // PPC, we could do better here
++ __ blt(&need_incremental);
++
++ // Let's look at the color of the object: If it is not black we don't have
++ // to inform the incremental marker.
++ __ JumpIfBlack(regs_.object(), regs_.scratch0(), regs_.scratch1(), &on_black);
++
++ regs_.Restore(masm);
++ if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
++ __ RememberedSetHelper(object_,
++ address_,
++ value_,
++ save_fp_regs_mode_,
++ MacroAssembler::kReturnAtEnd);
++ } else {
++ __ Ret();
++ }
++
++ __ bind(&on_black);
++
++ // Get the value from the slot.
++ __ LoadP(regs_.scratch0(), MemOperand(regs_.address(), 0));
++
++ if (mode == INCREMENTAL_COMPACTION) {
++ Label ensure_not_white;
++
++ __ CheckPageFlag(regs_.scratch0(), // Contains value.
++ regs_.scratch1(), // Scratch.
++ MemoryChunk::kEvacuationCandidateMask,
++ eq,
++ &ensure_not_white);
++
++ __ CheckPageFlag(regs_.object(),
++ regs_.scratch1(), // Scratch.
++ MemoryChunk::kSkipEvacuationSlotsRecordingMask,
++ eq,
++ &need_incremental);
++
++ __ bind(&ensure_not_white);
++ }
++
++ // We need extra registers for this, so we push the object and the address
++ // register temporarily.
++ __ Push(regs_.object(), regs_.address());
++ __ EnsureNotWhite(regs_.scratch0(), // The value.
++ regs_.scratch1(), // Scratch.
++ regs_.object(), // Scratch.
++ regs_.address(), // Scratch.
++ &need_incremental_pop_scratch);
++ __ Pop(regs_.object(), regs_.address());
++
++ regs_.Restore(masm);
++ if (on_no_need == kUpdateRememberedSetOnNoNeedToInformIncrementalMarker) {
++ __ RememberedSetHelper(object_,
++ address_,
++ value_,
++ save_fp_regs_mode_,
++ MacroAssembler::kReturnAtEnd);
++ } else {
++ __ Ret();
++ }
++
++ __ bind(&need_incremental_pop_scratch);
++ __ Pop(regs_.object(), regs_.address());
++
++ __ bind(&need_incremental);
++
++ // Fall through when we need to inform the incremental marker.
++}
++
++
++void StoreArrayLiteralElementStub::Generate(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : element value to store
++ // -- r4 : array literal
++ // -- r5 : map of array literal
++ // -- r6 : element index as smi
++ // -- r7 : array literal index in function as smi
++ // -----------------------------------
++
++ Label element_done;
++ Label double_elements;
++ Label smi_element;
++ Label slow_elements;
++ Label fast_elements;
++
++ __ CheckFastElements(r5, r8, &double_elements);
++ // FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS
++ __ JumpIfSmi(r3, &smi_element);
++ __ CheckFastSmiElements(r5, r8, &fast_elements);
++
++ // Store into the array literal requires a elements transition. Call into
++ // the runtime.
++ __ bind(&slow_elements);
++ // call.
++ __ Push(r4, r6, r3);
++ __ LoadP(r8, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ __ LoadP(r8, FieldMemOperand(r8, JSFunction::kLiteralsOffset));
++ __ Push(r8, r7);
++ __ TailCallRuntime(Runtime::kStoreArrayLiteralElement, 5, 1);
++
++ // Array literal has ElementsKind of FAST_*_ELEMENTS and value is an object.
++ __ bind(&fast_elements);
++ __ LoadP(r8, FieldMemOperand(r4, JSObject::kElementsOffset));
++ __ SmiToPtrArrayOffset(r9, r6);
++ __ add(r9, r8, r9);
++#if V8_TARGET_ARCH_PPC64
++ // add due to offset alignment requirements of StorePU
++ __ addi(r9, r9, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ StoreP(r3, MemOperand(r9));
++#else
++ __ StorePU(r3, MemOperand(r9, FixedArray::kHeaderSize - kHeapObjectTag));
++#endif
++ // Update the write barrier for the array store.
++ __ RecordWrite(r8, r9, r3, kLRHasNotBeenSaved, kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
++ __ Ret();
++
++ // Array literal has ElementsKind of FAST_*_SMI_ELEMENTS or FAST_*_ELEMENTS,
++ // and value is Smi.
++ __ bind(&smi_element);
++ __ LoadP(r8, FieldMemOperand(r4, JSObject::kElementsOffset));
++ __ SmiToPtrArrayOffset(r9, r6);
++ __ add(r9, r8, r9);
++ __ StoreP(r3, FieldMemOperand(r9, FixedArray::kHeaderSize), r0);
++ __ Ret();
++
++ // Array literal has ElementsKind of FAST_DOUBLE_ELEMENTS.
++ __ bind(&double_elements);
++ __ LoadP(r8, FieldMemOperand(r4, JSObject::kElementsOffset));
++ __ StoreNumberToDoubleElements(r3, r6, r4,
++ // Overwrites all regs after this.
++ r8, r9, r10, r22, r5,
++ &slow_elements);
++ __ Ret();
++}
++
++
++void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) {
++ if (entry_hook_ != NULL) {
++ ProfileEntryHookStub stub;
++ __ mflr(r0);
++ __ push(r0);
++ __ CallStub(&stub);
++ __ pop(r0);
++ __ mtlr(r0);
++ }
++}
++
++
++void ProfileEntryHookStub::Generate(MacroAssembler* masm) {
++ // The entry hook is a "push lr" instruction, followed by a call.
++ const int32_t kReturnAddressDistanceFromFunctionStart =
++ Assembler::kCallTargetAddressOffset + 2 * Assembler::kInstrSize;
++
++ // Save live volatile registers.
++ __ mflr(r3);
++ __ Push(r3, r30, r4);
++ const int32_t kNumSavedRegs = 3;
++
++ // Compute the function's address for the first argument.
++ __ subi(r3, r3, Operand(kReturnAddressDistanceFromFunctionStart));
++
++ // The caller's return address is above the saved temporaries.
++ // Grab that for the second argument to the hook.
++ __ addi(r4, sp, Operand(kNumSavedRegs * kPointerSize));
++
++ // Align the stack if necessary.
++ int frame_alignment = masm->ActivationFrameAlignment();
++ if (frame_alignment > kPointerSize) {
++ __ mr(r30, sp);
++ ASSERT(IsPowerOf2(frame_alignment));
++ ASSERT(-frame_alignment == -8);
++ __ ClearRightImm(sp, sp, Operand(3));
++ }
++
++#if !defined(USE_SIMULATOR)
++ __ mov(ip, Operand(reinterpret_cast<intptr_t>(&entry_hook_)));
++ __ LoadP(ip, MemOperand(ip));
++
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ // Function descriptor
++ __ LoadP(ToRegister(2), MemOperand(ip, kPointerSize));
++ __ LoadP(ip, MemOperand(ip, 0));
++#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++ // ip already set.
++#endif
++
++ // PPC LINUX ABI:
++ __ addi(sp, sp, Operand(-kNumRequiredStackFrameSlots * kPointerSize));
++#else
++ // Under the simulator we need to indirect the entry hook through a
++ // trampoline function at a known address.
++ Address trampoline_address = reinterpret_cast<Address>(
++ reinterpret_cast<intptr_t>(EntryHookTrampoline));
++ ApiFunction dispatcher(trampoline_address);
++ __ mov(ip, Operand(ExternalReference(&dispatcher,
++ ExternalReference::BUILTIN_CALL,
++ masm->isolate())));
++#endif
++ __ Call(ip);
++
++// For the most part this is true only when USE_SIMULATOR is true
++// The exception is when built with nativesim=true, then we need
++// Real PPC calling support plus simulation
++#if defined(V8_HOST_ARCH_PPC64) || defined(V8_HOST_ARCH_PPC)
++ __ addi(sp, sp, Operand(kNumRequiredStackFrameSlots * kPointerSize));
++#endif
++
++ // Restore the stack pointer if needed.
++ if (frame_alignment > kPointerSize) {
++ __ mr(sp, r30);
++ }
++
++ __ Pop(r0, r30, r4);
++ __ mtlr(r0);
++ __ Ret();
++}
++
++#undef __
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/code-stubs-ppc.h.ppc v8-3.14.5.10/src/ppc/code-stubs-ppc.h
+--- v8-3.14.5.10/src/ppc/code-stubs-ppc.h.ppc 2016-06-07 14:15:45.990393008 -0400
++++ v8-3.14.5.10/src/ppc/code-stubs-ppc.h 2016-06-07 14:15:45.990393008 -0400
+@@ -0,0 +1,884 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_CODE_STUBS_PPC_H_
++#define V8_PPC_CODE_STUBS_PPC_H_
++
++#include "ic-inl.h"
++
++namespace v8 {
++namespace internal {
++
++// Compute a transcendental math function natively, or call the
++// TranscendentalCache runtime function.
++class TranscendentalCacheStub: public CodeStub {
++ public:
++ enum ArgumentType {
++ TAGGED = 0 << TranscendentalCache::kTranscendentalTypeBits,
++ UNTAGGED = 1 << TranscendentalCache::kTranscendentalTypeBits
++ };
++
++ TranscendentalCacheStub(TranscendentalCache::Type type,
++ ArgumentType argument_type)
++ : type_(type), argument_type_(argument_type) { }
++ void Generate(MacroAssembler* masm);
++ private:
++ TranscendentalCache::Type type_;
++ ArgumentType argument_type_;
++ void GenerateCallCFunction(MacroAssembler* masm, Register scratch);
++
++ Major MajorKey() { return TranscendentalCache; }
++ int MinorKey() { return type_ | argument_type_; }
++ Runtime::FunctionId RuntimeFunction();
++};
++
++
++class StoreBufferOverflowStub: public CodeStub {
++ public:
++ explicit StoreBufferOverflowStub(SaveFPRegsMode save_fp)
++ : save_doubles_(save_fp) { }
++
++ void Generate(MacroAssembler* masm);
++
++ virtual bool IsPregenerated();
++ static void GenerateFixedRegStubsAheadOfTime();
++ virtual bool SometimesSetsUpAFrame() { return false; }
++
++ private:
++ SaveFPRegsMode save_doubles_;
++
++ Major MajorKey() { return StoreBufferOverflow; }
++ int MinorKey() { return (save_doubles_ == kSaveFPRegs) ? 1 : 0; }
++};
++
++
++class UnaryOpStub: public CodeStub {
++ public:
++ UnaryOpStub(Token::Value op,
++ UnaryOverwriteMode mode,
++ UnaryOpIC::TypeInfo operand_type = UnaryOpIC::UNINITIALIZED)
++ : op_(op),
++ mode_(mode),
++ operand_type_(operand_type) {
++ }
++
++ private:
++ Token::Value op_;
++ UnaryOverwriteMode mode_;
++
++ // Operand type information determined at runtime.
++ UnaryOpIC::TypeInfo operand_type_;
++
++ virtual void PrintName(StringStream* stream);
++
++ class ModeBits: public BitField<UnaryOverwriteMode, 0, 1> {};
++ class OpBits: public BitField<Token::Value, 1, 7> {};
++ class OperandTypeInfoBits: public BitField<UnaryOpIC::TypeInfo, 8, 3> {};
++
++ Major MajorKey() { return UnaryOp; }
++ int MinorKey() {
++ return ModeBits::encode(mode_)
++ | OpBits::encode(op_)
++ | OperandTypeInfoBits::encode(operand_type_);
++ }
++
++ // Note: A lot of the helper functions below will vanish when we use virtual
++ // function instead of switch more often.
++ void Generate(MacroAssembler* masm);
++
++ void GenerateTypeTransition(MacroAssembler* masm);
++
++ void GenerateSmiStub(MacroAssembler* masm);
++ void GenerateSmiStubSub(MacroAssembler* masm);
++ void GenerateSmiStubBitNot(MacroAssembler* masm);
++ void GenerateSmiCodeSub(MacroAssembler* masm, Label* non_smi, Label* slow);
++ void GenerateSmiCodeBitNot(MacroAssembler* masm, Label* slow);
++
++ void GenerateHeapNumberStub(MacroAssembler* masm);
++ void GenerateHeapNumberStubSub(MacroAssembler* masm);
++ void GenerateHeapNumberStubBitNot(MacroAssembler* masm);
++ void GenerateHeapNumberCodeSub(MacroAssembler* masm, Label* slow);
++ void GenerateHeapNumberCodeBitNot(MacroAssembler* masm, Label* slow);
++
++ void GenerateGenericStub(MacroAssembler* masm);
++ void GenerateGenericStubSub(MacroAssembler* masm);
++ void GenerateGenericStubBitNot(MacroAssembler* masm);
++ void GenerateGenericCodeFallback(MacroAssembler* masm);
++
++ virtual int GetCodeKind() { return Code::UNARY_OP_IC; }
++
++ virtual InlineCacheState GetICState() {
++ return UnaryOpIC::ToState(operand_type_);
++ }
++
++ virtual void FinishCode(Handle<Code> code) {
++ code->set_unary_op_type(operand_type_);
++ }
++};
++
++
++class BinaryOpStub: public CodeStub {
++ public:
++ BinaryOpStub(Token::Value op, OverwriteMode mode)
++ : op_(op),
++ mode_(mode),
++ operands_type_(BinaryOpIC::UNINITIALIZED),
++ result_type_(BinaryOpIC::UNINITIALIZED) {
++ ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
++ }
++
++ BinaryOpStub(
++ int key,
++ BinaryOpIC::TypeInfo operands_type,
++ BinaryOpIC::TypeInfo result_type = BinaryOpIC::UNINITIALIZED)
++ : op_(OpBits::decode(key)),
++ mode_(ModeBits::decode(key)),
++ operands_type_(operands_type),
++ result_type_(result_type) { }
++
++ private:
++ enum SmiCodeGenerateHeapNumberResults {
++ ALLOW_HEAPNUMBER_RESULTS,
++ NO_HEAPNUMBER_RESULTS
++ };
++
++ Token::Value op_;
++ OverwriteMode mode_;
++
++ // Operand type information determined at runtime.
++ BinaryOpIC::TypeInfo operands_type_;
++ BinaryOpIC::TypeInfo result_type_;
++
++ virtual void PrintName(StringStream* stream);
++
++ // Minor key encoding in 16 bits RRRTTTVOOOOOOOMM.
++ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
++ class OpBits: public BitField<Token::Value, 2, 7> {};
++ class OperandTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 10, 3> {};
++ class ResultTypeInfoBits: public BitField<BinaryOpIC::TypeInfo, 13, 3> {};
++
++ Major MajorKey() { return BinaryOp; }
++ int MinorKey() {
++ return OpBits::encode(op_)
++ | ModeBits::encode(mode_)
++ | OperandTypeInfoBits::encode(operands_type_)
++ | ResultTypeInfoBits::encode(result_type_);
++ }
++
++ void Generate(MacroAssembler* masm);
++ void GenerateGeneric(MacroAssembler* masm);
++ void GenerateSmiSmiOperation(MacroAssembler* masm);
++ void GenerateFPOperation(MacroAssembler* masm,
++ bool smi_operands,
++ Label* not_numbers,
++ Label* gc_required);
++ void GenerateSmiCode(MacroAssembler* masm,
++ Label* use_runtime,
++ Label* gc_required,
++ SmiCodeGenerateHeapNumberResults heapnumber_results);
++ void GenerateLoadArguments(MacroAssembler* masm);
++ void GenerateReturn(MacroAssembler* masm);
++ void GenerateUninitializedStub(MacroAssembler* masm);
++ void GenerateSmiStub(MacroAssembler* masm);
++ void GenerateInt32Stub(MacroAssembler* masm);
++ void GenerateHeapNumberStub(MacroAssembler* masm);
++ void GenerateOddballStub(MacroAssembler* masm);
++ void GenerateStringStub(MacroAssembler* masm);
++ void GenerateBothStringStub(MacroAssembler* masm);
++ void GenerateGenericStub(MacroAssembler* masm);
++ void GenerateAddStrings(MacroAssembler* masm);
++ void GenerateCallRuntime(MacroAssembler* masm);
++
++ void GenerateHeapResultAllocation(MacroAssembler* masm,
++ Register result,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required);
++ void GenerateRegisterArgsPush(MacroAssembler* masm);
++ void GenerateTypeTransition(MacroAssembler* masm);
++ void GenerateTypeTransitionWithSavedArgs(MacroAssembler* masm);
++
++ virtual int GetCodeKind() { return Code::BINARY_OP_IC; }
++
++ virtual InlineCacheState GetICState() {
++ return BinaryOpIC::ToState(operands_type_);
++ }
++
++ virtual void FinishCode(Handle<Code> code) {
++ code->set_binary_op_type(operands_type_);
++ code->set_binary_op_result_type(result_type_);
++ }
++
++ friend class CodeGenerator;
++};
++
++
++class StringHelper : public AllStatic {
++ public:
++ // Generate code for copying characters using a simple loop. This should only
++ // be used in places where the number of characters is small and the
++ // additional setup and checking in GenerateCopyCharactersLong adds too much
++ // overhead. Copying of overlapping regions is not supported.
++ // Dest register ends at the position after the last character written.
++ static void GenerateCopyCharacters(MacroAssembler* masm,
++ Register dest,
++ Register src,
++ Register count,
++ Register scratch,
++ bool ascii);
++
++ // Generate code for copying a large number of characters. This function
++ // is allowed to spend extra time setting up conditions to make copying
++ // faster. Copying of overlapping regions is not supported.
++ // Dest register ends at the position after the last character written.
++ static void GenerateCopyCharactersLong(MacroAssembler* masm,
++ Register dest,
++ Register src,
++ Register count,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Register scratch5,
++ int flags);
++
++
++ // Probe the symbol table for a two character string. If the string is
++ // not found by probing a jump to the label not_found is performed. This jump
++ // does not guarantee that the string is not in the symbol table. If the
++ // string is found the code falls through with the string in register r0.
++ // Contents of both c1 and c2 registers are modified. At the exit c1 is
++ // guaranteed to contain halfword with low and high bytes equal to
++ // initial contents of c1 and c2 respectively.
++ static void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
++ Register c1,
++ Register c2,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Register scratch5,
++ Label* not_found);
++
++ // Generate string hash.
++ static void GenerateHashInit(MacroAssembler* masm,
++ Register hash,
++ Register character,
++ Register scratch);
++
++ static void GenerateHashAddCharacter(MacroAssembler* masm,
++ Register hash,
++ Register character,
++ Register scratch);
++
++ static void GenerateHashGetHash(MacroAssembler* masm,
++ Register hash,
++ Register scratch);
++
++ private:
++ DISALLOW_IMPLICIT_CONSTRUCTORS(StringHelper);
++};
++
++
++// Flag that indicates how to generate code for the stub StringAddStub.
++enum StringAddFlags {
++ NO_STRING_ADD_FLAGS = 0,
++ // Omit left string check in stub (left is definitely a string).
++ NO_STRING_CHECK_LEFT_IN_STUB = 1 << 0,
++ // Omit right string check in stub (right is definitely a string).
++ NO_STRING_CHECK_RIGHT_IN_STUB = 1 << 1,
++ // Omit both string checks in stub.
++ NO_STRING_CHECK_IN_STUB =
++ NO_STRING_CHECK_LEFT_IN_STUB | NO_STRING_CHECK_RIGHT_IN_STUB
++};
++
++
++class StringAddStub: public CodeStub {
++ public:
++ explicit StringAddStub(StringAddFlags flags) : flags_(flags) {}
++
++ private:
++ Major MajorKey() { return StringAdd; }
++ int MinorKey() { return flags_; }
++
++ void Generate(MacroAssembler* masm);
++
++ void GenerateConvertArgument(MacroAssembler* masm,
++ int stack_offset,
++ Register arg,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Label* slow);
++
++ const StringAddFlags flags_;
++};
++
++
++class SubStringStub: public CodeStub {
++ public:
++ SubStringStub() {}
++
++ private:
++ Major MajorKey() { return SubString; }
++ int MinorKey() { return 0; }
++
++ void Generate(MacroAssembler* masm);
++};
++
++
++
++class StringCompareStub: public CodeStub {
++ public:
++ StringCompareStub() { }
++
++ // Compares two flat ASCII strings and returns result in r0.
++ static void GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
++ Register left,
++ Register right,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3);
++
++ // Compares two flat ASCII strings for equality and returns result
++ // in r0.
++ static void GenerateFlatAsciiStringEquals(MacroAssembler* masm,
++ Register left,
++ Register right,
++ Register scratch1,
++ Register scratch2);
++
++ private:
++ virtual Major MajorKey() { return StringCompare; }
++ virtual int MinorKey() { return 0; }
++ virtual void Generate(MacroAssembler* masm);
++
++ static void GenerateAsciiCharsCompareLoop(MacroAssembler* masm,
++ Register left,
++ Register right,
++ Register length,
++ Register scratch1,
++ Label* chars_not_equal);
++};
++
++
++class NumberToStringStub: public CodeStub {
++ public:
++ NumberToStringStub() { }
++
++ // Generate code to do a lookup in the number string cache. If the number in
++ // the register object is found in the cache the generated code falls through
++ // with the result in the result register. The object and the result register
++ // can be the same. If the number is not found in the cache the code jumps to
++ // the label not_found with only the content of register object unchanged.
++ static void GenerateLookupNumberStringCache(MacroAssembler* masm,
++ Register object,
++ Register result,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ bool object_is_smi,
++ Label* not_found);
++
++ private:
++ Major MajorKey() { return NumberToString; }
++ int MinorKey() { return 0; }
++
++ void Generate(MacroAssembler* masm);
++};
++
++
++class RecordWriteStub: public CodeStub {
++ public:
++ RecordWriteStub(Register object,
++ Register value,
++ Register address,
++ RememberedSetAction remembered_set_action,
++ SaveFPRegsMode fp_mode)
++ : object_(object),
++ value_(value),
++ address_(address),
++ remembered_set_action_(remembered_set_action),
++ save_fp_regs_mode_(fp_mode),
++ regs_(object, // An input reg.
++ address, // An input reg.
++ value) { // One scratch reg.
++ }
++
++ enum Mode {
++ STORE_BUFFER_ONLY,
++ INCREMENTAL,
++ INCREMENTAL_COMPACTION
++ };
++
++ virtual bool IsPregenerated();
++ static void GenerateFixedRegStubsAheadOfTime();
++ virtual bool SometimesSetsUpAFrame() { return false; }
++
++ static void PatchBranchIntoNop(MacroAssembler* masm, int pos) {
++ masm->instr_at_put(pos, (masm->instr_at(pos) & ~kBOfieldMask) | BT);
++ // roohack ASSERT(Assembler::IsTstImmediate(masm->instr_at(pos)));
++ }
++
++ static void PatchNopIntoBranch(MacroAssembler* masm, int pos) {
++ masm->instr_at_put(pos, (masm->instr_at(pos) & ~kBOfieldMask) | BF);
++ // roohack ASSERT(Assembler::IsBranch(masm->instr_at(pos)));
++ }
++
++ static Mode GetMode(Code* stub) {
++ Instr first_instruction = Assembler::instr_at(stub->instruction_start() +
++ Assembler::kInstrSize);
++ Instr second_instruction = Assembler::instr_at(stub->instruction_start() +
++ (Assembler::kInstrSize*2));
++
++ if (BF == (first_instruction & kBOfieldMask)) {
++ return INCREMENTAL;
++ }
++
++ // roohack ASSERT(Assembler::IsTstImmediate(first_instruction));
++
++ if (BF == (second_instruction & kBOfieldMask)) {
++ return INCREMENTAL_COMPACTION;
++ }
++
++ // roohack ASSERT(Assembler::IsTstImmediate(second_instruction));
++
++ return STORE_BUFFER_ONLY;
++ }
++
++ static void Patch(Code* stub, Mode mode) {
++ MacroAssembler masm(NULL,
++ stub->instruction_start(),
++ stub->instruction_size());
++ switch (mode) {
++ case STORE_BUFFER_ONLY:
++ ASSERT(GetMode(stub) == INCREMENTAL ||
++ GetMode(stub) == INCREMENTAL_COMPACTION);
++
++ PatchBranchIntoNop(&masm, Assembler::kInstrSize);
++ PatchBranchIntoNop(&masm, Assembler::kInstrSize*2);
++ break;
++ case INCREMENTAL:
++ ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
++ PatchNopIntoBranch(&masm, Assembler::kInstrSize);
++ break;
++ case INCREMENTAL_COMPACTION:
++ ASSERT(GetMode(stub) == STORE_BUFFER_ONLY);
++ PatchNopIntoBranch(&masm, Assembler::kInstrSize*2);
++ break;
++ }
++ ASSERT(GetMode(stub) == mode);
++ CPU::FlushICache(stub->instruction_start()+Assembler::kInstrSize,
++ 2 * Assembler::kInstrSize);
++ }
++
++ private:
++ // This is a helper class for freeing up 3 scratch registers. The input is
++ // two registers that must be preserved and one scratch register provided by
++ // the caller.
++ class RegisterAllocation {
++ public:
++ RegisterAllocation(Register object,
++ Register address,
++ Register scratch0)
++ : object_(object),
++ address_(address),
++ scratch0_(scratch0) {
++ ASSERT(!AreAliased(scratch0, object, address, no_reg));
++ scratch1_ = GetRegThatIsNotOneOf(object_, address_, scratch0_);
++ }
++
++ void Save(MacroAssembler* masm) {
++ ASSERT(!AreAliased(object_, address_, scratch1_, scratch0_));
++ // We don't have to save scratch0_ because it was given to us as
++ // a scratch register.
++ masm->push(scratch1_);
++ }
++
++ void Restore(MacroAssembler* masm) {
++ masm->pop(scratch1_);
++ }
++
++ // If we have to call into C then we need to save and restore all caller-
++ // saved registers that were not already preserved. The scratch registers
++ // will be restored by other means so we don't bother pushing them here.
++ void SaveCallerSaveRegisters(MacroAssembler* masm, SaveFPRegsMode mode) {
++ masm->mflr(r0);
++ masm->push(r0);
++ masm->MultiPush(kJSCallerSaved & ~scratch1_.bit());
++ if (mode == kSaveFPRegs) {
++ // Save all volatile VFP registers except d0.
++ const int kNumRegs = DwVfpRegister::kNumVolatileRegisters - 1;
++ masm->subi(sp, sp, Operand(kDoubleSize * kNumRegs));
++ for (int i = kNumRegs; i > 0; i--) {
++ DwVfpRegister reg = DwVfpRegister::from_code(i);
++ masm->stfd(reg, MemOperand(sp, (i - 1) * kDoubleSize));
++ }
++ }
++ }
++
++ inline void RestoreCallerSaveRegisters(MacroAssembler*masm,
++ SaveFPRegsMode mode) {
++ if (mode == kSaveFPRegs) {
++ // Restore all VFP registers except d0.
++ const int kNumRegs = DwVfpRegister::kNumVolatileRegisters - 1;
++ for (int i = kNumRegs; i > 0; i--) {
++ DwVfpRegister reg = DwVfpRegister::from_code(i);
++ masm->lfd(reg, MemOperand(sp, (i - 1) * kDoubleSize));
++ }
++ masm->addi(sp, sp, Operand(kDoubleSize * kNumRegs));
++ }
++ masm->MultiPop(kJSCallerSaved & ~scratch1_.bit());
++ masm->pop(r0);
++ masm->mtlr(r0);
++ }
++
++ inline Register object() { return object_; }
++ inline Register address() { return address_; }
++ inline Register scratch0() { return scratch0_; }
++ inline Register scratch1() { return scratch1_; }
++
++ private:
++ Register object_;
++ Register address_;
++ Register scratch0_;
++ Register scratch1_;
++
++ Register GetRegThatIsNotOneOf(Register r1,
++ Register r2,
++ Register r3) {
++ for (int i = 0; i < Register::kNumAllocatableRegisters; i++) {
++ Register candidate = Register::FromAllocationIndex(i);
++ if (candidate.is(r1)) continue;
++ if (candidate.is(r2)) continue;
++ if (candidate.is(r3)) continue;
++ return candidate;
++ }
++ UNREACHABLE();
++ return no_reg;
++ }
++ friend class RecordWriteStub;
++ };
++
++ enum OnNoNeedToInformIncrementalMarker {
++ kReturnOnNoNeedToInformIncrementalMarker,
++ kUpdateRememberedSetOnNoNeedToInformIncrementalMarker
++ };
++
++ void Generate(MacroAssembler* masm);
++ void GenerateIncremental(MacroAssembler* masm, Mode mode);
++ void CheckNeedsToInformIncrementalMarker(
++ MacroAssembler* masm,
++ OnNoNeedToInformIncrementalMarker on_no_need,
++ Mode mode);
++ void InformIncrementalMarker(MacroAssembler* masm, Mode mode);
++
++ Major MajorKey() { return RecordWrite; }
++
++ int MinorKey() {
++ return ObjectBits::encode(object_.code()) |
++ ValueBits::encode(value_.code()) |
++ AddressBits::encode(address_.code()) |
++ RememberedSetActionBits::encode(remembered_set_action_) |
++ SaveFPRegsModeBits::encode(save_fp_regs_mode_);
++ }
++
++ void Activate(Code* code) {
++ code->GetHeap()->incremental_marking()->ActivateGeneratedStub(code);
++ }
++
++ class ObjectBits: public BitField<int, 0, 5> {};
++ class ValueBits: public BitField<int, 5, 5> {};
++ class AddressBits: public BitField<int, 10, 5> {};
++ class RememberedSetActionBits: public BitField<RememberedSetAction, 15, 1> {};
++ class SaveFPRegsModeBits: public BitField<SaveFPRegsMode, 16, 1> {};
++
++ Register object_;
++ Register value_;
++ Register address_;
++ RememberedSetAction remembered_set_action_;
++ SaveFPRegsMode save_fp_regs_mode_;
++ Label slow_;
++ RegisterAllocation regs_;
++};
++
++
++// Enter C code from generated RegExp code in a way that allows
++// the C code to fix the return address in case of a GC.
++class RegExpCEntryStub: public CodeStub {
++ public:
++ RegExpCEntryStub() {}
++ virtual ~RegExpCEntryStub() {}
++ void Generate(MacroAssembler* masm);
++
++ private:
++ Major MajorKey() { return RegExpCEntry; }
++ int MinorKey() { return 0; }
++
++ bool NeedsImmovableCode() { return true; }
++};
++
++
++// Trampoline stub to call into native code. To call safely into native code
++// in the presence of compacting GC (which can move code objects) we need to
++// keep the code which called into native pinned in the memory. Currently the
++// simplest approach is to generate such stub early enough so it can never be
++// moved by GC
++class DirectCEntryStub: public CodeStub {
++ public:
++ DirectCEntryStub() {}
++ void Generate(MacroAssembler* masm);
++ void GenerateCall(MacroAssembler* masm, ExternalReference function);
++ void GenerateCall(MacroAssembler* masm, Register target);
++
++ private:
++ Major MajorKey() { return DirectCEntry; }
++ int MinorKey() { return 0; }
++
++ bool NeedsImmovableCode() { return true; }
++};
++
++
++class FloatingPointHelper : public AllStatic {
++ public:
++ enum Destination {
++ kFPRegisters,
++ kCoreRegisters
++ };
++
++
++ // Loads smis from r0 and r1 (right and left in binary operations) into
++ // floating point registers. Depending on the destination the values ends up
++ // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is
++ // floating point registers VFP3 must be supported. If core registers are
++ // requested when VFP3 is supported d6 and d7 will be scratched.
++ static void LoadSmis(MacroAssembler* masm,
++ Register scratch1,
++ Register scratch2);
++
++ // Loads objects from r0 and r1 (right and left in binary operations) into
++ // floating point registers. Depending on the destination the values ends up
++ // either d7 and d6 or in r2/r3 and r0/r1 respectively. If the destination is
++ // floating point registers VFP3 must be supported. If core registers are
++ // requested when VFP3 is supported d6 and d7 will still be scratched. If
++ // either r0 or r1 is not a number (not smi and not heap number object) the
++ // not_number label is jumped to with r0 and r1 intact.
++ static void LoadOperands(MacroAssembler* masm,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* not_number);
++
++ // Convert the smi or heap number in object to an int32 using the rules
++ // for ToInt32 as described in ECMAScript 9.5.: the value is truncated
++ // and brought into the range -2^31 .. +2^31 - 1.
++ static void ConvertNumberToInt32(MacroAssembler* masm,
++ Register object,
++ Register dst,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ DwVfpRegister double_scratch,
++ Label* not_int32);
++
++ // Converts the integer (untagged smi) in |src| to a double, storing
++ // the result to |double_dst|
++ static void ConvertIntToDouble(MacroAssembler* masm,
++ Register src,
++ DwVfpRegister double_dst);
++
++ // Converts the unsigned integer (untagged smi) in |src| to
++ // a double, storing the result to |double_dst|
++ static void ConvertUnsignedIntToDouble(MacroAssembler* masm,
++ Register src,
++ DwVfpRegister double_dst);
++
++ // Converts the integer (untagged smi) in |src| to
++ // a float, storing the result in |dst|
++ // Warning: The value in |int_scrach| will be changed in the process!
++ static void ConvertIntToFloat(MacroAssembler* masm,
++ const DwVfpRegister dst,
++ const Register src,
++ const Register int_scratch);
++
++ /*
++ // Converts the double in |double_value| to an integer, storing the
++ // result in |int_dst|.
++ // Warning: The value in |double_value| will be changed in the process!
++ static void ConvertDoubleToInt(MacroAssembler* masm,
++ DwVfpRegister double_value,
++ Register int_dst,
++ Register scratch1,
++ DwVfpRegister double_scratch);
++
++ // Converts the double in |double_value| to an unsigned integer,
++ // storing the result in |int_dst|.
++ // Warning: The value in |double_value| will be changed in the process!
++ static void ConvertDoubleToUnsignedInt(MacroAssembler* masm,
++ DwVfpRegister double_value,
++ Register int_dst,
++ Register scratch1,
++ DwVfpRegister double_scratch);
++ */
++
++ // Load the number from object into double_dst in the double format.
++ // Control will jump to not_int32 if the value cannot be exactly represented
++ // by a 32-bit integer.
++ // Floating point value in the 32-bit integer range that are not exact integer
++ // won't be loaded.
++ static void LoadNumberAsInt32Double(MacroAssembler* masm,
++ Register object,
++ DwVfpRegister double_dst,
++ DwVfpRegister double_scratch,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* not_int32);
++
++ // Loads the number from object into dst as a 32-bit integer.
++ // Control will jump to not_int32 if the object cannot be exactly represented
++ // by a 32-bit integer.
++ // Floating point value in the 32-bit integer range that are not exact integer
++ // won't be converted.
++ // scratch3 is not used when VFP3 is supported.
++ static void LoadNumberAsInt32(MacroAssembler* masm,
++ Register object,
++ Register dst,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ DwVfpRegister double_scratch0,
++ DwVfpRegister double_scratch1,
++ Label* not_int32);
++
++ // Generate non VFP3 code to check if a double can be exactly represented by a
++ // 32-bit integer. This does not check for 0 or -0, which need
++ // to be checked for separately.
++ // Control jumps to not_int32 if the value is not a 32-bit integer, and falls
++ // through otherwise.
++ // src1 and src2 will be cloberred.
++ //
++ // Expected input:
++ // - src1: higher (exponent) part of the double value.
++ // - src2: lower (mantissa) part of the double value.
++ // Output status:
++ // - dst: 32 higher bits of the mantissa. (mantissa[51:20])
++ // - src2: contains 1.
++ // - other registers are clobbered.
++ static void DoubleIs32BitInteger(MacroAssembler* masm,
++ Register src1,
++ Register src2,
++ Register dst,
++ Register scratch,
++ Label* not_int32);
++
++ // Generates code to call a C function to do a double operation using core
++ // registers. (Used when VFP3 is not supported.)
++ // This code never falls through, but returns with a heap number containing
++ // the result in r0.
++ // Register heapnumber_result must be a heap number in which the
++ // result of the operation will be stored.
++ // Requires the following layout on entry:
++ // r0: Left value (least significant part of mantissa).
++ // r1: Left value (sign, exponent, top of mantissa).
++ // r2: Right value (least significant part of mantissa).
++ // r3: Right value (sign, exponent, top of mantissa).
++ static void CallCCodeForDoubleOperation(MacroAssembler* masm,
++ Token::Value op,
++ Register heap_number_result,
++ Register scratch);
++
++ private:
++ static void LoadNumber(MacroAssembler* masm,
++ Register object,
++ DwVfpRegister dst,
++ Register heap_number_map,
++ Register scratch1,
++ Register scratch2,
++ Label* not_number);
++};
++
++
++class StringDictionaryLookupStub: public CodeStub {
++ public:
++ enum LookupMode { POSITIVE_LOOKUP, NEGATIVE_LOOKUP };
++
++ explicit StringDictionaryLookupStub(LookupMode mode) : mode_(mode) { }
++
++ void Generate(MacroAssembler* masm);
++
++ static void GenerateNegativeLookup(MacroAssembler* masm,
++ Label* miss,
++ Label* done,
++ Register receiver,
++ Register properties,
++ Handle<String> name,
++ Register scratch0);
++
++ static void GeneratePositiveLookup(MacroAssembler* masm,
++ Label* miss,
++ Label* done,
++ Register elements,
++ Register name,
++ Register r0,
++ Register r1);
++
++ virtual bool SometimesSetsUpAFrame() { return false; }
++
++ private:
++ static const int kInlinedProbes = 4;
++ static const int kTotalProbes = 20;
++
++ static const int kCapacityOffset =
++ StringDictionary::kHeaderSize +
++ StringDictionary::kCapacityIndex * kPointerSize;
++
++ static const int kElementsStartOffset =
++ StringDictionary::kHeaderSize +
++ StringDictionary::kElementsStartIndex * kPointerSize;
++
++ Major MajorKey() { return StringDictionaryLookup; }
++
++ int MinorKey() {
++ return LookupModeBits::encode(mode_);
++ }
++
++ class LookupModeBits: public BitField<LookupMode, 0, 1> {};
++
++ LookupMode mode_;
++};
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_CODE_STUBS_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/constants-ppc.cc.ppc v8-3.14.5.10/src/ppc/constants-ppc.cc
+--- v8-3.14.5.10/src/ppc/constants-ppc.cc.ppc 2016-06-07 14:15:45.990393008 -0400
++++ v8-3.14.5.10/src/ppc/constants-ppc.cc 2016-06-07 14:15:45.990393008 -0400
+@@ -0,0 +1,122 @@
++// Copyright 2009 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "constants-ppc.h"
++
++
++namespace v8 {
++namespace internal {
++
++// These register names are defined in a way to match the native disassembler
++// formatting. See for example the command "objdump -d <binary file>".
++const char* Registers::names_[kNumRegisters] = {
++ "r0", "sp", "r2", "r3", "r4",
"r5", "r6", "r7",
++ "r8", "r9", "r10", "r11", "r12",
"r13", "r14", "r15",
++ "r16", "r17", "r18", "r19", "r20",
"r21", "r22",
++ "r23", "r24", "r25", "r26", "r27",
"r28", "r29",
++ "r30", "fp"
++};
++
++
++// List of alias names which can be used when referring to PPC registers.
++const Registers::RegisterAlias Registers::aliases_[] = {
++ {10, "sl"},
++ {11, "r11"},
++ {12, "r12"},
++ {13, "r13"},
++ {14, "r14"},
++ {15, "r15"},
++ {kNoRegister, NULL}
++};
++
++
++const char* Registers::Name(int reg) {
++ const char* result;
++ if ((0 <= reg) && (reg < kNumRegisters)) {
++ result = names_[reg];
++ } else {
++ result = "noreg";
++ }
++ return result;
++}
++
++// Power
++const char* FPRegisters::names_[kNumFPRegisters] = {
++ "d0", "d1", "d2", "d3", "d4",
"d5", "d6", "d7",
++ "d8", "d9", "d10", "d11", "d12",
"d13", "d14", "d15",
++ "d16", "d17", "d18", "d19", "d20",
"d21", "d22", "d23",
++ "d24", "d25", "d26", "d27", "d28",
"d29", "d30", "d31"
++};
++const char* FPRegisters::Name(int reg) {
++ ASSERT((0 <= reg) && (reg < kNumFPRegisters));
++ return names_[reg];
++}
++int FPRegisters::Number(const char* name) {
++ for (int i = 0; i < kNumFPRegisters; i++) {
++ if (strcmp(names_[i], name) == 0) {
++ return i;
++ }
++ }
++
++ // No register with the requested name found.
++ return kNoRegister;
++}
++
++// end of Power
++
++int Registers::Number(const char* name) {
++ // Look through the canonical names.
++ for (int i = 0; i < kNumRegisters; i++) {
++ if (strcmp(names_[i], name) == 0) {
++ return i;
++ }
++ }
++
++ // Look through the alias names.
++ int i = 0;
++ while (aliases_[i].reg != kNoRegister) {
++ if (strcmp(aliases_[i].name, name) == 0) {
++ return aliases_[i].reg;
++ }
++ i++;
++ }
++
++ // No register with the requested name found.
++ return kNoRegister;
++}
++
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/constants-ppc.h.ppc v8-3.14.5.10/src/ppc/constants-ppc.h
+--- v8-3.14.5.10/src/ppc/constants-ppc.h.ppc 2016-06-07 14:15:45.990393008 -0400
++++ v8-3.14.5.10/src/ppc/constants-ppc.h 2016-06-07 14:15:45.990393008 -0400
+@@ -0,0 +1,637 @@
++// Copyright 2011 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_CONSTANTS_PPC_H_
++#define V8_PPC_CONSTANTS_PPC_H_
++
++namespace v8 {
++namespace internal {
++
++// Number of registers
++const int kNumRegisters = 32;
++
++// FP support.
++const int kNumFPDoubleRegisters = 32;
++const int kNumFPRegisters = kNumFPDoubleRegisters;
++
++const int kNoRegister = -1;
++
++// For FlushICache
++// This constant will be different for other versions of PowerPC
++// It must be a power of 2
++const unsigned int kCacheLineSizeLog2 = 7;
++const unsigned int kCacheLineSize = (1 << kCacheLineSizeLog2);
++
++// sign-extend the least significant 16-bit of value <imm>
++#define SIGN_EXT_IMM16(imm) ((static_cast<int>(imm) << 16) >> 16)
++
++// -----------------------------------------------------------------------------
++// Conditions.
++
++// Defines constants and accessor classes to assemble, disassemble and
++// simulate PPC instructions.
++//
++// Section references in the code refer to the "PowerPC Microprocessor
++// Family: The Programmer.s Reference Guide" from 10/95
++//
https://www-01.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF7...
++//
++
++// Constants for specific fields are defined in their respective named enums.
++// General constants are in an anonymous enum in class Instr.
++enum Condition {
++ kNoCondition = -1,
++ eq = 0, // Equal.
++ ne = 1, // Not equal.
++ ge = 2, // Greater or equal.
++ lt = 3, // Less than.
++ gt = 4, // Greater than.
++ le = 5, // Less then or equal
++ unordered = 6, // Floating-point unordered
++ ordered = 7,
++ overflow = 8, // Summary overflow
++ nooverflow = 9,
++ al = 10 // Always.
++};
++
++
++inline Condition NegateCondition(Condition cond) {
++ ASSERT(cond != al);
++ return static_cast<Condition>(cond ^ ne);
++}
++
++
++// Corresponds to transposing the operands of a comparison.
++inline Condition ReverseCondition(Condition cond) {
++ switch (cond) {
++ case lt:
++ return gt;
++ case gt:
++ return lt;
++ case ge:
++ return le;
++ case le:
++ return ge;
++ default:
++ return cond;
++ };
++}
++
++// -----------------------------------------------------------------------------
++// Instructions encoding.
++
++// Instr is merely used by the Assembler to distinguish 32bit integers
++// representing instructions from usual 32 bit values.
++// Instruction objects are pointers to 32bit values, and provide methods to
++// access the various ISA fields.
++typedef int32_t Instr;
++
++// Opcodes as defined in section 4.2 table 34 (32bit PowerPC)
++enum Opcode {
++ TWI = 3 << 26, // Trap Word Immediate
++ MULLI = 7 << 26, // Multiply Low Immediate
++ SUBFIC = 8 << 26, // Subtract from Immediate Carrying
++ CMPLI = 10 << 26, // Compare Logical Immediate
++ CMPI = 11 << 26, // Compare Immediate
++ ADDIC = 12 << 26, // Add Immediate Carrying
++ ADDICx = 13 << 26, // Add Immediate Carrying and Record
++ ADDI = 14 << 26, // Add Immediate
++ ADDIS = 15 << 26, // Add Immediate Shifted
++ BCX = 16 << 26, // Branch Conditional
++ SC = 17 << 26, // System Call
++ BX = 18 << 26, // Branch
++ EXT1 = 19 << 26, // Extended code set 1
++ RLWIMIX = 20 << 26, // Rotate Left Word Immediate then Mask Insert
++ RLWINMX = 21 << 26, // Rotate Left Word Immediate then AND with Mask
++ RLWNMX = 23 << 26, // Rotate Left then AND with Mask
++ ORI = 24 << 26, // OR Immediate
++ ORIS = 25 << 26, // OR Immediate Shifted
++ XORI = 26 << 26, // XOR Immediate
++ XORIS = 27 << 26, // XOR Immediate Shifted
++ ANDIx = 28 << 26, // AND Immediate
++ ANDISx = 29 << 26, // AND Immediate Shifted
++ EXT5 = 30 << 26, // Extended code set 5 - 64bit only
++ EXT2 = 31 << 26, // Extended code set 2
++ LWZ = 32 << 26, // Load Word and Zero
++ LWZU = 33 << 26, // Load Word with Zero Update
++ LBZ = 34 << 26, // Load Byte and Zero
++ LBZU = 35 << 26, // Load Byte and Zero with Update
++ STW = 36 << 26, // Store
++ STWU = 37 << 26, // Store Word with Update
++ STB = 38 << 26, // Store Byte
++ STBU = 39 << 26, // Store Byte with Update
++ LHZ = 40 << 26, // Load Half and Zero
++ LHZU = 41 << 26, // Load Half and Zero with Update
++ LHA = 42 << 26, // Load Half Algebraic
++ LHAU = 43 << 26, // Load Half Algebraic with Update
++ STH = 44 << 26, // Store Half
++ STHU = 45 << 26, // Store Half with Update
++ LMW = 46 << 26, // Load Multiple Word
++ STMW = 47 << 26, // Store Multiple Word
++ LFS = 48 << 26, // Load Floating-Point Single
++ LFSU = 49 << 26, // Load Floating-Point Single with Update
++ LFD = 50 << 26, // Load Floating-Point Double
++ LFDU = 51 << 26, // Load Floating-Point Double with Update
++ STFS = 52 << 26, // Store Floating-Point Single
++ STFSU = 53 << 26, // Store Floating-Point Single with Update
++ STFD = 54 << 26, // Store Floating-Point Double
++ STFDU = 55 << 26, // Store Floating-Point Double with Update
++ LD = 58 << 26, // Load Double Word
++ EXT3 = 59 << 26, // Extended code set 3
++ STD = 62 << 26, // Store Double Word (optionally with Update)
++ EXT4 = 63 << 26 // Extended code set 4
++};
++
++// Bits 10-1
++enum OpcodeExt1 {
++ MCRF = 0 << 1, // Move Condition Register Field
++ BCLRX = 16 << 1, // Branch Conditional Link Register
++ CRNOR = 33 << 1, // Condition Register NOR)
++ RFI = 50 << 1, // Return from Interrupt
++ CRANDC = 129 << 1, // Condition Register AND with Complement
++ ISYNC = 150 << 1, // Instruction Synchronize
++ CRXOR = 193 << 1, // Condition Register XOR
++ CRNAND = 225 << 1, // Condition Register NAND
++ CRAND = 257 << 1, // Condition Register AND
++ CREQV = 289 << 1, // Condition Register Equivalent
++ CRORC = 417 << 1, // Condition Register OR with Complement
++ CROR = 449 << 1, // Condition Register OR
++ BCCTRX = 528 << 1 // Branch Conditional to Count Register
++};
++
++// Bits 9-1 or 10-1
++enum OpcodeExt2 {
++ CMP = 0 << 1,
++ TW = 4 << 1,
++ SUBFCX = 8 << 1,
++ ADDCX = 10 << 1,
++ MULHWUX = 11 << 1,
++ MFCR = 19 << 1,
++ LWARX = 20 << 1,
++ LDX = 21 << 1,
++ LWZX = 23 << 1, // load word zero w/ x-form
++ SLWX = 24 << 1,
++ CNTLZWX = 26 << 1,
++ SLDX = 27 << 1,
++ ANDX = 28 << 1,
++ CMPL = 32 << 1,
++ SUBFX = 40 << 1,
++ LDUX = 53 << 1,
++ DCBST = 54 << 1,
++ LWZUX = 55 << 1, // load word zero w/ update x-form
++ CNTLZDX = 58 << 1,
++ ANDCX = 60 << 1,
++ MULHWX = 75 << 1,
++ DCBF = 86 << 1,
++ LBZX = 87 << 1, // load byte zero w/ x-form
++ NEGX = 104 << 1,
++ LBZUX = 119 << 1, // load byte zero w/ update x-form
++ NORX = 124 << 1,
++ SUBFEX = 136 << 1,
++ ADDEX = 138 << 1,
++ STDX = 149 << 1,
++ STWX = 151 << 1, // store word w/ x-form
++ STDUX = 181 << 1,
++ STWUX = 183 << 1, // store word w/ update x-form
++/*
++ MTCRF
++ MTMSR
++ STWCXx
++ SUBFZEX
++*/
++ ADDZEX = 202 << 1, // Add to Zero Extended
++/*
++ MTSR
++*/
++ STBX = 215 << 1, // store byte w/ x-form
++ MULLD = 233 << 1, // Multiply Low Double Word
++ MULLW = 235 << 1, // Multiply Low Word
++ STBUX = 247 << 1, // store byte w/ update x-form
++ ADDX = 266 << 1, // Add
++ LHZX = 279 << 1, // load half-word zero w/ x-form
++ LHZUX = 311 << 1, // load half-word zero w/ update x-form
++ LHAX =343 << 1, // load half-word algebraic w/ x-form
++ LHAUX = 375 << 1, // load half-word algebraic w/ update x-form
++ XORX = 316 << 1, // Exclusive OR
++ MFSPR = 339 <<1, // Move from Special-Purpose-Register
++ STHX = 407 << 1, // store half-word w/ x-form
++ STHUX = 439 << 1, // store half-word w/ update x-form
++ ORX = 444 << 1, // Or
++ MTSPR = 467 <<1, // Move to Special-Purpose-Register
++ DIVD = 489 << 1, // Divide Double Word
++ DIVW = 491 << 1, // Divide Word
++
++ // Below represent bits 10-1 (any value >= 512)
++ LFSX = 535 << 1, // load float-single w/ x-form
++ SRWX = 536 << 1, // Shift Right Word
++ SRDX = 539 << 1, // Shift Right Double Word
++ LFSUX = 567 << 1, // load float-single w/ update x-form
++ SYNC = 598 << 1, // Synchronize
++ LFDX = 599 << 1, // load float-double w/ x-form
++ LFDUX = 631 << 1, // load float-double w/ update X-form
++ STFSX = 663 << 1, // store float-single w/ x-form
++ STFSUX = 695 << 1, // store float-single w/ update x-form
++ STFDX = 727 << 1, // store float-double w/ x-form
++ STFDUX = 759 << 1, // store float-double w/ update x-form
++ SRAW = 792 << 1, // Shift Right Algebraic Word
++ SRAD = 794 << 1, // Shift Right Algebraic Double Word
++ SRAWIX = 824 << 1, // Shift Right Algebraic Word Immediate
++ SRADIX = 413 << 2, // Shift Right Algebraic Double Word Immediate
++ EXTSH = 922 << 1, // Extend Sign Halfword
++ EXTSB = 954 << 1, // Extend Sign Byte
++ ICBI = 982 << 1, // Instruction Cache Block Invalidate
++ EXTSW = 986 << 1 // Extend Sign Word
++};
++
++// Some use Bits 10-1 and other only 5-1 for the opcode
++enum OpcodeExt4 {
++ // Bits 5-1
++ FDIV = 18 << 1, // Floating Divide
++ FSUB = 20 << 1, // Floating Subtract
++ FADD = 21 << 1, // Floating Add
++ FSQRT = 22 << 1, // Floating Square Root
++ FSEL = 23 << 1, // Floating Select
++ FMUL = 25 << 1, // Floating Multiply
++
++ // Bits 10-1
++ FCMPU = 0 << 1, // Floating Compare Unordered
++ FRSP = 12 << 1, // Floating-Point Rounding
++ FCTIW = 14 << 1, // Floating Convert to Integer Word X-form
++ FCTIWZ = 15 << 1, // Floating Convert to Integer Word with Round to Zero
++ FNEG = 40 << 1, // Floating Negate
++ MCRFS = 64 << 1, // Move to Condition Register from FPSCR
++ FMR = 72 << 1, // Floating Move Register
++ MTFSFI = 134 << 1, // Move to FPSCR Field Immediate
++ FABS = 264 << 1, // Floating Absolute Value
++ FRIM = 488 << 1, // Floating Round to Integer Minus
++ MFFS = 583 << 1, // move from FPSCR x-form
++ MTFSF = 711 << 1, // move to FPSCR fields XFL-form
++ FCFID = 846 << 1, // Floating convert from integer doubleword
++ FCTID = 814 << 1, // Floating convert from integer doubleword
++ FCTIDZ = 815 << 1 // Floating convert from integer doubleword
++};
++
++// Bits 4-2
++enum OpcodeExt5 {
++ RLDICL = 0 << 2, // Rotate Left Double Word Immediate then Clear Left
++ RLDICR = 1 << 2, // Rotate Left Double Word Immediate then Clear Right
++ RLDIC = 2 << 2, // Rotate Left Double Word Immediate then Clear
++ RLDIMI = 3 << 2 // Rotate Left Double Word Immediate then Mask Insert
++};
++
++// Instruction encoding bits and masks.
++enum {
++ // Instruction encoding bit
++ B1 = 1 << 1,
++ B4 = 1 << 4,
++ B5 = 1 << 5,
++ B7 = 1 << 7,
++ B8 = 1 << 8,
++ B9 = 1 << 9,
++ B12 = 1 << 12,
++ B18 = 1 << 18,
++ B19 = 1 << 19,
++ B20 = 1 << 20,
++ B22 = 1 << 22,
++ B23 = 1 << 23,
++ B24 = 1 << 24,
++ B25 = 1 << 25,
++ B26 = 1 << 26,
++ B27 = 1 << 27,
++ B28 = 1 << 28,
++
++ B6 = 1 << 6,
++ B10 = 1 << 10,
++ B11 = 1 << 11,
++ B16 = 1 << 16,
++ B17 = 1 << 17,
++ B21 = 1 << 21,
++
++ // Instruction bit masks
++ kCondMask = 0x1F << 21,
++ kOff12Mask = (1 << 12) - 1,
++ kImm24Mask = (1 << 24) - 1,
++ kOff16Mask = (1 << 16) - 1,
++ kImm16Mask = (1 << 16) - 1,
++ kImm26Mask = (1 << 26) - 1,
++ kBOfieldMask = 0x1f << 21,
++ kOpcodeMask = 0x3f << 26,
++ kExt2OpcodeMask = 0x1f << 1,
++ kExt5OpcodeMask = 0x3 << 2,
++ kBOMask = 0x1f << 21,
++ kBIMask = 0x1F << 16,
++ kBDMask = 0x14 << 2,
++ kAAMask = 0x01 << 1,
++ kLKMask = 0x01,
++ kRCMask = 0x01,
++ kTOMask = 0x1f << 21
++};
++
++// the following is to differentiate different faked opcodes for
++// the BOGUS PPC instruction we invented (when bit 25 is 0) or to mark
++// different stub code (when bit 25 is 1)
++// - use primary opcode 1 for undefined instruction
++// - use bit 25 to indicate whether the opcode is for fake-arm
++// instr or stub-marker
++// - use the least significant 6-bit to indicate FAKE_OPCODE_T or
++// MARKER_T
++#define FAKE_OPCODE 1 << 26
++#define MARKER_SUBOPCODE_BIT 25
++#define MARKER_SUBOPCODE 1 << MARKER_SUBOPCODE_BIT
++#define FAKER_SUBOPCODE 0 << MARKER_SUBOPCODE_BIT
++
++enum FAKE_OPCODE_T {
++ fBKPT = 14,
++ fLastFaker // can't be more than 128 (2^^7)
++};
++#define FAKE_OPCODE_HIGH_BIT 7 // fake opcode has to fall into bit 0~7
++#define F_NEXT_AVAILABLE_STUB_MARKER 369 // must be less than 2^^9 (512)
++#define STUB_MARKER_HIGH_BIT 9 // stub marker has to fall into bit 0~9
++// -----------------------------------------------------------------------------
++// Addressing modes and instruction variants.
++
++// Overflow Exception
++enum OEBit {
++ SetOE = 1 << 10, // Set overflow exception
++ LeaveOE = 0 << 10 // No overflow exception
++};
++
++// Record bit
++enum RCBit { // Bit 0
++ SetRC = 1, // LT,GT,EQ,SO
++ LeaveRC = 0 // None
++};
++
++// Link bit
++enum LKBit { // Bit 0
++ SetLK = 1, // Load effective address of next instruction
++ LeaveLK = 0 // No action
++};
++
++enum BOfield { // Bits 25-21
++ DCBNZF = 0 << 21, // Decrement CTR; branch if CTR != 0 and condition false
++ DCBEZF = 2 << 21, // Decrement CTR; branch if CTR == 0 and condition false
++ BF = 4 << 21, // Branch if condition false
++ DCBNZT = 8 << 21, // Decrement CTR; branch if CTR != 0 and condition true
++ DCBEZT = 10 << 21, // Decrement CTR; branch if CTR == 0 and condition true
++ BT = 12 << 21, // Branch if condition true
++ DCBNZ = 16 << 21, // Decrement CTR; branch if CTR != 0
++ DCBEZ = 18 << 21, // Decrement CTR; branch if CTR == 0
++ BA = 20 << 21 // Branch always
++};
++
++#ifdef _AIX
++#undef CR_LT
++#undef CR_GT
++#undef CR_EQ
++#undef CR_SO
++#endif
++
++enum CRBit {
++ CR_LT = 0,
++ CR_GT = 1,
++ CR_EQ = 2,
++ CR_SO = 3,
++ CR_FU = 3
++};
++
++#define CRWIDTH 4
++
++// -----------------------------------------------------------------------------
++// Supervisor Call (svc) specific support.
++
++// Special Software Interrupt codes when used in the presence of the PPC
++// simulator.
++// svc (formerly swi) provides a 24bit immediate value. Use bits 22:0 for
++// standard SoftwareInterrupCode. Bit 23 is reserved for the stop feature.
++enum SoftwareInterruptCodes {
++ // transition to C code
++ kCallRtRedirected= 0x10,
++ // break point
++ kBreakpoint= 0x821008, // bits23-0 of 0x7d821008 = twge r2, r2
++ // stop
++ kStopCode = 1 << 23,
++ // info
++ kInfo = 0x9ff808 // bits23-0 of 0x7d9ff808 = twge r31, r31
++};
++const uint32_t kStopCodeMask = kStopCode - 1;
++const uint32_t kMaxStopCode = kStopCode - 1;
++const int32_t kDefaultStopCode = -1;
++
++// FP rounding modes.
++enum VFPRoundingMode {
++ RN = 0, // Round to Nearest.
++ RZ = 1, // Round towards zero.
++ RP = 2, // Round towards Plus Infinity.
++ RM = 3, // Round towards Minus Infinity.
++
++ // Aliases.
++ kRoundToNearest = RN,
++ kRoundToZero = RZ,
++ kRoundToPlusInf = RP,
++ kRoundToMinusInf = RM
++};
++
++const uint32_t kVFPRoundingModeMask = 3;
++
++enum CheckForInexactConversion {
++ kCheckForInexactConversion,
++ kDontCheckForInexactConversion
++};
++
++// -----------------------------------------------------------------------------
++// Specific instructions, constants, and masks.
++// These constants are declared in assembler-arm.cc, as they use named registers
++// and other constants.
++
++
++// add(sp, sp, 4) instruction (aka Pop())
++extern const Instr kPopInstruction;
++
++// str(r, MemOperand(sp, 4, NegPreIndex), al) instruction (aka push(r))
++// register r is not encoded.
++extern const Instr kPushRegPattern;
++
++// ldr(r, MemOperand(sp, 4, PostIndex), al) instruction (aka pop(r))
++// register r is not encoded.
++extern const Instr kPopRegPattern;
++
++// use TWI to indicate redirection call for simulation mode
++const Instr rtCallRedirInstr = TWI;
++
++// -----------------------------------------------------------------------------
++// Instruction abstraction.
++
++// The class Instruction enables access to individual fields defined in the PPC
++// architecture instruction set encoding.
++// Note that the Assembler uses typedef int32_t Instr.
++//
++// Example: Test whether the instruction at ptr does set the condition code
++// bits.
++//
++// bool InstructionSetsConditionCodes(byte* ptr) {
++// Instruction* instr = Instruction::At(ptr);
++// int type = instr->TypeValue();
++// return ((type == 0) || (type == 1)) && instr->HasS();
++// }
++//
++class Instruction {
++ public:
++ enum {
++ kInstrSize = 4,
++ kInstrSizeLog2 = 2,
++ kPCReadOffset = 8
++ };
++
++ // Helper macro to define static accessors.
++ // We use the cast to char* trick to bypass the strict anti-aliasing rules.
++ #define DECLARE_STATIC_TYPED_ACCESSOR(return_type, Name) \
++ static inline return_type Name(Instr instr) { \
++ char* temp = reinterpret_cast<char*>(&instr);
\
++ return reinterpret_cast<Instruction*>(temp)->Name();
\
++ }
++
++ #define DECLARE_STATIC_ACCESSOR(Name) DECLARE_STATIC_TYPED_ACCESSOR(int, Name)
++
++ // Get the raw instruction bits.
++ inline Instr InstructionBits() const {
++ return *reinterpret_cast<const Instr*>(this);
++ }
++
++ // Set the raw instruction bits to value.
++ inline void SetInstructionBits(Instr value) {
++ *reinterpret_cast<Instr*>(this) = value;
++ }
++
++ // Read one particular bit out of the instruction bits.
++ inline int Bit(int nr) const {
++ return (InstructionBits() >> nr) & 1;
++ }
++
++ // Read a bit field's value out of the instruction bits.
++ inline int Bits(int hi, int lo) const {
++ return (InstructionBits() >> lo) & ((2 << (hi - lo)) - 1);
++ }
++
++ // Read a bit field out of the instruction bits.
++ inline int BitField(int hi, int lo) const {
++ return InstructionBits() & (((2 << (hi - lo)) - 1) << lo);
++ }
++
++ // Static support.
++
++ // Read one particular bit out of the instruction bits.
++ static inline int Bit(Instr instr, int nr) {
++ return (instr >> nr) & 1;
++ }
++
++ // Read the value of a bit field out of the instruction bits.
++ static inline int Bits(Instr instr, int hi, int lo) {
++ return (instr >> lo) & ((2 << (hi - lo)) - 1);
++ }
++
++
++ // Read a bit field out of the instruction bits.
++ static inline int BitField(Instr instr, int hi, int lo) {
++ return instr & (((2 << (hi - lo)) - 1) << lo);
++ }
++
++ // PowerPC
++ inline int RSValue() const { return Bits(25, 21); }
++ inline int RTValue() const { return Bits(25, 21); }
++ inline int RAValue() const { return Bits(20, 16); }
++ DECLARE_STATIC_ACCESSOR(RAValue);
++ inline int RBValue() const { return Bits(15, 11); }
++ DECLARE_STATIC_ACCESSOR(RBValue);
++ inline int RCValue() const { return Bits(10, 6); }
++ DECLARE_STATIC_ACCESSOR(RCValue);
++ // end PowerPC
++
++ inline int OpcodeValue() const {
++ return static_cast<Opcode>(Bits(31, 26)); // PowerPC
++ }
++ inline Opcode OpcodeField() const {
++ return static_cast<Opcode>(BitField(24, 21));
++ }
++
++ // Fields used in Software interrupt instructions
++ inline SoftwareInterruptCodes SvcValue() const {
++ return static_cast<SoftwareInterruptCodes>(Bits(23, 0));
++ }
++
++ // Instructions are read of out a code stream. The only way to get a
++ // reference to an instruction is to convert a pointer. There is no way
++ // to allocate or create instances of class Instruction.
++ // Use the At(pc) function to create references to Instruction.
++ static Instruction* At(byte* pc) {
++ return reinterpret_cast<Instruction*>(pc);
++ }
++
++
++ private:
++ // We need to prevent the creation of instances of class Instruction.
++ DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction);
++};
++
++
++// Helper functions for converting between register numbers and names.
++class Registers {
++ public:
++ // Return the name of the register.
++ static const char* Name(int reg);
++
++ // Lookup the register number for the name provided.
++ static int Number(const char* name);
++
++ struct RegisterAlias {
++ int reg;
++ const char* name;
++ };
++
++ private:
++ static const char* names_[kNumRegisters];
++ static const RegisterAlias aliases_[];
++};
++
++// Helper functions for converting between FP register numbers and names.
++class FPRegisters {
++ public:
++ // Return the name of the register.
++ static const char* Name(int reg);
++
++ // Lookup the register number for the name provided.
++ static int Number(const char* name);
++
++ private:
++ static const char* names_[kNumFPRegisters];
++};
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_CONSTANTS_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/cpu-ppc.cc.ppc v8-3.14.5.10/src/ppc/cpu-ppc.cc
+--- v8-3.14.5.10/src/ppc/cpu-ppc.cc.ppc 2016-06-07 14:15:45.990393008 -0400
++++ v8-3.14.5.10/src/ppc/cpu-ppc.cc 2016-06-07 14:15:45.990393008 -0400
+@@ -0,0 +1,90 @@
++// Copyright 2006-2009 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// CPU specific code for ppc independent of OS goes here.
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "cpu.h"
++#include "macro-assembler.h"
++#include "simulator.h" // for cache flushing.
++
++namespace v8 {
++namespace internal {
++
++void CPU::SetUp() {
++ CpuFeatures::Probe();
++}
++
++bool CPU::SupportsCrankshaft() {
++ return true;
++}
++
++void CPU::FlushICache(void* buffer, size_t size) {
++ // Nothing to do flushing no instructions.
++ if (size == 0) {
++ return;
++ }
++
++#if defined (USE_SIMULATOR)
++ // Not generating PPC instructions for C-code. This means that we are
++ // building an PPC emulator based target. We should notify the simulator
++ // that the Icache was flushed.
++ // None of this code ends up in the snapshot so there are no issues
++ // around whether or not to generate the code when building snapshots.
++ Simulator::FlushICache(Isolate::Current()->simulator_i_cache(), buffer, size);
++#else
++
++ intptr_t mask = kCacheLineSize - 1;
++ byte *start = reinterpret_cast<byte *>(
++ reinterpret_cast<intptr_t>(buffer) & ~mask);
++ byte *end = static_cast<byte *>(buffer) + size;
++ for (byte *pointer = start; pointer < end; pointer += kCacheLineSize) {
++ __asm__(
++ "dcbf 0, %0 \n" \
++ "sync \n" \
++ "icbi 0, %0 \n" \
++ "isync \n"
++ : /* no output */
++ : "r" (pointer));
++ }
++
++#endif // USE_SIMULATOR
++}
++
++
++void CPU::DebugBreak() {
++ UNIMPLEMENTED(); // Unimplemented on PowerPC
++}
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/debug-ppc.cc.ppc v8-3.14.5.10/src/ppc/debug-ppc.cc
+--- v8-3.14.5.10/src/ppc/debug-ppc.cc.ppc 2016-06-07 14:15:45.991393002 -0400
++++ v8-3.14.5.10/src/ppc/debug-ppc.cc 2016-06-07 14:15:45.990393008 -0400
+@@ -0,0 +1,362 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "codegen.h"
++#include "debug.h"
++
++namespace v8 {
++namespace internal {
++
++#ifdef ENABLE_DEBUGGER_SUPPORT
++bool BreakLocationIterator::IsDebugBreakAtReturn() {
++ return Debug::IsDebugBreakAtReturn(rinfo());
++}
++
++
++void BreakLocationIterator::SetDebugBreakAtReturn() {
++ // Patch the code changing the return from JS function sequence from
++ //
++ // mr sp, fp
++ // lwz fp, 0(sp)
++ // lwz r0, 4(sp)
++ // mtlr r0
++ // addi sp, sp, <delta>
++ // blr
++ //
++ // to a call to the debug break return code.
++ // this uses a FIXED_SEQUENCE to load a 32bit constant
++ //
++ // lis r0, <address hi>
++ // ori r0, r0, <address lo>
++ // mtlr r0
++ // blrl
++ // bkpt
++ //
++ // The 64bit sequence is a bit longer
++ // lis r0, <address bits 63-48>
++ // ori r0, r0, <address bits 47-32>
++ // sldi r0, r0, 32
++ // oris r0, r0, <address bits 31-16>
++ // ori r0, r0, <address bits 15-0>
++ // mtlr r0
++ // blrl
++ // bkpt
++ //
++ CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
++// printf("SetDebugBreakAtReturn: pc=%08x\n", (unsigned
int)rinfo()->pc());
++ patcher.masm()->mov(v8::internal::r0,
++ Operand(reinterpret_cast<intptr_t>(
++ Isolate::Current()->debug()->debug_break_return()->entry())));
++ patcher.masm()->mtlr(v8::internal::r0);
++ patcher.masm()->bclr(BA, SetLK);
++ patcher.masm()->bkpt(0);
++}
++
++
++// Restore the JS frame exit code.
++void BreakLocationIterator::ClearDebugBreakAtReturn() {
++ rinfo()->PatchCode(original_rinfo()->pc(),
++ Assembler::kJSReturnSequenceInstructions);
++}
++
++
++// A debug break in the frame exit code is identified by the JS frame exit code
++// having been patched with a call instruction.
++bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
++ ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
++ return rinfo->IsPatchedReturnSequence();
++}
++
++
++bool BreakLocationIterator::IsDebugBreakAtSlot() {
++ ASSERT(IsDebugBreakSlot());
++ // Check whether the debug break slot instructions have been patched.
++ return rinfo()->IsPatchedDebugBreakSlotSequence();
++}
++
++
++void BreakLocationIterator::SetDebugBreakAtSlot() {
++ ASSERT(IsDebugBreakSlot());
++ // Patch the code changing the debug break slot code from
++ //
++ // ori r3, r3, 0
++ // ori r3, r3, 0
++ // ori r3, r3, 0
++ // ori r3, r3, 0
++ // ori r3, r3, 0
++ //
++ // to a call to the debug break code, using a FIXED_SEQUENCE.
++ //
++ // lis r0, <address hi>
++ // ori r0, r0, <address lo>
++ // mtlr r0
++ // blrl
++ //
++ // The 64bit sequence is +3 instructions longer for the load
++ //
++ CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
++// printf("SetDebugBreakAtSlot: pc=%08x\n", (unsigned int)rinfo()->pc());
++ patcher.masm()->mov(v8::internal::r0,
++ Operand(reinterpret_cast<intptr_t>(
++
Isolate::Current()->debug()->debug_break_slot()->entry())));
++ patcher.masm()->mtlr(v8::internal::r0);
++ patcher.masm()->bclr(BA, SetLK);
++}
++
++
++void BreakLocationIterator::ClearDebugBreakAtSlot() {
++ ASSERT(IsDebugBreakSlot());
++ rinfo()->PatchCode(original_rinfo()->pc(),
++ Assembler::kDebugBreakSlotInstructions);
++}
++
++const bool Debug::FramePaddingLayout::kIsSupported = false;
++
++
++#define __ ACCESS_MASM(masm)
++
++
++static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
++ RegList object_regs,
++ RegList non_object_regs) {
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++// printf("Generate_DebugBreakCallHelper\n");
++ // Store the registers containing live values on the expression stack to
++ // make sure that these are correctly updated during GC. Non object values
++ // are stored as a smi causing it to be untouched by GC.
++ ASSERT((object_regs & ~kJSCallerSaved) == 0);
++ ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
++ ASSERT((object_regs & non_object_regs) == 0);
++ if ((object_regs | non_object_regs) != 0) {
++ for (int i = 0; i < kNumJSCallerSaved; i++) {
++ int r = JSCallerSavedCode(i);
++ Register reg = { r };
++ if ((non_object_regs & (1 << r)) != 0) {
++ if (FLAG_debug_code) {
++ __ andis(r0, reg, Operand(0xc000));
++ __ Assert(eq, "Unable to encode value as smi", cr0);
++ }
++ __ SmiTag(reg);
++ }
++ }
++ __ MultiPush(object_regs | non_object_regs);
++ }
++
++#ifdef DEBUG
++ __ RecordComment("// Calling from debug break to runtime - come in -
over");
++#endif
++ __ mov(r3, Operand(0, RelocInfo::NONE)); // no arguments
++ __ mov(r4, Operand(ExternalReference::debug_break(masm->isolate())));
++
++ CEntryStub ceb(1);
++ __ CallStub(&ceb);
++
++ // Restore the register values from the expression stack.
++ if ((object_regs | non_object_regs) != 0) {
++ __ MultiPop(object_regs | non_object_regs);
++ for (int i = 0; i < kNumJSCallerSaved; i++) {
++ int r = JSCallerSavedCode(i);
++ Register reg = { r };
++ if ((non_object_regs & (1 << r)) != 0) {
++ __ SmiUntag(reg);
++ }
++ if (FLAG_debug_code &&
++ (((object_regs |non_object_regs) & (1 << r)) == 0)) {
++ __ mov(reg, Operand(kDebugZapValue));
++ }
++ }
++ }
++
++ // Leave the internal frame.
++ }
++
++ // Now that the break point has been handled, resume normal execution by
++ // jumping to the target address intended by the caller and that was
++ // overwritten by the address of DebugBreakXXX.
++ ExternalReference after_break_target =
++ ExternalReference(Debug_Address::AfterBreakTarget(), masm->isolate());
++ __ mov(ip, Operand(after_break_target));
++ __ LoadP(ip, MemOperand(ip));
++ __ Jump(ip);
++}
++
++
++void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
++ // Calling convention for IC load (from ic-ppc.cc).
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- r3 : receiver
++ // -- [sp] : receiver
++ // -----------------------------------
++ // Registers r3 and r5 contain objects that need to be pushed on the
++ // expression stack of the fake JS frame.
++ Generate_DebugBreakCallHelper(masm, r3.bit() | r5.bit(), 0);
++}
++
++
++void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
++ // Calling convention for IC store (from ic-ppc.cc).
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ // Registers r3, r4, and r5 contain objects that need to be pushed on the
++ // expression stack of the fake JS frame.
++ Generate_DebugBreakCallHelper(masm, r3.bit() | r4.bit() | r5.bit(), 0);
++}
++
++
++void Debug::GenerateKeyedLoadICDebugBreak(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ Generate_DebugBreakCallHelper(masm, r3.bit() | r4.bit(), 0);
++}
++
++
++void Debug::GenerateKeyedStoreICDebugBreak(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ Generate_DebugBreakCallHelper(masm, r3.bit() | r4.bit() | r5.bit(), 0);
++}
++
++
++void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
++ // Calling convention for IC call (from ic-ppc.cc)
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -----------------------------------
++ Generate_DebugBreakCallHelper(masm, r5.bit(), 0);
++}
++
++
++void Debug::GenerateReturnDebugBreak(MacroAssembler* masm) {
++ // In places other than IC call sites it is expected that r3 is TOS which
++ // is an object - this is not generally the case so this should be used with
++ // care.
++ Generate_DebugBreakCallHelper(masm, r3.bit(), 0);
++}
++
++
++void Debug::GenerateCallFunctionStubDebugBreak(MacroAssembler* masm) {
++ // Register state for CallFunctionStub (from code-stubs-ppc.cc).
++ // ----------- S t a t e -------------
++ // -- r4 : function
++ // -----------------------------------
++ Generate_DebugBreakCallHelper(masm, r4.bit(), 0);
++}
++
++
++void Debug::GenerateCallFunctionStubRecordDebugBreak(MacroAssembler* masm) {
++ // Register state for CallFunctionStub (from code-stubs-ppc.cc).
++ // ----------- S t a t e -------------
++ // -- r4 : function
++ // -- r5 : cache cell for call target
++ // -----------------------------------
++ Generate_DebugBreakCallHelper(masm, r4.bit() | r5.bit(), 0);
++}
++
++
++void Debug::GenerateCallConstructStubDebugBreak(MacroAssembler* masm) {
++ // Calling convention for CallConstructStub (from code-stubs-ppc.cc)
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments (not smi)
++ // -- r4 : constructor function
++ // -----------------------------------
++ Generate_DebugBreakCallHelper(masm, r4.bit(), r3.bit());
++}
++
++
++void Debug::GenerateCallConstructStubRecordDebugBreak(MacroAssembler* masm) {
++ // Calling convention for CallConstructStub (from code-stubs-ppc.cc)
++ // ----------- S t a t e -------------
++ // -- r3 : number of arguments (not smi)
++ // -- r4 : constructor function
++ // -- r5 : cache cell for call target
++ // -----------------------------------
++ Generate_DebugBreakCallHelper(masm, r4.bit() | r5.bit(), r3.bit());
++}
++
++
++void Debug::GenerateSlot(MacroAssembler* masm) {
++ // Generate enough nop's to make space for a call instruction. Avoid emitting
++ // the trampoline pool in the debug break slot code.
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm);
++ Label check_codesize;
++ __ bind(&check_codesize);
++ __ RecordDebugBreakSlot();
++ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
++ __ nop(MacroAssembler::DEBUG_BREAK_NOP);
++ }
++ ASSERT_EQ(Assembler::kDebugBreakSlotInstructions,
++ masm->InstructionsGeneratedSince(&check_codesize));
++}
++
++
++void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
++ // In the places where a debug break slot is inserted no registers can contain
++ // object pointers.
++ Generate_DebugBreakCallHelper(masm, 0, 0);
++}
++
++
++void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
++ masm->Abort("LiveEdit frame dropping is not supported on ppc");
++}
++
++
++void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
++ masm->Abort("LiveEdit frame dropping is not supported on ppc");
++}
++
++const bool Debug::kFrameDropperSupported = false;
++
++#undef __
++
++
++
++#endif // ENABLE_DEBUGGER_SUPPORT
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc.ppc
v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc
+--- v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc.ppc 2016-06-07 14:15:45.991393002 -0400
++++ v8-3.14.5.10/src/ppc/deoptimizer-ppc.cc 2016-06-07 14:15:45.991393002 -0400
+@@ -0,0 +1,1259 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#include "codegen.h"
++#include "deoptimizer.h"
++#include "full-codegen.h"
++#include "safepoint-table.h"
++
++namespace v8 {
++namespace internal {
++
++const int Deoptimizer::table_entry_size_ = 20;
++
++
++int Deoptimizer::patch_size() {
++#if V8_TARGET_ARCH_PPC64
++ const int kCallInstructionSizeInWords = 7;
++#else
++ const int kCallInstructionSizeInWords = 4;
++#endif
++ return kCallInstructionSizeInWords * Assembler::kInstrSize;
++}
++
++
++void Deoptimizer::DeoptimizeFunction(JSFunction* function) {
++ HandleScope scope;
++ AssertNoAllocation no_allocation;
++
++ if (!function->IsOptimized()) return;
++
++ // The optimized code is going to be patched, so we cannot use it
++ // any more. Play safe and reset the whole cache.
++ function->shared()->ClearOptimizedCodeMap();
++
++ // Get the optimized code.
++ Code* code = function->code();
++ Address code_start_address = code->instruction_start();
++
++ // Invalidate the relocation information, as it will become invalid by the
++ // code patching below, and is not needed any more.
++ code->InvalidateRelocation();
++
++ // For each LLazyBailout instruction insert a call to the corresponding
++ // deoptimization entry.
++ DeoptimizationInputData* deopt_data =
++ DeoptimizationInputData::cast(code->deoptimization_data());
++#ifdef DEBUG
++ Address prev_call_address = NULL;
++#endif
++ for (int i = 0; i < deopt_data->DeoptCount(); i++) {
++ if (deopt_data->Pc(i)->value() == -1) continue;
++ Address call_address = code_start_address + deopt_data->Pc(i)->value();
++ Address deopt_entry = GetDeoptimizationEntry(i, LAZY);
++ // We need calls to have a predictable size in the unoptimized code, but
++ // this is optimized code, so we don't have to have a predictable size.
++ int call_size_in_bytes =
++ MacroAssembler::CallSizeNotPredictableCodeSize(deopt_entry,
++ RelocInfo::NONE);
++ int call_size_in_words = call_size_in_bytes / Assembler::kInstrSize;
++ ASSERT(call_size_in_bytes % Assembler::kInstrSize == 0);
++ ASSERT(call_size_in_bytes <= patch_size());
++ CodePatcher patcher(call_address, call_size_in_words);
++ patcher.masm()->Call(deopt_entry, RelocInfo::NONE);
++ ASSERT(prev_call_address == NULL ||
++ call_address >= prev_call_address + patch_size());
++ ASSERT(call_address + patch_size() <= code->instruction_end());
++#ifdef DEBUG
++ prev_call_address = call_address;
++#endif
++ }
++
++ Isolate* isolate = code->GetIsolate();
++
++ // Add the deoptimizing code to the list.
++ DeoptimizingCodeListNode* node = new DeoptimizingCodeListNode(code);
++ DeoptimizerData* data = isolate->deoptimizer_data();
++ node->set_next(data->deoptimizing_code_list_);
++ data->deoptimizing_code_list_ = node;
++
++ // We might be in the middle of incremental marking with compaction.
++ // Tell collector to treat this code object in a special way and
++ // ignore all slots that might have been recorded on it.
++ isolate->heap()->mark_compact_collector()->InvalidateCode(code);
++
++ ReplaceCodeForRelatedFunctions(function, code);
++
++ if (FLAG_trace_deopt) {
++ PrintF("[forced deoptimization: ");
++ function->PrintName();
++ PrintF(" / %" V8PRIxPTR "]\n",
reinterpret_cast<uintptr_t>(function));
++ }
++}
++
++
++#if V8_TARGET_ARCH_PPC64
++static const int32_t kBranchBeforeStackCheck = 0x409c0020;
++static const int32_t kBranchBeforeInterrupt = 0x409c0044;
++#else
++static const int32_t kBranchBeforeStackCheck = 0x409c0014;
++static const int32_t kBranchBeforeInterrupt = 0x409c0024;
++#endif
++
++
++// This code has some dependency on the FIXED_SEQUENCE lis/ori
++void Deoptimizer::PatchStackCheckCodeAt(Code* unoptimized_code,
++ Address pc_after,
++ Code* check_code,
++ Code* replacement_code) {
++ const int kInstrSize = Assembler::kInstrSize;
++ // There are two 'Stack check' sequences from full-codegen-ppc.cc
++ // both have similar code - and FLAG_count_based_interrupts will
++ // control which we expect to find
++ //
++ // The call of the stack guard check has the following form:
++ // 409c0014 bge +40 -> 876 (0x25535c4c) ;; (ok)
++ // 3d802553 lis r12, 9555 ;; two part load
++ // 618c5000 ori r12, r12, 20480 ;; of stack guard address
++ // 7d8803a6 mtlr r12
++ // 4e800021 blrl
++ // <-- pc_after
++ //
++ // 64bit will have an expanded mov() [lis/ori] sequence
++
++ // Check we have a branch & link through r12 (ip)
++ ASSERT(Memory::int32_at(pc_after - 2 * kInstrSize) == 0x7d8803a6);
++ ASSERT(Memory::int32_at(pc_after - kInstrSize) == 0x4e800021);
++
++#if V8_TARGET_ARCH_PPC64
++ ASSERT(Assembler::Is64BitLoadIntoR12(
++ Assembler::instr_at(pc_after - 7 * kInstrSize),
++ Assembler::instr_at(pc_after - 6 * kInstrSize),
++ Assembler::instr_at(pc_after - 5 * kInstrSize),
++ Assembler::instr_at(pc_after - 4 * kInstrSize),
++ Assembler::instr_at(pc_after - 3 * kInstrSize)));
++ if (FLAG_count_based_interrupts) {
++ ASSERT_EQ(kBranchBeforeInterrupt,
++ Memory::int32_at(pc_after - 8 * kInstrSize));
++ } else {
++ ASSERT_EQ(kBranchBeforeStackCheck,
++ Memory::int32_at(pc_after - 8 * kInstrSize));
++ }
++#else
++ ASSERT(Assembler::Is32BitLoadIntoR12(
++ Assembler::instr_at(pc_after - 4 * kInstrSize),
++ Assembler::instr_at(pc_after - 3 * kInstrSize)));
++ if (FLAG_count_based_interrupts) {
++ ASSERT_EQ(kBranchBeforeInterrupt,
++ Memory::int32_at(pc_after - 5 * kInstrSize));
++ } else {
++ ASSERT_EQ(kBranchBeforeStackCheck,
++ Memory::int32_at(pc_after - 5 * kInstrSize));
++ }
++#endif
++
++ // We patch the code to the following form:
++ // 60000000 ori r0, r0, 0 ;; NOP
++ // 3d80NNNN lis r12, NNNN ;; two part load
++ // 618cNNNN ori r12, r12, NNNN ;; of on stack replace address
++ // 7d8803a6 mtlr r12
++ // 4e800021 blrl
++
++#if V8_TARGET_ARCH_PPC64
++ CodePatcher patcher(pc_after - 8 * kInstrSize, 6);
++
++ // Assemble the 64 bit value from the five part load and verify
++ // that it is the stack guard code
++ uint64_t stack_check_address =
++ (Memory::uint32_at(pc_after - 7 * kInstrSize) & 0xFFFF) << 16;
++ stack_check_address |=
++ (Memory::uint32_at(pc_after - 6 * kInstrSize) & 0xFFFF);
++ stack_check_address <<= 32;
++ stack_check_address |=
++ (Memory::uint32_at(pc_after - 4 * kInstrSize) & 0xFFFF) << 16;
++ stack_check_address |=
++ (Memory::uint32_at(pc_after - 3 * kInstrSize) & 0xFFFF);
++#else
++ CodePatcher patcher(pc_after - 5 * kInstrSize, 3);
++
++ // Assemble the 32 bit value from the two part load and verify
++ // that it is the stack guard code
++ uint32_t stack_check_address =
++ (Memory::int32_at(pc_after - 4 * kInstrSize) & 0xFFFF) << 16;
++ stack_check_address |=
++ (Memory::int32_at(pc_after - 3 * kInstrSize) & 0xFFFF);
++#endif
++ ASSERT(stack_check_address ==
++ reinterpret_cast<uintptr_t>(check_code->entry()));
++
++ // Replace conditional jump with NOP.
++ patcher.masm()->nop();
++
++ // Now modify the two part load (or 5 part on 64bit)
++ patcher.masm()->mov(ip,
++ Operand(reinterpret_cast<uintptr_t>(replacement_code->entry())));
++
++#if V8_TARGET_ARCH_PPC64
++ unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
++ unoptimized_code, pc_after - 7 * kInstrSize, replacement_code);
++#else
++ unoptimized_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
++ unoptimized_code, pc_after - 4 * kInstrSize, replacement_code);
++#endif
++}
++
++
++void Deoptimizer::RevertStackCheckCodeAt(Code* unoptimized_code,
++ Address pc_after,
++ Code* check_code,
++ Code* replacement_code) {
++ const int kInstrSize = Assembler::kInstrSize;
++
++ // Check we have a branch & link through r12 (ip)
++ ASSERT(Memory::int32_at(pc_after - 2 * kInstrSize) == 0x7d8803a6);
++ ASSERT(Memory::int32_at(pc_after - kInstrSize) == 0x4e800021);
++
++#if V8_TARGET_ARCH_PPC64
++ ASSERT(Assembler::Is64BitLoadIntoR12(
++ Assembler::instr_at(pc_after - 7 * kInstrSize),
++ Assembler::instr_at(pc_after - 6 * kInstrSize),
++ Assembler::instr_at(pc_after - 5 * kInstrSize),
++ Assembler::instr_at(pc_after - 4 * kInstrSize),
++ Assembler::instr_at(pc_after - 3 * kInstrSize)));
++#else
++ ASSERT(Assembler::Is32BitLoadIntoR12(
++ Assembler::instr_at(pc_after - 4 * kInstrSize),
++ Assembler::instr_at(pc_after - 3 * kInstrSize)));
++#endif
++
++#if V8_TARGET_ARCH_PPC64
++ // Replace NOP with conditional jump.
++ CodePatcher patcher(pc_after - 8 * kInstrSize, 6);
++ if (FLAG_count_based_interrupts) {
++ patcher.masm()->bc(+68, BF,
++ v8::internal::Assembler::encode_crbit(cr7, CR_LT)); // bge
++ ASSERT_EQ(kBranchBeforeInterrupt,
++ Memory::int32_at(pc_after - 8 * kInstrSize));
++ } else {
++ patcher.masm()->bc(+32, BF,
++ v8::internal::Assembler::encode_crbit(cr7, CR_LT)); // bge
++ ASSERT_EQ(kBranchBeforeStackCheck,
++ Memory::int32_at(pc_after - 8 * kInstrSize));
++ }
++#else
++ // Replace NOP with conditional jump.
++ CodePatcher patcher(pc_after - 5 * kInstrSize, 3);
++ if (FLAG_count_based_interrupts) {
++ patcher.masm()->bc(+36, BF,
++ v8::internal::Assembler::encode_crbit(cr7, CR_LT)); // bge
++ ASSERT_EQ(kBranchBeforeInterrupt,
++ Memory::int32_at(pc_after - 5 * kInstrSize));
++ } else {
++ patcher.masm()->bc(+20, BF,
++ v8::internal::Assembler::encode_crbit(cr7, CR_LT)); // bge
++ ASSERT_EQ(kBranchBeforeStackCheck,
++ Memory::int32_at(pc_after - 5 * kInstrSize));
++ }
++#endif
++
++#if V8_TARGET_ARCH_PPC64
++ // Assemble the 64 bit value from the five part load and verify
++ // that it is the stack guard code
++ uint64_t stack_check_address =
++ (Memory::uint32_at(pc_after - 7 * kInstrSize) & 0xFFFF) << 16;
++ stack_check_address |=
++ (Memory::uint32_at(pc_after - 6 * kInstrSize) & 0xFFFF);
++ stack_check_address <<= 32;
++ stack_check_address |=
++ (Memory::uint32_at(pc_after - 4 * kInstrSize) & 0xFFFF) << 16;
++ stack_check_address |=
++ (Memory::uint32_at(pc_after - 3 * kInstrSize) & 0xFFFF);
++#else
++ // Assemble the 32 bit value from the two part load and verify
++ // that it is the replacement code address
++ // This assumes a FIXED_SEQUENCE for lis/ori
++ uint32_t stack_check_address =
++ (Memory::int32_at(pc_after - 4 * kInstrSize) & 0xFFFF) << 16;
++ stack_check_address |=
++ (Memory::int32_at(pc_after - 3 * kInstrSize) & 0xFFFF);
++#endif
++ ASSERT(stack_check_address ==
++ reinterpret_cast<uintptr_t>(replacement_code->entry()));
++
++ // Now modify the two part load (or 5 part on 64bit)
++ patcher.masm()->mov(ip,
++ Operand(reinterpret_cast<uintptr_t>(check_code->entry())));
++
++#if V8_TARGET_ARCH_PPC64
++ check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
++ unoptimized_code, pc_after - 7 * kInstrSize, check_code);
++#else
++ check_code->GetHeap()->incremental_marking()->RecordCodeTargetPatch(
++ unoptimized_code, pc_after - 4 * kInstrSize, check_code);
++#endif
++}
++
++
++static int LookupBailoutId(DeoptimizationInputData* data, BailoutId ast_id) {
++ ByteArray* translations = data->TranslationByteArray();
++ int length = data->DeoptCount();
++ for (int i = 0; i < length; i++) {
++ if (data->AstId(i) == ast_id) {
++ TranslationIterator it(translations, data->TranslationIndex(i)->value());
++ int value = it.Next();
++ ASSERT(Translation::BEGIN == static_cast<Translation::Opcode>(value));
++ // Read the number of frames.
++ value = it.Next();
++ if (value == 1) return i;
++ }
++ }
++ UNREACHABLE();
++ return -1;
++}
++
++
++void Deoptimizer::DoComputeOsrOutputFrame() {
++ DeoptimizationInputData* data = DeoptimizationInputData::cast(
++ optimized_code_->deoptimization_data());
++ unsigned ast_id = data->OsrAstId()->value();
++
++ int bailout_id = LookupBailoutId(data, BailoutId(ast_id));
++ unsigned translation_index = data->TranslationIndex(bailout_id)->value();
++ ByteArray* translations = data->TranslationByteArray();
++
++ TranslationIterator iterator(translations, translation_index);
++ Translation::Opcode opcode =
++ static_cast<Translation::Opcode>(iterator.Next());
++ ASSERT(Translation::BEGIN == opcode);
++ USE(opcode);
++ int count = iterator.Next();
++ iterator.Skip(1); // Drop JS frame count.
++ ASSERT(count == 1);
++ USE(count);
++
++ opcode = static_cast<Translation::Opcode>(iterator.Next());
++ USE(opcode);
++ ASSERT(Translation::JS_FRAME == opcode);
++ unsigned node_id = iterator.Next();
++ USE(node_id);
++ ASSERT(node_id == ast_id);
++ int closure_id = iterator.Next();
++ USE(closure_id);
++ ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
++ unsigned height = iterator.Next();
++ unsigned height_in_bytes = height * kPointerSize;
++ USE(height_in_bytes);
++
++ unsigned fixed_size = ComputeFixedSize(function_);
++ unsigned input_frame_size = input_->GetFrameSize();
++ ASSERT(fixed_size + height_in_bytes == input_frame_size);
++
++ unsigned stack_slot_size = optimized_code_->stack_slots() * kPointerSize;
++ unsigned outgoing_height = data->ArgumentsStackHeight(bailout_id)->value();
++ unsigned outgoing_size = outgoing_height * kPointerSize;
++ unsigned output_frame_size = fixed_size + stack_slot_size + outgoing_size;
++ ASSERT(outgoing_size == 0); // OSR does not happen in the middle of a call.
++
++ if (FLAG_trace_osr) {
++ PrintF("[on-stack replacement: begin 0x%08" V8PRIxPTR " ",
++ reinterpret_cast<intptr_t>(function_));
++ function_->PrintName();
++ PrintF(" => node=%u, frame=%d->%d]\n",
++ ast_id,
++ input_frame_size,
++ output_frame_size);
++ }
++
++ // There's only one output frame in the OSR case.
++ output_count_ = 1;
++ output_ = new FrameDescription*[1];
++ output_[0] = new(output_frame_size) FrameDescription(
++ output_frame_size, function_);
++ output_[0]->SetFrameType(StackFrame::JAVA_SCRIPT);
++
++ // Clear the incoming parameters in the optimized frame to avoid
++ // confusing the garbage collector.
++ unsigned output_offset = output_frame_size - kPointerSize;
++ int parameter_count = function_->shared()->formal_parameter_count() + 1;
++ for (int i = 0; i < parameter_count; ++i) {
++ output_[0]->SetFrameSlot(output_offset, 0);
++ output_offset -= kPointerSize;
++ }
++
++ // Translate the incoming parameters. This may overwrite some of the
++ // incoming argument slots we've just cleared.
++ int input_offset = input_frame_size - kPointerSize;
++ bool ok = true;
++ int limit = input_offset - (parameter_count * kPointerSize);
++ while (ok && input_offset > limit) {
++ ok = DoOsrTranslateCommand(&iterator, &input_offset);
++ }
++
++ // There are no translation commands for the caller's pc and fp, the
++ // context, and the function. Set them up explicitly.
++ for (int i = StandardFrameConstants::kCallerPCOffset;
++ ok && i >= StandardFrameConstants::kMarkerOffset;
++ i -= kPointerSize) {
++ uintptr_t input_value = input_->GetFrameSlot(input_offset);
++ if (FLAG_trace_osr) {
++ const char* name = "UNKNOWN";
++ switch (i) {
++ case StandardFrameConstants::kCallerPCOffset:
++ name = "caller's pc";
++ break;
++ case StandardFrameConstants::kCallerFPOffset:
++ name = "fp";
++ break;
++ case StandardFrameConstants::kContextOffset:
++ name = "context";
++ break;
++ case StandardFrameConstants::kMarkerOffset:
++ name = "function";
++ break;
++ }
++ PrintF(" [sp + %d] <- 0x%08" V8PRIxPTR " ;"
++ " [sp + %d] (fixed part - %s)\n",
++ output_offset,
++ input_value,
++ input_offset,
++ name);
++ }
++
++ output_[0]->SetFrameSlot(output_offset, input_->GetFrameSlot(input_offset));
++ input_offset -= kPointerSize;
++ output_offset -= kPointerSize;
++ }
++
++ // Translate the rest of the frame.
++ while (ok && input_offset >= 0) {
++ ok = DoOsrTranslateCommand(&iterator, &input_offset);
++ }
++
++ // If translation of any command failed, continue using the input frame.
++ if (!ok) {
++ delete output_[0];
++ output_[0] = input_;
++ output_[0]->SetPc(reinterpret_cast<uintptr_t>(from_));
++ } else {
++ // Set up the frame pointer and the context pointer.
++ output_[0]->SetRegister(fp.code(), input_->GetRegister(fp.code()));
++ output_[0]->SetRegister(cp.code(), input_->GetRegister(cp.code()));
++
++ unsigned pc_offset = data->OsrPcOffset()->value();
++ uintptr_t pc = reinterpret_cast<uintptr_t>(
++ optimized_code_->entry() + pc_offset);
++ output_[0]->SetPc(pc);
++ }
++ Code* continuation = isolate_->builtins()->builtin(Builtins::kNotifyOSR);
++ output_[0]->SetContinuation(
++ reinterpret_cast<uintptr_t>(continuation->entry()));
++
++ if (FLAG_trace_osr) {
++ PrintF("[on-stack replacement translation %s: 0x%08" V8PRIxPTR "
",
++ ok ? "finished" : "aborted",
++ reinterpret_cast<intptr_t>(function_));
++ function_->PrintName();
++ PrintF(" => pc=0x%0" V8PRIxPTR "]\n",
output_[0]->GetPc());
++ }
++}
++
++
++void Deoptimizer::DoComputeArgumentsAdaptorFrame(TranslationIterator* iterator,
++ int frame_index) {
++ JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
++ unsigned height = iterator->Next();
++ unsigned height_in_bytes = height * kPointerSize;
++ if (FLAG_trace_deopt) {
++ PrintF(" translating arguments adaptor => height=%d\n",
height_in_bytes);
++ }
++
++ unsigned fixed_frame_size = ArgumentsAdaptorFrameConstants::kFrameSize;
++ unsigned output_frame_size = height_in_bytes + fixed_frame_size;
++
++ // Allocate and store the output frame description.
++ FrameDescription* output_frame =
++ new(output_frame_size) FrameDescription(output_frame_size, function);
++ output_frame->SetFrameType(StackFrame::ARGUMENTS_ADAPTOR);
++
++ // Arguments adaptor can not be topmost or bottommost.
++ ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
++ ASSERT(output_[frame_index] == NULL);
++ output_[frame_index] = output_frame;
++
++ // The top address of the frame is computed from the previous
++ // frame's top and this frame's size.
++ uintptr_t top_address;
++ top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
++ output_frame->SetTop(top_address);
++
++ // Compute the incoming parameter translation.
++ int parameter_count = height;
++ unsigned output_offset = output_frame_size;
++ for (int i = 0; i < parameter_count; ++i) {
++ output_offset -= kPointerSize;
++ DoTranslateCommand(iterator, frame_index, output_offset);
++ }
++
++ // Read caller's PC from the previous frame.
++ output_offset -= kPointerSize;
++ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
++ output_frame->SetFrameSlot(output_offset, callers_pc);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; caller's pc\n",
++ top_address + output_offset, output_offset, callers_pc);
++ }
++
++ // Read caller's FP from the previous frame, and set this frame's FP.
++ output_offset -= kPointerSize;
++ intptr_t value = output_[frame_index - 1]->GetFp();
++ output_frame->SetFrameSlot(output_offset, value);
++ intptr_t fp_value = top_address + output_offset;
++ output_frame->SetFp(fp_value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; caller's fp\n", fp_value, output_offset, value);
++ }
++
++ // A marker value is used in place of the context.
++ output_offset -= kPointerSize;
++ intptr_t context = reinterpret_cast<intptr_t>(
++ Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
++ output_frame->SetFrameSlot(output_offset, context);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; context (adaptor sentinel)\n",
++ top_address + output_offset, output_offset, context);
++ }
++
++ // The function was mentioned explicitly in the ARGUMENTS_ADAPTOR_FRAME.
++ output_offset -= kPointerSize;
++ value = reinterpret_cast<intptr_t>(function);
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; function\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // Number of incoming arguments.
++ output_offset -= kPointerSize;
++ value = reinterpret_cast<uintptr_t>(Smi::FromInt(height - 1));
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; argc (%d)\n",
++ top_address + output_offset, output_offset, value, height - 1);
++ }
++
++ ASSERT(0 == output_offset);
++
++ Builtins* builtins = isolate_->builtins();
++ Code* adaptor_trampoline =
++ builtins->builtin(Builtins::kArgumentsAdaptorTrampoline);
++ uintptr_t pc = reinterpret_cast<uintptr_t>(
++ adaptor_trampoline->instruction_start() +
++ isolate_->heap()->arguments_adaptor_deopt_pc_offset()->value());
++ output_frame->SetPc(pc);
++}
++
++
++void Deoptimizer::DoComputeConstructStubFrame(TranslationIterator* iterator,
++ int frame_index) {
++ Builtins* builtins = isolate_->builtins();
++ Code* construct_stub = builtins->builtin(Builtins::kJSConstructStubGeneric);
++ JSFunction* function = JSFunction::cast(ComputeLiteral(iterator->Next()));
++ unsigned height = iterator->Next();
++ unsigned height_in_bytes = height * kPointerSize;
++ if (FLAG_trace_deopt) {
++ PrintF(" translating construct stub => height=%d\n",
height_in_bytes);
++ }
++
++ unsigned fixed_frame_size = 8 * kPointerSize;
++ unsigned output_frame_size = height_in_bytes + fixed_frame_size;
++
++ // Allocate and store the output frame description.
++ FrameDescription* output_frame =
++ new(output_frame_size) FrameDescription(output_frame_size, function);
++ output_frame->SetFrameType(StackFrame::CONSTRUCT);
++
++ // Construct stub can not be topmost or bottommost.
++ ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
++ ASSERT(output_[frame_index] == NULL);
++ output_[frame_index] = output_frame;
++
++ // The top address of the frame is computed from the previous
++ // frame's top and this frame's size.
++ uintptr_t top_address;
++ top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
++ output_frame->SetTop(top_address);
++
++ // Compute the incoming parameter translation.
++ int parameter_count = height;
++ unsigned output_offset = output_frame_size;
++ for (int i = 0; i < parameter_count; ++i) {
++ output_offset -= kPointerSize;
++ DoTranslateCommand(iterator, frame_index, output_offset);
++ }
++
++ // Read caller's PC from the previous frame.
++ output_offset -= kPointerSize;
++ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
++ output_frame->SetFrameSlot(output_offset, callers_pc);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; caller's pc\n",
++ top_address + output_offset, output_offset, callers_pc);
++ }
++
++ // Read caller's FP from the previous frame, and set this frame's FP.
++ output_offset -= kPointerSize;
++ intptr_t value = output_[frame_index - 1]->GetFp();
++ output_frame->SetFrameSlot(output_offset, value);
++ intptr_t fp_value = top_address + output_offset;
++ output_frame->SetFp(fp_value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; caller's fp\n", fp_value, output_offset, value);
++ }
++
++ // The context can be gotten from the previous frame.
++ output_offset -= kPointerSize;
++ value = output_[frame_index - 1]->GetContext();
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; context\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // A marker value is used in place of the function.
++ output_offset -= kPointerSize;
++ value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::CONSTRUCT));
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; function (construct sentinel)\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // The output frame reflects a JSConstructStubGeneric frame.
++ output_offset -= kPointerSize;
++ value = reinterpret_cast<intptr_t>(construct_stub);
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; code object\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // Number of incoming arguments.
++ output_offset -= kPointerSize;
++ value = reinterpret_cast<uintptr_t>(Smi::FromInt(height - 1));
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; argc (%d)\n",
++ top_address + output_offset, output_offset, value, height - 1);
++ }
++
++ // Constructor function being invoked by the stub.
++ output_offset -= kPointerSize;
++ value = reinterpret_cast<intptr_t>(function);
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; constructor function\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // The newly allocated object was passed as receiver in the artificial
++ // constructor stub environment created by HEnvironment::CopyForInlining().
++ output_offset -= kPointerSize;
++ value = output_frame->GetFrameSlot(output_frame_size - kPointerSize);
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; allocated receiver\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ ASSERT(0 == output_offset);
++
++ uintptr_t pc = reinterpret_cast<uintptr_t>(
++ construct_stub->instruction_start() +
++ isolate_->heap()->construct_stub_deopt_pc_offset()->value());
++ output_frame->SetPc(pc);
++}
++
++
++void Deoptimizer::DoComputeAccessorStubFrame(TranslationIterator* iterator,
++ int frame_index,
++ bool is_setter_stub_frame) {
++ JSFunction* accessor = JSFunction::cast(ComputeLiteral(iterator->Next()));
++ // The receiver (and the implicit return value, if any) are expected in
++ // registers by the LoadIC/StoreIC, so they don't belong to the output stack
++ // frame. This means that we have to use a height of 0.
++ unsigned height = 0;
++ unsigned height_in_bytes = height * kPointerSize;
++ const char* kind = is_setter_stub_frame ? "setter" : "getter";
++ if (FLAG_trace_deopt) {
++ PrintF(" translating %s stub => height=%u\n", kind, height_in_bytes);
++ }
++
++ // We need 5 stack entries from StackFrame::INTERNAL (lr, fp, cp, frame type,
++ // code object, see MacroAssembler::EnterFrame). For a setter stub frames we
++ // need one additional entry for the implicit return value, see
++ // StoreStubCompiler::CompileStoreViaSetter.
++ unsigned fixed_frame_entries = 5 + (is_setter_stub_frame ? 1 : 0);
++ unsigned fixed_frame_size = fixed_frame_entries * kPointerSize;
++ unsigned output_frame_size = height_in_bytes + fixed_frame_size;
++
++ // Allocate and store the output frame description.
++ FrameDescription* output_frame =
++ new(output_frame_size) FrameDescription(output_frame_size, accessor);
++ output_frame->SetFrameType(StackFrame::INTERNAL);
++
++ // A frame for an accessor stub can not be the topmost or bottommost one.
++ ASSERT(frame_index > 0 && frame_index < output_count_ - 1);
++ ASSERT(output_[frame_index] == NULL);
++ output_[frame_index] = output_frame;
++
++ // The top address of the frame is computed from the previous frame's top and
++ // this frame's size.
++ uintptr_t top_address = (output_[frame_index - 1]->GetTop() -
++ output_frame_size);
++ output_frame->SetTop(top_address);
++
++ unsigned output_offset = output_frame_size;
++
++ // Read caller's PC from the previous frame.
++ output_offset -= kPointerSize;
++ intptr_t callers_pc = output_[frame_index - 1]->GetPc();
++ output_frame->SetFrameSlot(output_offset, callers_pc);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08"
V8PRIxPTR
++ " ; caller's pc\n",
++ top_address + output_offset, output_offset, callers_pc);
++ }
++
++ // Read caller's FP from the previous frame, and set this frame's FP.
++ output_offset -= kPointerSize;
++ intptr_t value = output_[frame_index - 1]->GetFp();
++ output_frame->SetFrameSlot(output_offset, value);
++ intptr_t fp_value = top_address + output_offset;
++ output_frame->SetFp(fp_value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08"
V8PRIxPTR
++ " ; caller's fp\n",
++ fp_value, output_offset, value);
++ }
++
++ // The context can be gotten from the previous frame.
++ output_offset -= kPointerSize;
++ value = output_[frame_index - 1]->GetContext();
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08"
V8PRIxPTR
++ " ; context\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // A marker value is used in place of the function.
++ output_offset -= kPointerSize;
++ value = reinterpret_cast<intptr_t>(Smi::FromInt(StackFrame::INTERNAL));
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08"
V8PRIxPTR
++ " ; function (%s sentinel)\n",
++ top_address + output_offset, output_offset, value, kind);
++ }
++
++ // Get Code object from accessor stub.
++ output_offset -= kPointerSize;
++ Builtins::Name name = is_setter_stub_frame ?
++ Builtins::kStoreIC_Setter_ForDeopt :
++ Builtins::kLoadIC_Getter_ForDeopt;
++ Code* accessor_stub = isolate_->builtins()->builtin(name);
++ value = reinterpret_cast<intptr_t>(accessor_stub);
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %u] <- 0x%08"
V8PRIxPTR
++ " ; code object\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // Skip receiver.
++ Translation::Opcode opcode =
++ static_cast<Translation::Opcode>(iterator->Next());
++ iterator->Skip(Translation::NumberOfOperandsFor(opcode));
++
++ if (is_setter_stub_frame) {
++ // The implicit return value was part of the artificial setter stub
++ // environment.
++ output_offset -= kPointerSize;
++ DoTranslateCommand(iterator, frame_index, output_offset);
++ }
++
++ ASSERT(0 == output_offset);
++
++ Smi* offset = is_setter_stub_frame ?
++ isolate_->heap()->setter_stub_deopt_pc_offset() :
++ isolate_->heap()->getter_stub_deopt_pc_offset();
++ intptr_t pc = reinterpret_cast<intptr_t>(
++ accessor_stub->instruction_start() + offset->value());
++ output_frame->SetPc(pc);
++}
++
++
++// This code is very similar to ia32 code, but relies on register names (fp, sp)
++// and how the frame is laid out.
++void Deoptimizer::DoComputeJSFrame(TranslationIterator* iterator,
++ int frame_index) {
++ // Read the ast node id, function, and frame height for this output frame.
++ BailoutId node_id = BailoutId(iterator->Next());
++ JSFunction* function;
++ if (frame_index != 0) {
++ function = JSFunction::cast(ComputeLiteral(iterator->Next()));
++ } else {
++ int closure_id = iterator->Next();
++ USE(closure_id);
++ ASSERT_EQ(Translation::kSelfLiteralId, closure_id);
++ function = function_;
++ }
++ unsigned height = iterator->Next();
++ unsigned height_in_bytes = height * kPointerSize;
++ if (FLAG_trace_deopt) {
++ PrintF(" translating ");
++ function->PrintName();
++ PrintF(" => node=%d, height=%d\n", node_id.ToInt(), height_in_bytes);
++ }
++
++ // The 'fixed' part of the frame consists of the incoming parameters and
++ // the part described by JavaScriptFrameConstants.
++ unsigned fixed_frame_size = ComputeFixedSize(function);
++ unsigned input_frame_size = input_->GetFrameSize();
++ unsigned output_frame_size = height_in_bytes + fixed_frame_size;
++
++ // Allocate and store the output frame description.
++ FrameDescription* output_frame =
++ new(output_frame_size) FrameDescription(output_frame_size, function);
++ output_frame->SetFrameType(StackFrame::JAVA_SCRIPT);
++
++ bool is_bottommost = (0 == frame_index);
++ bool is_topmost = (output_count_ - 1 == frame_index);
++ ASSERT(frame_index >= 0 && frame_index < output_count_);
++ ASSERT(output_[frame_index] == NULL);
++ output_[frame_index] = output_frame;
++
++ // The top address for the bottommost output frame can be computed from
++ // the input frame pointer and the output frame's height. For all
++ // subsequent output frames, it can be computed from the previous one's
++ // top address and the current frame's size.
++ uintptr_t top_address;
++ if (is_bottommost) {
++ // 2 = context and function in the frame.
++ top_address =
++ input_->GetRegister(fp.code()) - (2 * kPointerSize) - height_in_bytes;
++ } else {
++ top_address = output_[frame_index - 1]->GetTop() - output_frame_size;
++ }
++ output_frame->SetTop(top_address);
++
++ // Compute the incoming parameter translation.
++ int parameter_count = function->shared()->formal_parameter_count() + 1;
++ unsigned output_offset = output_frame_size;
++ unsigned input_offset = input_frame_size;
++ for (int i = 0; i < parameter_count; ++i) {
++ output_offset -= kPointerSize;
++ DoTranslateCommand(iterator, frame_index, output_offset);
++ }
++ input_offset -= (parameter_count * kPointerSize);
++
++ // There are no translation commands for the caller's pc and fp, the
++ // context, and the function. Synthesize their values and set them up
++ // explicitly.
++ //
++ // The caller's pc for the bottommost output frame is the same as in the
++ // input frame. For all subsequent output frames, it can be read from the
++ // previous one. This frame's pc can be computed from the non-optimized
++ // function code and AST id of the bailout.
++ output_offset -= kPointerSize;
++ input_offset -= kPointerSize;
++ intptr_t value;
++ if (is_bottommost) {
++ value = input_->GetFrameSlot(input_offset);
++ } else {
++ value = output_[frame_index - 1]->GetPc();
++ }
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; caller's pc\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // The caller's frame pointer for the bottommost output frame is the same
++ // as in the input frame. For all subsequent output frames, it can be
++ // read from the previous one. Also compute and set this frame's frame
++ // pointer.
++ output_offset -= kPointerSize;
++ input_offset -= kPointerSize;
++ if (is_bottommost) {
++ value = input_->GetFrameSlot(input_offset);
++ } else {
++ value = output_[frame_index - 1]->GetFp();
++ }
++ output_frame->SetFrameSlot(output_offset, value);
++ intptr_t fp_value = top_address + output_offset;
++ ASSERT(!is_bottommost || input_->GetRegister(fp.code()) == fp_value);
++ output_frame->SetFp(fp_value);
++ if (is_topmost) {
++ output_frame->SetRegister(fp.code(), fp_value);
++ }
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; caller's fp\n", fp_value, output_offset, value);
++ }
++
++ // For the bottommost output frame the context can be gotten from the input
++ // frame. For all subsequent output frames it can be gotten from the function
++ // so long as we don't inline functions that need local contexts.
++ output_offset -= kPointerSize;
++ input_offset -= kPointerSize;
++ if (is_bottommost) {
++ value = input_->GetFrameSlot(input_offset);
++ } else {
++ value = reinterpret_cast<intptr_t>(function->context());
++ }
++ output_frame->SetFrameSlot(output_offset, value);
++ output_frame->SetContext(value);
++ if (is_topmost) output_frame->SetRegister(cp.code(), value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; context\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // The function was mentioned explicitly in the BEGIN_FRAME.
++ output_offset -= kPointerSize;
++ input_offset -= kPointerSize;
++ value = reinterpret_cast<uintptr_t>(function);
++ // The function for the bottommost output frame should also agree with the
++ // input frame.
++ ASSERT(!is_bottommost || input_->GetFrameSlot(input_offset) == value);
++ output_frame->SetFrameSlot(output_offset, value);
++ if (FLAG_trace_deopt) {
++ PrintF(" 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08"
V8PRIxPTR
++ " ; function\n",
++ top_address + output_offset, output_offset, value);
++ }
++
++ // Translate the rest of the frame.
++ for (unsigned i = 0; i < height; ++i) {
++ output_offset -= kPointerSize;
++ DoTranslateCommand(iterator, frame_index, output_offset);
++ }
++ ASSERT(0 == output_offset);
++
++ // Compute this frame's PC, state, and continuation.
++ Code* non_optimized_code = function->shared()->code();
++ FixedArray* raw_data = non_optimized_code->deoptimization_data();
++ DeoptimizationOutputData* data = DeoptimizationOutputData::cast(raw_data);
++ Address start = non_optimized_code->instruction_start();
++ unsigned pc_and_state = GetOutputInfo(data, node_id, function->shared());
++ unsigned pc_offset = FullCodeGenerator::PcField::decode(pc_and_state);
++ uintptr_t pc_value = reinterpret_cast<uintptr_t>(start + pc_offset);
++ output_frame->SetPc(pc_value);
++#if 0 // applicable on PPC?
++ if (is_topmost) {
++ output_frame->SetRegister(pc.code(), pc_value);
++ }
++#endif
++
++ FullCodeGenerator::State state =
++ FullCodeGenerator::StateField::decode(pc_and_state);
++ output_frame->SetState(Smi::FromInt(state));
++
++
++ // Set the continuation for the topmost frame.
++ if (is_topmost && bailout_type_ != DEBUGGER) {
++ Builtins* builtins = isolate_->builtins();
++ Code* continuation = (bailout_type_ == EAGER)
++ ? builtins->builtin(Builtins::kNotifyDeoptimized)
++ : builtins->builtin(Builtins::kNotifyLazyDeoptimized);
++ output_frame->SetContinuation(
++ reinterpret_cast<uintptr_t>(continuation->entry()));
++ }
++}
++
++
++void Deoptimizer::FillInputFrame(Address tos, JavaScriptFrame* frame) {
++ // Set the register values. The values are not important as there are no
++ // callee saved registers in JavaScript frames, so all registers are
++ // spilled. Registers fp and sp are set to the correct values though.
++
++ for (int i = 0; i < Register::kNumRegisters; i++) {
++ input_->SetRegister(i, i * 4);
++ }
++ input_->SetRegister(sp.code(), reinterpret_cast<intptr_t>(frame->sp()));
++ input_->SetRegister(fp.code(), reinterpret_cast<intptr_t>(frame->fp()));
++ for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; i++) {
++ input_->SetDoubleRegister(i, 0.0);
++ }
++
++ // Fill the frame content from the actual data on the frame.
++ for (unsigned i = 0; i < input_->GetFrameSize(); i += kPointerSize) {
++ input_->SetFrameSlot(i, reinterpret_cast<intptr_t>(
++ Memory::Address_at(tos + i)));
++ }
++}
++
++
++#define __ masm()->
++
++// This code tries to be close to ia32 code so that any changes can be
++// easily ported.
++void Deoptimizer::EntryGenerator::Generate() {
++ GeneratePrologue();
++
++ Isolate* isolate = masm()->isolate();
++
++ // Unlike on ARM we don't save all the registers, just the useful ones.
++ // For the rest, there are gaps on the stack, so the offsets remain the same.
++ const int kNumberOfRegisters = Register::kNumRegisters;
++
++ RegList restored_regs = kJSCallerSaved | kCalleeSaved;
++ RegList saved_regs = restored_regs | sp.bit();
++
++ const int kDoubleRegsSize =
++ kDoubleSize * DwVfpRegister::kNumAllocatableRegisters;
++
++ // Save all FPU registers before messing with them.
++ __ subi(sp, sp, Operand(kDoubleRegsSize));
++ for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; ++i) {
++ DwVfpRegister fpu_reg = DwVfpRegister::FromAllocationIndex(i);
++ int offset = i * kDoubleSize;
++ __ stfd(fpu_reg, MemOperand(sp, offset));
++ }
++
++ // Push saved_regs (needed to populate FrameDescription::registers_).
++ // Leave gaps for other registers.
++ __ subi(sp, sp, Operand(kNumberOfRegisters * kPointerSize));
++ for (int16_t i = kNumberOfRegisters - 1; i >= 0; i--) {
++ if ((saved_regs & (1 << i)) != 0) {
++ __ StoreP(ToRegister(i), MemOperand(sp, kPointerSize * i));
++ }
++ }
++
++ const int kSavedRegistersAreaSize =
++ (kNumberOfRegisters * kPointerSize) + kDoubleRegsSize;
++
++ // Get the bailout id from the stack.
++ __ LoadP(r5, MemOperand(sp, kSavedRegistersAreaSize));
++
++ // Get the address of the location in the code object if possible (r6) (return
++ // address for lazy deoptimization) and compute the fp-to-sp delta in
++ // register r7.
++ if (type() == EAGER) {
++ __ li(r6, Operand::Zero());
++ // Correct one word for bailout id.
++ __ addi(r7, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
++ } else if (type() == OSR) {
++ __ mflr(r6);
++ // Correct one word for bailout id.
++ __ addi(r7, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
++ } else {
++ __ mflr(r6);
++ // Correct two words for bailout id and return address.
++ __ addi(r7, sp, Operand(kSavedRegistersAreaSize + (2 * kPointerSize)));
++ }
++ __ sub(r7, fp, r7);
++
++ // Allocate a new deoptimizer object.
++ // Pass six arguments in r3 to r8.
++ __ PrepareCallCFunction(6, r8);
++ __ LoadP(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ __ li(r4, Operand(type())); // bailout type,
++ // r5: bailout id already loaded.
++ // r6: code address or 0 already loaded.
++ // r7: Fp-to-sp delta.
++ __ mov(r8, Operand(ExternalReference::isolate_address()));
++ // Call Deoptimizer::New().
++ {
++ AllowExternalCallThatCantCauseGC scope(masm());
++ __ CallCFunction(ExternalReference::new_deoptimizer_function(isolate), 6);
++ }
++
++ // Preserve "deoptimizer" object in register r3 and get the input
++ // frame descriptor pointer to r4 (deoptimizer->input_);
++ __ LoadP(r4, MemOperand(r3, Deoptimizer::input_offset()));
++
++ // Copy core registers into FrameDescription::registers_[kNumRegisters].
++ ASSERT(Register::kNumRegisters == kNumberOfRegisters);
++ for (int i = 0; i < kNumberOfRegisters; i++) {
++ int offset = (i * kPointerSize) + FrameDescription::registers_offset();
++ __ LoadP(r5, MemOperand(sp, i * kPointerSize));
++ __ StoreP(r5, MemOperand(r4, offset));
++ }
++
++ // Copy VFP registers to
++ // double_registers_[DoubleRegister::kNumAllocatableRegisters]
++ int double_regs_offset = FrameDescription::double_registers_offset();
++ for (int i = 0; i < DwVfpRegister::kNumAllocatableRegisters; ++i) {
++ int dst_offset = i * kDoubleSize + double_regs_offset;
++ int src_offset = i * kDoubleSize + kNumberOfRegisters * kPointerSize;
++ __ lfd(d0, MemOperand(sp, src_offset));
++ __ stfd(d0, MemOperand(r4, dst_offset));
++ }
++
++ // Remove the bailout id, eventually return address, and the saved registers
++ // from the stack.
++ if (type() == EAGER || type() == OSR) {
++ __ addi(sp, sp, Operand(kSavedRegistersAreaSize + (1 * kPointerSize)));
++ } else {
++ __ addi(sp, sp, Operand(kSavedRegistersAreaSize + (2 * kPointerSize)));
++ }
++
++ // Compute a pointer to the unwinding limit in register r5; that is
++ // the first stack slot not part of the input frame.
++ __ LoadP(r5, MemOperand(r4, FrameDescription::frame_size_offset()));
++ __ add(r5, r5, sp);
++
++ // Unwind the stack down to - but not including - the unwinding
++ // limit and copy the contents of the activation frame to the input
++ // frame description.
++ __ addi(r6, r4, Operand(FrameDescription::frame_content_offset()));
++ Label pop_loop;
++ __ bind(&pop_loop);
++ __ pop(r7);
++ __ StoreP(r7, MemOperand(r6, 0));
++ __ addi(r6, r6, Operand(sizeof(intptr_t)));
++ __ cmp(r5, sp);
++ __ bne(&pop_loop);
++
++ // Compute the output frame in the deoptimizer.
++ __ push(r3); // Preserve deoptimizer object across call.
++ // r3: deoptimizer object; r4: scratch.
++ __ PrepareCallCFunction(1, r4);
++ // Call Deoptimizer::ComputeOutputFrames().
++ {
++ AllowExternalCallThatCantCauseGC scope(masm());
++ __ CallCFunction(
++ ExternalReference::compute_output_frames_function(isolate), 1);
++ }
++ __ pop(r3); // Restore deoptimizer object (class Deoptimizer).
++
++ // Replace the current (input) frame with the output frames.
++ Label outer_push_loop, inner_push_loop;
++ // Outer loop state: r3 = current "FrameDescription** output_",
++ // r4 = one past the last FrameDescription**.
++ __ lwz(r4, MemOperand(r3, Deoptimizer::output_count_offset()));
++ __ LoadP(r3, MemOperand(r3, Deoptimizer::output_offset())); // r3 is output_.
++ __ ShiftLeftImm(r4, r4, Operand(kPointerSizeLog2));
++ __ add(r4, r3, r4);
++ __ bind(&outer_push_loop);
++ // Inner loop state: r5 = current FrameDescription*, r6 = loop index.
++ __ LoadP(r5, MemOperand(r3, 0)); // output_[ix]
++ __ LoadP(r6, MemOperand(r5, FrameDescription::frame_size_offset()));
++
++ __ bind(&inner_push_loop);
++ __ addi(r6, r6, Operand(-sizeof(intptr_t)));
++ __ add(r9, r5, r6);
++ __ LoadP(r10, MemOperand(r9, FrameDescription::frame_content_offset()));
++ __ push(r10);
++ __ cmpi(r6, Operand::Zero());
++ __ bne(&inner_push_loop); // test for gt?
++
++ __ addi(r3, r3, Operand(kPointerSize));
++ __ cmp(r3, r4);
++ __ blt(&outer_push_loop);
++
++ // Push state, pc, and continuation from the last output frame.
++ if (type() != OSR) {
++ __ LoadP(r9, MemOperand(r5, FrameDescription::state_offset()));
++ __ push(r9);
++ }
++
++ __ LoadP(r9, MemOperand(r5, FrameDescription::pc_offset()));
++ __ push(r9);
++ __ LoadP(r9, MemOperand(r5, FrameDescription::continuation_offset()));
++ __ push(r9);
++
++ // Restore the registers from the last output frame.
++ ASSERT(!(ip.bit() & restored_regs));
++ __ mr(ip, r5);
++ for (int i = kNumberOfRegisters - 1; i >= 0; i--) {
++ int offset = (i * kPointerSize) + FrameDescription::registers_offset();
++ if ((restored_regs & (1 << i)) != 0) {
++ __ LoadP(ToRegister(i), MemOperand(ip, offset));
++ }
++ }
++
++ __ InitializeRootRegister();
++
++ __ pop(r10); // get continuation, leave pc on stack
++ __ pop(r0);
++ __ mtlr(r0);
++ __ Jump(r10);
++ __ stop("Unreachable.");
++}
++
++
++void Deoptimizer::TableEntryGenerator::GeneratePrologue() {
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm());
++
++ // Create a sequence of deoptimization entries. Note that any
++ // registers may be still live.
++ Label done;
++ for (int i = 0; i < count(); i++) {
++ int start = masm()->pc_offset();
++ USE(start);
++ if (type() == EAGER) {
++ __ nop();
++ __ nop();
++ } else {
++ // Emulate ia32 like call by pushing return address to stack.
++ __ mflr(r0);
++ __ push(r0);
++ }
++ __ li(ip, Operand(i));
++ __ push(ip);
++ __ b(&done);
++ ASSERT(masm()->pc_offset() - start == table_entry_size_);
++ }
++ __ bind(&done);
++}
++
++#undef __
++
++} } // namespace v8::internal
+diff -up v8-3.14.5.10/src/ppc/disasm-ppc.cc.ppc v8-3.14.5.10/src/ppc/disasm-ppc.cc
+--- v8-3.14.5.10/src/ppc/disasm-ppc.cc.ppc 2016-06-07 14:15:45.992392996 -0400
++++ v8-3.14.5.10/src/ppc/disasm-ppc.cc 2016-06-07 14:15:45.992392996 -0400
+@@ -0,0 +1,1339 @@
++// Copyright 2011 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++// A Disassembler object is used to disassemble a block of code instruction by
++// instruction. The default implementation of the NameConverter object can be
++// overriden to modify register names or to do symbol lookup on addresses.
++//
++// The example below will disassemble a block of code and print it to stdout.
++//
++// NameConverter converter;
++// Disassembler d(converter);
++// for (byte* pc = begin; pc < end;) {
++// v8::internal::EmbeddedVector<char, 256> buffer;
++// byte* prev_pc = pc;
++// pc += d.InstructionDecode(buffer, pc);
++// printf("%p %08x %s\n",
++// prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer);
++// }
++//
++// The Disassembler class also has a convenience method to disassemble a block
++// of code into a FILE*, meaning that the above functionality could also be
++// achieved by just calling Disassembler::Disassemble(stdout, begin, end);
++
++
++#include <assert.h>
++#include <stdio.h>
++#include <stdarg.h>
++#include <string.h>
++#ifndef WIN32
++#include <stdint.h>
++#endif
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "constants-ppc.h"
++#include "disasm.h"
++#include "macro-assembler.h"
++#include "platform.h"
++
++
++namespace v8 {
++namespace internal {
++
++
++//------------------------------------------------------------------------------
++
++// Decoder decodes and disassembles instructions into an output buffer.
++// It uses the converter to convert register names and call destinations into
++// more informative description.
++class Decoder {
++ public:
++ Decoder(const disasm::NameConverter& converter,
++ Vector<char> out_buffer)
++ : converter_(converter),
++ out_buffer_(out_buffer),
++ out_buffer_pos_(0) {
++ out_buffer_[out_buffer_pos_] = '\0';
++ }
++
++ ~Decoder() {}
++
++ // Writes one disassembled instruction into 'buffer' (0-terminated).
++ // Returns the length of the disassembled machine instruction in bytes.
++ int InstructionDecode(byte* instruction);
++
++ private:
++ // Bottleneck functions to print into the out_buffer.
++ void PrintChar(const char ch);
++ void Print(const char* str);
++
++ // Printing of common values.
++ void PrintRegister(int reg);
++ void PrintDRegister(int reg);
++ int FormatFPRegister(Instruction* instr, const char* format);
++ void PrintSoftwareInterrupt(SoftwareInterruptCodes svc);
++
++ // Handle formatting of instructions and their options.
++ int FormatRegister(Instruction* instr, const char* option);
++ int FormatOption(Instruction* instr, const char* option);
++ void Format(Instruction* instr, const char* format);
++ void Unknown(Instruction* instr);
++ void UnknownFormat(Instruction* instr, const char* opcname);
++ void MarkerFormat(Instruction* instr, const char* opcname, int id);
++
++ // PowerPC decoding
++ void DecodeExt1(Instruction* instr);
++ void DecodeExt2(Instruction* instr);
++ void DecodeExt4(Instruction* instr);
++ void DecodeExt5(Instruction* instr);
++
++ const disasm::NameConverter& converter_;
++ Vector<char> out_buffer_;
++ int out_buffer_pos_;
++
++ DISALLOW_COPY_AND_ASSIGN(Decoder);
++};
++
++
++// Support for assertions in the Decoder formatting functions.
++#define STRING_STARTS_WITH(string, compare_string) \
++ (strncmp(string, compare_string, strlen(compare_string)) == 0)
++
++
++// Append the ch to the output buffer.
++void Decoder::PrintChar(const char ch) {
++ out_buffer_[out_buffer_pos_++] = ch;
++}
++
++
++// Append the str to the output buffer.
++void Decoder::Print(const char* str) {
++ char cur = *str++;
++ while (cur != '\0' && (out_buffer_pos_ < (out_buffer_.length() -
1))) {
++ PrintChar(cur);
++ cur = *str++;
++ }
++ out_buffer_[out_buffer_pos_] = 0;
++}
++
++// Print the register name according to the active name converter.
++void Decoder::PrintRegister(int reg) {
++ Print(converter_.NameOfCPURegister(reg));
++}
++
++// Print the double FP register name according to the active name converter.
++void Decoder::PrintDRegister(int reg) {
++ Print(FPRegisters::Name(reg));
++}
++
++// Print SoftwareInterrupt codes. Factoring this out reduces the complexity of
++// the FormatOption method.
++void Decoder::PrintSoftwareInterrupt(SoftwareInterruptCodes svc) {
++ switch (svc) {
++ case kCallRtRedirected:
++ Print("call rt redirected");
++ return;
++ case kBreakpoint:
++ Print("breakpoint");
++ return;
++ default:
++ if (svc >= kStopCode) {
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%d - 0x%x",
++ svc & kStopCodeMask,
++ svc & kStopCodeMask);
++ } else {
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%d",
++ svc);
++ }
++ return;
++ }
++}
++
++// Handle all register based formatting in this function to reduce the
++// complexity of FormatOption.
++int Decoder::FormatRegister(Instruction* instr, const char* format) {
++ ASSERT(format[0] == 'r');
++
++ if ((format[1] == 't') || (format[1] == 's')) { // 'rt &
'rs register
++ int reg = instr->RTValue();
++ PrintRegister(reg);
++ return 2;
++ } else if (format[1] == 'a') { // 'ra: RA register
++ int reg = instr->RAValue();
++ PrintRegister(reg);
++ return 2;
++ } else if (format[1] == 'b') { // 'rb: RB register
++ int reg = instr->RBValue();
++ PrintRegister(reg);
++ return 2;
++ }
++
++ UNREACHABLE();
++ return -1;
++}
++
++
++// Handle all FP register based formatting in this function to reduce the
++// complexity of FormatOption.
++int Decoder::FormatFPRegister(Instruction* instr, const char* format) {
++ ASSERT(format[0] == 'D');
++
++ int retval = 2;
++ int reg = -1;
++ if (format[1] == 't') {
++ reg = instr->RTValue();
++ } else if (format[1] == 'a') {
++ reg = instr->RAValue();
++ } else if (format[1] == 'b') {
++ reg = instr->RBValue();
++ } else if (format[1] == 'c') {
++ reg = instr->RCValue();
++ } else {
++ UNREACHABLE();
++ }
++
++ PrintDRegister(reg);
++
++ return retval;
++}
++
++// FormatOption takes a formatting string and interprets it based on
++// the current instructions. The format string points to the first
++// character of the option string (the option escape has already been
++// consumed by the caller.) FormatOption returns the number of
++// characters that were consumed from the formatting string.
++int Decoder::FormatOption(Instruction* instr, const char* format) {
++ switch (format[0]) {
++ case 'o': {
++ if (instr->Bit(10) == 1) {
++ Print("o");
++ }
++ return 1;
++ }
++ case '.': {
++ if (instr->Bit(0) == 1) {
++ Print(".");
++ } else {
++ Print(" "); // ensure consistent spacing
++ }
++ return 1;
++ }
++ case 'r': {
++ return FormatRegister(instr, format);
++ }
++ case 'D': {
++ return FormatFPRegister(instr, format);
++ }
++ case 'i': { // int16
++ int32_t value = (instr->Bits(15, 0) << 16) >> 16;
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%d", value);
++ return 5;
++ }
++ case 'u': { // uint16
++ int32_t value = instr->Bits(15, 0);
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%d", value);
++ return 6;
++ }
++ case 'l': {
++ // Link (LK) Bit 0
++ if (instr->Bit(0) == 1) {
++ Print("l");
++ }
++ return 1;
++ }
++ case 'a': {
++ // Absolute Address Bit 1
++ if (instr->Bit(1) == 1) {
++ Print("a");
++ }
++ return 1;
++ }
++ case 't': { // 'target: target of branch instructions
++ // target26 or target16
++ ASSERT(STRING_STARTS_WITH(format, "target"));
++ if ((format[6] == '2') && (format[7] == '6')) {
++ int off = ((instr->Bits(25, 2)) << 8) >> 6;
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%+d -> %s",
++ off,
++ converter_.NameOfAddress(
++ reinterpret_cast<byte*>(instr) + off));
++ return 8;
++ } else if ((format[6] == '1') && (format[7] == '6')) {
++ int off = ((instr->Bits(15, 2)) << 18) >> 16;
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%+d -> %s",
++ off,
++ converter_.NameOfAddress(
++ reinterpret_cast<byte*>(instr) + off));
++ return 8;
++ }
++ case 's': {
++ ASSERT(format[1] == 'h');
++ int32_t value = 0;
++ int32_t opcode = instr->OpcodeValue() << 26;
++ int32_t sh = instr->Bits(15, 11);
++ if (opcode == EXT5 ||
++ (opcode == EXT2 &&
++ instr->Bits(10, 2) << 2 == SRADIX)) {
++ // SH Bits 1 and 15-11 (split field)
++ value = (sh | (instr->Bit(1) << 5));
++ } else {
++ // SH Bits 15-11
++ value = (sh << 26) >> 26;
++ }
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%d", value);
++ return 2;
++ }
++ case 'm': {
++ int32_t value = 0;
++ if (format[1] == 'e') {
++ if (instr->OpcodeValue() << 26 != EXT5) {
++ // ME Bits 10-6
++ value = (instr->Bits(10, 6) << 26) >> 26;
++ } else {
++ // ME Bits 5 and 10-6 (split field)
++ value = (instr->Bits(10, 6) | (instr->Bit(5) << 5));
++ }
++ } else if (format[1] == 'b') {
++ if (instr->OpcodeValue() << 26 != EXT5) {
++ // MB Bits 5-1
++ value = (instr->Bits(5, 1) << 26) >> 26;
++ } else {
++ // MB Bits 5 and 10-6 (split field)
++ value = (instr->Bits(10, 6) | (instr->Bit(5) << 5));
++ }
++ } else {
++ UNREACHABLE(); // bad format
++ }
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%d", value);
++ return 2;
++ }
++ }
++#if V8_TARGET_ARCH_PPC64
++ case 'd': { // ds value for offset
++ int32_t value = SIGN_EXT_IMM16(instr->Bits(15, 0) & ~3);
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%d", value);
++ return 1;
++ }
++#endif
++ default: {
++ UNREACHABLE();
++ break;
++ }
++ }
++
++ UNREACHABLE();
++ return -1;
++}
++
++
++// Format takes a formatting string for a whole instruction and prints it into
++// the output buffer. All escaped options are handed to FormatOption to be
++// parsed further.
++void Decoder::Format(Instruction* instr, const char* format) {
++ char cur = *format++;
++ while ((cur != 0) && (out_buffer_pos_ < (out_buffer_.length() - 1))) {
++ if (cur == '\'') { // Single quote is used as the formatting escape.
++ format += FormatOption(instr, format);
++ } else {
++ out_buffer_[out_buffer_pos_++] = cur;
++ }
++ cur = *format++;
++ }
++ out_buffer_[out_buffer_pos_] = '\0';
++}
++
++
++// The disassembler may end up decoding data inlined in the code. We do not want
++// it to crash if the data does not ressemble any known instruction.
++#define VERIFY(condition) \
++ if (!(condition)) { \
++ Unknown(instr); \
++ return; \
++ }
++
++
++// For currently unimplemented decodings the disassembler calls Unknown(instr)
++// which will just print "unknown" of the instruction bits.
++void Decoder::Unknown(Instruction* instr) {
++ Format(instr, "unknown");
++}
++
++// For currently unimplemented decodings the disassembler calls
++// UnknownFormat(instr) which will just print opcode name of the
++// instruction bits.
++void Decoder::UnknownFormat(Instruction* instr, const char* name) {
++ char buffer[100];
++ snprintf(buffer, sizeof(buffer), "%s (unknown-format)", name);
++ Format(instr, buffer);
++}
++
++void Decoder::MarkerFormat(Instruction* instr, const char* name, int id) {
++ char buffer[100];
++ snprintf(buffer, sizeof(buffer), "%s %d", name, id);
++ Format(instr, buffer);
++}
++
++// PowerPC
++void Decoder::DecodeExt1(Instruction* instr) {
++ switch (instr->Bits(10, 1) << 1) {
++ case MCRF: {
++ UnknownFormat(instr, "mcrf"); // not used by V8
++ break;
++ }
++ case BCLRX: {
++ switch (instr->Bits(25, 21) << 21) {
++ case DCBNZF: {
++ UnknownFormat(instr, "bclrx-dcbnzf");
++ break;
++ }
++ case DCBEZF: {
++ UnknownFormat(instr, "bclrx-dcbezf");
++ break;
++ }
++ case BF: {
++ UnknownFormat(instr, "bclrx-bf");
++ break;
++ }
++ case DCBNZT: {
++ UnknownFormat(instr, "bclrx-dcbbzt");
++ break;
++ }
++ case DCBEZT: {
++ UnknownFormat(instr, "bclrx-dcbnezt");
++ break;
++ }
++ case BT: {
++ UnknownFormat(instr, "bclrx-bt");
++ break;
++ }
++ case DCBNZ: {
++ UnknownFormat(instr, "bclrx-dcbnz");
++ break;
++ }
++ case DCBEZ: {
++ UnknownFormat(instr, "bclrx-dcbez"); // not used by V8
++ break;
++ }
++ case BA: {
++ if (instr->Bit(0) == 1) {
++ Format(instr, "blrl");
++ } else {
++ Format(instr, "blr");
++ }
++ break;
++ }
++ }
++ break;
++ }
++ case BCCTRX: {
++ switch (instr->Bits(25, 21) << 21) {
++ case DCBNZF: {
++ UnknownFormat(instr, "bcctrx-dcbnzf");
++ break;
++ }
++ case DCBEZF: {
++ UnknownFormat(instr, "bcctrx-dcbezf");
++ break;
++ }
++ case BF: {
++ UnknownFormat(instr, "bcctrx-bf");
++ break;
++ }
++ case DCBNZT: {
++ UnknownFormat(instr, "bcctrx-dcbnzt");
++ break;
++ }
++ case DCBEZT: {
++ UnknownFormat(instr, "bcctrx-dcbezf");
++ break;
++ }
++ case BT: {
++ UnknownFormat(instr, "bcctrx-bt");
++ break;
++ }
++ case DCBNZ: {
++ UnknownFormat(instr, "bcctrx-dcbnz");
++ break;
++ }
++ case DCBEZ: {
++ UnknownFormat(instr, "bcctrx-dcbez");
++ break;
++ }
++ case BA: {
++ if (instr->Bit(0) == 1) {
++ Format(instr, "bctrl");
++ } else {
++ Format(instr, "bctr");
++ }
++ break;
++ }
++ default: {
++ UNREACHABLE();
++ }
++ }
++ break;
++ }
++ case CRNOR: {
++ Format(instr, "crnor (stuff)");
++ break;
++ }
++ case RFI: {
++ Format(instr, "rfi (stuff)");
++ break;
++ }
++ case CRANDC: {
++ Format(instr, "crandc (stuff)");
++ break;
++ }
++ case ISYNC: {
++ Format(instr, "isync (stuff)");
++ break;
++ }
++ case CRXOR: {
++ Format(instr, "crxor (stuff)");
++ break;
++ }
++ case CRNAND: {
++ UnknownFormat(instr, "crnand");
++ break;
++ }
++ case CRAND: {
++ UnknownFormat(instr, "crand");
++ break;
++ }
++ case CREQV: {
++ UnknownFormat(instr, "creqv");
++ break;
++ }
++ case CRORC: {
++ UnknownFormat(instr, "crorc");
++ break;
++ }
++ case CROR: {
++ UnknownFormat(instr, "cror");
++ break;
++ }
++ default: {
++ Unknown(instr); // not used by V8
++ }
++ }
++}
++
++void Decoder::DecodeExt2(Instruction* instr) {
++ // Some encodings are 10-1 bits, handle those first
++ switch (instr->Bits(10, 1) << 1) {
++ case SRWX: {
++ Format(instr, "srw'. 'ra, 'rs, 'rb");
++ return;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case SRDX: {
++ Format(instr, "srd'. 'ra, 'rs, 'rb");
++ return;
++ }
++#endif
++ case SRAW: {
++ Format(instr, "sraw'. 'ra, 'rs, 'rb");
++ return;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case SRAD: {
++ Format(instr, "srad'. 'ra, 'rs, 'rb");
++ return;
++ }
++#endif
++ case SRAWIX: {
++ Format(instr, "srawi'. 'ra,'rs,'sh");
++ return;
++ }
++ case EXTSH: {
++ Format(instr, "extsh'. 'ra, 'rs");
++ return;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case EXTSW: {
++ Format(instr, "extsw'. 'ra, 'rs");
++ return;
++ }
++#endif
++ case EXTSB: {
++ Format(instr, "extsb'. 'ra, 'rs");
++ return;
++ }
++ case LFSX: {
++ Format(instr, "lfsx 'rt, 'ra, 'rb");
++ return;
++ }
++ case LFSUX: {
++ Format(instr, "lfsux 'rt, 'ra, 'rb");
++ return;
++ }
++ case LFDX: {
++ Format(instr, "lfdx 'rt, 'ra, 'rb");
++ return;
++ }
++ case LFDUX: {
++ Format(instr, "lfdux 'rt, 'ra, 'rb");
++ return;
++ }
++ case STFSX: {
++ Format(instr, "stfsx 'rs, 'ra, 'rb");
++ return;
++ }
++ case STFSUX: {
++ Format(instr, "stfsux 'rs, 'ra, 'rb");
++ return;
++ }
++ case STFDX: {
++ Format(instr, "stfdx 'rs, 'ra, 'rb");
++ return;
++ }
++ case STFDUX: {
++ Format(instr, "stfdux 'rs, 'ra, 'rb");
++ return;
++ }
++ }
++
++ switch (instr->Bits(10, 2) << 2) {
++ case SRADIX: {
++ Format(instr, "sradi'. 'ra,'rs,'sh");
++ return;
++ }
++ }
++
++ // ?? are all of these xo_form?
++ switch (instr->Bits(9, 1) << 1) {
++ case CMP: {
++ Format(instr, "cmp 'ra, 'rb");
++ break;
++ }
++ case SLWX: {
++ Format(instr, "slw'. 'ra, 'rs, 'rb");
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case SLDX: {
++ Format(instr, "sld'. 'ra, 'rs, 'rb");
++ break;
++ }
++#endif
++ case SUBFCX: {
++ Format(instr, "subfc'. 'rt, 'ra, 'rb");
++ break;
++ }
++ case ADDCX: {
++ Format(instr, "addc'. 'rt, 'ra, 'rb");
++ break;
++ }
++ case CNTLZWX: {
++ Format(instr, "cntlzw'. 'ra, 'rs");
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case CNTLZDX: {
++ Format(instr, "cntlzd'. 'ra, 'rs");
++ break;
++ }
++#endif
++ case ANDX: {
++ Format(instr, "and'. 'ra, 'rs, 'rb");
++ break;
++ }
++ case ANDCX: {
++ Format(instr, "andc'. 'ra, 'rs, 'rb");
++ break;
++ }
++ case CMPL: {
++ Format(instr, "cmpl 'ra, 'rb");
++ break;
++ }
++ case NEGX: {
++ Format(instr, "neg'. 'rt, 'ra");
++ break;
++ }
++ case NORX: {
++ Format(instr, "nor'. 'rt, 'ra, 'rb");
++ break;
++ }
++ case SUBFX: {
++ Format(instr, "subf'. 'rt, 'ra, 'rb");
++ break;
++ }
++ case MULHWX: {
++ Format(instr, "mulhw'o'. 'rt, 'ra, 'rb");
++ break;
++ }
++ case ADDZEX: {
++ Format(instr, "addze'. 'rt, 'ra");
++ break;
++ }
++ case MULLW: {
++ Format(instr, "mullw'o'. 'rt, 'ra, 'rb");
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case MULLD: {
++ Format(instr, "mulld'o'. 'rt, 'ra, 'rb");
++ break;
++ }
++#endif
++ case DIVW: {
++ Format(instr, "divw'o'. 'rt, 'ra, 'rb");
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case DIVD: {
++ Format(instr, "divd'o'. 'rt, 'ra, 'rb");
++ break;
++ }
++#endif
++ case ADDX: {
++ Format(instr, "add'o 'rt, 'ra, 'rb");
++ break;
++ }
++ case XORX: {
++ Format(instr, "xor'. 'ra, 'rs, 'rb");
++ break;
++ }
++ case ORX: {
++ if ( instr->RTValue() == instr->RBValue() ) {
++ Format(instr, "mr 'ra, 'rb");
++ } else {
++ Format(instr, "or 'ra, 'rs, 'rb");
++ }
++ break;
++ }
++ case MFSPR: {
++ int spr = instr->Bits(20, 11);
++ if (256 == spr) {
++ Format(instr, "mflr 'rt");
++ } else {
++ Format(instr, "mfspr 'rt ??");
++ }
++ break;
++ }
++ case MTSPR: {
++ int spr = instr->Bits(20, 11);
++ if (256 == spr) {
++ Format(instr, "mtlr 'rt");
++ } else if (288 == spr) {
++ Format(instr, "mtctr 'rt");
++ } else {
++ Format(instr, "mtspr 'rt ??");
++ }
++ break;
++ }
++ case MFCR: {
++ Format(instr, "mfcr 'rt");
++ break;
++ }
++ case STWX: {
++ Format(instr, "stwx 'rs, 'ra, 'rb");
++ break;
++ }
++ case STWUX: {
++ Format(instr, "stwux 'rs, 'ra, 'rb");
++ break;
++ }
++ case STBX: {
++ Format(instr, "stbx 'rs, 'ra, 'rb");
++ break;
++ }
++ case STBUX: {
++ Format(instr, "stbux 'rs, 'ra, 'rb");
++ break;
++ }
++ case STHX: {
++ Format(instr, "sthx 'rs, 'ra, 'rb");
++ break;
++ }
++ case STHUX: {
++ Format(instr, "sthux 'rs, 'ra, 'rb");
++ break;
++ }
++ case LWZX: {
++ Format(instr, "lwzx 'rt, 'ra, 'rb");
++ break;
++ }
++ case LWZUX: {
++ Format(instr, "lwzux 'rt, 'ra, 'rb");
++ break;
++ }
++ case LBZX: {
++ Format(instr, "lbzx 'rt, 'ra, 'rb");
++ break;
++ }
++ case LBZUX: {
++ Format(instr, "lbzux 'rt, 'ra, 'rb");
++ break;
++ }
++ case LHZX: {
++ Format(instr, "lhzx 'rt, 'ra, 'rb");
++ break;
++ }
++ case LHZUX: {
++ Format(instr, "lhzux 'rt, 'ra, 'rb");
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case LDX: {
++ Format(instr, "ldx 'rt, 'ra, 'rb");
++ break;
++ }
++ case LDUX: {
++ Format(instr, "ldux 'rt, 'ra, 'rb");
++ break;
++ }
++ case STDX: {
++ Format(instr, "stdx 'rt, 'ra, 'rb");
++ break;
++ }
++ case STDUX: {
++ Format(instr, "stdux 'rt, 'ra, 'rb");
++ break;
++ }
++#endif
++ default: {
++ Unknown(instr); // not used by V8
++ }
++ }
++}
++
++void Decoder::DecodeExt4(Instruction* instr) {
++ switch (instr->Bits(5, 1) << 1) {
++ case FDIV: {
++ Format(instr, "fdiv'. 'Dt, 'Da, 'Db");
++ return;
++ }
++ case FSUB: {
++ Format(instr, "fsub'. 'Dt, 'Da, 'Db");
++ return;
++ }
++ case FADD: {
++ Format(instr, "fadd'. 'Dt, 'Da, 'Db");
++ return;
++ }
++ case FSQRT: {
++ Format(instr, "fsqrt'. 'Dt, 'Db");
++ return;
++ }
++ case FSEL: {
++ Format(instr, "fsel'. 'Dt, 'Da, 'Dc, 'Db");
++ return;
++ }
++ case FMUL: {
++ Format(instr, "fmul'. 'Dt, 'Da, 'Dc");
++ return;
++ }
++ }
++
++ switch (instr->Bits(10, 1) << 1) {
++ case FCMPU: {
++ Format(instr, "fcmpu 'Da, 'Db");
++ break;
++ }
++ case FRSP: {
++ Format(instr, "frsp'. 'Dt, 'Db");
++ break;
++ }
++ case FCFID: {
++ Format(instr, "fcfid'. 'Dt, 'Db");
++ break;
++ }
++ case FCTID: {
++ Format(instr, "fctid 'Dt, 'Db");
++ break;
++ }
++ case FCTIDZ: {
++ Format(instr, "fctidz 'Dt, 'Db");
++ break;
++ }
++ case FCTIW: {
++ Format(instr, "fctiw'. 'Dt, 'Db");
++ break;
++ }
++ case FCTIWZ: {
++ Format(instr, "fctiwz'. 'Dt, 'Db");
++ break;
++ }
++ case FMR: {
++ Format(instr, "fmr'. 'Dt, 'Db");
++ break;
++ }
++ case MTFSFI: {
++ Format(instr, "mtfsfi'. ?,?");
++ break;
++ }
++ case MFFS: {
++ Format(instr, "mffs'. 'Dt");
++ break;
++ }
++ case MTFSF: {
++ Format(instr, "mtfsf'. 'Db ?,?,?");
++ break;
++ }
++ case FABS: {
++ Format(instr, "fabs'. 'Dt, 'Db");
++ break;
++ }
++ case FRIM: {
++ Format(instr, "frim 'Dt, 'Db");
++ break;
++ }
++ case FNEG: {
++ Format(instr, "fneg'. 'Dt, 'Db");
++ break;
++ }
++ default: {
++ Unknown(instr); // not used by V8
++ }
++ }
++}
++
++void Decoder::DecodeExt5(Instruction* instr) {
++ switch (instr->Bits(4, 2) << 2) {
++ case RLDICL: {
++ Format(instr, "rldicl'. 'ra, 'rs, 'sh, 'mb");
++ break;
++ }
++ case RLDICR: {
++ Format(instr, "rldicr'. 'ra, 'rs, 'sh, 'me");
++ break;
++ }
++ case RLDIC: {
++ Format(instr, "rldic'. 'ra, 'rs, 'sh, 'mb");
++ break;
++ }
++ case RLDIMI: {
++ Format(instr, "rldimi'. 'ra, 'rs, 'sh, 'mb");
++ break;
++ }
++ default: {
++ Unknown(instr); // not used by V8
++ }
++ }
++}
++
++#undef VERIFIY
++
++// Disassemble the instruction at *instr_ptr into the output buffer.
++int Decoder::InstructionDecode(byte* instr_ptr) {
++ Instruction* instr = Instruction::At(instr_ptr);
++ // Print raw instruction bytes.
++ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_,
++ "%08x ",
++ instr->InstructionBits());
++
++ switch (instr->OpcodeValue() << 26) {
++ case TWI: {
++ PrintSoftwareInterrupt(instr->SvcValue());
++ break;
++ }
++ case MULLI: {
++ UnknownFormat(instr, "mulli");
++ break;
++ }
++ case SUBFIC: {
++ Format(instr, "subfic 'rt, 'ra, 'int16");
++ break;
++ }
++ case CMPLI: {
++ Format(instr, "cmpli 'ra, 'uint16");
++ break;
++ }
++ case CMPI: {
++ Format(instr, "cmpi 'ra, 'int16");
++ break;
++ }
++ case ADDIC: {
++ Format(instr, "addic 'rt, 'ra, 'int16");
++ break;
++ }
++ case ADDICx: {
++ UnknownFormat(instr, "addicx");
++ break;
++ }
++ case ADDI: {
++ if ( instr->RAValue() == 0 ) {
++ // this is load immediate
++ Format(instr, "li 'rt, 'int16");
++ } else {
++ Format(instr, "addi 'rt, 'ra, 'int16");
++ }
++ break;
++ }
++ case ADDIS: {
++ if ( instr->RAValue() == 0 ) {
++ Format(instr, "lis 'rt, 'int16");
++ } else {
++ Format(instr, "addis 'rt, 'ra, 'int16");
++ }
++ break;
++ }
++ case BCX: {
++ int bo = instr->Bits(25, 21) << 21;
++ int bi = instr->Bits(20, 16);
++ switch (bi) {
++ case 2:
++ case 30:
++ if (BT == bo) {
++ Format(instr, "beq'l'a 'target16");
++ break;
++ }
++ if (BF == bo) {
++ Format(instr, "bne'l'a 'target16");
++ break;
++ }
++ Format(instr, "bc'l'a 'target16");
++ break;
++ case 29:
++ if (BT == bo) {
++ Format(instr, "bgt'l'a 'target16");
++ break;
++ }
++ if (BF == bo) {
++ Format(instr, "ble'l'a 'target16");
++ break;
++ }
++ Format(instr, "bc'l'a 'target16");
++ break;
++ case 28:
++ if (BT == bo) {
++ Format(instr, "blt'l'a 'target16");
++ break;
++ }
++ if (BF == bo) {
++ Format(instr, "bge'l'a 'target16");
++ break;
++ }
++ Format(instr, "bc'l'a 'target16");
++ break;
++ default:
++ Format(instr, "bc'l'a 'target16");
++ break;
++ }
++ break;
++ }
++ case SC: {
++ UnknownFormat(instr, "sc");
++ break;
++ }
++ case BX: {
++ Format(instr, "b'l'a 'target26");
++ break;
++ }
++ case EXT1: {
++ DecodeExt1(instr);
++ break;
++ }
++ case RLWIMIX: {
++ Format(instr, "rlwimi'. 'ra, 'rs, 'sh, 'me,
'mb");
++ break;
++ }
++ case RLWINMX: {
++ Format(instr, "rlwinm'. 'ra, 'rs, 'sh, 'me,
'mb");
++ break;
++ }
++ case RLWNMX: {
++ UnknownFormat(instr, "rlwnmx");
++ break;
++ }
++ case ORI: {
++ Format(instr, "ori 'ra, 'rs, 'uint16");
++ break;
++ }
++ case ORIS: {
++ Format(instr, "oris 'ra, 'rs, 'uint16");
++ break;
++ }
++ case XORI: {
++ Format(instr, "xori 'ra, 'rs, 'uint16");
++ break;
++ }
++ case XORIS: {
++ Format(instr, "xoris 'ra, 'rs, 'uint16");
++ break;
++ }
++ case ANDIx: {
++ Format(instr, "andi. 'ra, 'rs, 'uint16");
++ break;
++ }
++ case ANDISx: {
++ Format(instr, "andis. 'ra, 'rs, 'uint16");
++ break;
++ }
++ case EXT2: {
++ DecodeExt2(instr);
++ break;
++ }
++ case LWZ: {
++ Format(instr, "lwz 'rt, 'int16('ra)");
++ break;
++ }
++ case LWZU: {
++ Format(instr, "lwzu 'rt, 'int16('ra)");
++ break;
++ }
++ case LBZ: {
++ Format(instr, "lbz 'rt, 'int16('ra)");
++ break;
++ }
++ case LBZU: {
++ Format(instr, "lbzu 'rt, 'int16('ra)");
++ break;
++ }
++ case STW: {
++ Format(instr, "stw 'rs, 'int16('ra)");
++ break;
++ }
++ case STWU: {
++ Format(instr, "stwu 'rs, 'int16('ra)");
++ break;
++ }
++ case STB: {
++ Format(instr, "stb 'rs, 'int16('ra)");
++ break;
++ }
++ case STBU: {
++ Format(instr, "stbu 'rs, 'int16('ra)");
++ break;
++ }
++ case LHZ: {
++ Format(instr, "lhz 'rt, 'int16('ra)");
++ break;
++ }
++ case LHZU: {
++ Format(instr, "lhzu 'rt, 'int16('ra)");
++ break;
++ }
++ case LHA: {
++ Format(instr, "lha 'rt, 'int16('ra)");
++ break;
++ }
++ case LHAU: {
++ Format(instr, "lhau 'rt, 'int16('ra)");
++ break;
++ }
++ case STH: {
++ Format(instr, "sth 'rs, 'int16('ra)");
++ break;
++ }
++ case STHU: {
++ Format(instr, "sthu 'rs, 'int16('ra)");
++ break;
++ }
++ case LMW: {
++ UnknownFormat(instr, "lmw");
++ break;
++ }
++ case STMW: {
++ UnknownFormat(instr, "stmw");
++ break;
++ }
++ case LFS: {
++ Format(instr, "lfs 'Dt, 'int16('ra)");
++ break;
++ }
++ case LFSU: {
++ Format(instr, "lfsu 'Dt, 'int16('ra)");
++ break;
++ }
++ case LFD: {
++ Format(instr, "lfd 'Dt, 'int16('ra)");
++ break;
++ }
++ case LFDU: {
++ Format(instr, "lfdu 'Dt, 'int16('ra)");
++ break;
++ }
++ case STFS: {
++ Format(instr, "stfs 'Dt, 'int16('ra)");
++ break;
++ }
++ case STFSU: {
++ Format(instr, "stfsu 'Dt, 'int16('ra)");
++ break;
++ }
++ case STFD: {
++ Format(instr, "stfd 'Dt, 'int16('ra)");
++ break;
++ }
++ case STFDU: {
++ Format(instr, "stfdu 'Dt, 'int16('ra)");
++ break;
++ }
++ case EXT3:
++ case EXT4: {
++ DecodeExt4(instr);
++ break;
++ }
++ case EXT5: {
++ DecodeExt5(instr);
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case LD: {
++ switch (instr->Bits(1, 0)) {
++ case 0:
++ Format(instr, "ld 'rt, 'd('ra)");
++ break;
++ case 1:
++ Format(instr, "ldu 'rt, 'd('ra)");
++ break;
++ case 2:
++ Format(instr, "lwa 'rt, 'd('ra)");
++ break;
++ }
++ break;
++ }
++ case STD: { // could be STD or STDU
++ if (instr->Bit(0) == 0) {
++ Format(instr, "std 'rs, 'd('ra)");
++ } else {
++ Format(instr, "stdu 'rs, 'd('ra)");
++ }
++ break;
++ }
++#endif
++
++ case FAKE_OPCODE: {
++ if (instr->Bits(MARKER_SUBOPCODE_BIT, MARKER_SUBOPCODE_BIT) == 1) {
++ int marker_code = instr->Bits(STUB_MARKER_HIGH_BIT, 0);
++ ASSERT(marker_code < F_NEXT_AVAILABLE_STUB_MARKER);
++ MarkerFormat(instr, "stub-marker ", marker_code);
++ } else {
++ int fake_opcode = instr->Bits(FAKE_OPCODE_HIGH_BIT, 0);
++ MarkerFormat(instr, "faker-opcode ", fake_opcode);
++ }
++ break;
++ }
++ default: {
++ Unknown(instr);
++ break;
++ }
++ }
++
++ return Instruction::kInstrSize;
++}
++
++
++} } // namespace v8::internal
++
++
++
++//------------------------------------------------------------------------------
++
++namespace disasm {
++
++
++const char* NameConverter::NameOfAddress(byte* addr) const {
++ v8::internal::OS::SNPrintF(tmp_buffer_, "%p", addr);
++ return tmp_buffer_.start();
++}
++
++
++const char* NameConverter::NameOfConstant(byte* addr) const {
++ return NameOfAddress(addr);
++}
++
++
++const char* NameConverter::NameOfCPURegister(int reg) const {
++ return v8::internal::Registers::Name(reg);
++}
++
++const char* NameConverter::NameOfByteCPURegister(int reg) const {
++ UNREACHABLE(); // PPC does not have the concept of a byte register
++ return "nobytereg";
++}
++
++
++const char* NameConverter::NameOfXMMRegister(int reg) const {
++ UNREACHABLE(); // PPC does not have any XMM registers
++ return "noxmmreg";
++}
++
++const char* NameConverter::NameInCode(byte* addr) const {
++ // The default name converter is called for unknown code. So we will not try
++ // to access any memory.
++ return "";
++}
++
++
++//------------------------------------------------------------------------------
++
++Disassembler::Disassembler(const NameConverter& converter)
++ : converter_(converter) {}
++
++
++Disassembler::~Disassembler() {}
++
++
++int Disassembler::InstructionDecode(v8::internal::Vector<char> buffer,
++ byte* instruction) {
++ v8::internal::Decoder d(converter_, buffer);
++ return d.InstructionDecode(instruction);
++}
++
++
++// The PPC assembler does not currently use constant pools.
++int Disassembler::ConstantPoolSizeAt(byte* instruction) {
++ return -1;
++}
++
++
++void Disassembler::Disassemble(FILE* f, byte* begin, byte* end) {
++ NameConverter converter;
++ Disassembler d(converter);
++ for (byte* pc = begin; pc < end;) {
++ v8::internal::EmbeddedVector<char, 128> buffer;
++ buffer[0] = '\0';
++ byte* prev_pc = pc;
++ pc += d.InstructionDecode(buffer, pc);
++ fprintf(f, "%p %08x %s\n",
++ prev_pc, *reinterpret_cast<int32_t*>(prev_pc), buffer.start());
++ }
++}
++
++
++} // namespace disasm
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/frames-ppc.cc.ppc v8-3.14.5.10/src/ppc/frames-ppc.cc
+--- v8-3.14.5.10/src/ppc/frames-ppc.cc.ppc 2016-06-07 14:15:45.992392996 -0400
++++ v8-3.14.5.10/src/ppc/frames-ppc.cc 2016-06-07 14:15:45.992392996 -0400
+@@ -0,0 +1,48 @@
++// Copyright 2011 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "frames-inl.h"
++
++namespace v8 {
++namespace internal {
++
++
++Address ExitFrame::ComputeStackPointer(Address fp) {
++ return Memory::Address_at(fp + ExitFrameConstants::kSPOffset);
++}
++
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/frames-ppc.h.ppc v8-3.14.5.10/src/ppc/frames-ppc.h
+--- v8-3.14.5.10/src/ppc/frames-ppc.h.ppc 2016-06-07 14:15:45.992392996 -0400
++++ v8-3.14.5.10/src/ppc/frames-ppc.h 2016-06-07 14:15:45.992392996 -0400
+@@ -0,0 +1,228 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_FRAMES_PPC_H_
++#define V8_PPC_FRAMES_PPC_H_
++
++namespace v8 {
++namespace internal {
++
++
++// Register list in load/store instructions
++// Note that the bit values must match those used in actual instruction encoding
++const int kNumRegs = 32;
++
++
++// Caller-saved/arguments registers
++const RegList kJSCallerSaved =
++ 1 << 3 | // r3 a1
++ 1 << 4 | // r4 a2
++ 1 << 5 | // r5 a3
++ 1 << 6 | // r6 a4
++ 1 << 7 | // r7 a5
++ 1 << 8 | // r8 a6
++ 1 << 9 | // r9 a7
++ 1 << 10 | // r10 a8
++ 1 << 11;
++
++const int kNumJSCallerSaved = 9;
++
++typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved];
++
++// Return the code of the n-th caller-saved register available to JavaScript
++// e.g. JSCallerSavedReg(0) returns r0.code() == 0
++int JSCallerSavedCode(int n);
++
++
++// Callee-saved registers preserved when switching from C to JavaScript
++// N.B. Do not bother saving all non-volatiles -- only those that v8
++// modifies without saving/restoring inline.
++const RegList kCalleeSaved =
++ 1 << 14 | // r14 (argument passing in CEntryStub)
++ 1 << 15 | // r15 (argument passing in CEntryStub)
++ 1 << 16 | // r16 (argument passing in CEntryStub)
++ // r17-r19 unused
++ 1 << 20 | // r20 (cp in Javascript code)
++ 1 << 21 | // r21 (roots array in Javascript code)
++ 1 << 22 | // r22 (r9 hack in Javascript code)
++ // r23-r25 unused
++ 1 << 26 | // r26 (HandleScope logic in MacroAssembler)
++ 1 << 27 | // r27 (HandleScope logic in MacroAssembler)
++ 1 << 28 | // r28 (HandleScope logic in MacroAssembler)
++ 1 << 29 | // r29 (HandleScope logic in MacroAssembler)
++ // r30 used but saved/restored inline
++ 1 << 31; // r31 (fp in Javascript code)
++
++
++const int kNumCalleeSaved = 11;
++
++// Number of registers for which space is reserved in safepoints. Must be a
++// multiple of 8.
++// TODO(regis): Only 8 registers may actually be sufficient. Revisit.
++const int kNumSafepointRegisters = 32;
++
++// Define the list of registers actually saved at safepoints.
++// Note that the number of saved registers may be smaller than the reserved
++// space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters.
++const RegList kSafepointSavedRegisters = kJSCallerSaved | kCalleeSaved;
++const int kNumSafepointSavedRegisters = kNumJSCallerSaved + kNumCalleeSaved;
++
++// The following constants describe the stack frame linkage area as
++// defined by the ABI.
++#if defined(V8_TARGET_ARCH_PPC64) && __BYTE_ORDER == __LITTLE_ENDIAN
++// [0] back chain
++// [1] condition register save area
++// [2] link register save area
++// [3] TOC save area
++// [4] Parameter1 save area
++// ...
++// [11] Parameter8 save area
++// [12] Parameter9 slot (if necessary)
++// ...
++const int kNumRequiredStackFrameSlots = 12;
++const int kStackFrameLRSlot = 2;
++const int kStackFrameExtraParamSlot = 12;
++#elif defined(_AIX) || defined(V8_TARGET_ARCH_PPC64)
++// [0] back chain
++// [1] condition register save area
++// [2] link register save area
++// [3] reserved for compiler
++// [4] reserved by binder
++// [5] TOC save area
++// [6] Parameter1 save area
++// ...
++// [13] Parameter8 save area
++// [14] Parameter9 slot (if necessary)
++// ...
++const int kNumRequiredStackFrameSlots = 14;
++const int kStackFrameLRSlot = 2;
++const int kStackFrameExtraParamSlot = 14;
++#else
++// [0] back chain
++// [1] link register save area
++// [2] Parameter9 slot (if necessary)
++// ...
++const int kNumRequiredStackFrameSlots = 2;
++const int kStackFrameLRSlot = 1;
++const int kStackFrameExtraParamSlot = 2;
++#endif
++
++// ----------------------------------------------------
++
++
++class StackHandlerConstants : public AllStatic {
++ public:
++ static const int kNextOffset = 0 * kPointerSize;
++ static const int kCodeOffset = 1 * kPointerSize;
++#if defined(V8_TARGET_ARCH_PPC64) && (__BYTE_ORDER == __BIG_ENDIAN)
++ static const int kStateOffset = 2 * kPointerSize + kIntSize;
++#else
++ static const int kStateOffset = 2 * kPointerSize;
++#endif
++ static const int kStateSlot = 2 * kPointerSize;
++ static const int kContextOffset = 3 * kPointerSize;
++ static const int kFPOffset = 4 * kPointerSize;
++
++ static const int kSize = kFPOffset + kPointerSize;
++};
++
++
++class EntryFrameConstants : public AllStatic {
++ public:
++ static const int kCallerFPOffset = -3 * kPointerSize;
++};
++
++
++class ExitFrameConstants : public AllStatic {
++ public:
++ static const int kCodeOffset = -2 * kPointerSize;
++ static const int kSPOffset = -1 * kPointerSize;
++
++ // The caller fields are below the frame pointer on the stack.
++ static const int kCallerFPOffset = 0 * kPointerSize;
++ // The calling JS function is below FP.
++ static const int kCallerPCOffset = 1 * kPointerSize;
++
++ // FP-relative displacement of the caller's SP. It points just
++ // below the saved PC.
++ static const int kCallerSPDisplacement = 2 * kPointerSize;
++};
++
++
++class StandardFrameConstants : public AllStatic {
++ public:
++ // Fixed part of the frame consists of return address, caller fp,
++ // context and function.
++ static const int kFixedFrameSize = 4 * kPointerSize;
++ static const int kExpressionsOffset = -3 * kPointerSize;
++ static const int kMarkerOffset = -2 * kPointerSize;
++ static const int kContextOffset = -1 * kPointerSize;
++ static const int kCallerFPOffset = 0 * kPointerSize;
++ static const int kCallerPCOffset = 1 * kPointerSize;
++ static const int kCallerSPOffset = 2 * kPointerSize;
++};
++
++
++class JavaScriptFrameConstants : public AllStatic {
++ public:
++ // FP-relative.
++ static const int kLocal0Offset = StandardFrameConstants::kExpressionsOffset;
++ static const int kLastParameterOffset = +2 * kPointerSize;
++ static const int kFunctionOffset = StandardFrameConstants::kMarkerOffset;
++
++ // Caller SP-relative.
++ static const int kParam0Offset = -2 * kPointerSize;
++ static const int kReceiverOffset = -1 * kPointerSize;
++};
++
++
++class ArgumentsAdaptorFrameConstants : public AllStatic {
++ public:
++ static const int kLengthOffset = StandardFrameConstants::kExpressionsOffset;
++ static const int kFrameSize =
++ StandardFrameConstants::kFixedFrameSize + kPointerSize;
++};
++
++
++class InternalFrameConstants : public AllStatic {
++ public:
++ static const int kCodeOffset = StandardFrameConstants::kExpressionsOffset;
++};
++
++
++inline Object* JavaScriptFrame::function_slot_object() const {
++ const int offset = JavaScriptFrameConstants::kFunctionOffset;
++ return Memory::Object_at(fp() + offset);
++}
++
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_FRAMES_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/full-codegen-ppc.cc.ppc
v8-3.14.5.10/src/ppc/full-codegen-ppc.cc
+--- v8-3.14.5.10/src/ppc/full-codegen-ppc.cc.ppc 2016-06-07 14:15:45.994392985 -0400
++++ v8-3.14.5.10/src/ppc/full-codegen-ppc.cc 2016-06-07 14:15:45.994392985 -0400
+@@ -0,0 +1,4609 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "code-stubs.h"
++#include "codegen.h"
++#include "compiler.h"
++#include "debug.h"
++#include "full-codegen.h"
++#include "isolate-inl.h"
++#include "parser.h"
++#include "scopes.h"
++#include "stub-cache.h"
++
++#include "ppc/code-stubs-ppc.h"
++#include "ppc/macro-assembler-ppc.h"
++
++namespace v8 {
++namespace internal {
++
++#define __ ACCESS_MASM(masm_)
++
++// A patch site is a location in the code which it is possible to patch. This
++// class has a number of methods to emit the code which is patchable and the
++// method EmitPatchInfo to record a marker back to the patchable code. This
++// marker is a cmpi rx, #yyy instruction, and x * 0x0000ffff + yyy (raw 16 bit
++// immediate value is used) is the delta from the pc to the first instruction of
++// the patchable code.
++// See PatchInlinedSmiCode in ic-ppc.cc for the code that patches it
++class JumpPatchSite BASE_EMBEDDED {
++ public:
++ explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) {
++#ifdef DEBUG
++ info_emitted_ = false;
++#endif
++ }
++
++ ~JumpPatchSite() {
++ ASSERT(patch_site_.is_bound() == info_emitted_);
++ }
++
++ // When initially emitting this ensure that a jump is always generated to skip
++ // the inlined smi code.
++ void EmitJumpIfNotSmi(Register reg, Label* target) {
++ ASSERT(!patch_site_.is_bound() && !info_emitted_);
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
++ __ bind(&patch_site_);
++ __ cmp(reg, reg, cr0);
++ __ beq(target, cr0); // Always taken before patched.
++ }
++
++ // When initially emitting this ensure that a jump is never generated to skip
++ // the inlined smi code.
++ void EmitJumpIfSmi(Register reg, Label* target) {
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
++ ASSERT(!patch_site_.is_bound() && !info_emitted_);
++ __ bind(&patch_site_);
++ __ cmp(reg, reg, cr0);
++ __ bne(target, cr0); // Never taken before patched.
++ }
++
++ void EmitPatchInfo() {
++ if (patch_site_.is_bound()) {
++ int delta_to_patch_site = masm_->InstructionsGeneratedSince(&patch_site_);
++ Register reg;
++ // I believe this is using reg as the high bits of of the offset
++ reg.set_code(delta_to_patch_site / kOff16Mask);
++ __ cmpi(reg, Operand(delta_to_patch_site % kOff16Mask));
++#ifdef DEBUG
++ info_emitted_ = true;
++#endif
++ } else {
++ __ nop(); // Signals no inlined code.
++ }
++ }
++
++ private:
++ MacroAssembler* masm_;
++ Label patch_site_;
++#ifdef DEBUG
++ bool info_emitted_;
++#endif
++};
++
++
++// Generate code for a JS function. On entry to the function the receiver
++// and arguments have been pushed on the stack left to right. The actual
++// argument count matches the formal parameter count expected by the
++// function.
++//
++// The live registers are:
++// o r4: the JS function object being called (i.e., ourselves)
++// o cp: our context
++// o fp: our caller's frame pointer (aka r31)
++// o sp: stack pointer
++// o lr: return address (bogus.. PPC has no lr reg)
++//
++// The function builds a JS frame. Please see JavaScriptFrameConstants in
++// frames-ppc.h for its layout.
++void FullCodeGenerator::Generate() {
++ CompilationInfo* info = info_;
++ handler_table_ =
++ isolate()->factory()->NewFixedArray(function()->handler_count(),
TENURED);
++ profiling_counter_ = isolate()->factory()->NewJSGlobalPropertyCell(
++ Handle<Smi>(Smi::FromInt(FLAG_interrupt_budget)));
++ SetFunctionPosition(function());
++ Comment cmnt(masm_, "[ function compiled by full code generator");
++
++ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
++
++#ifdef DEBUG
++ if (strlen(FLAG_stop_at) > 0 &&
++ info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
++ __ stop("stop-at");
++ }
++#endif
++
++ // Strict mode functions and builtins need to replace the receiver
++ // with undefined when called as functions (without an explicit
++ // receiver object). r8 is zero for method calls and non-zero for
++ // function calls.
++ if (!info->is_classic_mode() || info->is_native()) {
++ Label ok;
++ __ cmpi(r8, Operand::Zero());
++ __ beq(&ok);
++ int receiver_offset = info->scope()->num_parameters() * kPointerSize;
++ __ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
++ __ StoreP(r5, MemOperand(sp, receiver_offset), r0);
++ __ bind(&ok);
++ }
++
++ // Open a frame scope to indicate that there is a frame on the stack. The
++ // MANUAL indicates that the scope shouldn't actually generate code to set up
++ // the frame (that is done below).
++ FrameScope frame_scope(masm_, StackFrame::MANUAL);
++
++ int locals_count = info->scope()->num_stack_slots();
++
++ __ mflr(r0);
++ __ Push(r0, fp, cp, r4);
++
++ if (locals_count > 0) {
++ // Load undefined value here, so the value is ready for the loop
++ // below.
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ }
++ // Adjust fp to point to caller's fp.
++ __ addi(fp, sp, Operand(2 * kPointerSize));
++
++ { Comment cmnt(masm_, "[ Allocate locals");
++ for (int i = 0; i < locals_count; i++) {
++ __ push(ip);
++ }
++ }
++
++ bool function_in_register = true;
++
++ // Possibly allocate a local context.
++ int heap_slots = info->scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
++ if (heap_slots > 0) {
++ // Argument to NewContext is the function, which is still in r4.
++ Comment cmnt(masm_, "[ Allocate context");
++ __ push(r4);
++ if (FLAG_harmony_scoping && info->scope()->is_global_scope()) {
++ __ Push(info->scope()->GetScopeInfo());
++ __ CallRuntime(Runtime::kNewGlobalContext, 2);
++ } else if (heap_slots <= FastNewContextStub::kMaximumSlots) {
++ FastNewContextStub stub(heap_slots);
++ __ CallStub(&stub);
++ } else {
++ __ CallRuntime(Runtime::kNewFunctionContext, 1);
++ }
++ function_in_register = false;
++ // Context is returned in both r3 and cp. It replaces the context
++ // passed to us. It's saved in the stack and kept live in cp.
++ __ StoreP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ // Copy any necessary parameters into the context.
++ int num_parameters = info->scope()->num_parameters();
++ for (int i = 0; i < num_parameters; i++) {
++ Variable* var = scope()->parameter(i);
++ if (var->IsContextSlot()) {
++ int parameter_offset = StandardFrameConstants::kCallerSPOffset +
++ (num_parameters - 1 - i) * kPointerSize;
++ // Load parameter from stack.
++ __ LoadP(r3, MemOperand(fp, parameter_offset), r0);
++ // Store it in the context.
++ MemOperand target = ContextOperand(cp, var->index());
++ __ StoreP(r3, target, r0);
++
++ // Update the write barrier.
++ __ RecordWriteContextSlot(
++ cp, target.offset(), r3, r6, kLRHasBeenSaved, kDontSaveFPRegs);
++ }
++ }
++ }
++
++ Variable* arguments = scope()->arguments();
++ if (arguments != NULL) {
++ // Function uses arguments object.
++ Comment cmnt(masm_, "[ Allocate arguments object");
++ if (!function_in_register) {
++ // Load this again, if it's used by the local context below.
++ __ LoadP(r6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ } else {
++ __ mr(r6, r4);
++ }
++ // Receiver is just before the parameters on the caller's stack.
++ int num_parameters = info->scope()->num_parameters();
++ int offset = num_parameters * kPointerSize;
++ __ addi(r5, fp,
++ Operand(StandardFrameConstants::kCallerSPOffset + offset));
++ __ LoadSmiLiteral(r4, Smi::FromInt(num_parameters));
++ __ Push(r6, r5, r4);
++
++ // Arguments to ArgumentsAccessStub:
++ // function, receiver address, parameter count.
++ // The stub will rewrite receiever and parameter count if the previous
++ // stack frame was an arguments adapter frame.
++ ArgumentsAccessStub::Type type;
++ if (!is_classic_mode()) {
++ type = ArgumentsAccessStub::NEW_STRICT;
++ } else if (function()->has_duplicate_parameters()) {
++ type = ArgumentsAccessStub::NEW_NON_STRICT_SLOW;
++ } else {
++ type = ArgumentsAccessStub::NEW_NON_STRICT_FAST;
++ }
++ ArgumentsAccessStub stub(type);
++ __ CallStub(&stub);
++
++ SetVar(arguments, r3, r4, r5);
++ }
++
++ if (FLAG_trace) {
++ __ CallRuntime(Runtime::kTraceEnter, 0);
++ }
++
++ // Visit the declarations and body unless there is an illegal
++ // redeclaration.
++ if (scope()->HasIllegalRedeclaration()) {
++ Comment cmnt(masm_, "[ Declarations");
++ scope()->VisitIllegalRedeclaration(this);
++
++ } else {
++ PrepareForBailoutForId(BailoutId::FunctionEntry(), NO_REGISTERS);
++ { Comment cmnt(masm_, "[ Declarations");
++ // For named function expressions, declare the function name as a
++ // constant.
++ if (scope()->is_function_scope() && scope()->function() != NULL) {
++ VariableDeclaration* function = scope()->function();
++ ASSERT(function->proxy()->var()->mode() == CONST ||
++ function->proxy()->var()->mode() == CONST_HARMONY);
++ ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
++ VisitVariableDeclaration(function);
++ }
++ VisitDeclarations(scope()->declarations());
++ }
++
++ { Comment cmnt(masm_, "[ Stack check");
++ PrepareForBailoutForId(BailoutId::Declarations(), NO_REGISTERS);
++ Label ok;
++ __ LoadRoot(ip, Heap::kStackLimitRootIndex);
++ __ cmpl(sp, ip);
++ // This is a FIXED_SEQUENCE and must match the other StackCheck code
++ __ bc_short(ge, &ok);
++ StackCheckStub stub;
++ __ CallStub(&stub);
++ __ bind(&ok);
++ }
++
++ { Comment cmnt(masm_, "[ Body");
++ ASSERT(loop_depth() == 0);
++ VisitStatements(function()->body());
++ ASSERT(loop_depth() == 0);
++ }
++ }
++
++ // Always emit a 'return undefined' in case control fell off the end of
++ // the body.
++ { Comment cmnt(masm_, "[ return <undefined>;");
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ }
++ EmitReturnSequence();
++}
++
++
++void FullCodeGenerator::ClearAccumulator() {
++ __ LoadSmiLiteral(r3, Smi::FromInt(0));
++}
++
++
++void FullCodeGenerator::EmitProfilingCounterDecrement(int delta) {
++ __ mov(r5, Operand(profiling_counter_));
++ __ LoadP(r6, FieldMemOperand(r5, JSGlobalPropertyCell::kValueOffset));
++ __ SubSmiLiteral(r6, r6, Smi::FromInt(delta), r0);
++ __ StoreP(r6, FieldMemOperand(r5, JSGlobalPropertyCell::kValueOffset), r0);
++ __ cmpi(r6, Operand::Zero());
++}
++
++
++void FullCodeGenerator::EmitProfilingCounterReset() {
++ int reset_value = FLAG_interrupt_budget;
++ if (info_->ShouldSelfOptimize() && !FLAG_retry_self_opt) {
++ // Self-optimization is a one-off thing: if it fails, don't try again.
++ reset_value = Smi::kMaxValue;
++ }
++ if (isolate()->IsDebuggerActive()) {
++ // Detect debug break requests as soon as possible.
++ reset_value = FLAG_interrupt_budget >> 4;
++ }
++ __ mov(r5, Operand(profiling_counter_));
++ __ LoadSmiLiteral(r6, Smi::FromInt(reset_value));
++ __ StoreP(r6, FieldMemOperand(r5, JSGlobalPropertyCell::kValueOffset), r0);
++}
++
++
++// N.B. Deoptimizer::PatchStackCheckCodeAt manipulates the branch
++// instruction to the ok label below. Thus a change to this sequence
++// (i.e. change in instruction count between the branch and
++// destination) may require a corresponding change to that logic as
++// well.
++void FullCodeGenerator::EmitStackCheck(IterationStatement* stmt,
++ Label* back_edge_target) {
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
++ Comment cmnt(masm_, "[ Stack check");
++ Label ok;
++
++ if (FLAG_count_based_interrupts) {
++ int weight = 1;
++ if (FLAG_weighted_back_edges) {
++ ASSERT(back_edge_target->is_bound());
++ int distance = masm_->SizeOfCodeGeneratedSince(back_edge_target);
++ weight = Min(kMaxBackEdgeWeight,
++ Max(1, distance / kBackEdgeDistanceUnit));
++ }
++ EmitProfilingCounterDecrement(weight);
++ __ bc_short(ge, &ok);
++ InterruptStub stub;
++ __ CallStub(&stub);
++ } else {
++ __ LoadRoot(ip, Heap::kStackLimitRootIndex);
++ __ cmpl(sp, ip);
++ // This is a FIXED_SEQUENCE and must match the other StackCheck code
++ __ bc_short(ge, &ok);
++ StackCheckStub stub;
++ __ CallStub(&stub);
++ }
++
++ // Record a mapping of this PC offset to the OSR id. This is used to find
++ // the AST id from the unoptimized code in order to use it as a key into
++ // the deoptimization input data found in the optimized code.
++ RecordStackCheck(stmt->OsrEntryId());
++
++ if (FLAG_count_based_interrupts) {
++ EmitProfilingCounterReset();
++ }
++
++ __ bind(&ok);
++ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
++ // Record a mapping of the OSR id to this PC. This is used if the OSR
++ // entry becomes the target of a bailout. We don't expect it to be, but
++ // we want it to work if it is.
++ PrepareForBailoutForId(stmt->OsrEntryId(), NO_REGISTERS);
++}
++
++
++void FullCodeGenerator::EmitReturnSequence() {
++ Comment cmnt(masm_, "[ Return sequence");
++ if (return_label_.is_bound()) {
++ __ b(&return_label_);
++ } else {
++ __ bind(&return_label_);
++ if (FLAG_trace) {
++ // Push the return value on the stack as the parameter.
++ // Runtime::TraceExit returns its parameter in r3
++ __ push(r3);
++ __ CallRuntime(Runtime::kTraceExit, 1);
++ }
++ if (FLAG_interrupt_at_exit || FLAG_self_optimization) {
++ // Pretend that the exit is a backwards jump to the entry.
++ int weight = 1;
++ if (info_->ShouldSelfOptimize()) {
++ weight = FLAG_interrupt_budget / FLAG_self_opt_count;
++ } else if (FLAG_weighted_back_edges) {
++ int distance = masm_->pc_offset();
++ weight = Min(kMaxBackEdgeWeight,
++ Max(1, distance / kBackEdgeDistanceUnit));
++ }
++ EmitProfilingCounterDecrement(weight);
++ Label ok;
++ __ bge(&ok);
++ __ push(r3);
++ if (info_->ShouldSelfOptimize() && FLAG_direct_self_opt) {
++ __ LoadP(r5, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ __ push(r5);
++ __ CallRuntime(Runtime::kOptimizeFunctionOnNextCall, 1);
++ } else {
++ InterruptStub stub;
++ __ CallStub(&stub);
++ }
++ __ pop(r3);
++ EmitProfilingCounterReset();
++ __ bind(&ok);
++ }
++
++#ifdef DEBUG
++ // Add a label for checking the size of the code used for returning.
++ Label check_exit_codesize;
++ masm_->bind(&check_exit_codesize);
++#endif
++ // Make sure that the constant pool is not emitted inside of the return
++ // sequence.
++ { Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
++ // Here we use masm_-> instead of the __ macro to avoid the code coverage
++ // tool from instrumenting as we rely on the code size here.
++ int32_t sp_delta = (info_->scope()->num_parameters() + 1) * kPointerSize;
++ CodeGenerator::RecordPositions(masm_, function()->end_position() - 1);
++ __ RecordJSReturn();
++ masm_->mr(sp, fp);
++ masm_->LoadP(fp, MemOperand(sp));
++ masm_->LoadP(r0, MemOperand(sp, kPointerSize));
++ masm_->mtlr(r0);
++ masm_->Add(sp, sp, (uint32_t)(sp_delta + (2 * kPointerSize)), r0);
++ masm_->blr();
++#if V8_TARGET_ARCH_PPC64
++ // With 64bit we need a couple of nop() instructions to ensure we have
++ // enough space to SetDebugBreakAtReturn()
++ masm_->nop();
++ masm_->nop();
++#endif
++ }
++
++#ifdef DEBUG
++ // Check that the size of the code used for returning is large enough
++ // for the debugger's requirements.
++ ASSERT(Assembler::kJSReturnSequenceInstructions <=
++ masm_->InstructionsGeneratedSince(&check_exit_codesize));
++#endif
++ }
++}
++
++
++void FullCodeGenerator::EffectContext::Plug(Variable* var) const {
++ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
++}
++
++
++void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const {
++ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
++ codegen()->GetVar(result_register(), var);
++}
++
++
++void FullCodeGenerator::StackValueContext::Plug(Variable* var) const {
++ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
++ codegen()->GetVar(result_register(), var);
++ __ push(result_register());
++}
++
++
++void FullCodeGenerator::TestContext::Plug(Variable* var) const {
++ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
++ // For simplicity we always test the accumulator register.
++ codegen()->GetVar(result_register(), var);
++ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
++ codegen()->DoTest(this);
++}
++
++
++void FullCodeGenerator::EffectContext::Plug(Heap::RootListIndex index) const {
++}
++
++
++void FullCodeGenerator::AccumulatorValueContext::Plug(
++ Heap::RootListIndex index) const {
++ __ LoadRoot(result_register(), index);
++}
++
++
++void FullCodeGenerator::StackValueContext::Plug(
++ Heap::RootListIndex index) const {
++ __ LoadRoot(result_register(), index);
++ __ push(result_register());
++}
++
++
++void FullCodeGenerator::TestContext::Plug(Heap::RootListIndex index) const {
++ codegen()->PrepareForBailoutBeforeSplit(condition(),
++ true,
++ true_label_,
++ false_label_);
++ if (index == Heap::kUndefinedValueRootIndex ||
++ index == Heap::kNullValueRootIndex ||
++ index == Heap::kFalseValueRootIndex) {
++ if (false_label_ != fall_through_) __ b(false_label_);
++ } else if (index == Heap::kTrueValueRootIndex) {
++ if (true_label_ != fall_through_) __ b(true_label_);
++ } else {
++ __ LoadRoot(result_register(), index);
++ codegen()->DoTest(this);
++ }
++}
++
++
++void FullCodeGenerator::EffectContext::Plug(Handle<Object> lit) const {
++}
++
++
++void FullCodeGenerator::AccumulatorValueContext::Plug(
++ Handle<Object> lit) const {
++ __ mov(result_register(), Operand(lit));
++}
++
++
++void FullCodeGenerator::StackValueContext::Plug(Handle<Object> lit) const {
++ // Immediates cannot be pushed directly.
++ __ mov(result_register(), Operand(lit));
++ __ push(result_register());
++}
++
++
++void FullCodeGenerator::TestContext::Plug(Handle<Object> lit) const {
++ codegen()->PrepareForBailoutBeforeSplit(condition(),
++ true,
++ true_label_,
++ false_label_);
++ ASSERT(!lit->IsUndetectableObject()); // There are no undetectable literals.
++ if (lit->IsUndefined() || lit->IsNull() || lit->IsFalse()) {
++ if (false_label_ != fall_through_) __ b(false_label_);
++ } else if (lit->IsTrue() || lit->IsJSObject()) {
++ if (true_label_ != fall_through_) __ b(true_label_);
++ } else if (lit->IsString()) {
++ if (String::cast(*lit)->length() == 0) {
++ if (false_label_ != fall_through_) __ b(false_label_);
++ } else {
++ if (true_label_ != fall_through_) __ b(true_label_);
++ }
++ } else if (lit->IsSmi()) {
++ if (Smi::cast(*lit)->value() == 0) {
++ if (false_label_ != fall_through_) __ b(false_label_);
++ } else {
++ if (true_label_ != fall_through_) __ b(true_label_);
++ }
++ } else {
++ // For simplicity we always test the accumulator register.
++ __ mov(result_register(), Operand(lit));
++ codegen()->DoTest(this);
++ }
++}
++
++
++void FullCodeGenerator::EffectContext::DropAndPlug(int count,
++ Register reg) const {
++ ASSERT(count > 0);
++ __ Drop(count);
++}
++
++
++void FullCodeGenerator::AccumulatorValueContext::DropAndPlug(
++ int count,
++ Register reg) const {
++ ASSERT(count > 0);
++ __ Drop(count);
++ __ Move(result_register(), reg);
++}
++
++
++void FullCodeGenerator::StackValueContext::DropAndPlug(int count,
++ Register reg) const {
++ ASSERT(count > 0);
++ if (count > 1) __ Drop(count - 1);
++ __ StoreP(reg, MemOperand(sp, 0));
++}
++
++
++void FullCodeGenerator::TestContext::DropAndPlug(int count,
++ Register reg) const {
++ ASSERT(count > 0);
++ // For simplicity we always test the accumulator register.
++ __ Drop(count);
++ __ Move(result_register(), reg);
++ codegen()->PrepareForBailoutBeforeSplit(condition(), false, NULL, NULL);
++ codegen()->DoTest(this);
++}
++
++
++void FullCodeGenerator::EffectContext::Plug(Label* materialize_true,
++ Label* materialize_false) const {
++ ASSERT(materialize_true == materialize_false);
++ __ bind(materialize_true);
++}
++
++
++void FullCodeGenerator::AccumulatorValueContext::Plug(
++ Label* materialize_true,
++ Label* materialize_false) const {
++ Label done;
++ __ bind(materialize_true);
++ __ LoadRoot(result_register(), Heap::kTrueValueRootIndex);
++ __ b(&done);
++ __ bind(materialize_false);
++ __ LoadRoot(result_register(), Heap::kFalseValueRootIndex);
++ __ bind(&done);
++}
++
++
++void FullCodeGenerator::StackValueContext::Plug(
++ Label* materialize_true,
++ Label* materialize_false) const {
++ Label done;
++ __ bind(materialize_true);
++ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
++ __ push(ip);
++ __ b(&done);
++ __ bind(materialize_false);
++ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
++ __ push(ip);
++ __ bind(&done);
++}
++
++
++void FullCodeGenerator::TestContext::Plug(Label* materialize_true,
++ Label* materialize_false) const {
++ ASSERT(materialize_true == true_label_);
++ ASSERT(materialize_false == false_label_);
++}
++
++
++void FullCodeGenerator::EffectContext::Plug(bool flag) const {
++}
++
++
++void FullCodeGenerator::AccumulatorValueContext::Plug(bool flag) const {
++ Heap::RootListIndex value_root_index =
++ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
++ __ LoadRoot(result_register(), value_root_index);
++}
++
++
++void FullCodeGenerator::StackValueContext::Plug(bool flag) const {
++ Heap::RootListIndex value_root_index =
++ flag ? Heap::kTrueValueRootIndex : Heap::kFalseValueRootIndex;
++ __ LoadRoot(ip, value_root_index);
++ __ push(ip);
++}
++
++
++void FullCodeGenerator::TestContext::Plug(bool flag) const {
++ codegen()->PrepareForBailoutBeforeSplit(condition(),
++ true,
++ true_label_,
++ false_label_);
++ if (flag) {
++ if (true_label_ != fall_through_) __ b(true_label_);
++ } else {
++ if (false_label_ != fall_through_) __ b(false_label_);
++ }
++}
++
++
++void FullCodeGenerator::DoTest(Expression* condition,
++ Label* if_true,
++ Label* if_false,
++ Label* fall_through) {
++ ToBooleanStub stub(result_register());
++ __ CallStub(&stub);
++ __ cmpi(result_register(), Operand::Zero());
++ Split(ne, if_true, if_false, fall_through);
++}
++
++
++void FullCodeGenerator::Split(Condition cond,
++ Label* if_true,
++ Label* if_false,
++ Label* fall_through,
++ CRegister cr) {
++ if (if_false == fall_through) {
++ __ b(cond, if_true, cr);
++ } else if (if_true == fall_through) {
++ __ b(NegateCondition(cond), if_false, cr);
++ } else {
++ __ b(cond, if_true, cr);
++ __ b(if_false);
++ }
++}
++
++
++MemOperand FullCodeGenerator::StackOperand(Variable* var) {
++ ASSERT(var->IsStackAllocated());
++ // Offset is negative because higher indexes are at lower addresses.
++ int offset = -var->index() * kPointerSize;
++ // Adjust by a (parameter or local) base offset.
++ if (var->IsParameter()) {
++ offset += (info_->scope()->num_parameters() + 1) * kPointerSize;
++ } else {
++ offset += JavaScriptFrameConstants::kLocal0Offset;
++ }
++ return MemOperand(fp, offset);
++}
++
++
++MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) {
++ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
++ if (var->IsContextSlot()) {
++ int context_chain_length = scope()->ContextChainLength(var->scope());
++ __ LoadContext(scratch, context_chain_length);
++ return ContextOperand(scratch, var->index());
++ } else {
++ return StackOperand(var);
++ }
++}
++
++
++void FullCodeGenerator::GetVar(Register dest, Variable* var) {
++ // Use destination as scratch.
++ MemOperand location = VarOperand(var, dest);
++ __ LoadP(dest, location, r0);
++}
++
++
++void FullCodeGenerator::SetVar(Variable* var,
++ Register src,
++ Register scratch0,
++ Register scratch1) {
++ ASSERT(var->IsContextSlot() || var->IsStackAllocated());
++ ASSERT(!scratch0.is(src));
++ ASSERT(!scratch0.is(scratch1));
++ ASSERT(!scratch1.is(src));
++ MemOperand location = VarOperand(var, scratch0);
++ __ StoreP(src, location, r0);
++
++ // Emit the write barrier code if the location is in the heap.
++ if (var->IsContextSlot()) {
++ __ RecordWriteContextSlot(scratch0,
++ location.offset(),
++ src,
++ scratch1,
++ kLRHasBeenSaved,
++ kDontSaveFPRegs);
++ }
++}
++
++
++void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
++ bool should_normalize,
++ Label* if_true,
++ Label* if_false) {
++ // Only prepare for bailouts before splits if we're in a test
++ // context. Otherwise, we let the Visit function deal with the
++ // preparation to avoid preparing with the same AST id twice.
++ if (!context()->IsTest() || !info_->IsOptimizable()) return;
++
++ Label skip;
++ if (should_normalize) __ b(&skip);
++ PrepareForBailout(expr, TOS_REG);
++ if (should_normalize) {
++ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
++ __ cmp(r3, ip);
++ Split(eq, if_true, if_false, NULL);
++ __ bind(&skip);
++ }
++}
++
++
++void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
++ // The variable in the declaration always resides in the current function
++ // context.
++ ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
++ if (generate_debug_code_) {
++ // Check that we're not inside a with or catch context.
++ __ LoadP(r4, FieldMemOperand(cp, HeapObject::kMapOffset));
++ __ CompareRoot(r4, Heap::kWithContextMapRootIndex);
++ __ Check(ne, "Declaration in with context.");
++ __ CompareRoot(r4, Heap::kCatchContextMapRootIndex);
++ __ Check(ne, "Declaration in catch context.");
++ }
++}
++
++
++void FullCodeGenerator::VisitVariableDeclaration(
++ VariableDeclaration* declaration) {
++ // If it was not possible to allocate the variable at compile time, we
++ // need to "declare" it at runtime to make sure it actually exists in the
++ // local context.
++ VariableProxy* proxy = declaration->proxy();
++ VariableMode mode = declaration->mode();
++ Variable* variable = proxy->var();
++ bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
++ switch (variable->location()) {
++ case Variable::UNALLOCATED:
++ globals_->Add(variable->name(), zone());
++ globals_->Add(variable->binding_needs_init()
++ ? isolate()->factory()->the_hole_value()
++ : isolate()->factory()->undefined_value(),
++ zone());
++ break;
++
++ case Variable::PARAMETER:
++ case Variable::LOCAL:
++ if (hole_init) {
++ Comment cmnt(masm_, "[ VariableDeclaration");
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ StoreP(ip, StackOperand(variable));
++ }
++ break;
++
++ case Variable::CONTEXT:
++ if (hole_init) {
++ Comment cmnt(masm_, "[ VariableDeclaration");
++ EmitDebugCheckDeclarationContext(variable);
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ StoreP(ip, ContextOperand(cp, variable->index()), r0);
++ // No write barrier since the_hole_value is in old space.
++ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
++ }
++ break;
++
++ case Variable::LOOKUP: {
++ Comment cmnt(masm_, "[ VariableDeclaration");
++ __ mov(r5, Operand(variable->name()));
++ // Declaration nodes are always introduced in one of four modes.
++ ASSERT(IsDeclaredVariableMode(mode));
++ PropertyAttributes attr =
++ IsImmutableVariableMode(mode) ? READ_ONLY : NONE;
++ __ LoadSmiLiteral(r4, Smi::FromInt(attr));
++ // Push initial value, if any.
++ // Note: For variables we must not push an initial value (such as
++ // 'undefined') because we may have a (legal) redeclaration and we
++ // must not destroy the current value.
++ if (hole_init) {
++ __ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
++ __ Push(cp, r5, r4, r3);
++ } else {
++ __ LoadSmiLiteral(r3, Smi::FromInt(0)); // Indicates no initial value.
++ __ Push(cp, r5, r4, r3);
++ }
++ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
++ break;
++ }
++ }
++}
++
++
++void FullCodeGenerator::VisitFunctionDeclaration(
++ FunctionDeclaration* declaration) {
++ VariableProxy* proxy = declaration->proxy();
++ Variable* variable = proxy->var();
++ switch (variable->location()) {
++ case Variable::UNALLOCATED: {
++ globals_->Add(variable->name(), zone());
++ Handle<SharedFunctionInfo> function =
++ Compiler::BuildFunctionInfo(declaration->fun(), script());
++ // Check for stack-overflow exception.
++ if (function.is_null()) return SetStackOverflow();
++ globals_->Add(function, zone());
++ break;
++ }
++
++ case Variable::PARAMETER:
++ case Variable::LOCAL: {
++ Comment cmnt(masm_, "[ FunctionDeclaration");
++ VisitForAccumulatorValue(declaration->fun());
++ __ StoreP(result_register(), StackOperand(variable));
++ break;
++ }
++
++ case Variable::CONTEXT: {
++ Comment cmnt(masm_, "[ FunctionDeclaration");
++ EmitDebugCheckDeclarationContext(variable);
++ VisitForAccumulatorValue(declaration->fun());
++ __ StoreP(result_register(),
++ ContextOperand(cp, variable->index()), r0);
++ int offset = Context::SlotOffset(variable->index());
++ // We know that we have written a function, which is not a smi.
++ __ RecordWriteContextSlot(cp,
++ offset,
++ result_register(),
++ r5,
++ kLRHasBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
++ break;
++ }
++
++ case Variable::LOOKUP: {
++ Comment cmnt(masm_, "[ FunctionDeclaration");
++ __ mov(r5, Operand(variable->name()));
++ __ LoadSmiLiteral(r4, Smi::FromInt(NONE));
++ __ Push(cp, r5, r4);
++ // Push initial value for function declaration.
++ VisitForStackValue(declaration->fun());
++ __ CallRuntime(Runtime::kDeclareContextSlot, 4);
++ break;
++ }
++ }
++}
++
++
++void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
++ VariableProxy* proxy = declaration->proxy();
++ Variable* variable = proxy->var();
++ Handle<JSModule> instance =
declaration->module()->interface()->Instance();
++ ASSERT(!instance.is_null());
++
++ switch (variable->location()) {
++ case Variable::UNALLOCATED: {
++ Comment cmnt(masm_, "[ ModuleDeclaration");
++ globals_->Add(variable->name(), zone());
++ globals_->Add(instance, zone());
++ Visit(declaration->module());
++ break;
++ }
++
++ case Variable::CONTEXT: {
++ Comment cmnt(masm_, "[ ModuleDeclaration");
++ EmitDebugCheckDeclarationContext(variable);
++ __ mov(r4, Operand(instance));
++ __ StoreP(r4, ContextOperand(cp, variable->index()), r0);
++ Visit(declaration->module());
++ break;
++ }
++
++ case Variable::PARAMETER:
++ case Variable::LOCAL:
++ case Variable::LOOKUP:
++ UNREACHABLE();
++ }
++}
++
++
++void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
++ VariableProxy* proxy = declaration->proxy();
++ Variable* variable = proxy->var();
++ switch (variable->location()) {
++ case Variable::UNALLOCATED:
++ // TODO(rossberg)
++ break;
++
++ case Variable::CONTEXT: {
++ Comment cmnt(masm_, "[ ImportDeclaration");
++ EmitDebugCheckDeclarationContext(variable);
++ // TODO(rossberg)
++ break;
++ }
++
++ case Variable::PARAMETER:
++ case Variable::LOCAL:
++ case Variable::LOOKUP:
++ UNREACHABLE();
++ }
++}
++
++
++void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
++ // TODO(rossberg)
++}
++
++
++void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
++ // Call the runtime to declare the globals.
++ // The context is the first argument.
++ __ mov(r4, Operand(pairs));
++ __ LoadSmiLiteral(r3, Smi::FromInt(DeclareGlobalsFlags()));
++ __ Push(cp, r4, r3);
++ __ CallRuntime(Runtime::kDeclareGlobals, 3);
++ // Return value is ignored.
++}
++
++
++void FullCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
++ Comment cmnt(masm_, "[ SwitchStatement");
++ Breakable nested_statement(this, stmt);
++ SetStatementPosition(stmt);
++
++ // Keep the switch value on the stack until a case matches.
++ VisitForStackValue(stmt->tag());
++ PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
++
++ ZoneList<CaseClause*>* clauses = stmt->cases();
++ CaseClause* default_clause = NULL; // Can occur anywhere in the list.
++
++ Label next_test; // Recycled for each test.
++ // Compile all the tests with branches to their bodies.
++ for (int i = 0; i < clauses->length(); i++) {
++ CaseClause* clause = clauses->at(i);
++ clause->body_target()->Unuse();
++
++ // The default is not a test, but remember it as final fall through.
++ if (clause->is_default()) {
++ default_clause = clause;
++ continue;
++ }
++
++ Comment cmnt(masm_, "[ Case comparison");
++ __ bind(&next_test);
++ next_test.Unuse();
++
++ // Compile the label expression.
++ VisitForAccumulatorValue(clause->label());
++
++ // Perform the comparison as if via '==='.
++ __ LoadP(r4, MemOperand(sp, 0)); // Switch value.
++ bool inline_smi_code = ShouldInlineSmiCase(Token::EQ_STRICT);
++ JumpPatchSite patch_site(masm_);
++ if (inline_smi_code) {
++ Label slow_case;
++ __ orx(r5, r4, r3);
++ patch_site.EmitJumpIfNotSmi(r5, &slow_case);
++
++ __ cmp(r4, r3);
++ __ bne(&next_test);
++ __ Drop(1); // Switch value is no longer needed.
++ __ b(clause->body_target());
++ __ bind(&slow_case);
++ }
++
++ // Record position before stub call for type feedback.
++ SetSourcePosition(clause->position());
++ Handle<Code> ic = CompareIC::GetUninitialized(Token::EQ_STRICT);
++ CallIC(ic, RelocInfo::CODE_TARGET, clause->CompareId());
++ patch_site.EmitPatchInfo();
++
++ __ cmpi(r3, Operand::Zero());
++ __ bne(&next_test);
++ __ Drop(1); // Switch value is no longer needed.
++ __ b(clause->body_target());
++ }
++
++ // Discard the test value and jump to the default if present, otherwise to
++ // the end of the statement.
++ __ bind(&next_test);
++ __ Drop(1); // Switch value is no longer needed.
++ if (default_clause == NULL) {
++ __ b(nested_statement.break_label());
++ } else {
++ __ b(default_clause->body_target());
++ }
++
++ // Compile all the case bodies.
++ for (int i = 0; i < clauses->length(); i++) {
++ Comment cmnt(masm_, "[ Case body");
++ CaseClause* clause = clauses->at(i);
++ __ bind(clause->body_target());
++ PrepareForBailoutForId(clause->EntryId(), NO_REGISTERS);
++ VisitStatements(clause->statements());
++ }
++
++ __ bind(nested_statement.break_label());
++ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
++}
++
++
++void FullCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
++ Comment cmnt(masm_, "[ ForInStatement");
++ SetStatementPosition(stmt);
++
++ Label loop, exit;
++ ForIn loop_statement(this, stmt);
++ increment_loop_depth();
++
++ // Get the object to enumerate over. Both SpiderMonkey and JSC
++ // ignore null and undefined in contrast to the specification; see
++ // ECMA-262 section 12.6.4.
++ VisitForAccumulatorValue(stmt->enumerable());
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ cmp(r3, ip);
++ __ beq(&exit);
++ Register null_value = r7;
++ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
++ __ cmp(r3, null_value);
++ __ beq(&exit);
++
++ PrepareForBailoutForId(stmt->PrepareId(), TOS_REG);
++
++ // Convert the object to a JS object.
++ Label convert, done_convert;
++ __ JumpIfSmi(r3, &convert);
++ __ CompareObjectType(r3, r4, r4, FIRST_SPEC_OBJECT_TYPE);
++ __ bge(&done_convert);
++ __ bind(&convert);
++ __ push(r3);
++ __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
++ __ bind(&done_convert);
++ __ push(r3);
++
++ // Check for proxies.
++ Label call_runtime;
++ STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
++ __ CompareObjectType(r3, r4, r4, LAST_JS_PROXY_TYPE);
++ __ ble(&call_runtime);
++
++ // Check cache validity in generated code. This is a fast case for
++ // the JSObject::IsSimpleEnum cache validity checks. If we cannot
++ // guarantee cache validity, call the runtime system to check cache
++ // validity or get the property names in a fixed array.
++ __ CheckEnumCache(null_value, &call_runtime);
++
++ // The enum cache is valid. Load the map of the object being
++ // iterated over and use the cache for the iteration.
++ Label use_cache;
++ __ LoadP(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ b(&use_cache);
++
++ // Get the set of properties to enumerate.
++ __ bind(&call_runtime);
++ __ push(r3); // Duplicate the enumerable object on the stack.
++ __ CallRuntime(Runtime::kGetPropertyNamesFast, 1);
++
++ // If we got a map from the runtime call, we can do a fast
++ // modification check. Otherwise, we got a fixed array, and we have
++ // to do a slow check.
++ Label fixed_array;
++ __ LoadP(r5, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kMetaMapRootIndex);
++ __ cmp(r5, ip);
++ __ bne(&fixed_array);
++
++ // We got a map in register r3. Get the enumeration cache from it.
++ Label no_descriptors;
++ __ bind(&use_cache);
++
++ __ EnumLength(r4, r3);
++ __ CmpSmiLiteral(r4, Smi::FromInt(0), r0);
++ __ beq(&no_descriptors);
++
++ __ LoadInstanceDescriptors(r3, r5);
++ __ LoadP(r5, FieldMemOperand(r5, DescriptorArray::kEnumCacheOffset));
++ __ LoadP(r5,
++ FieldMemOperand(r5, DescriptorArray::kEnumCacheBridgeCacheOffset));
++
++ // Set up the four remaining stack slots.
++ __ push(r3); // Map.
++ __ LoadSmiLiteral(r3, Smi::FromInt(0));
++ // Push enumeration cache, enumeration cache length (as smi) and zero.
++ __ Push(r5, r4, r3);
++ __ b(&loop);
++
++ __ bind(&no_descriptors);
++ __ Drop(1);
++ __ b(&exit);
++
++ // We got a fixed array in register r3. Iterate through that.
++ Label non_proxy;
++ __ bind(&fixed_array);
++
++ Handle<JSGlobalPropertyCell> cell =
++ isolate()->factory()->NewJSGlobalPropertyCell(
++ Handle<Object>(
++ Smi::FromInt(TypeFeedbackCells::kForInFastCaseMarker)));
++ RecordTypeFeedbackCell(stmt->ForInFeedbackId(), cell);
++ __ LoadHeapObject(r4, cell);
++ __ LoadSmiLiteral(r5, Smi::FromInt(TypeFeedbackCells::kForInSlowCaseMarker));
++ __ StoreP(r5, FieldMemOperand(r4, JSGlobalPropertyCell::kValueOffset), r0);
++
++ __ LoadSmiLiteral(r4, Smi::FromInt(1)); // Smi indicates slow check
++ __ LoadP(r5, MemOperand(sp, 0 * kPointerSize)); // Get enumerated object
++ STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
++ __ CompareObjectType(r5, r6, r6, LAST_JS_PROXY_TYPE);
++ __ bgt(&non_proxy);
++ __ LoadSmiLiteral(r4, Smi::FromInt(0)); // Zero indicates proxy
++ __ bind(&non_proxy);
++ __ Push(r4, r3); // Smi and array
++ __ LoadP(r4, FieldMemOperand(r3, FixedArray::kLengthOffset));
++ __ LoadSmiLiteral(r3, Smi::FromInt(0));
++ __ Push(r4, r3); // Fixed array length (as smi) and initial index.
++
++ // Generate code for doing the condition check.
++ PrepareForBailoutForId(stmt->BodyId(), NO_REGISTERS);
++ __ bind(&loop);
++ // Load the current count to r3, load the length to r4.
++ __ LoadP(r3, MemOperand(sp, 0 * kPointerSize));
++ __ LoadP(r4, MemOperand(sp, 1 * kPointerSize));
++ __ cmpl(r3, r4); // Compare to the array length.
++ __ bge(loop_statement.break_label());
++
++ // Get the current entry of the array into register r6.
++ __ LoadP(r5, MemOperand(sp, 2 * kPointerSize));
++ __ addi(r5, r5, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(r6, r3);
++ __ LoadPX(r6, MemOperand(r6, r5));
++
++ // Get the expected map from the stack or a smi in the
++ // permanent slow case into register r2.
++ __ LoadP(r5, MemOperand(sp, 3 * kPointerSize));
++
++ // Check if the expected map still matches that of the enumerable.
++ // If not, we may have to filter the key.
++ Label update_each;
++ __ LoadP(r4, MemOperand(sp, 4 * kPointerSize));
++ __ LoadP(r7, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ cmp(r7, r5);
++ __ beq(&update_each);
++
++ // For proxies, no filtering is done.
++ // TODO(rossberg): What if only a prototype is a proxy? Not specified yet.
++ __ CmpSmiLiteral(r5, Smi::FromInt(0), r0);
++ __ beq(&update_each);
++
++ // Convert the entry to a string or (smi) 0 if it isn't a property
++ // any more. If the property has been removed while iterating, we
++ // just skip it.
++ __ push(r4); // Enumerable.
++ __ push(r6); // Current entry.
++ __ InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION);
++ __ mr(r6, r3);
++ __ cmpi(r6, Operand::Zero());
++ __ beq(loop_statement.continue_label());
++
++ // Update the 'each' property or variable from the possibly filtered
++ // entry in register r6.
++ __ bind(&update_each);
++ __ mr(result_register(), r6);
++ // Perform the assignment as if via '='.
++ { EffectContext context(this);
++ EmitAssignment(stmt->each());
++ }
++
++ // Generate code for the body of the loop.
++ Visit(stmt->body());
++
++ // Generate code for the going to the next element by incrementing
++ // the index (smi) stored on top of the stack.
++ __ bind(loop_statement.continue_label());
++ __ pop(r3);
++ __ AddSmiLiteral(r3, r3, Smi::FromInt(1), r0);
++ __ push(r3);
++
++ EmitStackCheck(stmt, &loop);
++ __ b(&loop);
++
++ // Remove the pointers stored on the stack.
++ __ bind(loop_statement.break_label());
++ __ Drop(5);
++
++ // Exit and decrement the loop depth.
++ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
++ __ bind(&exit);
++ decrement_loop_depth();
++}
++
++
++void FullCodeGenerator::EmitNewClosure(Handle<SharedFunctionInfo> info,
++ bool pretenure) {
++ // Use the fast case closure allocation code that allocates in new
++ // space for nested functions that don't need literals cloning. If
++ // we're running with the --always-opt or the --prepare-always-opt
++ // flag, we need to use the runtime function so that the new function
++ // we are creating here gets a chance to have its code optimized and
++ // doesn't just get a copy of the existing unoptimized code.
++ if (!FLAG_always_opt &&
++ !FLAG_prepare_always_opt &&
++ !pretenure &&
++ scope()->is_function_scope() &&
++ info->num_literals() == 0) {
++ FastNewClosureStub stub(info->language_mode());
++ __ mov(r3, Operand(info));
++ __ push(r3);
++ __ CallStub(&stub);
++ } else {
++ __ mov(r3, Operand(info));
++ __ LoadRoot(r4, pretenure ? Heap::kTrueValueRootIndex
++ : Heap::kFalseValueRootIndex);
++ __ Push(cp, r3, r4);
++ __ CallRuntime(Runtime::kNewClosure, 3);
++ }
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
++ Comment cmnt(masm_, "[ VariableProxy");
++ EmitVariableLoad(expr);
++}
++
++
++void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var,
++ TypeofState typeof_state,
++ Label* slow) {
++ Register current = cp;
++ Register next = r4;
++ Register temp = r5;
++
++ Scope* s = scope();
++ while (s != NULL) {
++ if (s->num_heap_slots() > 0) {
++ if (s->calls_non_strict_eval()) {
++ // Check that extension is NULL.
++ __ LoadP(temp, ContextOperand(current, Context::EXTENSION_INDEX));
++ __ cmpi(temp, Operand::Zero());
++ __ bne(slow);
++ }
++ // Load next context in chain.
++ __ LoadP(next, ContextOperand(current, Context::PREVIOUS_INDEX));
++ // Walk the rest of the chain without clobbering cp.
++ current = next;
++ }
++ // If no outer scope calls eval, we do not need to check more
++ // context extensions.
++ if (!s->outer_scope_calls_non_strict_eval() || s->is_eval_scope()) break;
++ s = s->outer_scope();
++ }
++
++ if (s->is_eval_scope()) {
++ Label loop, fast;
++ if (!current.is(next)) {
++ __ Move(next, current);
++ }
++ __ bind(&loop);
++ // Terminate at native context.
++ __ LoadP(temp, FieldMemOperand(next, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kNativeContextMapRootIndex);
++ __ cmp(temp, ip);
++ __ beq(&fast);
++ // Check that extension is NULL.
++ __ LoadP(temp, ContextOperand(next, Context::EXTENSION_INDEX));
++ __ cmpi(temp, Operand::Zero());
++ __ bne(slow);
++ // Load next context in chain.
++ __ LoadP(next, ContextOperand(next, Context::PREVIOUS_INDEX));
++ __ b(&loop);
++ __ bind(&fast);
++ }
++
++ __ LoadP(r3, GlobalObjectOperand());
++ __ mov(r5, Operand(var->name()));
++ RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
++ ? RelocInfo::CODE_TARGET
++ : RelocInfo::CODE_TARGET_CONTEXT;
++ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
++ CallIC(ic, mode);
++}
++
++
++MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var,
++ Label* slow) {
++ ASSERT(var->IsContextSlot());
++ Register context = cp;
++ Register next = r6;
++ Register temp = r7;
++
++ for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) {
++ if (s->num_heap_slots() > 0) {
++ if (s->calls_non_strict_eval()) {
++ // Check that extension is NULL.
++ __ LoadP(temp, ContextOperand(context, Context::EXTENSION_INDEX));
++ __ cmpi(temp, Operand::Zero());
++ __ bne(slow);
++ }
++ __ LoadP(next, ContextOperand(context, Context::PREVIOUS_INDEX));
++ // Walk the rest of the chain without clobbering cp.
++ context = next;
++ }
++ }
++ // Check that last extension is NULL.
++ __ LoadP(temp, ContextOperand(context, Context::EXTENSION_INDEX));
++ __ cmpi(temp, Operand::Zero());
++ __ bne(slow);
++
++ // This function is used only for loads, not stores, so it's safe to
++ // return an cp-based operand (the write barrier cannot be allowed to
++ // destroy the cp register).
++ return ContextOperand(context, var->index());
++}
++
++
++void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var,
++ TypeofState typeof_state,
++ Label* slow,
++ Label* done) {
++ // Generate fast-case code for variables that might be shadowed by
++ // eval-introduced variables. Eval is used a lot without
++ // introducing variables. In those cases, we do not want to
++ // perform a runtime call for all variables in the scope
++ // containing the eval.
++ if (var->mode() == DYNAMIC_GLOBAL) {
++ EmitLoadGlobalCheckExtensions(var, typeof_state, slow);
++ __ b(done);
++ } else if (var->mode() == DYNAMIC_LOCAL) {
++ Variable* local = var->local_if_not_shadowed();
++ __ LoadP(r3, ContextSlotOperandCheckExtensions(local, slow));
++ if (local->mode() == CONST ||
++ local->mode() == CONST_HARMONY ||
++ local->mode() == LET) {
++ __ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
++ __ bne(done);
++ if (local->mode() == CONST) {
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ } else { // LET || CONST_HARMONY
++ __ mov(r3, Operand(var->name()));
++ __ push(r3);
++ __ CallRuntime(Runtime::kThrowReferenceError, 1);
++ }
++ }
++ __ b(done);
++ }
++}
++
++
++void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) {
++ // Record position before possible IC call.
++ SetSourcePosition(proxy->position());
++ Variable* var = proxy->var();
++
++ // Three cases: global variables, lookup variables, and all other types of
++ // variables.
++ switch (var->location()) {
++ case Variable::UNALLOCATED: {
++ Comment cmnt(masm_, "Global variable");
++ // Use inline caching. Variable name is passed in r5 and the global
++ // object (receiver) in r3.
++ __ LoadP(r3, GlobalObjectOperand());
++ __ mov(r5, Operand(var->name()));
++ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
++ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
++ context()->Plug(r3);
++ break;
++ }
++
++ case Variable::PARAMETER:
++ case Variable::LOCAL:
++ case Variable::CONTEXT: {
++ Comment cmnt(masm_, var->IsContextSlot()
++ ? "Context variable"
++ : "Stack variable");
++ if (var->binding_needs_init()) {
++ // var->scope() may be NULL when the proxy is located in eval code and
++ // refers to a potential outside binding. Currently those bindings are
++ // always looked up dynamically, i.e. in that case
++ // var->location() == LOOKUP.
++ // always holds.
++ ASSERT(var->scope() != NULL);
++
++ // Check if the binding really needs an initialization check. The check
++ // can be skipped in the following situation: we have a LET or CONST
++ // binding in harmony mode, both the Variable and the VariableProxy have
++ // the same declaration scope (i.e. they are both in global code, in the
++ // same function or in the same eval code) and the VariableProxy is in
++ // the source physically located after the initializer of the variable.
++ //
++ // We cannot skip any initialization checks for CONST in non-harmony
++ // mode because const variables may be declared but never initialized:
++ // if (false) { const x; }; var y = x;
++ //
++ // The condition on the declaration scopes is a conservative check for
++ // nested functions that access a binding and are called before the
++ // binding is initialized:
++ // function() { f(); let x = 1; function f() { x = 2; } }
++ //
++ bool skip_init_check;
++ if (var->scope()->DeclarationScope() != scope()->DeclarationScope()) {
++ skip_init_check = false;
++ } else {
++ // Check that we always have valid source position.
++ ASSERT(var->initializer_position() != RelocInfo::kNoPosition);
++ ASSERT(proxy->position() != RelocInfo::kNoPosition);
++ skip_init_check = var->mode() != CONST &&
++ var->initializer_position() < proxy->position();
++ }
++
++ if (!skip_init_check) {
++ Label done;
++ // Let and const need a read barrier.
++ GetVar(r3, var);
++ __ CompareRoot(r3, Heap::kTheHoleValueRootIndex);
++ __ bne(&done);
++ if (var->mode() == LET || var->mode() == CONST_HARMONY) {
++ // Throw a reference error when using an uninitialized let/const
++ // binding in harmony mode.
++ __ mov(r3, Operand(var->name()));
++ __ push(r3);
++ __ CallRuntime(Runtime::kThrowReferenceError, 1);
++ } else {
++ // Uninitalized const bindings outside of harmony mode are unholed.
++ ASSERT(var->mode() == CONST);
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ }
++ __ bind(&done);
++ context()->Plug(r3);
++ break;
++ }
++ }
++ context()->Plug(var);
++ break;
++ }
++
++ case Variable::LOOKUP: {
++ Label done, slow;
++ // Generate code for loading from variables potentially shadowed
++ // by eval-introduced variables.
++ EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done);
++ __ bind(&slow);
++ Comment cmnt(masm_, "Lookup variable");
++ __ mov(r4, Operand(var->name()));
++ __ Push(cp, r4); // Context and name.
++ __ CallRuntime(Runtime::kLoadContextSlot, 2);
++ __ bind(&done);
++ context()->Plug(r3);
++ }
++ }
++}
++
++
++void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
++ Comment cmnt(masm_, "[ RegExpLiteral");
++ Label materialized;
++ // Registers will be used as follows:
++ // r8 = materialized value (RegExp literal)
++ // r7 = JS function, literals array
++ // r6 = literal index
++ // r5 = RegExp pattern
++ // r4 = RegExp flags
++ // r3 = RegExp literal clone
++ __ LoadP(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ __ LoadP(r7, FieldMemOperand(r3, JSFunction::kLiteralsOffset));
++ int literal_offset =
++ FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
++ __ LoadP(r8, FieldMemOperand(r7, literal_offset), r0);
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ cmp(r8, ip);
++ __ bne(&materialized);
++
++ // Create regexp literal using runtime function.
++ // Result will be in r3.
++ __ LoadSmiLiteral(r6, Smi::FromInt(expr->literal_index()));
++ __ mov(r5, Operand(expr->pattern()));
++ __ mov(r4, Operand(expr->flags()));
++ __ Push(r7, r6, r5, r4);
++ __ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4);
++ __ mr(r8, r3);
++
++ __ bind(&materialized);
++ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
++ Label allocated, runtime_allocate;
++ __ AllocateInNewSpace(size, r3, r5, r6, &runtime_allocate, TAG_OBJECT);
++ __ b(&allocated);
++
++ __ bind(&runtime_allocate);
++ __ push(r8);
++ __ LoadSmiLiteral(r3, Smi::FromInt(size));
++ __ push(r3);
++ __ CallRuntime(Runtime::kAllocateInNewSpace, 1);
++ __ pop(r8);
++
++ __ bind(&allocated);
++ // After this, registers are used as follows:
++ // r3: Newly allocated regexp.
++ // r8: Materialized regexp.
++ // r5: temp.
++ __ CopyFields(r3, r8, r5.bit(), size / kPointerSize);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitAccessor(Expression* expression) {
++ if (expression == NULL) {
++ __ LoadRoot(r4, Heap::kNullValueRootIndex);
++ __ push(r4);
++ } else {
++ VisitForStackValue(expression);
++ }
++}
++
++
++void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
++ Comment cmnt(masm_, "[ ObjectLiteral");
++ Handle<FixedArray> constant_properties = expr->constant_properties();
++ __ LoadP(r6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ __ LoadP(r6, FieldMemOperand(r6, JSFunction::kLiteralsOffset));
++ __ LoadSmiLiteral(r5, Smi::FromInt(expr->literal_index()));
++ __ mov(r4, Operand(constant_properties));
++ int flags = expr->fast_elements()
++ ? ObjectLiteral::kFastElements
++ : ObjectLiteral::kNoFlags;
++ flags |= expr->has_function()
++ ? ObjectLiteral::kHasFunction
++ : ObjectLiteral::kNoFlags;
++ __ LoadSmiLiteral(r3, Smi::FromInt(flags));
++ __ Push(r6, r5, r4, r3);
++ int properties_count = constant_properties->length() / 2;
++ if (expr->depth() > 1) {
++ __ CallRuntime(Runtime::kCreateObjectLiteral, 4);
++ } else if (flags != ObjectLiteral::kFastElements ||
++ properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) {
++ __ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4);
++ } else {
++ FastCloneShallowObjectStub stub(properties_count);
++ __ CallStub(&stub);
++ }
++
++ // If result_saved is true the result is on top of the stack. If
++ // result_saved is false the result is in r3.
++ bool result_saved = false;
++
++ // Mark all computed expressions that are bound to a key that
++ // is shadowed by a later occurrence of the same key. For the
++ // marked expressions, no store code is emitted.
++ expr->CalculateEmitStore(zone());
++
++ AccessorTable accessor_table(zone());
++ for (int i = 0; i < expr->properties()->length(); i++) {
++ ObjectLiteral::Property* property = expr->properties()->at(i);
++ if (property->IsCompileTimeValue()) continue;
++
++ Literal* key = property->key();
++ Expression* value = property->value();
++ if (!result_saved) {
++ __ push(r3); // Save result on stack
++ result_saved = true;
++ }
++ switch (property->kind()) {
++ case ObjectLiteral::Property::CONSTANT:
++ UNREACHABLE();
++ case ObjectLiteral::Property::MATERIALIZED_LITERAL:
++ ASSERT(!CompileTimeValue::IsCompileTimeValue(property->value()));
++ // Fall through.
++ case ObjectLiteral::Property::COMPUTED:
++ if (key->handle()->IsSymbol()) {
++ if (property->emit_store()) {
++ VisitForAccumulatorValue(value);
++ __ mov(r5, Operand(key->handle()));
++ __ LoadP(r4, MemOperand(sp));
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->StoreIC_Initialize()
++ : isolate()->builtins()->StoreIC_Initialize_Strict();
++ CallIC(ic, RelocInfo::CODE_TARGET, key->LiteralFeedbackId());
++ PrepareForBailoutForId(key->id(), NO_REGISTERS);
++ } else {
++ VisitForEffect(value);
++ }
++ break;
++ }
++ // Fall through.
++ case ObjectLiteral::Property::PROTOTYPE:
++ // Duplicate receiver on stack.
++ __ LoadP(r3, MemOperand(sp));
++ __ push(r3);
++ VisitForStackValue(key);
++ VisitForStackValue(value);
++ if (property->emit_store()) {
++ __ LoadSmiLiteral(r3, Smi::FromInt(NONE)); // PropertyAttributes
++ __ push(r3);
++ __ CallRuntime(Runtime::kSetProperty, 4);
++ } else {
++ __ Drop(3);
++ }
++ break;
++ case ObjectLiteral::Property::GETTER:
++ accessor_table.lookup(key)->second->getter = value;
++ break;
++ case ObjectLiteral::Property::SETTER:
++ accessor_table.lookup(key)->second->setter = value;
++ break;
++ }
++ }
++
++ // Emit code to define accessors, using only a single call to the runtime for
++ // each pair of corresponding getters and setters.
++ for (AccessorTable::Iterator it = accessor_table.begin();
++ it != accessor_table.end();
++ ++it) {
++ __ LoadP(r3, MemOperand(sp)); // Duplicate receiver.
++ __ push(r3);
++ VisitForStackValue(it->first);
++ EmitAccessor(it->second->getter);
++ EmitAccessor(it->second->setter);
++ __ LoadSmiLiteral(r3, Smi::FromInt(NONE));
++ __ push(r3);
++ __ CallRuntime(Runtime::kDefineOrRedefineAccessorProperty, 5);
++ }
++
++ if (expr->has_function()) {
++ ASSERT(result_saved);
++ __ LoadP(r3, MemOperand(sp));
++ __ push(r3);
++ __ CallRuntime(Runtime::kToFastProperties, 1);
++ }
++
++ if (result_saved) {
++ context()->PlugTOS();
++ } else {
++ context()->Plug(r3);
++ }
++}
++
++
++void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
++ Comment cmnt(masm_, "[ ArrayLiteral");
++
++ ZoneList<Expression*>* subexprs = expr->values();
++ int length = subexprs->length();
++ Handle<FixedArray> constant_elements = expr->constant_elements();
++ ASSERT_EQ(2, constant_elements->length());
++ ElementsKind constant_elements_kind =
++
static_cast<ElementsKind>(Smi::cast(constant_elements->get(0))->value());
++ bool has_fast_elements = IsFastObjectElementsKind(constant_elements_kind);
++ Handle<FixedArrayBase> constant_elements_values(
++ FixedArrayBase::cast(constant_elements->get(1)));
++
++ __ LoadP(r6, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ __ LoadP(r6, FieldMemOperand(r6, JSFunction::kLiteralsOffset));
++ __ LoadSmiLiteral(r5, Smi::FromInt(expr->literal_index()));
++ __ mov(r4, Operand(constant_elements));
++ __ Push(r6, r5, r4);
++ if (has_fast_elements && constant_elements_values->map() ==
++ isolate()->heap()->fixed_cow_array_map()) {
++ FastCloneShallowArrayStub stub(
++ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS, length);
++ __ CallStub(&stub);
++ __ IncrementCounter(
++ isolate()->counters()->cow_arrays_created_stub(), 1, r4, r5);
++ } else if (expr->depth() > 1) {
++ __ CallRuntime(Runtime::kCreateArrayLiteral, 3);
++ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
++ __ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
++ } else {
++ ASSERT(IsFastSmiOrObjectElementsKind(constant_elements_kind) ||
++ FLAG_smi_only_arrays);
++ FastCloneShallowArrayStub::Mode mode = has_fast_elements
++ ? FastCloneShallowArrayStub::CLONE_ELEMENTS
++ : FastCloneShallowArrayStub::CLONE_ANY_ELEMENTS;
++ FastCloneShallowArrayStub stub(mode, length);
++ __ CallStub(&stub);
++ }
++
++ bool result_saved = false; // Is the result saved to the stack?
++
++ // Emit code to evaluate all the non-constant subexpressions and to store
++ // them into the newly cloned array.
++ for (int i = 0; i < length; i++) {
++ Expression* subexpr = subexprs->at(i);
++ // If the subexpression is a literal or a simple materialized literal it
++ // is already set in the cloned array.
++ if (subexpr->AsLiteral() != NULL ||
++ CompileTimeValue::IsCompileTimeValue(subexpr)) {
++ continue;
++ }
++
++ if (!result_saved) {
++ __ push(r3);
++ result_saved = true;
++ }
++ VisitForAccumulatorValue(subexpr);
++
++ if (IsFastObjectElementsKind(constant_elements_kind)) {
++ int offset = FixedArray::kHeaderSize + (i * kPointerSize);
++ __ LoadP(r8, MemOperand(sp)); // Copy of array literal.
++ __ LoadP(r4, FieldMemOperand(r8, JSObject::kElementsOffset));
++ __ StoreP(result_register(), FieldMemOperand(r4, offset), r0);
++ // Update the write barrier for the array store.
++ __ RecordWriteField(r4, offset, result_register(), r5,
++ kLRHasBeenSaved, kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET, INLINE_SMI_CHECK);
++ } else {
++ __ LoadP(r4, MemOperand(sp)); // Copy of array literal.
++ __ LoadP(r5, FieldMemOperand(r4, JSObject::kMapOffset));
++ __ LoadSmiLiteral(r6, Smi::FromInt(i));
++ __ LoadSmiLiteral(r7, Smi::FromInt(expr->literal_index()));
++ StoreArrayLiteralElementStub stub;
++ __ CallStub(&stub);
++ }
++
++ PrepareForBailoutForId(expr->GetIdForElement(i), NO_REGISTERS);
++ }
++
++ if (result_saved) {
++ context()->PlugTOS();
++ } else {
++ context()->Plug(r3);
++ }
++}
++
++
++void FullCodeGenerator::VisitAssignment(Assignment* expr) {
++ Comment cmnt(masm_, "[ Assignment");
++ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
++ // on the left-hand side.
++ if (!expr->target()->IsValidLeftHandSide()) {
++ VisitForEffect(expr->target());
++ return;
++ }
++
++ // Left-hand side can only be a property, a global or a (parameter or local)
++ // slot.
++ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
++ LhsKind assign_type = VARIABLE;
++ Property* property = expr->target()->AsProperty();
++ if (property != NULL) {
++ assign_type = (property->key()->IsPropertyName())
++ ? NAMED_PROPERTY
++ : KEYED_PROPERTY;
++ }
++
++ // Evaluate LHS expression.
++ switch (assign_type) {
++ case VARIABLE:
++ // Nothing to do here.
++ break;
++ case NAMED_PROPERTY:
++ if (expr->is_compound()) {
++ // We need the receiver both on the stack and in the accumulator.
++ VisitForAccumulatorValue(property->obj());
++ __ push(result_register());
++ } else {
++ VisitForStackValue(property->obj());
++ }
++ break;
++ case KEYED_PROPERTY:
++ if (expr->is_compound()) {
++ VisitForStackValue(property->obj());
++ VisitForAccumulatorValue(property->key());
++ __ LoadP(r4, MemOperand(sp, 0));
++ __ push(r3);
++ } else {
++ VisitForStackValue(property->obj());
++ VisitForStackValue(property->key());
++ }
++ break;
++ }
++
++ // For compound assignments we need another deoptimization point after the
++ // variable/property load.
++ if (expr->is_compound()) {
++ { AccumulatorValueContext context(this);
++ switch (assign_type) {
++ case VARIABLE:
++ EmitVariableLoad(expr->target()->AsVariableProxy());
++ PrepareForBailout(expr->target(), TOS_REG);
++ break;
++ case NAMED_PROPERTY:
++ EmitNamedPropertyLoad(property);
++ PrepareForBailoutForId(property->LoadId(), TOS_REG);
++ break;
++ case KEYED_PROPERTY:
++ EmitKeyedPropertyLoad(property);
++ PrepareForBailoutForId(property->LoadId(), TOS_REG);
++ break;
++ }
++ }
++
++ Token::Value op = expr->binary_op();
++ __ push(r3); // Left operand goes on the stack.
++ VisitForAccumulatorValue(expr->value());
++
++ OverwriteMode mode = expr->value()->ResultOverwriteAllowed()
++ ? OVERWRITE_RIGHT
++ : NO_OVERWRITE;
++ SetSourcePosition(expr->position() + 1);
++ AccumulatorValueContext context(this);
++ if (ShouldInlineSmiCase(op)) {
++ EmitInlineSmiBinaryOp(expr->binary_operation(),
++ op,
++ mode,
++ expr->target(),
++ expr->value());
++ } else {
++ EmitBinaryOp(expr->binary_operation(), op, mode);
++ }
++
++ // Deoptimization point in case the binary operation may have side effects.
++ PrepareForBailout(expr->binary_operation(), TOS_REG);
++ } else {
++ VisitForAccumulatorValue(expr->value());
++ }
++
++ // Record source position before possible IC call.
++ SetSourcePosition(expr->position());
++
++ // Store the value.
++ switch (assign_type) {
++ case VARIABLE:
++ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
++ expr->op());
++ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
++ context()->Plug(r3);
++ break;
++ case NAMED_PROPERTY:
++ EmitNamedPropertyAssignment(expr);
++ break;
++ case KEYED_PROPERTY:
++ EmitKeyedPropertyAssignment(expr);
++ break;
++ }
++}
++
++
++void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
++ SetSourcePosition(prop->position());
++ Literal* key = prop->key()->AsLiteral();
++ __ mov(r5, Operand(key->handle()));
++ // Call load IC. It has arguments receiver and property name r3 and r5.
++ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
++ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
++}
++
++
++void FullCodeGenerator::EmitKeyedPropertyLoad(Property* prop) {
++ SetSourcePosition(prop->position());
++ // Call keyed load IC. It has arguments key and receiver in r3 and r4.
++ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
++ CallIC(ic, RelocInfo::CODE_TARGET, prop->PropertyFeedbackId());
++}
++
++
++void FullCodeGenerator::EmitInlineSmiBinaryOp(BinaryOperation* expr,
++ Token::Value op,
++ OverwriteMode mode,
++ Expression* left_expr,
++ Expression* right_expr) {
++ Label done, smi_case, stub_call;
++
++ Register scratch1 = r5;
++ Register scratch2 = r6;
++
++ // Get the arguments.
++ Register left = r4;
++ Register right = r3;
++ __ pop(left);
++
++ // Perform combined smi check on both operands.
++ __ orx(scratch1, left, right);
++ STATIC_ASSERT(kSmiTag == 0);
++ JumpPatchSite patch_site(masm_);
++ patch_site.EmitJumpIfSmi(scratch1, &smi_case);
++
++ __ bind(&stub_call);
++ BinaryOpStub stub(op, mode);
++ CallIC(stub.GetCode(), RelocInfo::CODE_TARGET,
++ expr->BinaryOperationFeedbackId());
++ patch_site.EmitPatchInfo();
++ __ b(&done);
++
++ __ bind(&smi_case);
++ // Smi case. This code works the same way as the smi-smi case in the type
++ // recording binary operation stub, see
++ // BinaryOpStub::GenerateSmiSmiOperation for comments.
++ switch (op) {
++ case Token::SAR:
++ __ b(&stub_call);
++ __ GetLeastBitsFromSmi(scratch1, right, 5);
++ __ ShiftRightArith(right, left, scratch1);
++ __ ClearRightImm(right, right, Operand(kSmiTagSize + kSmiShiftSize));
++ break;
++ case Token::SHL: {
++ __ b(&stub_call);
++ __ GetLeastBitsFromSmi(scratch2, right, 5);
++#if V8_TARGET_ARCH_PPC64
++ __ ShiftLeft(right, left, scratch2);
++#else
++ __ SmiUntag(scratch1, left);
++ __ ShiftLeft(scratch1, scratch1, scratch2);
++ // Check that the *signed* result fits in a smi
++ __ JumpIfNotSmiCandidate(scratch1, scratch2, &stub_call);
++ __ SmiTag(right, scratch1);
++#endif
++ break;
++ }
++ case Token::SHR: {
++ __ b(&stub_call);
++ __ SmiUntag(scratch1, left);
++ __ GetLeastBitsFromSmi(scratch2, right, 5);
++ __ srw(scratch1, scratch1, scratch2);
++ // Unsigned shift is not allowed to produce a negative number.
++ __ JumpIfNotUnsignedSmiCandidate(scratch1, r0, &stub_call);
++ __ SmiTag(right, scratch1);
++ break;
++ }
++ case Token::ADD: {
++ Label add_no_overflow;
++ // C = A+B; C overflows if A/B have same sign and C has diff sign than A
++ __ xor_(r0, left, right);
++ __ add(scratch1, left, right);
++ __ TestSignBit(r0, r0);
++ __ bne(&add_no_overflow, cr0);
++ __ xor_(r0, right, scratch1);
++ __ TestSignBit(r0, r0);
++ __ bne(&stub_call, cr0);
++ __ bind(&add_no_overflow);
++ __ mr(right, scratch1);
++ break;
++ }
++ case Token::SUB: {
++ Label sub_no_overflow;
++ // C = A-B; C overflows if A/B have diff signs and C has diff sign than A
++ __ xor_(r0, left, right);
++ __ sub(scratch1, left, right);
++ __ TestSignBit(r0, r0);
++ __ beq(&sub_no_overflow, cr0);
++ __ xor_(r0, scratch1, left);
++ __ TestSignBit(r0, r0);
++ __ bne(&stub_call, cr0);
++ __ bind(&sub_no_overflow);
++ __ mr(right, scratch1);
++ break;
++ }
++ case Token::MUL: {
++ Label mul_zero;
++#if V8_TARGET_ARCH_PPC64
++ // Remove tag from both operands.
++ __ SmiUntag(ip, right);
++ __ SmiUntag(r0, left);
++ __ Mul(scratch1, r0, ip);
++ // Check for overflowing the smi range - no overflow if higher 33 bits of
++ // the result are identical.
++ __ TestIfInt32(scratch1, scratch2, ip);
++ __ bne(&stub_call);
++#else
++ __ SmiUntag(ip, right);
++ __ mullw(scratch1, left, ip);
++ __ mulhw(scratch2, left, ip);
++ // Check for overflowing the smi range - no overflow if higher 33 bits of
++ // the result are identical.
++ __ TestIfInt32(scratch2, scratch1, ip);
++ __ bne(&stub_call);
++#endif
++ // Go slow on zero result to handle -0.
++ __ cmpi(scratch1, Operand::Zero());
++ __ beq(&mul_zero);
++#if V8_TARGET_ARCH_PPC64
++ __ SmiTag(right, scratch1);
++#else
++ __ mr(right, scratch1);
++#endif
++ __ b(&done);
++ // We need -0 if we were multiplying a negative number with 0 to get 0.
++ // We know one of them was zero.
++ __ bind(&mul_zero);
++ __ add(scratch2, right, left);
++ __ cmpi(scratch2, Operand::Zero());
++ __ blt(&stub_call);
++ __ LoadSmiLiteral(right, Smi::FromInt(0));
++ break;
++ }
++ case Token::BIT_OR:
++ __ orx(right, left, right);
++ break;
++ case Token::BIT_AND:
++ __ and_(right, left, right);
++ break;
++ case Token::BIT_XOR:
++ __ xor_(right, left, right);
++ break;
++ default:
++ UNREACHABLE();
++ }
++
++ __ bind(&done);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitBinaryOp(BinaryOperation* expr,
++ Token::Value op,
++ OverwriteMode mode) {
++ __ pop(r4);
++ BinaryOpStub stub(op, mode);
++ JumpPatchSite patch_site(masm_); // unbound, signals no inlined smi code.
++ CallIC(stub.GetCode(), RelocInfo::CODE_TARGET,
++ expr->BinaryOperationFeedbackId());
++ patch_site.EmitPatchInfo();
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitAssignment(Expression* expr) {
++ // Invalid left-hand sides are rewritten to have a 'throw
++ // ReferenceError' on the left-hand side.
++ if (!expr->IsValidLeftHandSide()) {
++ VisitForEffect(expr);
++ return;
++ }
++
++ // Left-hand side can only be a property, a global or a (parameter or local)
++ // slot.
++ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
++ LhsKind assign_type = VARIABLE;
++ Property* prop = expr->AsProperty();
++ if (prop != NULL) {
++ assign_type = (prop->key()->IsPropertyName())
++ ? NAMED_PROPERTY
++ : KEYED_PROPERTY;
++ }
++
++ switch (assign_type) {
++ case VARIABLE: {
++ Variable* var = expr->AsVariableProxy()->var();
++ EffectContext context(this);
++ EmitVariableAssignment(var, Token::ASSIGN);
++ break;
++ }
++ case NAMED_PROPERTY: {
++ __ push(r3); // Preserve value.
++ VisitForAccumulatorValue(prop->obj());
++ __ mr(r4, r3);
++ __ pop(r3); // Restore value.
++ __ mov(r5, Operand(prop->key()->AsLiteral()->handle()));
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->StoreIC_Initialize()
++ : isolate()->builtins()->StoreIC_Initialize_Strict();
++ CallIC(ic);
++ break;
++ }
++ case KEYED_PROPERTY: {
++ __ push(r3); // Preserve value.
++ VisitForStackValue(prop->obj());
++ VisitForAccumulatorValue(prop->key());
++ __ mr(r4, r3);
++ __ pop(r5);
++ __ pop(r3); // Restore value.
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->KeyedStoreIC_Initialize()
++ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
++ CallIC(ic);
++ break;
++ }
++ }
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitVariableAssignment(Variable* var,
++ Token::Value op) {
++ if (var->IsUnallocated()) {
++ // Global var, const, or let.
++ __ mov(r5, Operand(var->name()));
++ __ LoadP(r4, GlobalObjectOperand());
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->StoreIC_Initialize()
++ : isolate()->builtins()->StoreIC_Initialize_Strict();
++ CallIC(ic, RelocInfo::CODE_TARGET_CONTEXT);
++
++ } else if (op == Token::INIT_CONST) {
++ // Const initializers need a write barrier.
++ ASSERT(!var->IsParameter()); // No const parameters.
++ if (var->IsStackLocal()) {
++ Label skip;
++ __ LoadP(r4, StackOperand(var));
++ __ CompareRoot(r4, Heap::kTheHoleValueRootIndex);
++ __ bne(&skip);
++ __ StoreP(result_register(), StackOperand(var));
++ __ bind(&skip);
++ } else {
++ ASSERT(var->IsContextSlot() || var->IsLookupSlot());
++ // Like var declarations, const declarations are hoisted to function
++ // scope. However, unlike var initializers, const initializers are
++ // able to drill a hole to that function context, even from inside a
++ // 'with' context. We thus bypass the normal static scope lookup for
++ // var->IsContextSlot().
++ __ push(r3);
++ __ mov(r3, Operand(var->name()));
++ __ Push(cp, r3); // Context and name.
++ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3);
++ }
++
++ } else if (var->mode() == LET && op != Token::INIT_LET) {
++ // Non-initializing assignment to let variable needs a write barrier.
++ if (var->IsLookupSlot()) {
++ __ push(r3); // Value.
++ __ mov(r4, Operand(var->name()));
++ __ LoadSmiLiteral(r3, Smi::FromInt(language_mode()));
++ __ Push(cp, r4, r3); // Context, name, strict mode.
++ __ CallRuntime(Runtime::kStoreContextSlot, 4);
++ } else {
++ ASSERT(var->IsStackAllocated() || var->IsContextSlot());
++ Label assign;
++ MemOperand location = VarOperand(var, r4);
++ __ LoadP(r6, location);
++ __ CompareRoot(r6, Heap::kTheHoleValueRootIndex);
++ __ bne(&assign);
++ __ mov(r6, Operand(var->name()));
++ __ push(r6);
++ __ CallRuntime(Runtime::kThrowReferenceError, 1);
++ // Perform the assignment.
++ __ bind(&assign);
++ __ StoreP(result_register(), location, r0);
++ if (var->IsContextSlot()) {
++ // RecordWrite may destroy all its register arguments.
++ __ mr(r6, result_register());
++ int offset = Context::SlotOffset(var->index());
++ __ RecordWriteContextSlot(
++ r4, offset, r6, r5, kLRHasBeenSaved, kDontSaveFPRegs);
++ }
++ }
++
++ } else if (!var->is_const_mode() || op == Token::INIT_CONST_HARMONY) {
++ // Assignment to var or initializing assignment to let/const
++ // in harmony mode.
++ if (var->IsStackAllocated() || var->IsContextSlot()) {
++ MemOperand location = VarOperand(var, r4);
++ if (generate_debug_code_ && op == Token::INIT_LET) {
++ // Check for an uninitialized let binding.
++ __ LoadP(r5, location);
++ __ CompareRoot(r5, Heap::kTheHoleValueRootIndex);
++ __ Check(eq, "Let binding re-initialization.");
++ }
++ // Perform the assignment.
++ __ StoreP(r3, location, r0);
++ if (var->IsContextSlot()) {
++ __ mr(r6, r3);
++ int offset = Context::SlotOffset(var->index());
++ __ RecordWriteContextSlot(
++ r4, offset, r6, r5, kLRHasBeenSaved, kDontSaveFPRegs);
++ }
++ } else {
++ ASSERT(var->IsLookupSlot());
++ __ push(r3); // Value.
++ __ mov(r4, Operand(var->name()));
++ __ LoadSmiLiteral(r3, Smi::FromInt(language_mode()));
++ __ Push(cp, r4, r3); // Context, name, strict mode.
++ __ CallRuntime(Runtime::kStoreContextSlot, 4);
++ }
++ }
++ // Non-initializing assignments to consts are ignored.
++}
++
++
++void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
++ // Assignment to a property, using a named store IC.
++ Property* prop = expr->target()->AsProperty();
++ ASSERT(prop != NULL);
++ ASSERT(prop->key()->AsLiteral() != NULL);
++
++ // Record source code position before IC call.
++ SetSourcePosition(expr->position());
++ __ mov(r5, Operand(prop->key()->AsLiteral()->handle()));
++ __ pop(r4);
++
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->StoreIC_Initialize()
++ : isolate()->builtins()->StoreIC_Initialize_Strict();
++ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
++
++ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitKeyedPropertyAssignment(Assignment* expr) {
++ // Assignment to a property, using a keyed store IC.
++
++ // Record source code position before IC call.
++ SetSourcePosition(expr->position());
++ __ pop(r4); // Key.
++ __ pop(r5);
++
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->KeyedStoreIC_Initialize()
++ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
++ CallIC(ic, RelocInfo::CODE_TARGET, expr->AssignmentFeedbackId());
++
++ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::VisitProperty(Property* expr) {
++ Comment cmnt(masm_, "[ Property");
++ Expression* key = expr->key();
++
++ if (key->IsPropertyName()) {
++ VisitForAccumulatorValue(expr->obj());
++ EmitNamedPropertyLoad(expr);
++ PrepareForBailoutForId(expr->LoadId(), TOS_REG);
++ context()->Plug(r3);
++ } else {
++ VisitForStackValue(expr->obj());
++ VisitForAccumulatorValue(expr->key());
++ __ pop(r4);
++ EmitKeyedPropertyLoad(expr);
++ context()->Plug(r3);
++ }
++}
++
++
++void FullCodeGenerator::CallIC(Handle<Code> code,
++ RelocInfo::Mode rmode,
++ TypeFeedbackId ast_id) {
++ ic_total_count_++;
++ __ Call(code, rmode, ast_id);
++}
++
++void FullCodeGenerator::EmitCallWithIC(Call* expr,
++ Handle<Object> name,
++ RelocInfo::Mode mode) {
++ // Code common for calls using the IC.
++ ZoneList<Expression*>* args = expr->arguments();
++ int arg_count = args->length();
++ { PreservePositionScope scope(masm()->positions_recorder());
++ for (int i = 0; i < arg_count; i++) {
++ VisitForStackValue(args->at(i));
++ }
++ __ mov(r5, Operand(name));
++ }
++ // Record source position for debugger.
++ SetSourcePosition(expr->position());
++ // Call the IC initialization code.
++ Handle<Code> ic =
++ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
++ CallIC(ic, mode, expr->CallFeedbackId());
++ RecordJSReturnSite(expr);
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr,
++ Expression* key) {
++ // Load the key.
++ VisitForAccumulatorValue(key);
++
++ // Swap the name of the function and the receiver on the stack to follow
++ // the calling convention for call ICs.
++ __ pop(r4);
++ __ push(r3);
++ __ push(r4);
++
++ // Code common for calls using the IC.
++ ZoneList<Expression*>* args = expr->arguments();
++ int arg_count = args->length();
++ { PreservePositionScope scope(masm()->positions_recorder());
++ for (int i = 0; i < arg_count; i++) {
++ VisitForStackValue(args->at(i));
++ }
++ }
++ // Record source position for debugger.
++ SetSourcePosition(expr->position());
++ // Call the IC initialization code.
++ Handle<Code> ic =
++ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
++ __ LoadP(r5, MemOperand(sp, (arg_count + 1) * kPointerSize), r0); // Key.
++ CallIC(ic, RelocInfo::CODE_TARGET, expr->CallFeedbackId());
++ RecordJSReturnSite(expr);
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ context()->DropAndPlug(1, r3); // Drop the key still on the stack.
++}
++
++
++void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) {
++ // Code common for calls using the call stub.
++ ZoneList<Expression*>* args = expr->arguments();
++ int arg_count = args->length();
++ { PreservePositionScope scope(masm()->positions_recorder());
++ for (int i = 0; i < arg_count; i++) {
++ VisitForStackValue(args->at(i));
++ }
++ }
++ // Record source position for debugger.
++ SetSourcePosition(expr->position());
++
++ // Record call targets in unoptimized code.
++ flags = static_cast<CallFunctionFlags>(flags | RECORD_CALL_TARGET);
++ Handle<Object> uninitialized =
++ TypeFeedbackCells::UninitializedSentinel(isolate());
++ Handle<JSGlobalPropertyCell> cell =
++ isolate()->factory()->NewJSGlobalPropertyCell(uninitialized);
++ RecordTypeFeedbackCell(expr->CallFeedbackId(), cell);
++ __ mov(r5, Operand(cell));
++
++ CallFunctionStub stub(arg_count, flags);
++ __ LoadP(r4, MemOperand(sp, (arg_count + 1) * kPointerSize), r0);
++ __ CallStub(&stub);
++ RecordJSReturnSite(expr);
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ context()->DropAndPlug(1, r3);
++}
++
++
++void FullCodeGenerator::EmitResolvePossiblyDirectEval(int arg_count) {
++ // Push copy of the first argument or undefined if it doesn't exist.
++ if (arg_count > 0) {
++ __ LoadP(r4, MemOperand(sp, arg_count * kPointerSize), r0);
++ } else {
++ __ LoadRoot(r4, Heap::kUndefinedValueRootIndex);
++ }
++ __ push(r4);
++
++ // Push the receiver of the enclosing function.
++ int receiver_offset = 2 + info_->scope()->num_parameters();
++ __ LoadP(r4, MemOperand(fp, receiver_offset * kPointerSize), r0);
++ __ push(r4);
++ // Push the language mode.
++ __ LoadSmiLiteral(r4, Smi::FromInt(language_mode()));
++ __ push(r4);
++
++ // Push the start position of the scope the calls resides in.
++ __ LoadSmiLiteral(r4, Smi::FromInt(scope()->start_position()));
++ __ push(r4);
++
++ // Do the runtime call.
++ __ CallRuntime(Runtime::kResolvePossiblyDirectEval, 5);
++}
++
++
++void FullCodeGenerator::VisitCall(Call* expr) {
++#ifdef DEBUG
++ // We want to verify that RecordJSReturnSite gets called on all paths
++ // through this function. Avoid early returns.
++ expr->return_is_recorded_ = false;
++#endif
++
++ Comment cmnt(masm_, "[ Call");
++ Expression* callee = expr->expression();
++ VariableProxy* proxy = callee->AsVariableProxy();
++ Property* property = callee->AsProperty();
++
++ if (proxy != NULL && proxy->var()->is_possibly_eval()) {
++ // In a call to eval, we first call %ResolvePossiblyDirectEval to
++ // resolve the function we need to call and the receiver of the
++ // call. Then we call the resolved function using the given
++ // arguments.
++ ZoneList<Expression*>* args = expr->arguments();
++ int arg_count = args->length();
++
++ { PreservePositionScope pos_scope(masm()->positions_recorder());
++ VisitForStackValue(callee);
++ __ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
++ __ push(r5); // Reserved receiver slot.
++
++ // Push the arguments.
++ for (int i = 0; i < arg_count; i++) {
++ VisitForStackValue(args->at(i));
++ }
++
++ // Push a copy of the function (found below the arguments) and
++ // resolve eval.
++ __ LoadP(r4, MemOperand(sp, (arg_count + 1) * kPointerSize), r0);
++ __ push(r4);
++ EmitResolvePossiblyDirectEval(arg_count);
++
++ // The runtime call returns a pair of values in r3 (function) and
++ // r4 (receiver). Touch up the stack with the right values.
++ __ StoreP(r3, MemOperand(sp, (arg_count + 1) * kPointerSize), r0);
++ __ StoreP(r4, MemOperand(sp, arg_count * kPointerSize), r0);
++ }
++
++ // Record source position for debugger.
++ SetSourcePosition(expr->position());
++ CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
++ __ LoadP(r4, MemOperand(sp, (arg_count + 1) * kPointerSize), r0);
++ __ CallStub(&stub);
++ RecordJSReturnSite(expr);
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ context()->DropAndPlug(1, r3);
++ } else if (proxy != NULL && proxy->var()->IsUnallocated()) {
++ // Push global object as receiver for the call IC.
++ __ LoadP(r3, GlobalObjectOperand());
++ __ push(r3);
++ EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT);
++ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
++ // Call to a lookup slot (dynamically introduced variable).
++ Label slow, done;
++
++ { PreservePositionScope scope(masm()->positions_recorder());
++ // Generate code for loading from variables potentially shadowed
++ // by eval-introduced variables.
++ EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow,
&done);
++ }
++
++ __ bind(&slow);
++ // Call the runtime to find the function to call (returned in r3)
++ // and the object holding it (returned in edx).
++ __ push(context_register());
++ __ mov(r5, Operand(proxy->name()));
++ __ push(r5);
++ __ CallRuntime(Runtime::kLoadContextSlot, 2);
++ __ Push(r3, r4); // Function, receiver.
++
++ // If fast case code has been generated, emit code to push the
++ // function and receiver and have the slow path jump around this
++ // code.
++ if (done.is_linked()) {
++ Label call;
++ __ b(&call);
++ __ bind(&done);
++ // Push function.
++ __ push(r3);
++ // The receiver is implicitly the global receiver. Indicate this
++ // by passing the hole to the call function stub.
++ __ LoadRoot(r4, Heap::kTheHoleValueRootIndex);
++ __ push(r4);
++ __ bind(&call);
++ }
++
++ // The receiver is either the global receiver or an object found
++ // by LoadContextSlot. That object could be the hole if the
++ // receiver is implicitly the global object.
++ EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT);
++ } else if (property != NULL) {
++ { PreservePositionScope scope(masm()->positions_recorder());
++ VisitForStackValue(property->obj());
++ }
++ if (property->key()->IsPropertyName()) {
++ EmitCallWithIC(expr,
++ property->key()->AsLiteral()->handle(),
++ RelocInfo::CODE_TARGET);
++ } else {
++ EmitKeyedCallWithIC(expr, property->key());
++ }
++ } else {
++ // Call to an arbitrary expression not handled specially above.
++ { PreservePositionScope scope(masm()->positions_recorder());
++ VisitForStackValue(callee);
++ }
++ // Load global receiver object.
++ __ LoadP(r4, GlobalObjectOperand());
++ __ LoadP(r4, FieldMemOperand(r4, GlobalObject::kGlobalReceiverOffset));
++ __ push(r4);
++ // Emit function call.
++ EmitCallWithStub(expr, NO_CALL_FUNCTION_FLAGS);
++ }
++
++#ifdef DEBUG
++ // RecordJSReturnSite should have been called.
++ ASSERT(expr->return_is_recorded_);
++#endif
++}
++
++
++void FullCodeGenerator::VisitCallNew(CallNew* expr) {
++ Comment cmnt(masm_, "[ CallNew");
++ // According to ECMA-262, section 11.2.2, page 44, the function
++ // expression in new calls must be evaluated before the
++ // arguments.
++
++ // Push constructor on the stack. If it's not a function it's used as
++ // receiver for CALL_NON_FUNCTION, otherwise the value on the stack is
++ // ignored.
++ VisitForStackValue(expr->expression());
++
++ // Push the arguments ("left-to-right") on the stack.
++ ZoneList<Expression*>* args = expr->arguments();
++ int arg_count = args->length();
++ for (int i = 0; i < arg_count; i++) {
++ VisitForStackValue(args->at(i));
++ }
++
++ // Call the construct call builtin that handles allocation and
++ // constructor invocation.
++ SetSourcePosition(expr->position());
++
++ // Load function and argument count into r4 and r3.
++ __ mov(r3, Operand(arg_count));
++ __ LoadP(r4, MemOperand(sp, arg_count * kPointerSize), r0);
++
++ // Record call targets in unoptimized code.
++ Handle<Object> uninitialized =
++ TypeFeedbackCells::UninitializedSentinel(isolate());
++ Handle<JSGlobalPropertyCell> cell =
++ isolate()->factory()->NewJSGlobalPropertyCell(uninitialized);
++ RecordTypeFeedbackCell(expr->CallNewFeedbackId(), cell);
++ __ mov(r5, Operand(cell));
++
++ CallConstructStub stub(RECORD_CALL_TARGET);
++ __ Call(stub.GetCode(), RelocInfo::CONSTRUCT_CALL);
++ PrepareForBailoutForId(expr->ReturnId(), TOS_REG);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitIsSmi(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ __ andi(r0, r3, Operand(kSmiTagMask));
++ Split(eq, if_true, if_false, fall_through, cr0);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsNonNegativeSmi(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ __ TestIfPositiveSmi(r3, r0);
++ Split(eq, if_true, if_false, fall_through, cr0);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsObject(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ JumpIfSmi(r3, if_false);
++ __ LoadRoot(ip, Heap::kNullValueRootIndex);
++ __ cmp(r3, ip);
++ __ beq(if_true);
++ __ LoadP(r5, FieldMemOperand(r3, HeapObject::kMapOffset));
++ // Undetectable objects behave like undefined when tested with typeof.
++ __ lbz(r4, FieldMemOperand(r5, Map::kBitFieldOffset));
++ __ andi(r0, r4, Operand(1 << Map::kIsUndetectable));
++ __ bne(if_false, cr0);
++ __ lbz(r4, FieldMemOperand(r5, Map::kInstanceTypeOffset));
++ __ cmpi(r4, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ __ blt(if_false);
++ __ cmpi(r4, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(le, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsSpecObject(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ JumpIfSmi(r3, if_false);
++ __ CompareObjectType(r3, r4, r4, FIRST_SPEC_OBJECT_TYPE);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(ge, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsUndetectableObject(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ JumpIfSmi(r3, if_false);
++ __ LoadP(r4, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ lbz(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
++ __ andi(r0, r4, Operand(1 << Map::kIsUndetectable));
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(ne, if_true, if_false, fall_through, cr0);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsStringWrapperSafeForDefaultValueOf(
++ CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ AssertNotSmi(r3);
++
++ __ LoadP(r4, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ lbz(ip, FieldMemOperand(r4, Map::kBitField2Offset));
++ __ andi(r0, ip, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
++ __ bne(if_true, cr0);
++
++ // Check for fast case object. Generate false result for slow case object.
++ __ LoadP(r5, FieldMemOperand(r3, JSObject::kPropertiesOffset));
++ __ LoadP(r5, FieldMemOperand(r5, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
++ __ cmp(r5, ip);
++ __ beq(if_false);
++
++ // Look for valueOf symbol in the descriptor array, and indicate false if
++ // found. Since we omit an enumeration index check, if it is added via a
++ // transition that shares its descriptor array, this is a false positive.
++ Label entry, loop, done;
++
++ // Skip loop if no descriptors are valid.
++ __ NumberOfOwnDescriptors(r6, r4);
++ __ cmpi(r6, Operand::Zero());
++ __ beq(&done);
++
++ __ LoadInstanceDescriptors(r4, r7);
++ // r7: descriptor array.
++ // r6: valid entries in the descriptor array.
++ __ mov(ip, Operand(DescriptorArray::kDescriptorSize));
++ __ Mul(r6, r6, ip);
++ // Calculate location of the first key name.
++ __ addi(r7, r7, Operand(DescriptorArray::kFirstOffset - kHeapObjectTag));
++ // Calculate the end of the descriptor array.
++ __ mr(r5, r7);
++ __ SmiToPtrArrayOffset(ip, r6);
++ __ add(r5, r5, ip);
++
++ // Loop through all the keys in the descriptor array. If one of these is the
++ // symbol valueOf the result is false.
++ // The use of ip to store the valueOf symbol asumes that it is not otherwise
++ // used in the loop below.
++ __ mov(ip, Operand(FACTORY->value_of_symbol()));
++ __ b(&entry);
++ __ bind(&loop);
++ __ LoadP(r6, MemOperand(r7, 0));
++ __ cmp(r6, ip);
++ __ beq(if_false);
++ __ addi(r7, r7, Operand(DescriptorArray::kDescriptorSize * kPointerSize));
++ __ bind(&entry);
++ __ cmp(r7, r5);
++ __ bne(&loop);
++
++ __ bind(&done);
++ // If a valueOf property is not found on the object check that its
++ // prototype is the un-modified String prototype. If not result is false.
++ __ LoadP(r5, FieldMemOperand(r4, Map::kPrototypeOffset));
++ __ JumpIfSmi(r5, if_false);
++ __ LoadP(r5, FieldMemOperand(r5, HeapObject::kMapOffset));
++ __ LoadP(r6, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
++ __ LoadP(r6, FieldMemOperand(r6, GlobalObject::kNativeContextOffset));
++ __ LoadP(r6, ContextOperand(r6,
++ Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
++ __ cmp(r5, r6);
++ __ bne(if_false);
++
++ // Set the bit in the map to indicate that it has been checked safe for
++ // default valueOf and set true result.
++ __ lbz(r5, FieldMemOperand(r4, Map::kBitField2Offset));
++ __ ori(r5, r5, Operand(1 << Map::kStringWrapperSafeForDefaultValueOf));
++ __ stb(r5, FieldMemOperand(r4, Map::kBitField2Offset));
++ __ b(if_true);
++
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsFunction(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ JumpIfSmi(r3, if_false);
++ __ CompareObjectType(r3, r4, r5, JS_FUNCTION_TYPE);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(eq, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsArray(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ JumpIfSmi(r3, if_false);
++ __ CompareObjectType(r3, r4, r4, JS_ARRAY_TYPE);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(eq, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitIsRegExp(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ JumpIfSmi(r3, if_false);
++ __ CompareObjectType(r3, r4, r4, JS_REGEXP_TYPE);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(eq, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++
++void FullCodeGenerator::EmitIsConstructCall(CallRuntime* expr) {
++ ASSERT(expr->arguments()->length() == 0);
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ // Get the frame pointer for the calling frame.
++ __ LoadP(r5, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++
++ // Skip the arguments adaptor frame if it exists.
++ Label check_frame_marker;
++ __ LoadP(r4, MemOperand(r5, StandardFrameConstants::kContextOffset));
++ __ CmpSmiLiteral(r4, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++ __ bne(&check_frame_marker);
++ __ LoadP(r5, MemOperand(r5, StandardFrameConstants::kCallerFPOffset));
++
++ // Check the marker in the calling frame.
++ __ bind(&check_frame_marker);
++ __ LoadP(r4, MemOperand(r5, StandardFrameConstants::kMarkerOffset));
++ STATIC_ASSERT(StackFrame::CONSTRUCT < 0x4000);
++ __ CmpSmiLiteral(r4, Smi::FromInt(StackFrame::CONSTRUCT), r0);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(eq, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitObjectEquals(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 2);
++
++ // Load the two objects into registers and perform the comparison.
++ VisitForStackValue(args->at(0));
++ VisitForAccumulatorValue(args->at(1));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ pop(r4);
++ __ cmp(r3, r4);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(eq, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitArguments(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++
++ // ArgumentsAccessStub expects the key in edx and the formal
++ // parameter count in r3.
++ VisitForAccumulatorValue(args->at(0));
++ __ mr(r4, r3);
++ __ LoadSmiLiteral(r3, Smi::FromInt(info_->scope()->num_parameters()));
++ ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitArgumentsLength(CallRuntime* expr) {
++ ASSERT(expr->arguments()->length() == 0);
++ Label exit;
++ // Get the number of formal parameters.
++ __ LoadSmiLiteral(r3, Smi::FromInt(info_->scope()->num_parameters()));
++
++ // Check if the calling frame is an arguments adaptor frame.
++ __ LoadP(r5, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++ __ LoadP(r6, MemOperand(r5, StandardFrameConstants::kContextOffset));
++ __ CmpSmiLiteral(r6, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++ __ bne(&exit);
++
++ // Arguments adaptor case: Read the arguments length from the
++ // adaptor frame.
++ __ LoadP(r3, MemOperand(r5, ArgumentsAdaptorFrameConstants::kLengthOffset));
++
++ __ bind(&exit);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitClassOf(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ Label done, null, function, non_function_constructor;
++
++ VisitForAccumulatorValue(args->at(0));
++
++ // If the object is a smi, we return null.
++ __ JumpIfSmi(r3, &null);
++
++ // Check that the object is a JS object but take special care of JS
++ // functions to make sure they have 'Function' as their class.
++ // Assume that there are only two callable types, and one of them is at
++ // either end of the type range for JS object types. Saves extra comparisons.
++ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
++ __ CompareObjectType(r3, r3, r4, FIRST_SPEC_OBJECT_TYPE);
++ // Map is now in r3.
++ __ blt(&null);
++ STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE ==
++ FIRST_SPEC_OBJECT_TYPE + 1);
++ __ beq(&function);
++
++ __ cmpi(r4, Operand(LAST_SPEC_OBJECT_TYPE));
++ STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE ==
++ LAST_SPEC_OBJECT_TYPE - 1);
++ __ beq(&function);
++ // Assume that there is no larger type.
++ STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE == LAST_TYPE - 1);
++
++ // Check if the constructor in the map is a JS function.
++ __ LoadP(r3, FieldMemOperand(r3, Map::kConstructorOffset));
++ __ CompareObjectType(r3, r4, r4, JS_FUNCTION_TYPE);
++ __ bne(&non_function_constructor);
++
++ // r3 now contains the constructor function. Grab the
++ // instance class name from there.
++ __ LoadP(r3, FieldMemOperand(r3, JSFunction::kSharedFunctionInfoOffset));
++ __ LoadP(r3,
++ FieldMemOperand(r3, SharedFunctionInfo::kInstanceClassNameOffset));
++ __ b(&done);
++
++ // Functions have class 'Function'.
++ __ bind(&function);
++ __ LoadRoot(r3, Heap::kfunction_class_symbolRootIndex);
++ __ b(&done);
++
++ // Objects with a non-function constructor have class 'Object'.
++ __ bind(&non_function_constructor);
++ __ LoadRoot(r3, Heap::kObject_symbolRootIndex);
++ __ b(&done);
++
++ // Non-JS objects have class null.
++ __ bind(&null);
++ __ LoadRoot(r3, Heap::kNullValueRootIndex);
++
++ // All done.
++ __ bind(&done);
++
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitLog(CallRuntime* expr) {
++ // Conditionally generate a log call.
++ // Args:
++ // 0 (literal string): The type of logging (corresponds to the flags).
++ // This is used to determine whether or not to generate the log call.
++ // 1 (string): Format string. Access the string at argument index 2
++ // with '%2s' (see Logger::LogRuntime for all the formats).
++ // 2 (array): Arguments to the format string.
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT_EQ(args->length(), 3);
++ if (CodeGenerator::ShouldGenerateLog(args->at(0))) {
++ VisitForStackValue(args->at(1));
++ VisitForStackValue(args->at(2));
++ __ CallRuntime(Runtime::kLog, 2);
++ }
++
++ // Finally, we're expected to leave a value on the top of the stack.
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitRandomHeapNumber(CallRuntime* expr) {
++ ASSERT(expr->arguments()->length() == 0);
++ Label slow_allocate_heapnumber;
++ Label heapnumber_allocated;
++
++ __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r7, r4, r5, r9, &slow_allocate_heapnumber);
++ __ b(&heapnumber_allocated);
++
++ __ bind(&slow_allocate_heapnumber);
++ // Allocate a heap number.
++ __ CallRuntime(Runtime::kNumberAlloc, 0);
++ __ mr(r7, r3);
++
++ __ bind(&heapnumber_allocated);
++
++ // Optimization opportunity here
++ // See other platforms for reference
++ __ PrepareCallCFunction(2, r3);
++ __ LoadP(r4,
++ ContextOperand(context_register(), Context::GLOBAL_OBJECT_INDEX));
++ __ mr(r3, r7);
++ __ LoadP(r4, FieldMemOperand(r4, GlobalObject::kNativeContextOffset));
++ __ CallCFunction(
++ ExternalReference::fill_heap_number_with_random_function(isolate()), 2);
++
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitSubString(CallRuntime* expr) {
++ // Load the arguments on the stack and call the stub.
++ SubStringStub stub;
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 3);
++ VisitForStackValue(args->at(0));
++ VisitForStackValue(args->at(1));
++ VisitForStackValue(args->at(2));
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitRegExpExec(CallRuntime* expr) {
++ // Load the arguments on the stack and call the stub.
++ RegExpExecStub stub;
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 4);
++ VisitForStackValue(args->at(0));
++ VisitForStackValue(args->at(1));
++ VisitForStackValue(args->at(2));
++ VisitForStackValue(args->at(3));
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitValueOf(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForAccumulatorValue(args->at(0)); // Load the object.
++
++ Label done;
++ // If the object is a smi return the object.
++ __ JumpIfSmi(r3, &done);
++ // If the object is not a value type, return the object.
++ __ CompareObjectType(r3, r4, r4, JS_VALUE_TYPE);
++ __ bne(&done);
++ __ LoadP(r3, FieldMemOperand(r3, JSValue::kValueOffset));
++
++ __ bind(&done);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitDateField(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 2);
++ ASSERT_NE(NULL, args->at(1)->AsLiteral());
++ Smi* index = Smi::cast(*(args->at(1)->AsLiteral()->handle()));
++
++ VisitForAccumulatorValue(args->at(0)); // Load the object.
++
++ Label runtime, done, not_date_object;
++ Register object = r3;
++ Register result = r3;
++ Register scratch0 = r22;
++ Register scratch1 = r4;
++
++ __ JumpIfSmi(object, ¬_date_object);
++ __ CompareObjectType(object, scratch1, scratch1, JS_DATE_TYPE);
++ __ bne(¬_date_object);
++
++ if (index->value() == 0) {
++ __ LoadP(result, FieldMemOperand(object, JSDate::kValueOffset));
++ __ b(&done);
++ } else {
++ if (index->value() < JSDate::kFirstUncachedField) {
++ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
++ __ mov(scratch1, Operand(stamp));
++ __ LoadP(scratch1, MemOperand(scratch1));
++ __ LoadP(scratch0, FieldMemOperand(object, JSDate::kCacheStampOffset));
++ __ cmp(scratch1, scratch0);
++ __ bne(&runtime);
++ __ LoadP(result, FieldMemOperand(object, JSDate::kValueOffset +
++ kPointerSize * index->value()),
++ scratch0);
++ __ b(&done);
++ }
++ __ bind(&runtime);
++ __ PrepareCallCFunction(2, scratch1);
++ __ LoadSmiLiteral(r4, index);
++ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
++ __ b(&done);
++ }
++
++ __ bind(¬_date_object);
++ __ CallRuntime(Runtime::kThrowNotDateError, 0);
++ __ bind(&done);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitMathPow(CallRuntime* expr) {
++ // Load the arguments on the stack and call the runtime function.
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 2);
++ VisitForStackValue(args->at(0));
++ VisitForStackValue(args->at(1));
++ MathPowStub stub(MathPowStub::ON_STACK);
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitSetValueOf(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 2);
++ VisitForStackValue(args->at(0)); // Load the object.
++ VisitForAccumulatorValue(args->at(1)); // Load the value.
++ __ pop(r4); // r3 = value. r4 = object.
++
++ Label done;
++ // If the object is a smi, return the value.
++ __ JumpIfSmi(r4, &done);
++
++ // If the object is not a value type, return the value.
++ __ CompareObjectType(r4, r5, r5, JS_VALUE_TYPE);
++ __ bne(&done);
++
++ // Store the value.
++ __ StoreP(r3, FieldMemOperand(r4, JSValue::kValueOffset), r0);
++ // Update the write barrier. Save the value as it will be
++ // overwritten by the write barrier code and is needed afterward.
++ __ mr(r5, r3);
++ __ RecordWriteField(
++ r4, JSValue::kValueOffset, r5, r6, kLRHasBeenSaved, kDontSaveFPRegs);
++
++ __ bind(&done);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitNumberToString(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT_EQ(args->length(), 1);
++ // Load the argument on the stack and call the stub.
++ VisitForStackValue(args->at(0));
++
++ NumberToStringStub stub;
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitStringCharFromCode(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForAccumulatorValue(args->at(0));
++
++ Label done;
++ StringCharFromCodeGenerator generator(r3, r4);
++ generator.GenerateFast(masm_);
++ __ b(&done);
++
++ NopRuntimeCallHelper call_helper;
++ generator.GenerateSlow(masm_, call_helper);
++
++ __ bind(&done);
++ context()->Plug(r4);
++}
++
++
++void FullCodeGenerator::EmitStringCharCodeAt(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 2);
++ VisitForStackValue(args->at(0));
++ VisitForAccumulatorValue(args->at(1));
++
++ Register object = r4;
++ Register index = r3;
++ Register result = r6;
++
++ __ pop(object);
++
++ Label need_conversion;
++ Label index_out_of_range;
++ Label done;
++ StringCharCodeAtGenerator generator(object,
++ index,
++ result,
++ &need_conversion,
++ &need_conversion,
++ &index_out_of_range,
++ STRING_INDEX_IS_NUMBER);
++ generator.GenerateFast(masm_);
++ __ b(&done);
++
++ __ bind(&index_out_of_range);
++ // When the index is out of range, the spec requires us to return
++ // NaN.
++ __ LoadRoot(result, Heap::kNanValueRootIndex);
++ __ b(&done);
++
++ __ bind(&need_conversion);
++ // Load the undefined value into the result register, which will
++ // trigger conversion.
++ __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
++ __ b(&done);
++
++ NopRuntimeCallHelper call_helper;
++ generator.GenerateSlow(masm_, call_helper);
++
++ __ bind(&done);
++ context()->Plug(result);
++}
++
++
++void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 2);
++ VisitForStackValue(args->at(0));
++ VisitForAccumulatorValue(args->at(1));
++
++ Register object = r4;
++ Register index = r3;
++ Register scratch = r6;
++ Register result = r3;
++
++ __ pop(object);
++
++ Label need_conversion;
++ Label index_out_of_range;
++ Label done;
++ StringCharAtGenerator generator(object,
++ index,
++ scratch,
++ result,
++ &need_conversion,
++ &need_conversion,
++ &index_out_of_range,
++ STRING_INDEX_IS_NUMBER);
++ generator.GenerateFast(masm_);
++ __ b(&done);
++
++ __ bind(&index_out_of_range);
++ // When the index is out of range, the spec requires us to return
++ // the empty string.
++ __ LoadRoot(result, Heap::kEmptyStringRootIndex);
++ __ b(&done);
++
++ __ bind(&need_conversion);
++ // Move smi zero into the result register, which will trigger
++ // conversion.
++ __ LoadSmiLiteral(result, Smi::FromInt(0));
++ __ b(&done);
++
++ NopRuntimeCallHelper call_helper;
++ generator.GenerateSlow(masm_, call_helper);
++
++ __ bind(&done);
++ context()->Plug(result);
++}
++
++
++void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT_EQ(2, args->length());
++ VisitForStackValue(args->at(0));
++ VisitForStackValue(args->at(1));
++
++ StringAddStub stub(NO_STRING_ADD_FLAGS);
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitStringCompare(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT_EQ(2, args->length());
++ VisitForStackValue(args->at(0));
++ VisitForStackValue(args->at(1));
++
++ StringCompareStub stub;
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitMathSin(CallRuntime* expr) {
++ // Load the argument on the stack and call the stub.
++ TranscendentalCacheStub stub(TranscendentalCache::SIN,
++ TranscendentalCacheStub::TAGGED);
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForStackValue(args->at(0));
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitMathCos(CallRuntime* expr) {
++ // Load the argument on the stack and call the stub.
++ TranscendentalCacheStub stub(TranscendentalCache::COS,
++ TranscendentalCacheStub::TAGGED);
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForStackValue(args->at(0));
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitMathTan(CallRuntime* expr) {
++ // Load the argument on the stack and call the stub.
++ TranscendentalCacheStub stub(TranscendentalCache::TAN,
++ TranscendentalCacheStub::TAGGED);
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForStackValue(args->at(0));
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitMathLog(CallRuntime* expr) {
++ // Load the argument on the stack and call the stub.
++ TranscendentalCacheStub stub(TranscendentalCache::LOG,
++ TranscendentalCacheStub::TAGGED);
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForStackValue(args->at(0));
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitMathSqrt(CallRuntime* expr) {
++ // Load the argument on the stack and call the runtime function.
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForStackValue(args->at(0));
++ __ CallRuntime(Runtime::kMath_sqrt, 1);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitCallFunction(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() >= 2);
++
++ int arg_count = args->length() - 2; // 2 ~ receiver and function.
++ for (int i = 0; i < arg_count + 1; i++) {
++ VisitForStackValue(args->at(i));
++ }
++ VisitForAccumulatorValue(args->last()); // Function.
++
++ Label runtime, done;
++ // Check for non-function argument (including proxy).
++ __ JumpIfSmi(r3, &runtime);
++ __ CompareObjectType(r3, r4, r4, JS_FUNCTION_TYPE);
++ __ bne(&runtime);
++
++ // InvokeFunction requires the function in r4. Move it in there.
++ __ mr(r4, result_register());
++ ParameterCount count(arg_count);
++ __ InvokeFunction(r4, count, CALL_FUNCTION,
++ NullCallWrapper(), CALL_AS_METHOD);
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ __ b(&done);
++
++ __ bind(&runtime);
++ __ push(r3);
++ __ CallRuntime(Runtime::kCall, args->length());
++ __ bind(&done);
++
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitRegExpConstructResult(CallRuntime* expr) {
++ RegExpConstructResultStub stub;
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 3);
++ VisitForStackValue(args->at(0));
++ VisitForStackValue(args->at(1));
++ VisitForStackValue(args->at(2));
++ __ CallStub(&stub);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitGetFromCache(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT_EQ(2, args->length());
++ ASSERT_NE(NULL, args->at(0)->AsLiteral());
++ int cache_id = Smi::cast(*(args->at(0)->AsLiteral()->handle()))->value();
++
++ Handle<FixedArray> jsfunction_result_caches(
++ isolate()->native_context()->jsfunction_result_caches());
++ if (jsfunction_result_caches->length() <= cache_id) {
++ __ Abort("Attempt to use undefined cache.");
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ context()->Plug(r3);
++ return;
++ }
++
++ VisitForAccumulatorValue(args->at(1));
++
++ Register key = r3;
++ Register cache = r4;
++ __ LoadP(cache, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
++ __ LoadP(cache, FieldMemOperand(cache, GlobalObject::kNativeContextOffset));
++ __ LoadP(cache,
++ ContextOperand(cache, Context::JSFUNCTION_RESULT_CACHES_INDEX));
++ __ LoadP(cache,
++ FieldMemOperand(cache, FixedArray::OffsetOfElementAt(cache_id)), r0);
++
++ Label done, not_found;
++ // tmp now holds finger offset as a smi.
++ __ LoadP(r5, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset));
++ // r5 now holds finger offset as a smi.
++ __ addi(r6, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ // r6 now points to the start of fixed array elements.
++ __ SmiToPtrArrayOffset(r5, r5);
++ __ LoadPUX(r5, MemOperand(r6, r5));
++ // r6 now points to the key of the pair.
++ __ cmp(key, r5);
++ __ bne(¬_found);
++
++ __ LoadP(r3, MemOperand(r6, kPointerSize));
++ __ b(&done);
++
++ __ bind(¬_found);
++ // Call runtime to perform the lookup.
++ __ Push(cache, key);
++ __ CallRuntime(Runtime::kGetFromCache, 2);
++
++ __ bind(&done);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitIsRegExpEquivalent(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT_EQ(2, args->length());
++
++ Register right = r3;
++ Register left = r4;
++ Register tmp = r5;
++ Register tmp2 = r6;
++
++ VisitForStackValue(args->at(0));
++ VisitForAccumulatorValue(args->at(1));
++ __ pop(left);
++
++ Label done, fail, ok;
++ __ cmp(left, right);
++ __ beq(&ok);
++ // Fail if either is a non-HeapObject.
++ __ and_(tmp, left, right);
++ __ JumpIfSmi(tmp, &fail);
++ __ LoadP(tmp, FieldMemOperand(left, HeapObject::kMapOffset));
++ __ lbz(tmp2, FieldMemOperand(tmp, Map::kInstanceTypeOffset));
++ __ cmpi(tmp2, Operand(JS_REGEXP_TYPE));
++ __ bne(&fail);
++ __ LoadP(tmp2, FieldMemOperand(right, HeapObject::kMapOffset));
++ __ cmp(tmp, tmp2);
++ __ bne(&fail);
++ __ LoadP(tmp, FieldMemOperand(left, JSRegExp::kDataOffset));
++ __ LoadP(tmp2, FieldMemOperand(right, JSRegExp::kDataOffset));
++ __ cmp(tmp, tmp2);
++ __ beq(&ok);
++ __ bind(&fail);
++ __ LoadRoot(r3, Heap::kFalseValueRootIndex);
++ __ b(&done);
++ __ bind(&ok);
++ __ LoadRoot(r3, Heap::kTrueValueRootIndex);
++ __ bind(&done);
++
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitHasCachedArrayIndex(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ VisitForAccumulatorValue(args->at(0));
++
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ __ lwz(r3, FieldMemOperand(r3, String::kHashFieldOffset));
++ // PPC - assume ip is free
++ __ mov(ip, Operand(String::kContainsCachedArrayIndexMask));
++ __ and_(r0, r3, ip);
++ __ cmpi(r0, Operand::Zero());
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Split(eq, if_true, if_false, fall_through);
++
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitGetCachedArrayIndex(CallRuntime* expr) {
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 1);
++ VisitForAccumulatorValue(args->at(0));
++
++ __ AssertString(r3);
++
++ __ lwz(r3, FieldMemOperand(r3, String::kHashFieldOffset));
++ __ IndexFromHash(r3, r3);
++
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::EmitFastAsciiArrayJoin(CallRuntime* expr) {
++ Label bailout, done, one_char_separator, long_separator,
++ non_trivial_array, not_size_one_array, loop,
++ empty_separator_loop, one_char_separator_loop,
++ one_char_separator_loop_entry, long_separator_loop;
++ ZoneList<Expression*>* args = expr->arguments();
++ ASSERT(args->length() == 2);
++ VisitForStackValue(args->at(1));
++ VisitForAccumulatorValue(args->at(0));
++
++ // All aliases of the same register have disjoint lifetimes.
++ Register array = r3;
++ Register elements = no_reg; // Will be r3.
++ Register result = no_reg; // Will be r3.
++ Register separator = r4;
++ Register array_length = r5;
++ Register result_pos = no_reg; // Will be r5
++ Register string_length = r6;
++ Register string = r7;
++ Register element = r8;
++ Register elements_end = r9;
++ Register scratch1 = r10;
++ Register scratch2 = r22;
++
++ // Separator operand is on the stack.
++ __ pop(separator);
++
++ // Check that the array is a JSArray.
++ __ JumpIfSmi(array, &bailout);
++ __ CompareObjectType(array, scratch1, scratch2, JS_ARRAY_TYPE);
++ __ bne(&bailout);
++
++ // Check that the array has fast elements.
++ __ CheckFastElements(scratch1, scratch2, &bailout);
++
++ // If the array has length zero, return the empty string.
++ __ LoadP(array_length, FieldMemOperand(array, JSArray::kLengthOffset));
++ __ SmiUntag(array_length);
++ __ cmpi(array_length, Operand::Zero());
++ __ bne(&non_trivial_array);
++ __ LoadRoot(r3, Heap::kEmptyStringRootIndex);
++ __ b(&done);
++
++ __ bind(&non_trivial_array);
++
++ // Get the FixedArray containing array's elements.
++ elements = array;
++ __ LoadP(elements, FieldMemOperand(array, JSArray::kElementsOffset));
++ array = no_reg; // End of array's live range.
++
++ // Check that all array elements are sequential ASCII strings, and
++ // accumulate the sum of their lengths, as a smi-encoded value.
++ __ li(string_length, Operand::Zero());
++ __ addi(element,
++ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ ShiftLeftImm(elements_end, array_length, Operand(kPointerSizeLog2));
++ __ add(elements_end, element, elements_end);
++ // Loop condition: while (element < elements_end).
++ // Live values in registers:
++ // elements: Fixed array of strings.
++ // array_length: Length of the fixed array of strings (not smi)
++ // separator: Separator string
++ // string_length: Accumulated sum of string lengths (smi).
++ // element: Current array element.
++ // elements_end: Array end.
++ if (generate_debug_code_) {
++ __ cmpi(array_length, Operand::Zero());
++ __ Assert(gt, "No empty arrays here in EmitFastAsciiArrayJoin");
++ }
++ __ bind(&loop);
++ __ LoadP(string, MemOperand(element));
++ __ addi(element, element, Operand(kPointerSize));
++ __ JumpIfSmi(string, &bailout);
++ __ LoadP(scratch1, FieldMemOperand(string, HeapObject::kMapOffset));
++ __ lbz(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
++ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
++ __ LoadP(scratch1, FieldMemOperand(string, SeqAsciiString::kLengthOffset));
++
++ __ AddAndCheckForOverflow(string_length, string_length, scratch1,
++ scratch2, r0);
++ __ BranchOnOverflow(&bailout);
++
++ __ cmp(element, elements_end);
++ __ blt(&loop);
++
++ // If array_length is 1, return elements[0], a string.
++ __ cmpi(array_length, Operand(1));
++ __ bne(¬_size_one_array);
++ __ LoadP(r3, FieldMemOperand(elements, FixedArray::kHeaderSize));
++ __ b(&done);
++
++ __ bind(¬_size_one_array);
++
++ // Live values in registers:
++ // separator: Separator string
++ // array_length: Length of the array.
++ // string_length: Sum of string lengths (smi).
++ // elements: FixedArray of strings.
++
++ // Check that the separator is a flat ASCII string.
++ __ JumpIfSmi(separator, &bailout);
++ __ LoadP(scratch1, FieldMemOperand(separator, HeapObject::kMapOffset));
++ __ lbz(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
++ __ JumpIfInstanceTypeIsNotSequentialAscii(scratch1, scratch2, &bailout);
++
++ // Add (separator length times array_length) - separator length to the
++ // string_length to get the length of the result string.
++ __ LoadP(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset));
++ __ sub(string_length, string_length, scratch1);
++#if V8_TARGET_ARCH_PPC64
++ __ SmiUntag(scratch1, scratch1);
++ __ Mul(scratch2, array_length, scratch1);
++ // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
++ // zero.
++ __ ShiftRightImm(ip, scratch2, Operand(31), SetRC);
++ __ bne(&bailout, cr0);
++ __ SmiTag(scratch2, scratch2);
++#else
++ // array_length is not smi but the other values are, so the result is a smi
++ __ mullw(scratch2, array_length, scratch1);
++ __ mulhw(ip, array_length, scratch1);
++ // Check for smi overflow. No overflow if higher 33 bits of 64-bit result are
++ // zero.
++ __ cmpi(ip, Operand::Zero());
++ __ bne(&bailout);
++ __ TestSignBit32(scratch2, r0);
++ __ bne(&bailout, cr0);
++#endif
++
++ __ AddAndCheckForOverflow(string_length, string_length, scratch2,
++ scratch1, r0);
++ __ BranchOnOverflow(&bailout);
++ __ SmiUntag(string_length);
++
++ // Get first element in the array to free up the elements register to be used
++ // for the result.
++ __ addi(element,
++ elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ result = elements; // End of live range for elements.
++ elements = no_reg;
++ // Live values in registers:
++ // element: First array element
++ // separator: Separator string
++ // string_length: Length of result string (not smi)
++ // array_length: Length of the array.
++ __ AllocateAsciiString(result,
++ string_length,
++ scratch1,
++ scratch2,
++ elements_end,
++ &bailout);
++ // Prepare for looping. Set up elements_end to end of the array. Set
++ // result_pos to the position of the result where to write the first
++ // character.
++ __ ShiftLeftImm(elements_end, array_length, Operand(kPointerSizeLog2));
++ __ add(elements_end, element, elements_end);
++ result_pos = array_length; // End of live range for array_length.
++ array_length = no_reg;
++ __ addi(result_pos,
++ result,
++ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++
++ // Check the length of the separator.
++ __ LoadP(scratch1, FieldMemOperand(separator, SeqAsciiString::kLengthOffset));
++ __ CmpSmiLiteral(scratch1, Smi::FromInt(1), r0);
++ __ beq(&one_char_separator);
++ __ bgt(&long_separator);
++
++ // Empty separator case
++ __ bind(&empty_separator_loop);
++ // Live values in registers:
++ // result_pos: the position to which we are currently copying characters.
++ // element: Current array element.
++ // elements_end: Array end.
++
++ // Copy next array element to the result.
++ __ LoadP(string, MemOperand(element));
++ __ addi(element, element, Operand(kPointerSize));
++ __ LoadP(string_length, FieldMemOperand(string, String::kLengthOffset));
++ __ SmiUntag(string_length);
++ __ addi(string, string,
++ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ __ CopyBytes(string, result_pos, string_length, scratch1);
++ __ cmp(element, elements_end);
++ __ blt(&empty_separator_loop); // End while (element < elements_end).
++ ASSERT(result.is(r3));
++ __ b(&done);
++
++ // One-character separator case
++ __ bind(&one_char_separator);
++ // Replace separator with its ASCII character value.
++ __ lbz(separator, FieldMemOperand(separator, SeqAsciiString::kHeaderSize));
++ // Jump into the loop after the code that copies the separator, so the first
++ // element is not preceded by a separator
++ __ b(&one_char_separator_loop_entry);
++
++ __ bind(&one_char_separator_loop);
++ // Live values in registers:
++ // result_pos: the position to which we are currently copying characters.
++ // element: Current array element.
++ // elements_end: Array end.
++ // separator: Single separator ASCII char (in lower byte).
++
++ // Copy the separator character to the result.
++ __ stb(separator, MemOperand(result_pos));
++ __ addi(result_pos, result_pos, Operand(1));
++
++ // Copy next array element to the result.
++ __ bind(&one_char_separator_loop_entry);
++ __ LoadP(string, MemOperand(element));
++ __ addi(element, element, Operand(kPointerSize));
++ __ LoadP(string_length, FieldMemOperand(string, String::kLengthOffset));
++ __ SmiUntag(string_length);
++ __ addi(string, string,
++ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ __ CopyBytes(string, result_pos, string_length, scratch1);
++ __ cmpl(element, elements_end);
++ __ blt(&one_char_separator_loop); // End while (element < elements_end).
++ ASSERT(result.is(r3));
++ __ b(&done);
++
++ // Long separator case (separator is more than one character). Entry is at the
++ // label long_separator below.
++ __ bind(&long_separator_loop);
++ // Live values in registers:
++ // result_pos: the position to which we are currently copying characters.
++ // element: Current array element.
++ // elements_end: Array end.
++ // separator: Separator string.
++
++ // Copy the separator to the result.
++ __ LoadP(string_length, FieldMemOperand(separator, String::kLengthOffset));
++ __ SmiUntag(string_length);
++ __ addi(string,
++ separator,
++ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ __ CopyBytes(string, result_pos, string_length, scratch1);
++
++ __ bind(&long_separator);
++ __ LoadP(string, MemOperand(element));
++ __ addi(element, element, Operand(kPointerSize));
++ __ LoadP(string_length, FieldMemOperand(string, String::kLengthOffset));
++ __ SmiUntag(string_length);
++ __ addi(string, string,
++ Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
++ __ CopyBytes(string, result_pos, string_length, scratch1);
++ __ cmpl(element, elements_end);
++ __ blt(&long_separator_loop); // End while (element < elements_end).
++ ASSERT(result.is(r3));
++ __ b(&done);
++
++ __ bind(&bailout);
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ __ bind(&done);
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
++ Handle<String> name = expr->name();
++ if (name->length() > 0 && name->Get(0) == '_') {
++ Comment cmnt(masm_, "[ InlineRuntimeCall");
++ EmitInlineRuntimeCall(expr);
++ return;
++ }
++
++ Comment cmnt(masm_, "[ CallRuntime");
++ ZoneList<Expression*>* args = expr->arguments();
++
++ if (expr->is_jsruntime()) {
++ // Prepare for calling JS runtime function.
++ __ LoadP(r3, GlobalObjectOperand());
++ __ LoadP(r3, FieldMemOperand(r3, GlobalObject::kBuiltinsOffset));
++ __ push(r3);
++ }
++
++ // Push the arguments ("left-to-right").
++ int arg_count = args->length();
++ for (int i = 0; i < arg_count; i++) {
++ VisitForStackValue(args->at(i));
++ }
++
++ if (expr->is_jsruntime()) {
++ // Call the JS runtime function.
++ __ mov(r5, Operand(expr->name()));
++ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
++ Handle<Code> ic =
++ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
++ CallIC(ic, mode, expr->CallRuntimeFeedbackId());
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ } else {
++ // Call the C runtime function.
++ __ CallRuntime(expr->function(), arg_count);
++ }
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
++ switch (expr->op()) {
++ case Token::DELETE: {
++ Comment cmnt(masm_, "[ UnaryOperation (DELETE)");
++ Property* property = expr->expression()->AsProperty();
++ VariableProxy* proxy = expr->expression()->AsVariableProxy();
++
++ if (property != NULL) {
++ VisitForStackValue(property->obj());
++ VisitForStackValue(property->key());
++ StrictModeFlag strict_mode_flag = (language_mode() == CLASSIC_MODE)
++ ? kNonStrictMode : kStrictMode;
++ __ LoadSmiLiteral(r4, Smi::FromInt(strict_mode_flag));
++ __ push(r4);
++ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
++ context()->Plug(r3);
++ } else if (proxy != NULL) {
++ Variable* var = proxy->var();
++ // Delete of an unqualified identifier is disallowed in strict mode
++ // but "delete this" is allowed.
++ ASSERT(language_mode() == CLASSIC_MODE || var->is_this());
++ if (var->IsUnallocated()) {
++ __ LoadP(r5, GlobalObjectOperand());
++ __ mov(r4, Operand(var->name()));
++ __ LoadSmiLiteral(r3, Smi::FromInt(kNonStrictMode));
++ __ Push(r5, r4, r3);
++ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION);
++ context()->Plug(r3);
++ } else if (var->IsStackAllocated() || var->IsContextSlot()) {
++ // Result of deleting non-global, non-dynamic variables is false.
++ // The subexpression does not have side effects.
++ context()->Plug(var->is_this());
++ } else {
++ // Non-global variable. Call the runtime to try to delete from the
++ // context where the variable was introduced.
++ __ push(context_register());
++ __ mov(r5, Operand(var->name()));
++ __ push(r5);
++ __ CallRuntime(Runtime::kDeleteContextSlot, 2);
++ context()->Plug(r3);
++ }
++ } else {
++ // Result of deleting non-property, non-variable reference is true.
++ // The subexpression may have side effects.
++ VisitForEffect(expr->expression());
++ context()->Plug(true);
++ }
++ break;
++ }
++
++ case Token::VOID: {
++ Comment cmnt(masm_, "[ UnaryOperation (VOID)");
++ VisitForEffect(expr->expression());
++ context()->Plug(Heap::kUndefinedValueRootIndex);
++ break;
++ }
++
++ case Token::NOT: {
++ Comment cmnt(masm_, "[ UnaryOperation (NOT)");
++ if (context()->IsEffect()) {
++ // Unary NOT has no side effects so it's only necessary to visit the
++ // subexpression. Match the optimizing compiler by not branching.
++ VisitForEffect(expr->expression());
++ } else if (context()->IsTest()) {
++ const TestContext* test = TestContext::cast(context());
++ // The labels are swapped for the recursive call.
++ VisitForControl(expr->expression(),
++ test->false_label(),
++ test->true_label(),
++ test->fall_through());
++ context()->Plug(test->true_label(), test->false_label());
++ } else {
++ // We handle value contexts explicitly rather than simply visiting
++ // for control and plugging the control flow into the context,
++ // because we need to prepare a pair of extra administrative AST ids
++ // for the optimizing compiler.
++ ASSERT(context()->IsAccumulatorValue() || context()->IsStackValue());
++ Label materialize_true, materialize_false, done;
++ VisitForControl(expr->expression(),
++ &materialize_false,
++ &materialize_true,
++ &materialize_true);
++ __ bind(&materialize_true);
++ PrepareForBailoutForId(expr->MaterializeTrueId(), NO_REGISTERS);
++ __ LoadRoot(r3, Heap::kTrueValueRootIndex);
++ if (context()->IsStackValue()) __ push(r3);
++ __ b(&done);
++ __ bind(&materialize_false);
++ PrepareForBailoutForId(expr->MaterializeFalseId(), NO_REGISTERS);
++ __ LoadRoot(r3, Heap::kFalseValueRootIndex);
++ if (context()->IsStackValue()) __ push(r3);
++ __ bind(&done);
++ }
++ break;
++ }
++
++ case Token::TYPEOF: {
++ Comment cmnt(masm_, "[ UnaryOperation (TYPEOF)");
++ { StackValueContext context(this);
++ VisitForTypeofValue(expr->expression());
++ }
++ __ CallRuntime(Runtime::kTypeof, 1);
++ context()->Plug(r3);
++ break;
++ }
++
++ case Token::ADD: {
++ Comment cmt(masm_, "[ UnaryOperation (ADD)");
++ VisitForAccumulatorValue(expr->expression());
++ Label no_conversion;
++ __ JumpIfSmi(result_register(), &no_conversion);
++ ToNumberStub convert_stub;
++ __ CallStub(&convert_stub);
++ __ bind(&no_conversion);
++ context()->Plug(result_register());
++ break;
++ }
++
++ case Token::SUB:
++ EmitUnaryOperation(expr, "[ UnaryOperation (SUB)");
++ break;
++
++ case Token::BIT_NOT:
++ EmitUnaryOperation(expr, "[ UnaryOperation (BIT_NOT)");
++ break;
++
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void FullCodeGenerator::EmitUnaryOperation(UnaryOperation* expr,
++ const char* comment) {
++ // TODO(svenpanne): Allowing format strings in Comment would be nice here...
++ Comment cmt(masm_, comment);
++ bool can_overwrite = expr->expression()->ResultOverwriteAllowed();
++ UnaryOverwriteMode overwrite =
++ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
++ UnaryOpStub stub(expr->op(), overwrite);
++ // UnaryOpStub expects the argument to be in the
++ // accumulator register r3.
++ VisitForAccumulatorValue(expr->expression());
++ SetSourcePosition(expr->position());
++ CallIC(stub.GetCode(), RelocInfo::CODE_TARGET,
++ expr->UnaryOperationFeedbackId());
++ context()->Plug(r3);
++}
++
++
++void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
++ Comment cmnt(masm_, "[ CountOperation");
++ SetSourcePosition(expr->position());
++
++ // Invalid left-hand sides are rewritten to have a 'throw ReferenceError'
++ // as the left-hand side.
++ if (!expr->expression()->IsValidLeftHandSide()) {
++ VisitForEffect(expr->expression());
++ return;
++ }
++
++ // Expression can only be a property, a global or a (parameter or local)
++ // slot.
++ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
++ LhsKind assign_type = VARIABLE;
++ Property* prop = expr->expression()->AsProperty();
++ // In case of a property we use the uninitialized expression context
++ // of the key to detect a named property.
++ if (prop != NULL) {
++ assign_type =
++ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
++ }
++
++ // Evaluate expression and get value.
++ if (assign_type == VARIABLE) {
++ ASSERT(expr->expression()->AsVariableProxy()->var() != NULL);
++ AccumulatorValueContext context(this);
++ EmitVariableLoad(expr->expression()->AsVariableProxy());
++ } else {
++ // Reserve space for result of postfix operation.
++ if (expr->is_postfix() && !context()->IsEffect()) {
++ __ LoadSmiLiteral(ip, Smi::FromInt(0));
++ __ push(ip);
++ }
++ if (assign_type == NAMED_PROPERTY) {
++ // Put the object both on the stack and in the accumulator.
++ VisitForAccumulatorValue(prop->obj());
++ __ push(r3);
++ EmitNamedPropertyLoad(prop);
++ } else {
++ VisitForStackValue(prop->obj());
++ VisitForAccumulatorValue(prop->key());
++ __ LoadP(r4, MemOperand(sp, 0));
++ __ push(r3);
++ EmitKeyedPropertyLoad(prop);
++ }
++ }
++
++ // We need a second deoptimization point after loading the value
++ // in case evaluating the property load my have a side effect.
++ if (assign_type == VARIABLE) {
++ PrepareForBailout(expr->expression(), TOS_REG);
++ } else {
++ PrepareForBailoutForId(prop->LoadId(), TOS_REG);
++ }
++
++ // Call ToNumber only if operand is not a smi.
++ Label no_conversion;
++ __ JumpIfSmi(r3, &no_conversion);
++ ToNumberStub convert_stub;
++ __ CallStub(&convert_stub);
++ __ bind(&no_conversion);
++
++ // Save result for postfix expressions.
++ if (expr->is_postfix()) {
++ if (!context()->IsEffect()) {
++ // Save the result on the stack. If we have a named or keyed property
++ // we store the result under the receiver that is currently on top
++ // of the stack.
++ switch (assign_type) {
++ case VARIABLE:
++ __ push(r3);
++ break;
++ case NAMED_PROPERTY:
++ __ StoreP(r3, MemOperand(sp, kPointerSize));
++ break;
++ case KEYED_PROPERTY:
++ __ StoreP(r3, MemOperand(sp, 2 * kPointerSize));
++ break;
++ }
++ }
++ }
++
++ // Inline smi case if we are in a loop.
++ Label stub_call, done;
++ JumpPatchSite patch_site(masm_);
++
++ int count_value = expr->op() == Token::INC ? 1 : -1;
++ __ LoadSmiLiteral(r4, Smi::FromInt(count_value));
++
++ if (ShouldInlineSmiCase(expr->op())) {
++ __ AddAndCheckForOverflow(r3, r3, r4, r5, r0);
++ __ BranchOnOverflow(&stub_call);
++
++ // We could eliminate this smi check if we split the code at
++ // the first smi check before calling ToNumber.
++ patch_site.EmitJumpIfSmi(r3, &done);
++
++ __ bind(&stub_call);
++ // Call stub. Undo operation first.
++ __ SubSmiLiteral(r3, r3, Smi::FromInt(count_value), r0);
++ }
++
++ // Record position before stub call.
++ SetSourcePosition(expr->position());
++
++ BinaryOpStub stub(Token::ADD, NO_OVERWRITE);
++ CallIC(stub.GetCode(), RelocInfo::CODE_TARGET, expr->CountBinOpFeedbackId());
++ patch_site.EmitPatchInfo();
++ __ bind(&done);
++
++ // Store the value returned in r3.
++ switch (assign_type) {
++ case VARIABLE:
++ if (expr->is_postfix()) {
++ { EffectContext context(this);
++ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
++ Token::ASSIGN);
++ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
++ context.Plug(r3);
++ }
++ // For all contexts except EffectConstant We have the result on
++ // top of the stack.
++ if (!context()->IsEffect()) {
++ context()->PlugTOS();
++ }
++ } else {
++ EmitVariableAssignment(expr->expression()->AsVariableProxy()->var(),
++ Token::ASSIGN);
++ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
++ context()->Plug(r3);
++ }
++ break;
++ case NAMED_PROPERTY: {
++ __ mov(r5, Operand(prop->key()->AsLiteral()->handle()));
++ __ pop(r4);
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->StoreIC_Initialize()
++ : isolate()->builtins()->StoreIC_Initialize_Strict();
++ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
++ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
++ if (expr->is_postfix()) {
++ if (!context()->IsEffect()) {
++ context()->PlugTOS();
++ }
++ } else {
++ context()->Plug(r3);
++ }
++ break;
++ }
++ case KEYED_PROPERTY: {
++ __ pop(r4); // Key.
++ __ pop(r5); // Receiver.
++ Handle<Code> ic = is_classic_mode()
++ ? isolate()->builtins()->KeyedStoreIC_Initialize()
++ : isolate()->builtins()->KeyedStoreIC_Initialize_Strict();
++ CallIC(ic, RelocInfo::CODE_TARGET, expr->CountStoreFeedbackId());
++ PrepareForBailoutForId(expr->AssignmentId(), TOS_REG);
++ if (expr->is_postfix()) {
++ if (!context()->IsEffect()) {
++ context()->PlugTOS();
++ }
++ } else {
++ context()->Plug(r3);
++ }
++ break;
++ }
++ }
++}
++
++
++void FullCodeGenerator::VisitForTypeofValue(Expression* expr) {
++ ASSERT(!context()->IsEffect());
++ ASSERT(!context()->IsTest());
++ VariableProxy* proxy = expr->AsVariableProxy();
++ if (proxy != NULL && proxy->var()->IsUnallocated()) {
++ Comment cmnt(masm_, "Global variable");
++ __ LoadP(r3, GlobalObjectOperand());
++ __ mov(r5, Operand(proxy->name()));
++ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
++ // Use a regular load, not a contextual load, to avoid a reference
++ // error.
++ CallIC(ic);
++ PrepareForBailout(expr, TOS_REG);
++ context()->Plug(r3);
++ } else if (proxy != NULL && proxy->var()->IsLookupSlot()) {
++ Label done, slow;
++
++ // Generate code for loading from variables potentially shadowed
++ // by eval-introduced variables.
++ EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done);
++
++ __ bind(&slow);
++ __ mov(r3, Operand(proxy->name()));
++ __ Push(cp, r3);
++ __ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
++ PrepareForBailout(expr, TOS_REG);
++ __ bind(&done);
++
++ context()->Plug(r3);
++ } else {
++ // This expression cannot throw a reference error at the top level.
++ VisitInDuplicateContext(expr);
++ }
++}
++
++
++void FullCodeGenerator::EmitLiteralCompareTypeof(Expression* expr,
++ Expression* sub_expr,
++ Handle<String> check) {
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ { AccumulatorValueContext context(this);
++ VisitForTypeofValue(sub_expr);
++ }
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++
++ if (check->Equals(isolate()->heap()->number_symbol())) {
++ __ JumpIfSmi(r3, if_true);
++ __ LoadP(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
++ __ cmp(r3, ip);
++ Split(eq, if_true, if_false, fall_through);
++ } else if (check->Equals(isolate()->heap()->string_symbol())) {
++ __ JumpIfSmi(r3, if_false);
++ // Check for undetectable objects => false.
++ __ CompareObjectType(r3, r3, r4, FIRST_NONSTRING_TYPE);
++ __ bge(if_false);
++ __ lbz(r4, FieldMemOperand(r3, Map::kBitFieldOffset));
++ STATIC_ASSERT((1 << Map::kIsUndetectable) < 0x8000);
++ __ andi(r0, r4, Operand(1 << Map::kIsUndetectable));
++ Split(eq, if_true, if_false, fall_through, cr0);
++ } else if (check->Equals(isolate()->heap()->boolean_symbol())) {
++ __ CompareRoot(r3, Heap::kTrueValueRootIndex);
++ __ beq(if_true);
++ __ CompareRoot(r3, Heap::kFalseValueRootIndex);
++ Split(eq, if_true, if_false, fall_through);
++ } else if (FLAG_harmony_typeof &&
++ check->Equals(isolate()->heap()->null_symbol())) {
++ __ CompareRoot(r3, Heap::kNullValueRootIndex);
++ Split(eq, if_true, if_false, fall_through);
++ } else if (check->Equals(isolate()->heap()->undefined_symbol())) {
++ __ CompareRoot(r3, Heap::kUndefinedValueRootIndex);
++ __ beq(if_true);
++ __ JumpIfSmi(r3, if_false);
++ // Check for undetectable objects => true.
++ __ LoadP(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ lbz(r4, FieldMemOperand(r3, Map::kBitFieldOffset));
++ __ andi(r0, r4, Operand(1 << Map::kIsUndetectable));
++ Split(ne, if_true, if_false, fall_through, cr0);
++
++ } else if (check->Equals(isolate()->heap()->function_symbol())) {
++ __ JumpIfSmi(r3, if_false);
++ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
++ __ CompareObjectType(r3, r3, r4, JS_FUNCTION_TYPE);
++ __ beq(if_true);
++ __ cmpi(r4, Operand(JS_FUNCTION_PROXY_TYPE));
++ Split(eq, if_true, if_false, fall_through);
++ } else if (check->Equals(isolate()->heap()->object_symbol())) {
++ __ JumpIfSmi(r3, if_false);
++ if (!FLAG_harmony_typeof) {
++ __ CompareRoot(r3, Heap::kNullValueRootIndex);
++ __ beq(if_true);
++ }
++ // Check for JS objects => true.
++ __ CompareObjectType(r3, r3, r4, FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
++ __ blt(if_false);
++ __ CompareInstanceType(r3, r4, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
++ __ bgt(if_false);
++ // Check for undetectable objects => false.
++ __ lbz(r4, FieldMemOperand(r3, Map::kBitFieldOffset));
++ __ andi(r0, r4, Operand(1 << Map::kIsUndetectable));
++ Split(eq, if_true, if_false, fall_through, cr0);
++ } else {
++ if (if_false != fall_through) __ b(if_false);
++ }
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
++ Comment cmnt(masm_, "[ CompareOperation");
++ SetSourcePosition(expr->position());
++
++ // First we try a fast inlined version of the compare when one of
++ // the operands is a literal.
++ if (TryLiteralCompare(expr)) return;
++
++ // Always perform the comparison for its control flow. Pack the result
++ // into the expression's context after the comparison is performed.
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ Token::Value op = expr->op();
++ VisitForStackValue(expr->left());
++ switch (op) {
++ case Token::IN:
++ VisitForStackValue(expr->right());
++ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION);
++ PrepareForBailoutBeforeSplit(expr, false, NULL, NULL);
++ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
++ __ cmp(r3, ip);
++ Split(eq, if_true, if_false, fall_through);
++ break;
++
++ case Token::INSTANCEOF: {
++ VisitForStackValue(expr->right());
++ InstanceofStub stub(InstanceofStub::kNoFlags);
++ __ CallStub(&stub);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ // The stub returns 0 for true.
++ __ cmpi(r3, Operand::Zero());
++ Split(eq, if_true, if_false, fall_through);
++ break;
++ }
++
++ default: {
++ VisitForAccumulatorValue(expr->right());
++ Condition cond = eq;
++ switch (op) {
++ case Token::EQ_STRICT:
++ case Token::EQ:
++ cond = eq;
++ break;
++ case Token::LT:
++ cond = lt;
++ break;
++ case Token::GT:
++ cond = gt;
++ break;
++ case Token::LTE:
++ cond = le;
++ break;
++ case Token::GTE:
++ cond = ge;
++ break;
++ case Token::IN:
++ case Token::INSTANCEOF:
++ default:
++ UNREACHABLE();
++ }
++ __ pop(r4);
++
++ bool inline_smi_code = ShouldInlineSmiCase(op);
++ JumpPatchSite patch_site(masm_);
++ if (inline_smi_code) {
++ Label slow_case;
++ __ orx(r5, r3, r4);
++ patch_site.EmitJumpIfNotSmi(r5, &slow_case);
++ __ cmp(r4, r3);
++ Split(cond, if_true, if_false, NULL);
++ __ bind(&slow_case);
++ }
++
++ // Record position and call the compare IC.
++ SetSourcePosition(expr->position());
++ Handle<Code> ic = CompareIC::GetUninitialized(op);
++ CallIC(ic, RelocInfo::CODE_TARGET, expr->CompareOperationFeedbackId());
++ patch_site.EmitPatchInfo();
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ __ cmpi(r3, Operand::Zero());
++ Split(cond, if_true, if_false, fall_through);
++ }
++ }
++
++ // Convert the result of the comparison into one expected for this
++ // expression's context.
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::EmitLiteralCompareNil(CompareOperation* expr,
++ Expression* sub_expr,
++ NilValue nil) {
++ Label materialize_true, materialize_false;
++ Label* if_true = NULL;
++ Label* if_false = NULL;
++ Label* fall_through = NULL;
++ context()->PrepareTest(&materialize_true, &materialize_false,
++ &if_true, &if_false, &fall_through);
++
++ VisitForAccumulatorValue(sub_expr);
++ PrepareForBailoutBeforeSplit(expr, true, if_true, if_false);
++ Heap::RootListIndex nil_value = nil == kNullValue ?
++ Heap::kNullValueRootIndex :
++ Heap::kUndefinedValueRootIndex;
++ __ LoadRoot(r4, nil_value);
++ __ cmp(r3, r4);
++ if (expr->op() == Token::EQ_STRICT) {
++ Split(eq, if_true, if_false, fall_through);
++ } else {
++ Heap::RootListIndex other_nil_value = nil == kNullValue ?
++ Heap::kUndefinedValueRootIndex :
++ Heap::kNullValueRootIndex;
++ __ beq(if_true);
++ __ LoadRoot(r4, other_nil_value);
++ __ cmp(r3, r4);
++ __ beq(if_true);
++ __ JumpIfSmi(r3, if_false);
++ // It can be an undetectable object.
++ __ LoadP(r4, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ lbz(r4, FieldMemOperand(r4, Map::kBitFieldOffset));
++ __ andi(r4, r4, Operand(1 << Map::kIsUndetectable));
++ __ cmpi(r4, Operand(1 << Map::kIsUndetectable));
++ Split(eq, if_true, if_false, fall_through);
++ }
++ context()->Plug(if_true, if_false);
++}
++
++
++void FullCodeGenerator::VisitThisFunction(ThisFunction* expr) {
++ __ LoadP(r3, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ context()->Plug(r3);
++}
++
++
++Register FullCodeGenerator::result_register() {
++ return r3;
++}
++
++
++Register FullCodeGenerator::context_register() {
++ return cp;
++}
++
++
++void FullCodeGenerator::StoreToFrameField(int frame_offset, Register value) {
++ ASSERT_EQ(static_cast<int>(POINTER_SIZE_ALIGN(frame_offset)), frame_offset);
++ __ StoreP(value, MemOperand(fp, frame_offset), r0);
++}
++
++
++void FullCodeGenerator::LoadContextField(Register dst, int context_index) {
++ __ LoadP(dst, ContextOperand(cp, context_index), r0);
++}
++
++
++void FullCodeGenerator::PushFunctionArgumentForContextAllocation() {
++ Scope* declaration_scope = scope()->DeclarationScope();
++ if (declaration_scope->is_global_scope() ||
++ declaration_scope->is_module_scope()) {
++ // Contexts nested in the native context have a canonical empty function
++ // as their closure, not the anonymous closure containing the global
++ // code. Pass a smi sentinel and let the runtime look up the empty
++ // function.
++ __ LoadSmiLiteral(ip, Smi::FromInt(0));
++ } else if (declaration_scope->is_eval_scope()) {
++ // Contexts created by a call to eval have the same closure as the
++ // context calling eval, not the anonymous closure containing the eval
++ // code. Fetch it from the context.
++ __ LoadP(ip, ContextOperand(cp, Context::CLOSURE_INDEX));
++ } else {
++ ASSERT(declaration_scope->is_function_scope());
++ __ LoadP(ip, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++ }
++ __ push(ip);
++}
++
++
++// ----------------------------------------------------------------------------
++// Non-local control flow support.
++
++void FullCodeGenerator::EnterFinallyBlock() {
++ ASSERT(!result_register().is(r4));
++ // Store result register while executing finally block.
++ __ push(result_register());
++ // Cook return address in link register to stack (smi encoded Code* delta)
++ __ mflr(r4);
++ __ mov(ip, Operand(masm_->CodeObject()));
++ __ sub(r4, r4, ip);
++ __ SmiTag(r4);
++
++ // Store result register while executing finally block.
++ __ push(r4);
++
++ // Store pending message while executing finally block.
++ ExternalReference pending_message_obj =
++ ExternalReference::address_of_pending_message_obj(isolate());
++ __ mov(ip, Operand(pending_message_obj));
++ __ LoadP(r4, MemOperand(ip));
++ __ push(r4);
++
++ ExternalReference has_pending_message =
++ ExternalReference::address_of_has_pending_message(isolate());
++ __ mov(ip, Operand(has_pending_message));
++ __ lbz(r4, MemOperand(ip));
++ __ SmiTag(r4);
++ __ push(r4);
++
++ ExternalReference pending_message_script =
++ ExternalReference::address_of_pending_message_script(isolate());
++ __ mov(ip, Operand(pending_message_script));
++ __ LoadP(r4, MemOperand(ip));
++ __ push(r4);
++}
++
++
++void FullCodeGenerator::ExitFinallyBlock() {
++ ASSERT(!result_register().is(r4));
++ // Restore pending message from stack.
++ __ pop(r4);
++ ExternalReference pending_message_script =
++ ExternalReference::address_of_pending_message_script(isolate());
++ __ mov(ip, Operand(pending_message_script));
++ __ StoreP(r4, MemOperand(ip));
++
++ __ pop(r4);
++ __ SmiUntag(r4);
++ ExternalReference has_pending_message =
++ ExternalReference::address_of_has_pending_message(isolate());
++ __ mov(ip, Operand(has_pending_message));
++ __ stb(r4, MemOperand(ip));
++
++ __ pop(r4);
++ ExternalReference pending_message_obj =
++ ExternalReference::address_of_pending_message_obj(isolate());
++ __ mov(ip, Operand(pending_message_obj));
++ __ StoreP(r4, MemOperand(ip));
++
++ // Restore result register from stack.
++ __ pop(r4);
++
++ // Uncook return address and return.
++ __ pop(result_register());
++ __ SmiUntag(r4);
++ __ mov(ip, Operand(masm_->CodeObject()));
++ __ add(ip, ip, r4);
++ __ mtctr(ip);
++ __ bcr();
++}
++
++
++#undef __
++
++#define __ ACCESS_MASM(masm())
++
++FullCodeGenerator::NestedStatement* FullCodeGenerator::TryFinally::Exit(
++ int* stack_depth,
++ int* context_length) {
++ // The macros used here must preserve the result register.
++
++ // Because the handler block contains the context of the finally
++ // code, we can restore it directly from there for the finally code
++ // rather than iteratively unwinding contexts via their previous
++ // links.
++ __ Drop(*stack_depth); // Down to the handler block.
++ if (*context_length > 0) {
++ // Restore the context to its dedicated register and the stack.
++ __ LoadP(cp, MemOperand(sp, StackHandlerConstants::kContextOffset));
++ __ StoreP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ }
++ __ PopTryHandler();
++ __ b(finally_entry_, SetLK);
++
++ *stack_depth = 0;
++ *context_length = 0;
++ return previous_;
++}
++
++#undef __
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/ic-ppc.cc.ppc v8-3.14.5.10/src/ppc/ic-ppc.cc
+--- v8-3.14.5.10/src/ppc/ic-ppc.cc.ppc 2016-06-07 14:15:45.995392979 -0400
++++ v8-3.14.5.10/src/ppc/ic-ppc.cc 2016-06-07 14:15:45.995392979 -0400
+@@ -0,0 +1,1832 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "assembler-ppc.h"
++#include "code-stubs.h"
++#include "codegen.h"
++#include "disasm.h"
++#include "ic-inl.h"
++#include "runtime.h"
++#include "stub-cache.h"
++
++namespace v8 {
++namespace internal {
++
++
++// ----------------------------------------------------------------------------
++// Static IC stub generators.
++//
++
++#define __ ACCESS_MASM(masm)
++
++static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
++ Register type,
++ Label* global_object) {
++ // Register usage:
++ // type: holds the receiver instance type on entry.
++ __ cmpi(type, Operand(JS_GLOBAL_OBJECT_TYPE));
++ __ beq(global_object);
++ __ cmpi(type, Operand(JS_BUILTINS_OBJECT_TYPE));
++ __ beq(global_object);
++ __ cmpi(type, Operand(JS_GLOBAL_PROXY_TYPE));
++ __ beq(global_object);
++}
++
++
++// Generated code falls through if the receiver is a regular non-global
++// JS object with slow properties and no interceptors.
++static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
++ Register receiver,
++ Register elements,
++ Register t0,
++ Register t1,
++ Label* miss) {
++ // Register usage:
++ // receiver: holds the receiver on entry and is unchanged.
++ // elements: holds the property dictionary on fall through.
++ // Scratch registers:
++ // t0: used to holds the receiver map.
++ // t1: used to holds the receiver instance type, receiver bit mask and
++ // elements map.
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, miss);
++
++ // Check that the receiver is a valid JS object.
++ __ CompareObjectType(receiver, t0, t1, FIRST_SPEC_OBJECT_TYPE);
++ __ blt(miss);
++
++ // If this assert fails, we have to check upper bound too.
++ STATIC_ASSERT(LAST_TYPE == LAST_SPEC_OBJECT_TYPE);
++
++ GenerateGlobalInstanceTypeCheck(masm, t1, miss);
++
++ // Check that the global object does not require access checks.
++ __ lbz(t1, FieldMemOperand(t0, Map::kBitFieldOffset));
++ __ andi(r0, t1, Operand((1 << Map::kIsAccessCheckNeeded) |
++ (1 << Map::kHasNamedInterceptor)));
++ __ bne(miss, cr0);
++
++ __ LoadP(elements, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
++ __ LoadP(t1, FieldMemOperand(elements, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
++ __ cmp(t1, ip);
++ __ bne(miss);
++}
++
++
++// Helper function used from LoadIC/CallIC GenerateNormal.
++//
++// elements: Property dictionary. It is not clobbered if a jump to the miss
++// label is done.
++// name: Property name. It is not clobbered if a jump to the miss label is
++// done
++// result: Register for the result. It is only updated if a jump to the miss
++// label is not done. Can be the same as elements or name clobbering
++// one of these in the case of not jumping to the miss label.
++// The two scratch registers need to be different from elements, name and
++// result.
++// The generated code assumes that the receiver has slow properties,
++// is not a global object and does not have interceptors.
++static void GenerateDictionaryLoad(MacroAssembler* masm,
++ Label* miss,
++ Register elements,
++ Register name,
++ Register result,
++ Register scratch1,
++ Register scratch2) {
++ // Main use of the scratch registers.
++ // scratch1: Used as temporary and to hold the capacity of the property
++ // dictionary.
++ // scratch2: Used as temporary.
++ Label done;
++
++ // Probe the dictionary.
++ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
++ miss,
++ &done,
++ elements,
++ name,
++ scratch1,
++ scratch2);
++
++ // If probing finds an entry check that the value is a normal
++ // property.
++ __ bind(&done); // scratch2 == elements + 4 * index
++ const int kElementsStartOffset = StringDictionary::kHeaderSize +
++ StringDictionary::kElementsStartIndex * kPointerSize;
++ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
++ __ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
++ __ mr(r0, scratch2);
++ __ LoadSmiLiteral(scratch2, Smi::FromInt(PropertyDetails::TypeField::kMask));
++ __ and_(scratch2, scratch1, scratch2, SetRC);
++ __ bne(miss, cr0);
++ __ mr(scratch2, r0);
++
++ // Get the value at the masked, scaled index and return.
++ __ LoadP(result,
++ FieldMemOperand(scratch2, kElementsStartOffset + 1 * kPointerSize));
++}
++
++
++// Helper function used from StoreIC::GenerateNormal.
++//
++// elements: Property dictionary. It is not clobbered if a jump to the miss
++// label is done.
++// name: Property name. It is not clobbered if a jump to the miss label is
++// done
++// value: The value to store.
++// The two scratch registers need to be different from elements, name and
++// result.
++// The generated code assumes that the receiver has slow properties,
++// is not a global object and does not have interceptors.
++static void GenerateDictionaryStore(MacroAssembler* masm,
++ Label* miss,
++ Register elements,
++ Register name,
++ Register value,
++ Register scratch1,
++ Register scratch2) {
++ // Main use of the scratch registers.
++ // scratch1: Used as temporary and to hold the capacity of the property
++ // dictionary.
++ // scratch2: Used as temporary.
++ Label done;
++
++ // Probe the dictionary.
++ StringDictionaryLookupStub::GeneratePositiveLookup(masm,
++ miss,
++ &done,
++ elements,
++ name,
++ scratch1,
++ scratch2);
++
++ // If probing finds an entry in the dictionary check that the value
++ // is a normal property that is not read only.
++ __ bind(&done); // scratch2 == elements + 4 * index
++ const int kElementsStartOffset = StringDictionary::kHeaderSize +
++ StringDictionary::kElementsStartIndex * kPointerSize;
++ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
++ int kTypeAndReadOnlyMask = PropertyDetails::TypeField::kMask |
++ PropertyDetails::AttributesField::encode(READ_ONLY);
++ __ LoadP(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
++ __ mr(r0, scratch2);
++ __ LoadSmiLiteral(scratch2, Smi::FromInt(kTypeAndReadOnlyMask));
++ __ and_(scratch2, scratch1, scratch2, SetRC);
++ __ bne(miss, cr0);
++ __ mr(scratch2, r0);
++
++ // Store the value at the masked, scaled index and return.
++ const int kValueOffset = kElementsStartOffset + kPointerSize;
++ __ addi(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
++ __ StoreP(value, MemOperand(scratch2));
++
++ // Update the write barrier. Make sure not to clobber the value.
++ __ mr(scratch1, value);
++ __ RecordWrite(
++ elements, scratch2, scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs);
++}
++
++
++void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- r3 : receiver
++ // -- sp[0] : receiver
++ // -----------------------------------
++ Label miss;
++
++ StubCompiler::GenerateLoadArrayLength(masm, r3, r6, &miss);
++ __ bind(&miss);
++ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
++}
++
++
++void LoadIC::GenerateStringLength(MacroAssembler* masm, bool support_wrappers) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- r3 : receiver
++ // -- sp[0] : receiver
++ // -----------------------------------
++ Label miss;
++
++ StubCompiler::GenerateLoadStringLength(masm, r3, r4, r6, &miss,
++ support_wrappers);
++ // Cache miss: Jump to runtime.
++ __ bind(&miss);
++ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
++}
++
++
++void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- r3 : receiver
++ // -- sp[0] : receiver
++ // -----------------------------------
++ Label miss;
++
++ StubCompiler::GenerateLoadFunctionPrototype(masm, r3, r4, r6, &miss);
++ __ bind(&miss);
++ StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
++}
++
++
++// Checks the receiver for special cases (value type, slow case bits).
++// Falls through for regular JS object.
++static void GenerateKeyedLoadReceiverCheck(MacroAssembler* masm,
++ Register receiver,
++ Register map,
++ Register scratch,
++ int interceptor_bit,
++ Label* slow) {
++ // Check that the object isn't a smi.
++ __ JumpIfSmi(receiver, slow);
++ // Get the map of the receiver.
++ __ LoadP(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
++ // Check bit field.
++ __ lbz(scratch, FieldMemOperand(map, Map::kBitFieldOffset));
++ ASSERT(((1 << Map::kIsAccessCheckNeeded) | (1 << interceptor_bit)) <
0x8000);
++ __ andi(r0, scratch,
++ Operand((1 << Map::kIsAccessCheckNeeded) | (1 <<
interceptor_bit)));
++ __ bne(slow, cr0);
++ // Check that the object is some kind of JS object EXCEPT JS Value type.
++ // In the case that the object is a value-wrapper object,
++ // we enter the runtime system to make sure that indexing into string
++ // objects work as intended.
++ ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
++ __ lbz(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
++ __ cmpi(scratch, Operand(JS_OBJECT_TYPE));
++ __ blt(slow);
++}
++
++
++// Loads an indexed element from a fast case array.
++// If not_fast_array is NULL, doesn't perform the elements map check.
++static void GenerateFastArrayLoad(MacroAssembler* masm,
++ Register receiver,
++ Register key,
++ Register elements,
++ Register scratch1,
++ Register scratch2,
++ Register result,
++ Label* not_fast_array,
++ Label* out_of_range) {
++ // Register use:
++ //
++ // receiver - holds the receiver on entry.
++ // Unchanged unless 'result' is the same register.
++ //
++ // key - holds the smi key on entry.
++ // Unchanged unless 'result' is the same register.
++ //
++ // elements - holds the elements of the receiver on exit.
++ //
++ // result - holds the result on exit if the load succeeded.
++ // Allowed to be the the same as 'receiver' or 'key'.
++ // Unchanged on bailout so 'receiver' and 'key' can be
safely
++ // used by further computation.
++ //
++ // Scratch registers:
++ //
++ // scratch1 - used to hold elements map and elements length.
++ // Holds the elements map if not_fast_array branch is taken.
++ //
++ // scratch2 - used to hold the loaded value.
++
++ __ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ if (not_fast_array != NULL) {
++ // Check that the object is in fast mode and writable.
++ __ LoadP(scratch1, FieldMemOperand(elements, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
++ __ cmp(scratch1, ip);
++ __ bne(not_fast_array);
++ } else {
++ __ AssertFastElements(elements);
++ }
++ // Check that the key (index) is within bounds.
++ __ LoadP(scratch1, FieldMemOperand(elements, FixedArray::kLengthOffset));
++ __ cmpl(key, scratch1);
++ __ bge(out_of_range);
++ // Fast case: Do the load.
++ __ addi(scratch1, elements,
++ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ // The key is a smi.
++ __ SmiToPtrArrayOffset(scratch2, key);
++ __ LoadPX(scratch2, MemOperand(scratch2, scratch1));
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(scratch2, ip);
++ // In case the loaded value is the_hole we have to consult GetProperty
++ // to ensure the prototype chain is searched.
++ __ beq(out_of_range);
++ __ mr(result, scratch2);
++}
++
++
++// Checks whether a key is an array index string or a symbol string.
++// Falls through if a key is a symbol.
++static void GenerateKeyStringCheck(MacroAssembler* masm,
++ Register key,
++ Register map,
++ Register hash,
++ Label* index_string,
++ Label* not_symbol) {
++ // assumes that r8 is free for scratch use
++ // The key is not a smi.
++ // Is it a string?
++ __ CompareObjectType(key, map, hash, FIRST_NONSTRING_TYPE);
++ __ bge(not_symbol);
++
++ // Is the string an array index, with cached numeric value?
++ __ lwz(hash, FieldMemOperand(key, String::kHashFieldOffset));
++ __ mov(r8, Operand(String::kContainsCachedArrayIndexMask));
++ __ and_(r0, hash, r8, SetRC);
++ __ beq(index_string, cr0);
++
++ // Is the string a symbol?
++ // map: key map
++ __ lbz(hash, FieldMemOperand(map, Map::kInstanceTypeOffset));
++ STATIC_ASSERT(kSymbolTag != 0);
++ __ andi(r0, hash, Operand(kIsSymbolMask));
++ __ beq(not_symbol, cr0);
++}
++
++
++// Defined in ic.cc.
++Object* CallIC_Miss(Arguments args);
++
++// The generated code does not accept smi keys.
++// The generated code falls through if both probes miss.
++void CallICBase::GenerateMonomorphicCacheProbe(MacroAssembler* masm,
++ int argc,
++ Code::Kind kind,
++ Code::ExtraICState extra_state) {
++ // ----------- S t a t e -------------
++ // -- r4 : receiver
++ // -- r5 : name
++ // -----------------------------------
++ Label number, non_number, non_string, boolean, probe, miss;
++
++ // Probe the stub cache.
++ Code::Flags flags = Code::ComputeFlags(kind,
++ MONOMORPHIC,
++ extra_state,
++ Code::NORMAL,
++ argc);
++ Isolate::Current()->stub_cache()->GenerateProbe(
++ masm, flags, r4, r5, r6, r7, r8, r9);
++
++ // If the stub cache probing failed, the receiver might be a value.
++ // For value objects, we use the map of the prototype objects for
++ // the corresponding JSValue for the cache and that is what we need
++ // to probe.
++ //
++ // Check for number.
++ __ JumpIfSmi(r4, &number);
++ __ CompareObjectType(r4, r6, r6, HEAP_NUMBER_TYPE);
++ __ bne(&non_number);
++ __ bind(&number);
++ StubCompiler::GenerateLoadGlobalFunctionPrototype(
++ masm, Context::NUMBER_FUNCTION_INDEX, r4);
++ __ b(&probe);
++
++ // Check for string.
++ __ bind(&non_number);
++ __ cmpli(r6, Operand(FIRST_NONSTRING_TYPE));
++ __ bge(&non_string);
++ StubCompiler::GenerateLoadGlobalFunctionPrototype(
++ masm, Context::STRING_FUNCTION_INDEX, r4);
++ __ b(&probe);
++
++ // Check for boolean.
++ __ bind(&non_string);
++ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
++ __ cmp(r4, ip);
++ __ beq(&boolean);
++ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
++ __ cmp(r4, ip);
++ __ bne(&miss);
++ __ bind(&boolean);
++ StubCompiler::GenerateLoadGlobalFunctionPrototype(
++ masm, Context::BOOLEAN_FUNCTION_INDEX, r4);
++
++ // Probe the stub cache for the value object.
++ __ bind(&probe);
++ Isolate::Current()->stub_cache()->GenerateProbe(
++ masm, flags, r4, r5, r6, r7, r8, r9);
++
++ __ bind(&miss);
++}
++
++
++static void GenerateFunctionTailCall(MacroAssembler* masm,
++ int argc,
++ Label* miss,
++ Register scratch) {
++ // r4: function
++
++ // Check that the value isn't a smi.
++ __ JumpIfSmi(r4, miss);
++
++ // Check that the value is a JSFunction.
++ __ CompareObjectType(r4, scratch, scratch, JS_FUNCTION_TYPE);
++ __ bne(miss);
++
++ // Invoke the function.
++ ParameterCount actual(argc);
++ __ InvokeFunction(r4, actual, JUMP_FUNCTION,
++ NullCallWrapper(), CALL_AS_METHOD);
++}
++
++
++void CallICBase::GenerateNormal(MacroAssembler* masm, int argc) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ // Get the receiver of the function from the stack into r4.
++ __ LoadP(r4, MemOperand(sp, argc * kPointerSize), r0);
++
++ GenerateStringDictionaryReceiverCheck(masm, r4, r3, r6, r7, &miss);
++
++ // r3: elements
++ // Search the dictionary - put result in register r4.
++ GenerateDictionaryLoad(masm, &miss, r3, r5, r4, r6, r7);
++
++ GenerateFunctionTailCall(masm, argc, &miss, r7);
++
++ __ bind(&miss);
++}
++
++
++void CallICBase::GenerateMiss(MacroAssembler* masm,
++ int argc,
++ IC::UtilityId id,
++ Code::ExtraICState extra_state) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Isolate* isolate = masm->isolate();
++
++ if (id == IC::kCallIC_Miss) {
++ __ IncrementCounter(isolate->counters()->call_miss(), 1, r6, r7);
++ } else {
++ __ IncrementCounter(isolate->counters()->keyed_call_miss(), 1, r6, r7);
++ }
++
++ // Get the receiver of the function from the stack.
++ __ LoadP(r6, MemOperand(sp, argc * kPointerSize), r0);
++
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ // Push the receiver and the name of the function.
++ __ Push(r6, r5);
++
++ // Call the entry.
++ __ li(r3, Operand(2));
++ __ mov(r4, Operand(ExternalReference(IC_Utility(id), isolate)));
++
++ CEntryStub stub(1);
++ __ CallStub(&stub);
++
++ // Move result to r4 and leave the internal frame.
++ __ mr(r4, r3);
++ }
++
++ // Check if the receiver is a global object of some sort.
++ // This can happen only for regular CallIC but not KeyedCallIC.
++ if (id == IC::kCallIC_Miss) {
++ Label invoke, global;
++ __ LoadP(r5, MemOperand(sp, argc * kPointerSize), r0); // receiver
++ __ JumpIfSmi(r5, &invoke);
++ __ CompareObjectType(r5, r6, r6, JS_GLOBAL_OBJECT_TYPE);
++ __ beq(&global);
++ __ cmpi(r6, Operand(JS_BUILTINS_OBJECT_TYPE));
++ __ bne(&invoke);
++
++ // Patch the receiver on the stack.
++ __ bind(&global);
++ __ LoadP(r5, FieldMemOperand(r5, GlobalObject::kGlobalReceiverOffset));
++ __ StoreP(r5, MemOperand(sp, argc * kPointerSize), r0);
++ __ bind(&invoke);
++ }
++
++ // Invoke the function.
++ CallKind call_kind = CallICBase::Contextual::decode(extra_state)
++ ? CALL_AS_FUNCTION
++ : CALL_AS_METHOD;
++ ParameterCount actual(argc);
++ __ InvokeFunction(r4,
++ actual,
++ JUMP_FUNCTION,
++ NullCallWrapper(),
++ call_kind);
++}
++
++
++void CallIC::GenerateMegamorphic(MacroAssembler* masm,
++ int argc,
++ Code::ExtraICState extra_ic_state) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++
++ // Get the receiver of the function from the stack into r4.
++ __ LoadP(r4, MemOperand(sp, argc * kPointerSize), r0);
++ GenerateMonomorphicCacheProbe(masm, argc, Code::CALL_IC, extra_ic_state);
++ GenerateMiss(masm, argc, extra_ic_state);
++}
++
++
++void KeyedCallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++
++ // Get the receiver of the function from the stack into r4.
++ __ LoadP(r4, MemOperand(sp, argc * kPointerSize), r0);
++
++ Label do_call, slow_call, slow_load, slow_reload_receiver;
++ Label check_number_dictionary, check_string, lookup_monomorphic_cache;
++ Label index_smi, index_string;
++
++ // Check that the key is a smi.
++ __ JumpIfNotSmi(r5, &check_string);
++ __ bind(&index_smi);
++ // Now the key is known to be a smi. This place is also jumped to from below
++ // where a numeric string is converted to a smi.
++
++ GenerateKeyedLoadReceiverCheck(
++ masm, r4, r3, r6, Map::kHasIndexedInterceptor, &slow_call);
++
++ GenerateFastArrayLoad(
++ masm, r4, r5, r7, r6, r3, r4, &check_number_dictionary, &slow_load);
++ Counters* counters = masm->isolate()->counters();
++ __ IncrementCounter(counters->keyed_call_generic_smi_fast(), 1, r3, r6);
++
++ __ bind(&do_call);
++ // receiver in r4 is not used after this point.
++ // r5: key
++ // r4: function
++ GenerateFunctionTailCall(masm, argc, &slow_call, r3);
++
++ __ bind(&check_number_dictionary);
++ // r5: key
++ // r6: elements map
++ // r7: elements
++ // Check whether the elements is a number dictionary.
++ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
++ __ cmp(r6, ip);
++ __ bne(&slow_load);
++ __ SmiUntag(r3, r5);
++ // r3: untagged index
++ __ LoadFromNumberDictionary(&slow_load, r7, r5, r4, r3, r6, r8);
++ __ IncrementCounter(counters->keyed_call_generic_smi_dict(), 1, r3, r6);
++ __ b(&do_call);
++
++ __ bind(&slow_load);
++ // This branch is taken when calling KeyedCallIC_Miss is neither required
++ // nor beneficial.
++ __ IncrementCounter(counters->keyed_call_generic_slow_load(), 1, r3, r6);
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ push(r5); // save the key
++ __ Push(r4, r5); // pass the receiver and the key
++ __ CallRuntime(Runtime::kKeyedGetProperty, 2);
++ __ pop(r5); // restore the key
++ }
++ __ mr(r4, r3);
++ __ b(&do_call);
++
++ __ bind(&check_string);
++ GenerateKeyStringCheck(masm, r5, r3, r6, &index_string, &slow_call);
++
++ // The key is known to be a symbol.
++ // If the receiver is a regular JS object with slow properties then do
++ // a quick inline probe of the receiver's dictionary.
++ // Otherwise do the monomorphic cache probe.
++ GenerateKeyedLoadReceiverCheck(
++ masm, r4, r3, r6, Map::kHasNamedInterceptor, &lookup_monomorphic_cache);
++
++ __ LoadP(r3, FieldMemOperand(r4, JSObject::kPropertiesOffset));
++ __ LoadP(r6, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
++ __ cmp(r6, ip);
++ __ bne(&lookup_monomorphic_cache);
++
++ GenerateDictionaryLoad(masm, &slow_load, r3, r5, r4, r6, r7);
++ __ IncrementCounter(counters->keyed_call_generic_lookup_dict(), 1, r3, r6);
++ __ b(&do_call);
++
++ __ bind(&lookup_monomorphic_cache);
++ __ IncrementCounter(counters->keyed_call_generic_lookup_cache(), 1, r3, r6);
++ GenerateMonomorphicCacheProbe(masm,
++ argc,
++ Code::KEYED_CALL_IC,
++ Code::kNoExtraICState);
++ // Fall through on miss.
++
++ __ bind(&slow_call);
++ // This branch is taken if:
++ // - the receiver requires boxing or access check,
++ // - the key is neither smi nor symbol,
++ // - the value loaded is not a function,
++ // - there is hope that the runtime will create a monomorphic call stub
++ // that will get fetched next time.
++ __ IncrementCounter(counters->keyed_call_generic_slow(), 1, r3, r6);
++ GenerateMiss(masm, argc);
++
++ __ bind(&index_string);
++ __ IndexFromHash(r6, r5);
++ // Now jump to the place where smi keys are handled.
++ __ b(&index_smi);
++}
++
++
++void KeyedCallIC::GenerateNormal(MacroAssembler* masm, int argc) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++
++ // Check if the name is a string.
++ Label miss;
++ __ JumpIfSmi(r5, &miss);
++ __ IsObjectJSStringType(r5, r3, &miss);
++
++ CallICBase::GenerateNormal(masm, argc);
++ __ bind(&miss);
++ GenerateMiss(masm, argc);
++}
++
++
++// Defined in ic.cc.
++Object* LoadIC_Miss(Arguments args);
++
++void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- r3 : receiver
++ // -- sp[0] : receiver
++ // -----------------------------------
++
++ // Probe the stub cache.
++ Code::Flags flags =
++ Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
++ Isolate::Current()->stub_cache()->GenerateProbe(
++ masm, flags, r3, r5, r6, r7, r8, r9);
++
++ // Cache miss: Jump to runtime.
++ GenerateMiss(masm);
++}
++
++
++void LoadIC::GenerateNormal(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- r3 : receiver
++ // -- sp[0] : receiver
++ // -----------------------------------
++ Label miss;
++
++ GenerateStringDictionaryReceiverCheck(masm, r3, r4, r6, r7, &miss);
++
++ // r4: elements
++ GenerateDictionaryLoad(masm, &miss, r4, r5, r3, r6, r7);
++ __ Ret();
++
++ // Cache miss: Jump to runtime.
++ __ bind(&miss);
++ GenerateMiss(masm);
++}
++
++
++void LoadIC::GenerateMiss(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- r3 : receiver
++ // -- sp[0] : receiver
++ // -----------------------------------
++ Isolate* isolate = masm->isolate();
++
++ __ IncrementCounter(isolate->counters()->load_miss(), 1, r6, r7);
++
++ __ Push(r3, r5);
++
++ // Perform tail call to the entry.
++ ExternalReference ref =
++ ExternalReference(IC_Utility(kLoadIC_Miss), isolate);
++ __ TailCallExternalReference(ref, 2, 1);
++}
++
++
++static MemOperand GenerateMappedArgumentsLookup(MacroAssembler* masm,
++ Register object,
++ Register key,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* unmapped_case,
++ Label* slow_case) {
++ Heap* heap = masm->isolate()->heap();
++
++ // Check that the receiver is a JSObject. Because of the map check
++ // later, we do not need to check for interceptors or whether it
++ // requires access checks.
++ __ JumpIfSmi(object, slow_case);
++ // Check that the object is some kind of JSObject.
++ __ CompareObjectType(object, scratch1, scratch2, FIRST_JS_RECEIVER_TYPE);
++ __ blt(slow_case);
++
++ // Check that the key is a positive smi.
++ __ mov(scratch1, Operand(0x80000001));
++ __ and_(r0, key, scratch1, SetRC);
++ __ bne(slow_case, cr0);
++
++ // Load the elements into scratch1 and check its map.
++ Handle<Map> arguments_map(heap->non_strict_arguments_elements_map());
++ __ LoadP(scratch1, FieldMemOperand(object, JSObject::kElementsOffset));
++ __ CheckMap(scratch1, scratch2, arguments_map, slow_case, DONT_DO_SMI_CHECK);
++
++ // Check if element is in the range of mapped arguments. If not, jump
++ // to the unmapped lookup with the parameter map in scratch1.
++ __ LoadP(scratch2, FieldMemOperand(scratch1, FixedArray::kLengthOffset));
++ __ SubSmiLiteral(scratch2, scratch2, Smi::FromInt(2), r0);
++ __ cmpl(key, scratch2);
++ __ bge(unmapped_case);
++
++ // Load element index and check whether it is the hole.
++ const int kOffset =
++ FixedArray::kHeaderSize + 2 * kPointerSize - kHeapObjectTag;
++
++ __ SmiToPtrArrayOffset(scratch3, key);
++ __ addi(scratch3, scratch3, Operand(kOffset));
++
++ __ LoadPX(scratch2, MemOperand(scratch1, scratch3));
++ __ LoadRoot(scratch3, Heap::kTheHoleValueRootIndex);
++ __ cmp(scratch2, scratch3);
++ __ beq(unmapped_case);
++
++ // Load value from context and return it. We can reuse scratch1 because
++ // we do not jump to the unmapped lookup (which requires the parameter
++ // map in scratch1).
++ __ LoadP(scratch1, FieldMemOperand(scratch1, FixedArray::kHeaderSize));
++ __ SmiToPtrArrayOffset(scratch3, scratch2);
++ __ addi(scratch3, scratch3, Operand(Context::kHeaderSize - kHeapObjectTag));
++ __ add(scratch1, scratch1, scratch3);
++ return MemOperand(scratch1);
++}
++
++
++static MemOperand GenerateUnmappedArgumentsLookup(MacroAssembler* masm,
++ Register key,
++ Register parameter_map,
++ Register scratch,
++ Label* slow_case) {
++ // Element is in arguments backing store, which is referenced by the
++ // second element of the parameter_map. The parameter_map register
++ // must be loaded with the parameter map of the arguments object and is
++ // overwritten.
++ const int kBackingStoreOffset = FixedArray::kHeaderSize + kPointerSize;
++ Register backing_store = parameter_map;
++ __ LoadP(backing_store, FieldMemOperand(parameter_map, kBackingStoreOffset));
++ Handle<Map>
fixed_array_map(masm->isolate()->heap()->fixed_array_map());
++ __ CheckMap(backing_store, scratch, fixed_array_map, slow_case,
++ DONT_DO_SMI_CHECK);
++ __ LoadP(scratch, FieldMemOperand(backing_store, FixedArray::kLengthOffset));
++ __ cmpl(key, scratch);
++ __ bge(slow_case);
++ __ SmiToPtrArrayOffset(scratch, key);
++ __ addi(scratch,
++ scratch,
++ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ add(backing_store, backing_store, scratch);
++ return MemOperand(backing_store);
++}
++
++
++void KeyedLoadIC::GenerateNonStrictArguments(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label slow, notin;
++ MemOperand mapped_location =
++ GenerateMappedArgumentsLookup(masm, r4, r3, r5, r6, r7, ¬in, &slow);
++ __ LoadP(r3, mapped_location);
++ __ Ret();
++ __ bind(¬in);
++ // The unmapped lookup expects that the parameter map is in r5.
++ MemOperand unmapped_location =
++ GenerateUnmappedArgumentsLookup(masm, r3, r5, r6, &slow);
++ __ LoadP(r5, unmapped_location);
++ __ LoadRoot(r6, Heap::kTheHoleValueRootIndex);
++ __ cmp(r5, r6);
++ __ beq(&slow);
++ __ mr(r3, r5);
++ __ Ret();
++ __ bind(&slow);
++ GenerateMiss(masm, false);
++}
++
++
++void KeyedStoreIC::GenerateNonStrictArguments(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -----------------------------------
++ Label slow, notin;
++ MemOperand mapped_location =
++ GenerateMappedArgumentsLookup(masm, r5, r4, r6, r7, r8, ¬in, &slow);
++ __ StoreP(r3, mapped_location);
++ __ mr(r9, r6); // r6 is modified by GenerateMappedArgumentsLookup
++ __ mr(r22, r3);
++ __ RecordWrite(r6, r9, r22, kLRHasNotBeenSaved, kDontSaveFPRegs);
++ __ Ret();
++ __ bind(¬in);
++ // The unmapped lookup expects that the parameter map is in r6.
++ MemOperand unmapped_location =
++ GenerateUnmappedArgumentsLookup(masm, r4, r6, r7, &slow);
++ __ StoreP(r3, unmapped_location);
++ __ mr(r9, r6); // r6 is modified by GenerateUnmappedArgumentsLookup
++ __ mr(r22, r3);
++ __ RecordWrite(r6, r9, r22, kLRHasNotBeenSaved, kDontSaveFPRegs);
++ __ Ret();
++ __ bind(&slow);
++ GenerateMiss(masm, false);
++}
++
++
++void KeyedCallIC::GenerateNonStrictArguments(MacroAssembler* masm,
++ int argc) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label slow, notin;
++ // Load receiver.
++ __ LoadP(r4, MemOperand(sp, argc * kPointerSize), r0);
++ MemOperand mapped_location =
++ GenerateMappedArgumentsLookup(masm, r4, r5, r6, r7, r8, ¬in, &slow);
++ __ LoadP(r4, mapped_location);
++ GenerateFunctionTailCall(masm, argc, &slow, r6);
++ __ bind(¬in);
++ // The unmapped lookup expects that the parameter map is in r6.
++ MemOperand unmapped_location =
++ GenerateUnmappedArgumentsLookup(masm, r5, r6, r7, &slow);
++ __ LoadP(r4, unmapped_location);
++ __ LoadRoot(r6, Heap::kTheHoleValueRootIndex);
++ __ cmp(r4, r6);
++ __ beq(&slow);
++ GenerateFunctionTailCall(masm, argc, &slow, r3);
++ __ bind(&slow);
++ GenerateMiss(masm, argc);
++}
++
++
++Object* KeyedLoadIC_Miss(Arguments args);
++
++
++void KeyedLoadIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Isolate* isolate = masm->isolate();
++
++ __ IncrementCounter(isolate->counters()->keyed_load_miss(), 1, r6, r7);
++
++ __ Push(r4, r3);
++
++ // Perform tail call to the entry.
++ ExternalReference ref = force_generic
++ ? ExternalReference(IC_Utility(kKeyedLoadIC_MissForceGeneric), isolate)
++ : ExternalReference(IC_Utility(kKeyedLoadIC_Miss), isolate);
++
++ __ TailCallExternalReference(ref, 2, 1);
++}
++
++
++void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++
++ __ Push(r4, r3);
++
++ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
++}
++
++
++void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label slow, check_string, index_smi, index_string, property_array_property;
++ Label probe_dictionary, check_number_dictionary;
++
++ Register key = r3;
++ Register receiver = r4;
++
++ Isolate* isolate = masm->isolate();
++
++ // Check that the key is a smi.
++ __ JumpIfNotSmi(key, &check_string);
++ __ bind(&index_smi);
++ // Now the key is known to be a smi. This place is also jumped to from below
++ // where a numeric string is converted to a smi.
++
++ GenerateKeyedLoadReceiverCheck(
++ masm, receiver, r5, r6, Map::kHasIndexedInterceptor, &slow);
++
++ // Check the receiver's map to see if it has fast elements.
++ __ CheckFastElements(r5, r6, &check_number_dictionary);
++
++ GenerateFastArrayLoad(
++ masm, receiver, key, r7, r6, r5, r3, NULL, &slow);
++ __ IncrementCounter(isolate->counters()->keyed_load_generic_smi(), 1, r5, r6);
++ __ Ret();
++
++ __ bind(&check_number_dictionary);
++ __ LoadP(r7, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ __ LoadP(r6, FieldMemOperand(r7, JSObject::kMapOffset));
++
++ // Check whether the elements is a number dictionary.
++ // r3: key
++ // r6: elements map
++ // r7: elements
++ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
++ __ cmp(r6, ip);
++ __ bne(&slow);
++ __ SmiUntag(r5, r3);
++ __ LoadFromNumberDictionary(&slow, r7, r3, r3, r5, r6, r8);
++ __ Ret();
++
++ // Slow case, key and receiver still in r3 and r4.
++ __ bind(&slow);
++ __ IncrementCounter(isolate->counters()->keyed_load_generic_slow(),
++ 1, r5, r6);
++ GenerateRuntimeGetProperty(masm);
++
++ __ bind(&check_string);
++ GenerateKeyStringCheck(masm, key, r5, r6, &index_string, &slow);
++
++ GenerateKeyedLoadReceiverCheck(
++ masm, receiver, r5, r6, Map::kHasNamedInterceptor, &slow);
++
++ // If the receiver is a fast-case object, check the keyed lookup
++ // cache. Otherwise probe the dictionary.
++ __ LoadP(r6, FieldMemOperand(r4, JSObject::kPropertiesOffset));
++ __ LoadP(r7, FieldMemOperand(r6, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHashTableMapRootIndex);
++ __ cmp(r7, ip);
++ __ beq(&probe_dictionary);
++
++ // Load the map of the receiver, compute the keyed lookup cache hash
++ // based on 32 bits of the map pointer and the string hash.
++ __ LoadP(r5, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ srawi(r6, r5, KeyedLookupCache::kMapHashShift);
++ __ lwz(r7, FieldMemOperand(r3, String::kHashFieldOffset));
++ __ srawi(r7, r7, String::kHashShift);
++ __ xor_(r6, r6, r7);
++ int mask = KeyedLookupCache::kCapacityMask & KeyedLookupCache::kHashMask;
++ __ mov(r7, Operand(mask));
++ __ and_(r6, r6, r7, LeaveRC);
++
++ // Load the key (consisting of map and symbol) from the cache and
++ // check for match.
++ Label load_in_object_property;
++ static const int kEntriesPerBucket = KeyedLookupCache::kEntriesPerBucket;
++ Label hit_on_nth_entry[kEntriesPerBucket];
++ ExternalReference cache_keys =
++ ExternalReference::keyed_lookup_cache_keys(isolate);
++
++ __ mov(r7, Operand(cache_keys));
++ __ mr(r0, r5);
++ __ ShiftLeftImm(r5, r6, Operand(kPointerSizeLog2 + 1));
++ __ add(r7, r7, r5);
++ __ mr(r5, r0);
++
++ for (int i = 0; i < kEntriesPerBucket - 1; i++) {
++ Label try_next_entry;
++ // Load map and move r7 to next entry.
++ __ LoadP(r8, MemOperand(r7));
++ __ addi(r7, r7, Operand(kPointerSize * 2));
++ __ cmp(r5, r8);
++ __ bne(&try_next_entry);
++ __ LoadP(r8, MemOperand(r7, -kPointerSize)); // Load symbol
++ __ cmp(r3, r8);
++ __ beq(&hit_on_nth_entry[i]);
++ __ bind(&try_next_entry);
++ }
++
++ // Last entry: Load map and move r7 to symbol.
++ __ LoadP(r8, MemOperand(r7));
++ __ addi(r7, r7, Operand(kPointerSize));
++ __ cmp(r5, r8);
++ __ bne(&slow);
++ __ LoadP(r8, MemOperand(r7));
++ __ cmp(r3, r8);
++ __ bne(&slow);
++
++ // Get field offset.
++ // r3 : key
++ // r4 : receiver
++ // r5 : receiver's map
++ // r6 : lookup cache index
++ ExternalReference cache_field_offsets =
++ ExternalReference::keyed_lookup_cache_field_offsets(isolate);
++
++ // Hit on nth entry.
++ for (int i = kEntriesPerBucket - 1; i >= 0; i--) {
++ __ bind(&hit_on_nth_entry[i]);
++ __ mov(r7, Operand(cache_field_offsets));
++ if (i != 0) {
++ __ addi(r6, r6, Operand(i));
++ }
++ __ ShiftLeftImm(r8, r6, Operand(2));
++ __ lwzx(r8, MemOperand(r8, r7));
++ __ lbz(r9, FieldMemOperand(r5, Map::kInObjectPropertiesOffset));
++ __ sub(r8, r8, r9);
++ __ cmpi(r8, Operand::Zero());
++ __ bge(&property_array_property);
++ if (i != 0) {
++ __ b(&load_in_object_property);
++ }
++ }
++
++ // Load in-object property.
++ __ bind(&load_in_object_property);
++ __ lbz(r9, FieldMemOperand(r5, Map::kInstanceSizeOffset));
++ __ add(r9, r9, r8); // Index from start of object.
++ __ subi(r4, r4, Operand(kHeapObjectTag)); // Remove the heap tag.
++ __ ShiftLeftImm(r3, r9, Operand(kPointerSizeLog2));
++ __ LoadPX(r3, MemOperand(r3, r4));
++ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
++ 1, r5, r6);
++ __ Ret();
++
++ // Load property array property.
++ __ bind(&property_array_property);
++ __ LoadP(r4, FieldMemOperand(r4, JSObject::kPropertiesOffset));
++ __ addi(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ ShiftLeftImm(r3, r8, Operand(kPointerSizeLog2));
++ __ LoadPX(r3, MemOperand(r3, r4));
++ __ IncrementCounter(isolate->counters()->keyed_load_generic_lookup_cache(),
++ 1, r5, r6);
++ __ Ret();
++
++ // Do a quick inline probe of the receiver's dictionary, if it
++ // exists.
++ __ bind(&probe_dictionary);
++ // r4: receiver
++ // r3: key
++ // r6: elements
++ __ LoadP(r5, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ lbz(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
++ GenerateGlobalInstanceTypeCheck(masm, r5, &slow);
++ // Load the property to r3.
++ GenerateDictionaryLoad(masm, &slow, r6, r3, r3, r5, r7);
++ __ IncrementCounter(isolate->counters()->keyed_load_generic_symbol(),
++ 1, r5, r6);
++ __ Ret();
++
++ __ bind(&index_string);
++ __ IndexFromHash(r6, key);
++ // Now jump to the place where smi keys are handled.
++ __ b(&index_smi);
++}
++
++
++void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key (index)
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ Register receiver = r4;
++ Register index = r3;
++ Register scratch = r6;
++ Register result = r3;
++
++ StringCharAtGenerator char_at_generator(receiver,
++ index,
++ scratch,
++ result,
++ &miss, // When not a string.
++ &miss, // When not a number.
++ &miss, // When index out of range.
++ STRING_INDEX_IS_ARRAY_INDEX);
++ char_at_generator.GenerateFast(masm);
++ __ Ret();
++
++ StubRuntimeCallHelper call_helper;
++ char_at_generator.GenerateSlow(masm, call_helper);
++
++ __ bind(&miss);
++ GenerateMiss(masm, false);
++}
++
++
++void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label slow;
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(r4, &slow);
++
++ // Check that the key is an array index, that is Uint32.
++ __ TestIfPositiveSmi(r3, r0);
++ __ bne(&slow, cr0);
++
++ // Get the map of the receiver.
++ __ LoadP(r5, FieldMemOperand(r4, HeapObject::kMapOffset));
++
++ // Check that it has indexed interceptor and access checks
++ // are not enabled for this object.
++ __ lbz(r6, FieldMemOperand(r5, Map::kBitFieldOffset));
++ __ andi(r6, r6, Operand(kSlowCaseBitFieldMask));
++ __ cmpi(r6, Operand(1 << Map::kHasIndexedInterceptor));
++ __ bne(&slow);
++
++ // Everything is fine, call runtime.
++ __ Push(r4, r3); // Receiver, key.
++
++ // Perform tail call to the entry.
++ __ TailCallExternalReference(
++ ExternalReference(IC_Utility(kKeyedLoadPropertyWithInterceptor),
++ masm->isolate()),
++ 2,
++ 1);
++
++ __ bind(&slow);
++ GenerateMiss(masm, false);
++}
++
++
++void KeyedStoreIC::GenerateMiss(MacroAssembler* masm, bool force_generic) {
++ // ---------- S t a t e --------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -----------------------------------
++
++ // Push receiver, key and value for runtime call.
++ __ Push(r5, r4, r3);
++
++ ExternalReference ref = force_generic
++ ? ExternalReference(IC_Utility(kKeyedStoreIC_MissForceGeneric),
++ masm->isolate())
++ : ExternalReference(IC_Utility(kKeyedStoreIC_Miss), masm->isolate());
++ __ TailCallExternalReference(ref, 3, 1);
++}
++
++
++void KeyedStoreIC::GenerateSlow(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -----------------------------------
++
++ // Push receiver, key and value for runtime call.
++ __ Push(r5, r4, r3);
++
++ // The slow case calls into the runtime to complete the store without causing
++ // an IC miss that would otherwise cause a transition to the generic stub.
++ ExternalReference ref =
++ ExternalReference(IC_Utility(kKeyedStoreIC_Slow), masm->isolate());
++ __ TailCallExternalReference(ref, 3, 1);
++}
++
++
++void KeyedStoreIC::GenerateTransitionElementsSmiToDouble(MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- r5 : receiver
++ // -- r6 : target map
++ // -- lr : return address
++ // -----------------------------------
++ // Must return the modified receiver in r0.
++ if (!FLAG_trace_elements_transitions) {
++ Label fail;
++ ElementsTransitionGenerator::GenerateSmiToDouble(masm, &fail);
++ __ mr(r3, r5);
++ __ Ret();
++ __ bind(&fail);
++ }
++
++ __ push(r5);
++ __ TailCallRuntime(Runtime::kTransitionElementsSmiToDouble, 1, 1);
++}
++
++
++void KeyedStoreIC::GenerateTransitionElementsDoubleToObject(
++ MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- r5 : receiver
++ // -- r6 : target map
++ // -- lr : return address
++ // -----------------------------------
++ // Must return the modified receiver in r3.
++ if (!FLAG_trace_elements_transitions) {
++ Label fail;
++ ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail);
++ __ mr(r3, r5);
++ __ Ret();
++ __ bind(&fail);
++ }
++
++ __ push(r5);
++ __ TailCallRuntime(Runtime::kTransitionElementsDoubleToObject, 1, 1);
++}
++
++
++void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm,
++ StrictModeFlag strict_mode) {
++ // ---------- S t a t e --------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -----------------------------------
++
++ // Push receiver, key and value for runtime call.
++ __ Push(r5, r4, r3);
++
++ __ LoadSmiLiteral(r4, Smi::FromInt(NONE)); // PropertyAttributes
++ __ LoadSmiLiteral(r3, Smi::FromInt(strict_mode)); // Strict mode.
++ __ Push(r4, r3);
++
++ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
++}
++
++
++static void KeyedStoreGenerateGenericHelper(
++ MacroAssembler* masm,
++ Label* fast_object,
++ Label* fast_double,
++ Label* slow,
++ KeyedStoreCheckMap check_map,
++ KeyedStoreIncrementLength increment_length,
++ Register value,
++ Register key,
++ Register receiver,
++ Register receiver_map,
++ Register elements_map,
++ Register elements) {
++ Label transition_smi_elements;
++ Label finish_object_store, non_double_value, transition_double_elements;
++ Label fast_double_without_map_check;
++
++ // Fast case: Do the store, could be either Object or double.
++ __ bind(fast_object);
++ Register scratch_value = r7;
++ Register address = r8;
++ if (check_map == kCheckMap) {
++ __ LoadP(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
++ __ mov(scratch_value,
++ Operand(masm->isolate()->factory()->fixed_array_map()));
++ __ cmp(elements_map, scratch_value);
++ __ bne(fast_double);
++ }
++ // Smi stores don't require further checks.
++ Label non_smi_value;
++ __ JumpIfNotSmi(value, &non_smi_value);
++
++ if (increment_length == kIncrementLength) {
++ // Add 1 to receiver->length.
++ __ AddSmiLiteral(scratch_value, key, Smi::FromInt(1), r0);
++ __ StoreP(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset),
++ r0);
++ }
++ // It's irrelevant whether array is smi-only or not when writing a smi.
++ __ addi(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(scratch_value, key);
++ __ StorePX(value, MemOperand(address, scratch_value));
++ __ Ret();
++
++ __ bind(&non_smi_value);
++ // Escape to elements kind transition case.
++ __ CheckFastObjectElements(receiver_map, scratch_value,
++ &transition_smi_elements);
++
++ // Fast elements array, store the value to the elements backing store.
++ __ bind(&finish_object_store);
++ if (increment_length == kIncrementLength) {
++ // Add 1 to receiver->length.
++ __ AddSmiLiteral(scratch_value, key, Smi::FromInt(1), r0);
++ __ StoreP(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset),
++ r0);
++ }
++ __ addi(address, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(scratch_value, key);
++ __ StorePUX(value, MemOperand(address, scratch_value));
++ // Update write barrier for the elements array address.
++ __ mr(scratch_value, value); // Preserve the value which is returned.
++ __ RecordWrite(elements,
++ address,
++ scratch_value,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ __ Ret();
++
++ __ bind(fast_double);
++ if (check_map == kCheckMap) {
++ // Check for fast double array case. If this fails, call through to the
++ // runtime.
++ __ CompareRoot(elements_map, Heap::kFixedDoubleArrayMapRootIndex);
++ __ bne(slow);
++ }
++ __ bind(&fast_double_without_map_check);
++ __ StoreNumberToDoubleElements(value,
++ key,
++ receiver,
++ elements, // Overwritten.
++ r6, // Scratch regs...
++ r7,
++ r8,
++ r9,
++ &transition_double_elements);
++ if (increment_length == kIncrementLength) {
++ // Add 1 to receiver->length.
++ __ AddSmiLiteral(scratch_value, key, Smi::FromInt(1), r0);
++ __ StoreP(scratch_value, FieldMemOperand(receiver, JSArray::kLengthOffset),
++ r0);
++ }
++ __ Ret();
++
++ __ bind(&transition_smi_elements);
++ // Transition the array appropriately depending on the value type.
++ __ LoadP(r7, FieldMemOperand(value, HeapObject::kMapOffset));
++ __ CompareRoot(r7, Heap::kHeapNumberMapRootIndex);
++ __ bne(&non_double_value);
++
++ // Value is a double. Transition FAST_SMI_ELEMENTS ->
++ // FAST_DOUBLE_ELEMENTS and complete the store.
++ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
++ FAST_DOUBLE_ELEMENTS,
++ receiver_map,
++ r7,
++ slow);
++ ASSERT(receiver_map.is(r6)); // Transition code expects map in r6
++ ElementsTransitionGenerator::GenerateSmiToDouble(masm, slow);
++ __ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ __ b(&fast_double_without_map_check);
++
++ __ bind(&non_double_value);
++ // Value is not a double, FAST_SMI_ELEMENTS -> FAST_ELEMENTS
++ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
++ FAST_ELEMENTS,
++ receiver_map,
++ r7,
++ slow);
++ ASSERT(receiver_map.is(r6)); // Transition code expects map in r6
++ ElementsTransitionGenerator::GenerateMapChangeElementsTransition(masm);
++ __ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ __ b(&finish_object_store);
++
++ __ bind(&transition_double_elements);
++ // Elements are FAST_DOUBLE_ELEMENTS, but value is an Object that's not a
++ // HeapNumber. Make sure that the receiver is a Array with FAST_ELEMENTS and
++ // transition array from FAST_DOUBLE_ELEMENTS to FAST_ELEMENTS
++ __ LoadTransitionedArrayMapConditional(FAST_DOUBLE_ELEMENTS,
++ FAST_ELEMENTS,
++ receiver_map,
++ r7,
++ slow);
++ ASSERT(receiver_map.is(r6)); // Transition code expects map in r6
++ ElementsTransitionGenerator::GenerateDoubleToObject(masm, slow);
++ __ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ __ b(&finish_object_store);
++}
++
++
++void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm,
++ StrictModeFlag strict_mode) {
++ // ---------- S t a t e --------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -----------------------------------
++ Label slow, fast_object, fast_object_grow;
++ Label fast_double, fast_double_grow;
++ Label array, extra, check_if_double_array;
++
++ // Register usage.
++ Register value = r3;
++ Register key = r4;
++ Register receiver = r5;
++ Register receiver_map = r6;
++ Register elements_map = r9;
++ Register elements = r10; // Elements array of the receiver.
++ // r7 and r8 are used as general scratch registers.
++
++ // Check that the key is a smi.
++ __ JumpIfNotSmi(key, &slow);
++ // Check that the object isn't a smi.
++ __ JumpIfSmi(receiver, &slow);
++ // Get the map of the object.
++ __ LoadP(receiver_map, FieldMemOperand(receiver, HeapObject::kMapOffset));
++ // Check that the receiver does not require access checks. We need
++ // to do this because this generic stub does not perform map checks.
++ __ lbz(ip, FieldMemOperand(receiver_map, Map::kBitFieldOffset));
++ __ andi(r0, ip, Operand(1 << Map::kIsAccessCheckNeeded));
++ __ bne(&slow, cr0);
++ // Check if the object is a JS array or not.
++ __ lbz(r7, FieldMemOperand(receiver_map, Map::kInstanceTypeOffset));
++ __ cmpi(r7, Operand(JS_ARRAY_TYPE));
++ __ beq(&array);
++ // Check that the object is some kind of JSObject.
++ __ cmpi(r7, Operand(FIRST_JS_OBJECT_TYPE));
++ __ blt(&slow);
++
++ // Object case: Check key against length in the elements array.
++ __ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ // Check array bounds. Both the key and the length of FixedArray are smis.
++ __ LoadP(ip, FieldMemOperand(elements, FixedArray::kLengthOffset));
++ __ cmpl(key, ip);
++ __ blt(&fast_object);
++
++ // Slow case, handle jump to runtime.
++ __ bind(&slow);
++ // Entry registers are intact.
++ // r3: value.
++ // r4: key.
++ // r5: receiver.
++ GenerateRuntimeSetProperty(masm, strict_mode);
++
++ // Extra capacity case: Check if there is extra capacity to
++ // perform the store and update the length. Used for adding one
++ // element to the array by writing to array[array.length].
++ __ bind(&extra);
++ // Condition code from comparing key and array length is still available.
++ __ bne(&slow); // Only support writing to writing to array[array.length].
++ // Check for room in the elements backing store.
++ // Both the key and the length of FixedArray are smis.
++ __ LoadP(ip, FieldMemOperand(elements, FixedArray::kLengthOffset));
++ __ cmpl(key, ip);
++ __ bge(&slow);
++ __ LoadP(elements_map, FieldMemOperand(elements, HeapObject::kMapOffset));
++ __ mov(ip, Operand(masm->isolate()->factory()->fixed_array_map()));
++ __ cmp(elements_map, ip); // PPC - I think I can re-use ip here
++ __ bne(&check_if_double_array);
++ __ b(&fast_object_grow);
++
++ __ bind(&check_if_double_array);
++ __ mov(ip, Operand(masm->isolate()->factory()->fixed_double_array_map()));
++ __ cmp(elements_map, ip); // PPC - another ip re-use
++ __ bne(&slow);
++ __ b(&fast_double_grow);
++
++ // Array case: Get the length and the elements array from the JS
++ // array. Check that the array is in fast mode (and writable); if it
++ // is the length is always a smi.
++ __ bind(&array);
++ __ LoadP(elements, FieldMemOperand(receiver, JSObject::kElementsOffset));
++
++ // Check the key against the length in the array.
++ __ LoadP(ip, FieldMemOperand(receiver, JSArray::kLengthOffset));
++ __ cmpl(key, ip);
++ __ bge(&extra);
++
++ KeyedStoreGenerateGenericHelper(masm, &fast_object, &fast_double,
++ &slow, kCheckMap, kDontIncrementLength,
++ value, key, receiver, receiver_map,
++ elements_map, elements);
++ KeyedStoreGenerateGenericHelper(masm, &fast_object_grow, &fast_double_grow,
++ &slow, kDontCheckMap, kIncrementLength,
++ value, key, receiver, receiver_map,
++ elements_map, elements);
++}
++
++
++void StoreIC::GenerateMegamorphic(MacroAssembler* masm,
++ StrictModeFlag strict_mode) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++
++ // Get the receiver from the stack and probe the stub cache.
++ Code::Flags flags =
++ Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode);
++
++ Isolate::Current()->stub_cache()->GenerateProbe(
++ masm, flags, r4, r5, r6, r7, r8, r9);
++
++ // Cache miss: Jump to runtime.
++ GenerateMiss(masm);
++}
++
++
++void StoreIC::GenerateMiss(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++
++ __ Push(r4, r5, r3);
++
++ // Perform tail call to the entry.
++ ExternalReference ref =
++ ExternalReference(IC_Utility(kStoreIC_Miss), masm->isolate());
++ __ TailCallExternalReference(ref, 3, 1);
++}
++
++
++void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ //
++ // This accepts as a receiver anything JSArray::SetElementsLength accepts
++ // (currently anything except for external arrays which means anything with
++ // elements of FixedArray type). Value must be a number, but only smis are
++ // accepted as the most common case.
++
++ Label miss;
++
++ Register receiver = r4;
++ Register value = r3;
++ Register scratch = r6;
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, &miss);
++
++ // Check that the object is a JS array.
++ __ CompareObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE);
++ __ bne(&miss);
++
++ // Check that elements are FixedArray.
++ // We rely on StoreIC_ArrayLength below to deal with all types of
++ // fast elements (including COW).
++ __ LoadP(scratch, FieldMemOperand(receiver, JSArray::kElementsOffset));
++ __ CompareObjectType(scratch, scratch, scratch, FIXED_ARRAY_TYPE);
++ __ bne(&miss);
++
++ // Check that the array has fast properties, otherwise the length
++ // property might have been redefined.
++ __ LoadP(scratch, FieldMemOperand(receiver, JSArray::kPropertiesOffset));
++ __ LoadP(scratch, FieldMemOperand(scratch, FixedArray::kMapOffset));
++ __ CompareRoot(scratch, Heap::kHashTableMapRootIndex);
++ __ beq(&miss);
++
++ // Check that value is a smi.
++ __ JumpIfNotSmi(value, &miss);
++
++ // Prepare tail call to StoreIC_ArrayLength.
++ __ Push(receiver, value);
++
++ ExternalReference ref =
++ ExternalReference(IC_Utility(kStoreIC_ArrayLength), masm->isolate());
++ __ TailCallExternalReference(ref, 2, 1);
++
++ __ bind(&miss);
++
++ GenerateMiss(masm);
++}
++
++
++void StoreIC::GenerateNormal(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ GenerateStringDictionaryReceiverCheck(masm, r4, r6, r7, r8, &miss);
++
++ GenerateDictionaryStore(masm, &miss, r6, r5, r3, r7, r8);
++ Counters* counters = masm->isolate()->counters();
++ __ IncrementCounter(counters->store_normal_hit(), 1, r7, r8);
++ __ Ret();
++
++ __ bind(&miss);
++ __ IncrementCounter(counters->store_normal_miss(), 1, r7, r8);
++ GenerateMiss(masm);
++}
++
++
++void StoreIC::GenerateGlobalProxy(MacroAssembler* masm,
++ StrictModeFlag strict_mode) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++
++ __ Push(r4, r5, r3);
++
++ __ LoadSmiLiteral(r4, Smi::FromInt(NONE)); // PropertyAttributes
++ __ LoadSmiLiteral(r3, Smi::FromInt(strict_mode));
++ __ Push(r4, r3);
++
++ // Do tail-call to runtime routine.
++ __ TailCallRuntime(Runtime::kSetProperty, 5, 1);
++}
++
++
++#undef __
++
++
++Condition CompareIC::ComputeCondition(Token::Value op) {
++ switch (op) {
++ case Token::EQ_STRICT:
++ case Token::EQ:
++ return eq;
++ case Token::LT:
++ return lt;
++ case Token::GT:
++ return gt;
++ case Token::LTE:
++ return le;
++ case Token::GTE:
++ return ge;
++ default:
++ UNREACHABLE();
++ return kNoCondition;
++ }
++}
++
++
++void CompareIC::UpdateCaches(Handle<Object> x, Handle<Object> y) {
++ HandleScope scope;
++ Handle<Code> rewritten;
++ State previous_state = GetState();
++ State state = TargetState(previous_state, false, x, y);
++ if (state == GENERIC) {
++ CompareStub stub(GetCondition(), strict(), NO_COMPARE_FLAGS, r4, r3);
++ rewritten = stub.GetCode();
++ } else {
++ ICCompareStub stub(op_, state);
++ if (state == KNOWN_OBJECTS) {
++ stub.set_known_map(Handle<Map>(Handle<JSObject>::cast(x)->map()));
++ }
++ rewritten = stub.GetCode();
++ }
++ set_target(*rewritten);
++
++#ifdef DEBUG
++ if (FLAG_trace_ic) {
++ PrintF("[CompareIC (%s->%s)#%s]\n",
++ GetStateName(previous_state),
++ GetStateName(state),
++ Token::Name(op_));
++ }
++#endif
++
++ // Activate inlined smi code.
++ if (previous_state == UNINITIALIZED) {
++ PatchInlinedSmiCode(address(), ENABLE_INLINED_SMI_CHECK);
++ }
++}
++
++//
++// This code is paired with the JumpPatchSite class in full-codegen-ppc.cc
++//
++void PatchInlinedSmiCode(Address address, InlinedSmiCheck check) {
++ Address cmp_instruction_address =
++ address + Assembler::kCallTargetAddressOffset;
++
++ // If the instruction following the call is not a cmp rx, #yyy, nothing
++ // was inlined.
++ Instr instr = Assembler::instr_at(cmp_instruction_address);
++ if (!Assembler::IsCmpImmediate(instr)) {
++ return;
++ }
++
++ // The delta to the start of the map check instruction and the
++ // condition code uses at the patched jump.
++ int delta = Assembler::GetCmpImmediateRawImmediate(instr);
++ delta +=
++ Assembler::GetCmpImmediateRegister(instr).code() * kOff16Mask;
++ // If the delta is 0 the instruction is cmp r0, #0 which also signals that
++ // nothing was inlined.
++ if (delta == 0) {
++ return;
++ }
++
++#ifdef DEBUG
++ if (FLAG_trace_ic) {
++ PrintF("[ patching ic at %p, cmp=%p, delta=%d\n",
++ address, cmp_instruction_address, delta);
++ }
++#endif
++
++ Address patch_address =
++ cmp_instruction_address - delta * Instruction::kInstrSize;
++ Instr instr_at_patch = Assembler::instr_at(patch_address);
++ Instr branch_instr =
++ Assembler::instr_at(patch_address + Instruction::kInstrSize);
++ // This is patching a conditional "jump if not smi/jump if smi" site.
++ // Enabling by changing from
++ // cmp cr0, rx, rx
++ // to
++ // rlwinm(r0, value, 0, 31, 31, SetRC);
++ // bc(label, BT/BF, 2)
++ // and vice-versa to be disabled again.
++ CodePatcher patcher(patch_address, 2);
++ Register reg = Assembler::GetRA(instr_at_patch);
++ if (check == ENABLE_INLINED_SMI_CHECK) {
++ ASSERT(Assembler::IsCmpRegister(instr_at_patch));
++ ASSERT_EQ(Assembler::GetRA(instr_at_patch).code(),
++ Assembler::GetRB(instr_at_patch).code());
++ patcher.masm()->TestIfSmi(reg, r0);
++ } else {
++ ASSERT(check == DISABLE_INLINED_SMI_CHECK);
++#if V8_TARGET_ARCH_PPC64
++ ASSERT(Assembler::IsRldicl(instr_at_patch));
++#else
++ ASSERT(Assembler::IsRlwinm(instr_at_patch));
++#endif
++ patcher.masm()->cmp(reg, reg, cr0);
++ }
++
++ ASSERT(Assembler::IsBranch(branch_instr));
++
++ // Invert the logic of the branch
++ if (Assembler::GetCondition(branch_instr) == eq) {
++ patcher.EmitCondition(ne);
++ } else {
++ ASSERT(Assembler::GetCondition(branch_instr) == ne);
++ patcher.EmitCondition(eq);
++ }
++}
++
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc.ppc
v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc
+--- v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc.ppc 2016-06-07 14:15:45.997392967 -0400
++++ v8-3.14.5.10/src/ppc/lithium-codegen-ppc.cc 2016-06-07 14:15:45.997392967 -0400
+@@ -0,0 +1,5715 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++
++#include "v8.h"
++
++#include "ppc/lithium-codegen-ppc.h"
++#include "ppc/lithium-gap-resolver-ppc.h"
++#include "code-stubs.h"
++#include "stub-cache.h"
++
++namespace v8 {
++namespace internal {
++
++
++class SafepointGenerator : public CallWrapper {
++ public:
++ SafepointGenerator(LCodeGen* codegen,
++ LPointerMap* pointers,
++ Safepoint::DeoptMode mode)
++ : codegen_(codegen),
++ pointers_(pointers),
++ deopt_mode_(mode) { }
++ virtual ~SafepointGenerator() { }
++
++ virtual void BeforeCall(int call_size) const { }
++
++ virtual void AfterCall() const {
++ codegen_->RecordSafepoint(pointers_, deopt_mode_);
++ }
++
++ private:
++ LCodeGen* codegen_;
++ LPointerMap* pointers_;
++ Safepoint::DeoptMode deopt_mode_;
++};
++
++
++#define __ masm()->
++
++bool LCodeGen::GenerateCode() {
++ HPhase phase("Z_Code generation", chunk());
++ ASSERT(is_unused());
++ status_ = GENERATING;
++
++ CodeStub::GenerateFPStubs();
++
++ // Open a frame scope to indicate that there is a frame on the stack. The
++ // NONE indicates that the scope shouldn't actually generate code to set up
++ // the frame (that is done in GeneratePrologue).
++ FrameScope frame_scope(masm_, StackFrame::NONE);
++
++ return GeneratePrologue() &&
++ GenerateBody() &&
++ GenerateDeferredCode() &&
++ GenerateDeoptJumpTable() &&
++ GenerateSafepointTable();
++}
++
++
++void LCodeGen::FinishCode(Handle<Code> code) {
++ ASSERT(is_done());
++ code->set_stack_slots(GetStackSlotCount());
++ code->set_safepoint_table_offset(safepoints_.GetCodeOffset());
++ PopulateDeoptimizationData(code);
++}
++
++
++void LCodeGen::Abort(const char* reason) {
++ info()->set_bailout_reason(reason);
++ status_ = ABORTED;
++}
++
++
++void LCodeGen::Comment(const char* format, ...) {
++ if (!FLAG_code_comments) return;
++ char buffer[4 * KB];
++ StringBuilder builder(buffer, ARRAY_SIZE(buffer));
++ va_list arguments;
++ va_start(arguments, format);
++ builder.AddFormattedList(format, arguments);
++ va_end(arguments);
++
++ // Copy the string before recording it in the assembler to avoid
++ // issues when the stack allocated buffer goes out of scope.
++ size_t length = builder.position();
++ Vector<char> copy = Vector<char>::New(length + 1);
++ memcpy(copy.start(), builder.Finalize(), copy.length());
++ masm()->RecordComment(copy.start());
++}
++
++
++bool LCodeGen::GeneratePrologue() {
++ ASSERT(is_generating());
++
++ ProfileEntryHookStub::MaybeCallEntryHook(masm_);
++
++#ifdef DEBUG
++ if (strlen(FLAG_stop_at) > 0 &&
++ info_->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
++ __ stop("stop_at");
++ }
++#endif
++
++ // r4: Callee's JS function.
++ // cp: Callee's context.
++ // fp: Caller's frame pointer.
++ // lr: Caller's pc.
++
++ // Strict mode functions and builtins need to replace the receiver
++ // with undefined when called as functions (without an explicit
++ // receiver object). r8 is zero for method calls and non-zero for
++ // function calls.
++ if (!info_->is_classic_mode() || info_->is_native()) {
++ Label ok;
++ __ cmpi(r8, Operand::Zero());
++ __ beq(&ok);
++ int receiver_offset = scope()->num_parameters() * kPointerSize;
++ __ LoadRoot(r5, Heap::kUndefinedValueRootIndex);
++ __ StoreP(r5, MemOperand(sp, receiver_offset));
++ __ bind(&ok);
++ }
++
++ __ mflr(r0);
++ __ Push(r0, fp, cp, r4);
++ __ addi(fp, sp, Operand(2 * kPointerSize)); // Adjust FP to point to saved FP
++
++ // Reserve space for the stack slots needed by the code.
++ int slots = GetStackSlotCount();
++ if (slots > 0) {
++ if (FLAG_debug_code) {
++ __ mov(r3, Operand(slots));
++ __ mov(r5, Operand(kSlotsZapValue));
++ __ mtctr(r3);
++ Label loop;
++ __ bind(&loop);
++ __ push(r5);
++ __ bdnz(&loop);
++ } else {
++ __ addi(sp, sp, Operand(-slots * kPointerSize));
++ }
++ }
++
++ // Possibly allocate a local context.
++ int heap_slots = scope()->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
++ if (heap_slots > 0) {
++ Comment(";;; Allocate local context");
++ // Argument to NewContext is the function, which is in r4.
++ __ push(r4);
++ if (heap_slots <= FastNewContextStub::kMaximumSlots) {
++ FastNewContextStub stub(heap_slots);
++ __ CallStub(&stub);
++ } else {
++ __ CallRuntime(Runtime::kNewFunctionContext, 1);
++ }
++ RecordSafepoint(Safepoint::kNoLazyDeopt);
++ // Context is returned in both r3 and cp. It replaces the context
++ // passed to us. It's saved in the stack and kept live in cp.
++ __ StoreP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ // Copy any necessary parameters into the context.
++ int num_parameters = scope()->num_parameters();
++ for (int i = 0; i < num_parameters; i++) {
++ Variable* var = scope()->parameter(i);
++ if (var->IsContextSlot()) {
++ int parameter_offset = StandardFrameConstants::kCallerSPOffset +
++ (num_parameters - 1 - i) * kPointerSize;
++ // Load parameter from stack.
++ __ LoadP(r3, MemOperand(fp, parameter_offset));
++ // Store it in the context.
++ MemOperand target = ContextOperand(cp, var->index());
++ __ StoreP(r3, target, r0);
++ // Update the write barrier. This clobbers r6 and r3.
++ __ RecordWriteContextSlot(
++ cp, target.offset(), r3, r6, kLRHasBeenSaved, kSaveFPRegs);
++ }
++ }
++ Comment(";;; End allocate local context");
++ }
++
++ // Trace the call.
++ if (FLAG_trace) {
++ __ CallRuntime(Runtime::kTraceEnter, 0);
++ }
++ return !is_aborted();
++}
++
++
++bool LCodeGen::GenerateBody() {
++ ASSERT(is_generating());
++ bool emit_instructions = true;
++ for (current_instruction_ = 0;
++ !is_aborted() && current_instruction_ < instructions_->length();
++ current_instruction_++) {
++ LInstruction* instr = instructions_->at(current_instruction_);
++ if (instr->IsLabel()) {
++ LLabel* label = LLabel::cast(instr);
++ emit_instructions = !label->HasReplacement();
++ }
++
++ if (emit_instructions) {
++ Comment(";;; @%d: %s.", current_instruction_, instr->Mnemonic());
++ instr->CompileToNative(this);
++ }
++ }
++ EnsureSpaceForLazyDeopt();
++ return !is_aborted();
++}
++
++
++bool LCodeGen::GenerateDeferredCode() {
++ ASSERT(is_generating());
++ if (deferred_.length() > 0) {
++ for (int i = 0; !is_aborted() && i < deferred_.length(); i++) {
++ LDeferredCode* code = deferred_[i];
++ __ bind(code->entry());
++ Comment(";;; Deferred code @%d: %s.",
++ code->instruction_index(),
++ code->instr()->Mnemonic());
++ code->Generate();
++ __ b(code->exit());
++ }
++ }
++
++ return !is_aborted();
++}
++
++bool LCodeGen::GenerateDeoptJumpTable() {
++ __ RecordComment("[ Deoptimisation jump table");
++ for (int i = 0; i < deopt_jump_table_.length(); i++) {
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
++ __ bind(&deopt_jump_table_[i].label);
++ __ Jump(deopt_jump_table_[i].address, RelocInfo::RUNTIME_ENTRY);
++ }
++ __ RecordComment("]");
++
++ // The deoptimization jump table is the last part of the instruction
++ // sequence. Mark the generated code as done unless we bailed out.
++ if (!is_aborted()) status_ = DONE;
++ return !is_aborted();
++}
++
++
++bool LCodeGen::GenerateSafepointTable() {
++ ASSERT(is_done());
++ safepoints_.Emit(masm(), GetStackSlotCount());
++ return !is_aborted();
++}
++
++
++Register LCodeGen::ToRegister(int index) const {
++ return Register::FromAllocationIndex(index);
++}
++
++
++DoubleRegister LCodeGen::ToDoubleRegister(int index) const {
++ return DoubleRegister::FromAllocationIndex(index);
++}
++
++
++Register LCodeGen::ToRegister(LOperand* op) const {
++ ASSERT(op->IsRegister());
++ return ToRegister(op->index());
++}
++
++
++Register LCodeGen::EmitLoadRegister(LOperand* op, Register scratch) {
++ if (op->IsRegister()) {
++ return ToRegister(op->index());
++ } else if (op->IsConstantOperand()) {
++ LConstantOperand* const_op = LConstantOperand::cast(op);
++ HConstant* constant = chunk_->LookupConstant(const_op);
++ Handle<Object> literal = constant->handle();
++ Representation r = chunk_->LookupLiteralRepresentation(const_op);
++ if (r.IsInteger32()) {
++ ASSERT(literal->IsNumber());
++ __ LoadIntLiteral(scratch, static_cast<intptr_t>(literal->Number()));
++ } else if (r.IsDouble()) {
++ Abort("EmitLoadRegister: Unsupported double immediate.");
++ } else {
++ ASSERT(r.IsTagged());
++ if (literal->IsSmi()) {
++ __ mov(scratch, Operand(literal));
++ } else {
++ __ LoadHeapObject(scratch, Handle<HeapObject>::cast(literal));
++ }
++ }
++ return scratch;
++ } else if (op->IsStackSlot() || op->IsArgument()) {
++ __ LoadP(scratch, ToMemOperand(op));
++ return scratch;
++ }
++ UNREACHABLE();
++ return scratch;
++}
++
++
++DoubleRegister LCodeGen::ToDoubleRegister(LOperand* op) const {
++ ASSERT(op->IsDoubleRegister());
++ return ToDoubleRegister(op->index());
++}
++
++
++Handle<Object> LCodeGen::ToHandle(LConstantOperand* op) const {
++ HConstant* constant = chunk_->LookupConstant(op);
++ ASSERT(chunk_->LookupLiteralRepresentation(op).IsTagged());
++ return constant->handle();
++}
++
++
++bool LCodeGen::IsInteger32(LConstantOperand* op) const {
++ return chunk_->LookupLiteralRepresentation(op).IsInteger32();
++}
++
++
++int LCodeGen::ToInteger32(LConstantOperand* op) const {
++ HConstant* constant = chunk_->LookupConstant(op);
++ ASSERT(chunk_->LookupLiteralRepresentation(op).IsInteger32());
++ ASSERT(constant->HasInteger32Value());
++ return constant->Integer32Value();
++}
++
++
++double LCodeGen::ToDouble(LConstantOperand* op) const {
++ HConstant* constant = chunk_->LookupConstant(op);
++ ASSERT(constant->HasDoubleValue());
++ return constant->DoubleValue();
++}
++
++
++Operand LCodeGen::ToOperand(LOperand* op) {
++ if (op->IsConstantOperand()) {
++ LConstantOperand* const_op = LConstantOperand::cast(op);
++ HConstant* constant = chunk()->LookupConstant(const_op);
++ Representation r = chunk_->LookupLiteralRepresentation(const_op);
++ if (r.IsInteger32()) {
++ ASSERT(constant->HasInteger32Value());
++ return Operand(constant->Integer32Value());
++ } else if (r.IsDouble()) {
++ Abort("ToOperand Unsupported double immediate.");
++ }
++ ASSERT(r.IsTagged());
++ return Operand(constant->handle());
++ } else if (op->IsRegister()) {
++ return Operand(ToRegister(op));
++ } else if (op->IsDoubleRegister()) {
++ Abort("ToOperand IsDoubleRegister unimplemented");
++ return Operand::Zero();
++ }
++ // Stack slots not implemented, use ToMemOperand instead.
++ UNREACHABLE();
++ return Operand::Zero();
++}
++
++
++MemOperand LCodeGen::ToMemOperand(LOperand* op) const {
++ ASSERT(!op->IsRegister());
++ ASSERT(!op->IsDoubleRegister());
++ ASSERT(op->IsStackSlot() || op->IsDoubleStackSlot());
++ int index = op->index();
++ if (index >= 0) {
++ // Local or spill slot. Skip the frame pointer, function, and
++ // context in the fixed part of the frame.
++ return MemOperand(fp, -(index + 3) * kPointerSize);
++ } else {
++ // Incoming parameter. Skip the return address.
++ return MemOperand(fp, -(index - 1) * kPointerSize);
++ }
++}
++
++
++MemOperand LCodeGen::ToHighMemOperand(LOperand* op) const {
++ ASSERT(op->IsDoubleStackSlot());
++ int index = op->index();
++ if (index >= 0) {
++ // Local or spill slot. Skip the frame pointer, function, context,
++ // and the first word of the double in the fixed part of the frame.
++ return MemOperand(fp, -(index + 3) * kPointerSize + kIntSize);
++ } else {
++ // Incoming parameter. Skip the return address and the first word of
++ // the double.
++ return MemOperand(fp, -(index - 1) * kPointerSize + kIntSize);
++ }
++}
++
++
++void LCodeGen::WriteTranslation(LEnvironment* environment,
++ Translation* translation,
++ int* arguments_index,
++ int* arguments_count) {
++ if (environment == NULL) return;
++
++ // The translation includes one command per value in the environment.
++ int translation_size = environment->values()->length();
++ // The output frame height does not include the parameters.
++ int height = translation_size - environment->parameter_count();
++
++ // Function parameters are arguments to the outermost environment. The
++ // arguments index points to the first element of a sequence of tagged
++ // values on the stack that represent the arguments. This needs to be
++ // kept in sync with the LArgumentsElements implementation.
++ *arguments_index = -environment->parameter_count();
++ *arguments_count = environment->parameter_count();
++
++ WriteTranslation(environment->outer(),
++ translation,
++ arguments_index,
++ arguments_count);
++ int closure_id = *info()->closure() != *environment->closure()
++ ? DefineDeoptimizationLiteral(environment->closure())
++ : Translation::kSelfLiteralId;
++
++ switch (environment->frame_type()) {
++ case JS_FUNCTION:
++ translation->BeginJSFrame(environment->ast_id(), closure_id, height);
++ break;
++ case JS_CONSTRUCT:
++ translation->BeginConstructStubFrame(closure_id, translation_size);
++ break;
++ case JS_GETTER:
++ ASSERT(translation_size == 1);
++ ASSERT(height == 0);
++ translation->BeginGetterStubFrame(closure_id);
++ break;
++ case JS_SETTER:
++ ASSERT(translation_size == 2);
++ ASSERT(height == 0);
++ translation->BeginSetterStubFrame(closure_id);
++ break;
++ case ARGUMENTS_ADAPTOR:
++ translation->BeginArgumentsAdaptorFrame(closure_id, translation_size);
++ break;
++ }
++
++ // Inlined frames which push their arguments cause the index to be
++ // bumped and a new stack area to be used for materialization.
++ if (environment->entry() != NULL &&
++ environment->entry()->arguments_pushed()) {
++ *arguments_index = *arguments_index < 0
++ ? GetStackSlotCount()
++ : *arguments_index + *arguments_count;
++ *arguments_count = environment->entry()->arguments_count() + 1;
++ }
++
++ for (int i = 0; i < translation_size; ++i) {
++ LOperand* value = environment->values()->at(i);
++ // spilled_registers_ and spilled_double_registers_ are either
++ // both NULL or both set.
++ if (environment->spilled_registers() != NULL && value != NULL) {
++ if (value->IsRegister() &&
++ environment->spilled_registers()[value->index()] != NULL) {
++ translation->MarkDuplicate();
++ AddToTranslation(translation,
++ environment->spilled_registers()[value->index()],
++ environment->HasTaggedValueAt(i),
++ environment->HasUint32ValueAt(i),
++ *arguments_index,
++ *arguments_count);
++ } else if (
++ value->IsDoubleRegister() &&
++ environment->spilled_double_registers()[value->index()] != NULL) {
++ translation->MarkDuplicate();
++ AddToTranslation(
++ translation,
++ environment->spilled_double_registers()[value->index()],
++ false,
++ false,
++ *arguments_index,
++ *arguments_count);
++ }
++ }
++
++ AddToTranslation(translation,
++ value,
++ environment->HasTaggedValueAt(i),
++ environment->HasUint32ValueAt(i),
++ *arguments_index,
++ *arguments_count);
++ }
++}
++
++
++void LCodeGen::AddToTranslation(Translation* translation,
++ LOperand* op,
++ bool is_tagged,
++ bool is_uint32,
++ int arguments_index,
++ int arguments_count) {
++ if (op == NULL) {
++ // TODO(twuerthinger): Introduce marker operands to indicate that this value
++ // is not present and must be reconstructed from the deoptimizer. Currently
++ // this is only used for the arguments object.
++ translation->StoreArgumentsObject(arguments_index, arguments_count);
++ } else if (op->IsStackSlot()) {
++ if (is_tagged) {
++ translation->StoreStackSlot(op->index());
++ } else if (is_uint32) {
++ translation->StoreUint32StackSlot(op->index());
++ } else {
++ translation->StoreInt32StackSlot(op->index());
++ }
++ } else if (op->IsDoubleStackSlot()) {
++ translation->StoreDoubleStackSlot(op->index());
++ } else if (op->IsArgument()) {
++ ASSERT(is_tagged);
++ int src_index = GetStackSlotCount() + op->index();
++ translation->StoreStackSlot(src_index);
++ } else if (op->IsRegister()) {
++ Register reg = ToRegister(op);
++ if (is_tagged) {
++ translation->StoreRegister(reg);
++ } else if (is_uint32) {
++ translation->StoreUint32Register(reg);
++ } else {
++ translation->StoreInt32Register(reg);
++ }
++ } else if (op->IsDoubleRegister()) {
++ DoubleRegister reg = ToDoubleRegister(op);
++ translation->StoreDoubleRegister(reg);
++ } else if (op->IsConstantOperand()) {
++ HConstant* constant = chunk()->LookupConstant(LConstantOperand::cast(op));
++ int src_index = DefineDeoptimizationLiteral(constant->handle());
++ translation->StoreLiteral(src_index);
++ } else {
++ UNREACHABLE();
++ }
++}
++
++
++void LCodeGen::CallCode(Handle<Code> code,
++ RelocInfo::Mode mode,
++ LInstruction* instr) {
++ CallCodeGeneric(code, mode, instr, RECORD_SIMPLE_SAFEPOINT);
++}
++
++
++void LCodeGen::CallCodeGeneric(Handle<Code> code,
++ RelocInfo::Mode mode,
++ LInstruction* instr,
++ SafepointMode safepoint_mode) {
++ ASSERT(instr != NULL);
++ LPointerMap* pointers = instr->pointer_map();
++ RecordPosition(pointers->position());
++ __ Call(code, mode);
++ RecordSafepointWithLazyDeopt(instr, safepoint_mode);
++
++ // Signal that we don't inline smi code before these stubs in the
++ // optimizing code generator.
++ if (code->kind() == Code::BINARY_OP_IC ||
++ code->kind() == Code::COMPARE_IC) {
++ __ nop();
++ }
++}
++
++
++void LCodeGen::CallRuntime(const Runtime::Function* function,
++ int num_arguments,
++ LInstruction* instr) {
++ ASSERT(instr != NULL);
++ LPointerMap* pointers = instr->pointer_map();
++ ASSERT(pointers != NULL);
++ RecordPosition(pointers->position());
++
++ __ CallRuntime(function, num_arguments);
++ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT);
++}
++
++
++void LCodeGen::CallRuntimeFromDeferred(Runtime::FunctionId id,
++ int argc,
++ LInstruction* instr) {
++ __ CallRuntimeSaveDoubles(id);
++ RecordSafepointWithRegisters(
++ instr->pointer_map(), argc, Safepoint::kNoLazyDeopt);
++}
++
++
++void LCodeGen::RegisterEnvironmentForDeoptimization(LEnvironment* environment,
++ Safepoint::DeoptMode mode) {
++ if (!environment->HasBeenRegistered()) {
++ // Physical stack frame layout:
++ // -x ............. -4 0 ..................................... y
++ // [incoming arguments] [spill slots] [pushed outgoing arguments]
++
++ // Layout of the environment:
++ // 0 ..................................................... size-1
++ // [parameters] [locals] [expression stack including arguments]
++
++ // Layout of the translation:
++ // 0 ........................................................ size - 1 + 4
++ // [expression stack including arguments] [locals] [4 words] [parameters]
++ // |>------------ translation_size ------------<|
++
++ int frame_count = 0;
++ int jsframe_count = 0;
++ int args_index = 0;
++ int args_count = 0;
++ for (LEnvironment* e = environment; e != NULL; e = e->outer()) {
++ ++frame_count;
++ if (e->frame_type() == JS_FUNCTION) {
++ ++jsframe_count;
++ }
++ }
++ Translation translation(&translations_, frame_count, jsframe_count, zone());
++ WriteTranslation(environment, &translation, &args_index, &args_count);
++ int deoptimization_index = deoptimizations_.length();
++ int pc_offset = masm()->pc_offset();
++ environment->Register(deoptimization_index,
++ translation.index(),
++ (mode == Safepoint::kLazyDeopt) ? pc_offset : -1);
++ deoptimizations_.Add(environment, zone());
++ }
++}
++
++
++void LCodeGen::DeoptimizeIf(Condition cond, LEnvironment* environment,
++ CRegister cr) {
++ RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt);
++ ASSERT(environment->HasBeenRegistered());
++ int id = environment->deoptimization_index();
++ Address entry = Deoptimizer::GetDeoptimizationEntry(id, Deoptimizer::EAGER);
++ if (entry == NULL) {
++ Abort("bailout was not prepared");
++ return;
++ }
++
++ ASSERT(FLAG_deopt_every_n_times < 2); // Other values not supported on PPC.
++
++ if (FLAG_deopt_every_n_times == 1 &&
++ info_->shared_info()->opt_count() == id) {
++ __ Jump(entry, RelocInfo::RUNTIME_ENTRY);
++ return;
++ }
++
++ if (FLAG_trap_on_deopt) __ stop("trap_on_deopt", cond, kDefaultStopCode,
cr);
++
++ if (cond == al) {
++ __ Jump(entry, RelocInfo::RUNTIME_ENTRY);
++ } else {
++ // We often have several deopts to the same entry, reuse the last
++ // jump entry if this is the case.
++ if (deopt_jump_table_.is_empty() ||
++ (deopt_jump_table_.last().address != entry)) {
++ deopt_jump_table_.Add(JumpTableEntry(entry), zone());
++ }
++ __ b(cond, &deopt_jump_table_.last().label, cr);
++ }
++}
++
++
++void LCodeGen::PopulateDeoptimizationData(Handle<Code> code) {
++ int length = deoptimizations_.length();
++ if (length == 0) return;
++ Handle<DeoptimizationInputData> data =
++ factory()->NewDeoptimizationInputData(length, TENURED);
++
++ Handle<ByteArray> translations = translations_.CreateByteArray();
++ data->SetTranslationByteArray(*translations);
++ data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_));
++
++ Handle<FixedArray> literals =
++ factory()->NewFixedArray(deoptimization_literals_.length(), TENURED);
++ for (int i = 0; i < deoptimization_literals_.length(); i++) {
++ literals->set(i, *deoptimization_literals_[i]);
++ }
++ data->SetLiteralArray(*literals);
++
++ data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id().ToInt()));
++ data->SetOsrPcOffset(Smi::FromInt(osr_pc_offset_));
++
++ // Populate the deoptimization entries.
++ for (int i = 0; i < length; i++) {
++ LEnvironment* env = deoptimizations_[i];
++ data->SetAstId(i, env->ast_id());
++ data->SetTranslationIndex(i, Smi::FromInt(env->translation_index()));
++ data->SetArgumentsStackHeight(i,
++ Smi::FromInt(env->arguments_stack_height()));
++ data->SetPc(i, Smi::FromInt(env->pc_offset()));
++ }
++ code->set_deoptimization_data(*data);
++}
++
++
++int LCodeGen::DefineDeoptimizationLiteral(Handle<Object> literal) {
++ int result = deoptimization_literals_.length();
++ for (int i = 0; i < deoptimization_literals_.length(); ++i) {
++ if (deoptimization_literals_[i].is_identical_to(literal)) return i;
++ }
++ deoptimization_literals_.Add(literal, zone());
++ return result;
++}
++
++
++void LCodeGen::PopulateDeoptimizationLiteralsWithInlinedFunctions() {
++ ASSERT(deoptimization_literals_.length() == 0);
++
++ const ZoneList<Handle<JSFunction> >* inlined_closures =
++ chunk()->inlined_closures();
++
++ for (int i = 0, length = inlined_closures->length();
++ i < length;
++ i++) {
++ DefineDeoptimizationLiteral(inlined_closures->at(i));
++ }
++
++ inlined_function_count_ = deoptimization_literals_.length();
++}
++
++
++void LCodeGen::RecordSafepointWithLazyDeopt(
++ LInstruction* instr, SafepointMode safepoint_mode) {
++ if (safepoint_mode == RECORD_SIMPLE_SAFEPOINT) {
++ RecordSafepoint(instr->pointer_map(), Safepoint::kLazyDeopt);
++ } else {
++ ASSERT(safepoint_mode == RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
++ RecordSafepointWithRegisters(
++ instr->pointer_map(), 0, Safepoint::kLazyDeopt);
++ }
++}
++
++
++void LCodeGen::RecordSafepoint(
++ LPointerMap* pointers,
++ Safepoint::Kind kind,
++ int arguments,
++ Safepoint::DeoptMode deopt_mode) {
++ ASSERT(expected_safepoint_kind_ == kind);
++
++ const ZoneList<LOperand*>* operands = pointers->GetNormalizedOperands();
++ Safepoint safepoint = safepoints_.DefineSafepoint(masm(),
++ kind, arguments, deopt_mode);
++ for (int i = 0; i < operands->length(); i++) {
++ LOperand* pointer = operands->at(i);
++ if (pointer->IsStackSlot()) {
++ safepoint.DefinePointerSlot(pointer->index(), zone());
++ } else if (pointer->IsRegister() && (kind &
Safepoint::kWithRegisters)) {
++ safepoint.DefinePointerRegister(ToRegister(pointer), zone());
++ }
++ }
++ if (kind & Safepoint::kWithRegisters) {
++ // Register cp always contains a pointer to the context.
++ safepoint.DefinePointerRegister(cp, zone());
++ }
++}
++
++
++void LCodeGen::RecordSafepoint(LPointerMap* pointers,
++ Safepoint::DeoptMode deopt_mode) {
++ RecordSafepoint(pointers, Safepoint::kSimple, 0, deopt_mode);
++}
++
++
++void LCodeGen::RecordSafepoint(Safepoint::DeoptMode deopt_mode) {
++ LPointerMap empty_pointers(RelocInfo::kNoPosition, zone());
++ RecordSafepoint(&empty_pointers, deopt_mode);
++}
++
++
++void LCodeGen::RecordSafepointWithRegisters(LPointerMap* pointers,
++ int arguments,
++ Safepoint::DeoptMode deopt_mode) {
++ RecordSafepoint(
++ pointers, Safepoint::kWithRegisters, arguments, deopt_mode);
++}
++
++
++void LCodeGen::RecordPosition(int position) {
++ if (position == RelocInfo::kNoPosition) return;
++ masm()->positions_recorder()->RecordPosition(position);
++}
++
++
++void LCodeGen::DoLabel(LLabel* label) {
++ if (label->is_loop_header()) {
++ Comment(";;; B%d - LOOP entry", label->block_id());
++ } else {
++ Comment(";;; B%d", label->block_id());
++ }
++ __ bind(label->label());
++ current_block_ = label->block_id();
++ DoGap(label);
++}
++
++
++void LCodeGen::DoParallelMove(LParallelMove* move) {
++ resolver_.Resolve(move);
++}
++
++
++void LCodeGen::DoGap(LGap* gap) {
++ for (int i = LGap::FIRST_INNER_POSITION;
++ i <= LGap::LAST_INNER_POSITION;
++ i++) {
++ LGap::InnerPosition inner_pos = static_cast<LGap::InnerPosition>(i);
++ LParallelMove* move = gap->GetParallelMove(inner_pos);
++ if (move != NULL) DoParallelMove(move);
++ }
++}
++
++
++void LCodeGen::DoInstructionGap(LInstructionGap* instr) {
++ DoGap(instr);
++}
++
++
++void LCodeGen::DoParameter(LParameter* instr) {
++ // Nothing to do.
++}
++
++
++void LCodeGen::DoCallStub(LCallStub* instr) {
++ ASSERT(ToRegister(instr->result()).is(r3));
++ switch (instr->hydrogen()->major_key()) {
++ case CodeStub::RegExpConstructResult: {
++ RegExpConstructResultStub stub;
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ break;
++ }
++ case CodeStub::RegExpExec: {
++ RegExpExecStub stub;
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ break;
++ }
++ case CodeStub::SubString: {
++ SubStringStub stub;
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ break;
++ }
++ case CodeStub::NumberToString: {
++ NumberToStringStub stub;
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ break;
++ }
++ case CodeStub::StringAdd: {
++ StringAddStub stub(NO_STRING_ADD_FLAGS);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ break;
++ }
++ case CodeStub::StringCompare: {
++ StringCompareStub stub;
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ break;
++ }
++ case CodeStub::TranscendentalCache: {
++ __ LoadP(r3, MemOperand(sp, 0));
++ TranscendentalCacheStub stub(instr->transcendental_type(),
++ TranscendentalCacheStub::TAGGED);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ break;
++ }
++ default:
++ UNREACHABLE();
++ }
++}
++
++
++void LCodeGen::DoUnknownOSRValue(LUnknownOSRValue* instr) {
++ // Nothing to do.
++}
++
++
++void LCodeGen::DoModI(LModI* instr) {
++ Register dividend = ToRegister(instr->left());
++ Register result = ToRegister(instr->result());
++ Register scratch = scratch0();
++ Label done;
++
++ if (instr->hydrogen()->HasPowerOf2Divisor()) {
++ int32_t divisor =
++ HConstant::cast(instr->hydrogen()->right())->Integer32Value();
++
++ if (divisor < 0) divisor = -divisor;
++
++ Label positive_dividend;
++ __ cmpi(dividend, Operand::Zero());
++ __ bge(&positive_dividend);
++ __ neg(result, dividend);
++ __ mov(scratch, Operand(divisor - 1));
++ __ and_(result, result, scratch, SetRC);
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ }
++ __ neg(result, result);
++ __ b(&done);
++ __ bind(&positive_dividend);
++ __ mov(scratch, Operand(divisor - 1));
++ __ and_(result, dividend, scratch);
++ } else {
++ Register divisor = ToRegister(instr->right());
++
++ __ divw(scratch, dividend, divisor);
++
++ // Check for x % 0.
++ if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
++ __ cmpi(divisor, Operand::Zero());
++ DeoptimizeIf(eq, instr->environment());
++ }
++
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(scratch, scratch);
++#endif
++ __ Mul(scratch, divisor, scratch);
++ __ sub(result, dividend, scratch, LeaveOE, SetRC);
++
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ __ cmpi(dividend, Operand::Zero());
++ __ bge(&done);
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ }
++ }
++
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoDivI(LDivI* instr) {
++ const Register left = ToRegister(instr->left());
++ const Register right = ToRegister(instr->right());
++ const Register scratch = scratch0();
++ const Register result = ToRegister(instr->result());
++
++ __ divw(result, left, right);
++
++ // Check for x / 0.
++ if (instr->hydrogen()->CheckFlag(HValue::kCanBeDivByZero)) {
++ __ cmpi(right, Operand::Zero());
++ DeoptimizeIf(eq, instr->environment());
++ }
++
++ // Check for (0 / -x) that will produce negative zero.
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ Label left_not_zero;
++ __ cmpi(left, Operand::Zero());
++ __ bne(&left_not_zero);
++ __ cmpi(right, Operand::Zero());
++ DeoptimizeIf(lt, instr->environment());
++ __ bind(&left_not_zero);
++ }
++
++ // Check for (-kMinInt / -1).
++ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
++ Label left_not_min_int;
++ __ Cmpi(left, Operand(kMinInt), r0);
++ __ bne(&left_not_min_int);
++ __ cmpi(right, Operand(-1));
++ DeoptimizeIf(eq, instr->environment());
++ __ bind(&left_not_min_int);
++ }
++
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(result, result);
++#endif
++
++ // Deoptimize on non-zero remainder
++ __ Mul(scratch, right, result);
++ __ cmp(left, scratch);
++ DeoptimizeIf(ne, instr->environment());
++}
++
++
++void LCodeGen::DoMathFloorOfDiv(LMathFloorOfDiv* instr) {
++ ASSERT(instr->right()->IsConstantOperand());
++
++ const Register dividend = ToRegister(instr->left());
++ int32_t divisor = ToInteger32(LConstantOperand::cast(instr->right()));
++ const Register result = ToRegister(instr->result());
++
++ switch (divisor) {
++ case 0:
++ DeoptimizeIf(al, instr->environment());
++ return;
++
++ case 1:
++ __ Move(result, dividend);
++ return;
++
++ case -1: {
++ OEBit oe;
++ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
++ __ li(r0, Operand::Zero()); // clear xer
++ __ mtxer(r0);
++ oe = SetOE;
++ } else {
++ oe = LeaveOE;
++ }
++ __ neg(result, dividend, oe, SetRC);
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ }
++ if (instr->hydrogen()->CheckFlag(HValue::kCanOverflow)) {
++ DeoptimizeIf(overflow, instr->environment(), cr0);
++ }
++ return;
++ }
++ }
++
++ uint32_t divisor_abs = abs(divisor);
++ if (IsPowerOf2(divisor_abs)) {
++ int32_t power = WhichPowerOf2(divisor_abs);
++ if (divisor < 0) {
++ __ neg(result, dividend, LeaveOE, SetRC);
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ }
++ __ ShiftRightArithImm(result, result, power);
++ } else {
++ __ ShiftRightArithImm(result, dividend, power);
++ }
++ } else {
++ Register scratch = ToRegister(instr->temp());
++
++ // Find b which: 2^b < divisor_abs < 2^(b+1).
++ unsigned b = 31 - CompilerIntrinsics::CountLeadingZeros(divisor_abs);
++ unsigned shift = 32 + b; // Precision +1bit (effectively).
++ double multiplier_f =
++ static_cast<double>(static_cast<uint64_t>(1) << shift) /
divisor_abs;
++ int64_t multiplier;
++ if (multiplier_f - floor(multiplier_f) < 0.5) {
++ multiplier = static_cast<int64_t>(floor(multiplier_f));
++ } else {
++ multiplier = static_cast<int64_t>(floor(multiplier_f)) + 1;
++ }
++ // The multiplier is a uint32.
++ ASSERT(multiplier > 0 &&
++ multiplier < (static_cast<int64_t>(1) << 32));
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(scratch, dividend);
++ if (divisor < 0 &&
++ instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ __ neg(scratch, scratch, LeaveOE, SetRC);
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ }
++ __ mov(result, Operand(multiplier));
++ __ Mul(result, result, scratch);
++ __ addis(result, result, Operand(0x4000));
++ __ ShiftRightArithImm(result, result, shift);
++#else
++ if (divisor < 0 &&
++ instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ __ cmpi(dividend, Operand::Zero());
++ DeoptimizeIf(eq, instr->environment());
++ }
++ __ mov(result, Operand(multiplier));
++ __ mullw(ip, result, dividend);
++ __ mulhw(result, result, dividend);
++
++ if (static_cast<int32_t>(multiplier) < 0) {
++ __ add(result, result, dividend);
++ }
++ if (divisor < 0) {
++ __ neg(result, result);
++
++ // Subtract one from result if -(low word) < 0xC0000000
++ __ neg(ip, ip);
++ __ srwi(scratch, ip, Operand(30));
++ __ addi(scratch, scratch, Operand(1));
++ __ srwi(scratch, scratch, Operand(2));
++ __ addi(scratch, scratch, Operand(-1));
++ __ add(result, result, scratch);
++ } else {
++ // Add one to result if low word >= 0xC0000000
++ __ srwi(scratch, ip, Operand(30));
++ __ addi(scratch, scratch, Operand(1));
++ __ srwi(scratch, scratch, Operand(2));
++ __ add(result, result, scratch);
++ }
++ __ ShiftRightArithImm(result, result, shift - 32);
++#endif
++ }
++}
++
++
++void LCodeGen::DoMulI(LMulI* instr) {
++ Register scratch = scratch0();
++ Register result = ToRegister(instr->result());
++ // Note that result may alias left.
++ Register left = ToRegister(instr->left());
++ LOperand* right_op = instr->right();
++
++ bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
++ bool bailout_on_minus_zero =
++ instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero);
++
++ if (right_op->IsConstantOperand() && !can_overflow) {
++ // Use optimized code for specific constants.
++ int32_t constant = ToInteger32(LConstantOperand::cast(right_op));
++
++ if (bailout_on_minus_zero && (constant < 0)) {
++ // The case of a null constant will be handled separately.
++ // If constant is negative and left is null, the result should be -0.
++ __ cmpi(left, Operand::Zero());
++ DeoptimizeIf(eq, instr->environment());
++ }
++
++ switch (constant) {
++ case -1:
++ __ neg(result, left);
++ break;
++ case 0:
++ if (bailout_on_minus_zero) {
++ // If left is strictly negative and the constant is null, the
++ // result is -0. Deoptimize if required, otherwise return 0.
++ __ cmpi(left, Operand::Zero());
++ DeoptimizeIf(lt, instr->environment());
++ }
++ __ li(result, Operand::Zero());
++ break;
++ case 1:
++ __ Move(result, left);
++ break;
++ default:
++ // Multiplying by powers of two and powers of two plus or minus
++ // one can be done faster with shifted operands.
++ // For other constants we emit standard code.
++ int32_t mask = constant >> 31;
++ uint32_t constant_abs = (constant + mask) ^ mask;
++
++ if (IsPowerOf2(constant_abs) ||
++ IsPowerOf2(constant_abs - 1) ||
++ IsPowerOf2(constant_abs + 1)) {
++ if (IsPowerOf2(constant_abs)) {
++ int32_t shift = WhichPowerOf2(constant_abs);
++ __ ShiftLeftImm(result, left, Operand(shift));
++ } else if (IsPowerOf2(constant_abs - 1)) {
++ int32_t shift = WhichPowerOf2(constant_abs - 1);
++ __ ShiftLeftImm(scratch, left, Operand(shift));
++ __ add(result, scratch, left);
++ } else if (IsPowerOf2(constant_abs + 1)) {
++ int32_t shift = WhichPowerOf2(constant_abs + 1);
++ __ ShiftLeftImm(scratch, left, Operand(shift));
++ __ sub(result, scratch, left);
++ }
++
++ // Correct the sign of the result is the constant is negative.
++ if (constant < 0) __ neg(result, result);
++
++ } else {
++ // Generate standard code.
++ __ mov(ip, Operand(constant));
++ __ Mul(result, left, ip);
++ }
++ }
++
++ } else {
++ Register right = EmitLoadRegister(right_op, ip);
++ if (bailout_on_minus_zero) {
++ __ orx(ToRegister(instr->temp()), left, right);
++ }
++
++ if (can_overflow) {
++ // scratch:result = left * right.
++#if V8_TARGET_ARCH_PPC64
++ __ Mul(result, left, right);
++ __ TestIfInt32(result, scratch, r0);
++ DeoptimizeIf(ne, instr->environment());
++#else
++ __ mulhw(scratch, left, right);
++ __ mullw(result, left, right);
++ __ TestIfInt32(scratch, result, r0);
++ DeoptimizeIf(ne, instr->environment());
++#endif
++ } else {
++ __ Mul(result, left, right);
++ }
++
++ if (bailout_on_minus_zero) {
++ // Bail out if the result is supposed to be negative zero.
++ Label done;
++ __ cmpi(result, Operand::Zero());
++ __ bne(&done);
++ __ cmpi(ToRegister(instr->temp()), Operand::Zero());
++ DeoptimizeIf(lt, instr->environment());
++ __ bind(&done);
++ }
++ }
++}
++
++
++void LCodeGen::DoBitI(LBitI* instr) {
++ LOperand* left_op = instr->left();
++ LOperand* right_op = instr->right();
++ ASSERT(left_op->IsRegister());
++ Register left = ToRegister(left_op);
++ Register result = ToRegister(instr->result());
++ Operand right(no_reg);
++
++ if (right_op->IsStackSlot() || right_op->IsArgument()) {
++ right = Operand(EmitLoadRegister(right_op, ip));
++ } else {
++ ASSERT(right_op->IsRegister() || right_op->IsConstantOperand());
++
++ if (right_op->IsConstantOperand() &&
++ is_uint16(ToInteger32(LConstantOperand::cast(right_op)))) {
++ switch (instr->op()) {
++ case Token::BIT_AND:
++ __ andi(result, left,
++ Operand(ToInteger32(LConstantOperand::cast(right_op))));
++ break;
++ case Token::BIT_OR:
++ __ ori(result, left,
++ Operand(ToInteger32(LConstantOperand::cast(right_op))));
++ break;
++ case Token::BIT_XOR:
++ __ xori(result, left,
++ Operand(ToInteger32(LConstantOperand::cast(right_op))));
++ break;
++ default:
++ UNREACHABLE();
++ break;
++ }
++ return;
++ }
++ right = ToOperand(right_op);
++ }
++
++ switch (instr->op()) {
++ case Token::BIT_AND:
++ __ And(result, left, right);
++ break;
++ case Token::BIT_OR:
++ __ Or(result, left, right);
++ break;
++ case Token::BIT_XOR:
++ __ Xor(result, left, right);
++ break;
++ default:
++ UNREACHABLE();
++ break;
++ }
++}
++
++
++void LCodeGen::DoShiftI(LShiftI* instr) {
++ // Both 'left' and 'right' are "used at start" (see
LCodeGen::DoShift), so
++ // result may alias either of them.
++ LOperand* right_op = instr->right();
++ Register left = ToRegister(instr->left());
++ Register result = ToRegister(instr->result());
++ Register scratch = scratch0();
++ if (right_op->IsRegister()) {
++ // Mask the right_op operand.
++ __ andi(scratch, ToRegister(right_op), Operand(0x1F));
++ switch (instr->op()) {
++ case Token::SAR:
++ __ sraw(result, left, scratch);
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(result, result);
++#endif
++ break;
++ case Token::SHR:
++ if (instr->can_deopt()) {
++ __ srw(result, left, scratch, SetRC);
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(result, result, SetRC);
++#endif
++ DeoptimizeIf(lt, instr->environment(), cr0);
++ } else {
++ __ srw(result, left, scratch);
++ }
++ break;
++ case Token::SHL:
++ __ slw(result, left, scratch);
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(result, result);
++#endif
++ break;
++ default:
++ UNREACHABLE();
++ break;
++ }
++ } else {
++ // Mask the right_op operand.
++ int value = ToInteger32(LConstantOperand::cast(right_op));
++ uint8_t shift_count = static_cast<uint8_t>(value & 0x1F);
++ switch (instr->op()) {
++ case Token::SAR:
++ if (shift_count != 0) {
++ __ srawi(result, left, shift_count);
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(result, result);
++#endif
++ } else {
++ __ Move(result, left);
++ }
++ break;
++ case Token::SHR:
++ if (shift_count != 0) {
++ __ srwi(result, left, Operand(shift_count));
++ } else {
++ if (instr->can_deopt()) {
++ __ TestSignBit32(left, r0);
++ DeoptimizeIf(ne, instr->environment(), cr0);
++ }
++ __ Move(result, left);
++ }
++ break;
++ case Token::SHL:
++ if (shift_count != 0) {
++ __ slwi(result, left, Operand(shift_count));
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(result, result);
++#endif
++ } else {
++ __ Move(result, left);
++ }
++ break;
++ default:
++ UNREACHABLE();
++ break;
++ }
++ }
++}
++
++
++void LCodeGen::DoSubI(LSubI* instr) {
++ LOperand* left = instr->left();
++ LOperand* right = instr->right();
++ LOperand* result = instr->result();
++ bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
++ if (!can_overflow && right->IsConstantOperand()) {
++ if (is_int16(ToInteger32(LConstantOperand::cast(right)))) {
++ __ subi(ToRegister(result), ToRegister(left),
++ Operand(ToInteger32(LConstantOperand::cast(right))));
++ return;
++ }
++ }
++ Register right_reg = EmitLoadRegister(right, ip);
++
++ if (!can_overflow) {
++ __ sub(ToRegister(result), ToRegister(left), right_reg);
++ } else {
++ __ SubAndCheckForOverflow(ToRegister(result),
++ ToRegister(left),
++ right_reg,
++ scratch0(), r0);
++ // Doptimize on overflow
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(scratch0(), scratch0(), SetRC);
++#endif
++ DeoptimizeIf(lt, instr->environment(), cr0);
++ }
++}
++
++
++void LCodeGen::DoConstantI(LConstantI* instr) {
++ ASSERT(instr->result()->IsRegister());
++ __ mov(ToRegister(instr->result()), Operand(instr->value()));
++}
++
++// TODO(penguin): put const to constant pool instead
++// of storing double to stack
++void LCodeGen::DoConstantD(LConstantD* instr) {
++ ASSERT(instr->result()->IsDoubleRegister());
++ DwVfpRegister result = ToDoubleRegister(instr->result());
++ double v = instr->value();
++ __ LoadDoubleLiteral(result, v, scratch0());
++}
++
++void LCodeGen::DoConstantT(LConstantT* instr) {
++ Handle<Object> value = instr->value();
++ if (value->IsSmi()) {
++ __ mov(ToRegister(instr->result()), Operand(value));
++ } else {
++ __ LoadHeapObject(ToRegister(instr->result()),
++ Handle<HeapObject>::cast(value));
++ }
++}
++
++
++void LCodeGen::DoJSArrayLength(LJSArrayLength* instr) {
++ Register result = ToRegister(instr->result());
++ Register array = ToRegister(instr->value());
++ __ LoadP(result, FieldMemOperand(array, JSArray::kLengthOffset));
++}
++
++
++void LCodeGen::DoFixedArrayBaseLength(LFixedArrayBaseLength* instr) {
++ Register result = ToRegister(instr->result());
++ Register array = ToRegister(instr->value());
++ __ LoadP(result, FieldMemOperand(array, FixedArrayBase::kLengthOffset));
++}
++
++
++void LCodeGen::DoMapEnumLength(LMapEnumLength* instr) {
++ Register result = ToRegister(instr->result());
++ Register map = ToRegister(instr->value());
++ __ EnumLength(result, map);
++}
++
++
++void LCodeGen::DoElementsKind(LElementsKind* instr) {
++ Register result = ToRegister(instr->result());
++ Register input = ToRegister(instr->value());
++
++ // Load map into |result|.
++ __ LoadP(result, FieldMemOperand(input, HeapObject::kMapOffset));
++ // Load the map's "bit field 2" into |result|
++ __ lbz(result, FieldMemOperand(result, Map::kBitField2Offset));
++ // Retrieve elements_kind from bit field 2.
++ __ ExtractBitMask(result, result, Map::kElementsKindMask);
++}
++
++
++void LCodeGen::DoValueOf(LValueOf* instr) {
++ Register input = ToRegister(instr->value());
++ Register result = ToRegister(instr->result());
++ Register map = ToRegister(instr->temp());
++ Label done, is_smi_or_object;
++
++ // If the object is a smi return the object.
++ __ JumpIfSmi(input, &is_smi_or_object);
++
++ // If the object is not a value type, return the object.
++ __ CompareObjectType(input, map, map, JS_VALUE_TYPE);
++ __ bne(&is_smi_or_object);
++
++ __ LoadP(result, FieldMemOperand(input, JSValue::kValueOffset));
++ __ b(&done);
++
++ __ bind(&is_smi_or_object);
++ __ Move(result, input);
++
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoDateField(LDateField* instr) {
++ Register object = ToRegister(instr->date());
++ Register result = ToRegister(instr->result());
++ Register scratch = ToRegister(instr->temp());
++ Smi* index = instr->index();
++ Label runtime, done;
++ ASSERT(object.is(result));
++ ASSERT(object.is(r3));
++ ASSERT(!scratch.is(scratch0()));
++ ASSERT(!scratch.is(object));
++
++ __ TestIfSmi(object, r0);
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ __ CompareObjectType(object, scratch, scratch, JS_DATE_TYPE);
++ DeoptimizeIf(ne, instr->environment());
++
++ if (index->value() == 0) {
++ __ LoadP(result, FieldMemOperand(object, JSDate::kValueOffset));
++ } else {
++ if (index->value() < JSDate::kFirstUncachedField) {
++ ExternalReference stamp = ExternalReference::date_cache_stamp(isolate());
++ __ mov(scratch, Operand(stamp));
++ __ LoadP(scratch, MemOperand(scratch));
++ __ LoadP(scratch0(), FieldMemOperand(object, JSDate::kCacheStampOffset));
++ __ cmp(scratch, scratch0());
++ __ bne(&runtime);
++ __ LoadP(result, FieldMemOperand(object, JSDate::kValueOffset +
++ kPointerSize * index->value()));
++ __ b(&done);
++ }
++ __ bind(&runtime);
++ __ PrepareCallCFunction(2, scratch);
++ __ LoadSmiLiteral(r4, index);
++ __ CallCFunction(ExternalReference::get_date_field_function(isolate()), 2);
++ __ bind(&done);
++ }
++}
++
++
++void LCodeGen::DoBitNotI(LBitNotI* instr) {
++ Register input = ToRegister(instr->value());
++ Register result = ToRegister(instr->result());
++ __ notx(result, input);
++}
++
++
++void LCodeGen::DoThrow(LThrow* instr) {
++ Register input_reg = EmitLoadRegister(instr->value(), ip);
++ __ push(input_reg);
++ CallRuntime(Runtime::kThrow, 1, instr);
++
++ if (FLAG_debug_code) {
++ __ stop("Unreachable code.");
++ }
++}
++
++
++void LCodeGen::DoAddI(LAddI* instr) {
++ LOperand* left = instr->left();
++ LOperand* right = instr->right();
++ LOperand* result = instr->result();
++ bool can_overflow = instr->hydrogen()->CheckFlag(HValue::kCanOverflow);
++
++ if (!can_overflow && right->IsConstantOperand()) {
++ if (is_int16(ToInteger32(LConstantOperand::cast(right)))) {
++ __ addi(ToRegister(result), ToRegister(left),
++ Operand(ToInteger32(LConstantOperand::cast(right))));
++ return;
++ }
++ }
++
++ if (!can_overflow) {
++ Register right_reg = EmitLoadRegister(right, ip);
++ __ add(ToRegister(result), ToRegister(left), right_reg);
++ } else { // can_overflow.
++ Register right_reg = EmitLoadRegister(right, ip);
++ __ AddAndCheckForOverflow(ToRegister(result),
++ ToRegister(left),
++ right_reg,
++ scratch0(), r0);
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(scratch0(), scratch0(), SetRC);
++#endif
++ // Doptimize on overflow
++ DeoptimizeIf(lt, instr->environment(), cr0);
++ }
++}
++
++
++void LCodeGen::DoMathMinMax(LMathMinMax* instr) {
++ LOperand* left = instr->left();
++ LOperand* right = instr->right();
++ HMathMinMax::Operation operation = instr->hydrogen()->operation();
++ Condition cond = (operation == HMathMinMax::kMathMin) ? le : ge;
++ if (instr->hydrogen()->representation().IsInteger32()) {
++ Register left_reg = ToRegister(left);
++ Register right_reg = EmitLoadRegister(right, ip);
++ Register result_reg = ToRegister(instr->result());
++ Label return_left, done;
++ __ cmp(left_reg, right_reg);
++ __ b(cond, &return_left);
++ __ Move(result_reg, right_reg);
++ __ b(&done);
++ __ bind(&return_left);
++ __ Move(result_reg, left_reg);
++ __ bind(&done);
++ } else {
++ ASSERT(instr->hydrogen()->representation().IsDouble());
++ DoubleRegister left_reg = ToDoubleRegister(left);
++ DoubleRegister right_reg = ToDoubleRegister(right);
++ DoubleRegister result_reg = ToDoubleRegister(instr->result());
++ Label check_nan_left, check_zero, return_left, return_right, done;
++ __ fcmpu(left_reg, right_reg);
++ __ bunordered(&check_nan_left);
++ __ beq(&check_zero);
++ __ b(cond, &return_left);
++ __ b(&return_right);
++
++ __ bind(&check_zero);
++ __ fcmpu(left_reg, kDoubleRegZero);
++ __ bne(&return_left); // left == right != 0.
++
++ // At this point, both left and right are either 0 or -0.
++ // N.B. The following works because +0 + -0 == +0
++ if (operation == HMathMinMax::kMathMin) {
++ // For min we want logical-or of sign bit: -(-L + -R)
++ __ fneg(left_reg, left_reg);
++ __ fsub(result_reg, left_reg, right_reg);
++ __ fneg(result_reg, result_reg);
++ } else {
++ // For max we want logical-and of sign bit: (L + R)
++ __ fadd(result_reg, left_reg, right_reg);
++ }
++ __ b(&done);
++
++ __ bind(&check_nan_left);
++ __ fcmpu(left_reg, left_reg);
++ __ bunordered(&return_left); // left == NaN.
++
++ __ bind(&return_right);
++ if (!right_reg.is(result_reg)) {
++ __ fmr(result_reg, right_reg);
++ }
++ __ b(&done);
++
++ __ bind(&return_left);
++ if (!left_reg.is(result_reg)) {
++ __ fmr(result_reg, left_reg);
++ }
++ __ bind(&done);
++ }
++}
++
++
++void LCodeGen::DoArithmeticD(LArithmeticD* instr) {
++ DoubleRegister left = ToDoubleRegister(instr->left());
++ DoubleRegister right = ToDoubleRegister(instr->right());
++ DoubleRegister result = ToDoubleRegister(instr->result());
++ switch (instr->op()) {
++ case Token::ADD:
++ __ fadd(result, left, right);
++ break;
++ case Token::SUB:
++ __ fsub(result, left, right);
++ break;
++ case Token::MUL:
++ __ fmul(result, left, right);
++ break;
++ case Token::DIV:
++ __ fdiv(result, left, right);
++ break;
++ case Token::MOD: {
++ // Save r3-r6 on the stack.
++ __ MultiPush(r3.bit() | r4.bit() | r5.bit() | r6.bit());
++
++ __ PrepareCallCFunction(0, 2, scratch0());
++ __ SetCallCDoubleArguments(left, right);
++ __ CallCFunction(
++ ExternalReference::double_fp_operation(Token::MOD, isolate()),
++ 0, 2);
++ // Move the result in the double result register.
++ __ GetCFunctionDoubleResult(result);
++
++ // Restore r3-r6.
++ __ MultiPop(r3.bit() | r4.bit() | r5.bit() | r6.bit());
++ break;
++ }
++ default:
++ UNREACHABLE();
++ break;
++ }
++}
++
++
++void LCodeGen::DoArithmeticT(LArithmeticT* instr) {
++ ASSERT(ToRegister(instr->left()).is(r4));
++ ASSERT(ToRegister(instr->right()).is(r3));
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ BinaryOpStub stub(instr->op(), NO_OVERWRITE);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ __ nop(); // Signals no inlined code.
++}
++
++
++int LCodeGen::GetNextEmittedBlock(int block) {
++ for (int i = block + 1; i < graph()->blocks()->length(); ++i) {
++ LLabel* label = chunk_->GetLabel(i);
++ if (!label->HasReplacement()) return i;
++ }
++ return -1;
++}
++
++
++void LCodeGen::EmitBranch(int left_block, int right_block, Condition cond,
++ CRegister cr) {
++ int next_block = GetNextEmittedBlock(current_block_);
++ right_block = chunk_->LookupDestination(right_block);
++ left_block = chunk_->LookupDestination(left_block);
++
++ if (right_block == left_block) {
++ EmitGoto(left_block);
++ } else if (left_block == next_block) {
++ __ b(NegateCondition(cond), chunk_->GetAssemblyLabel(right_block), cr);
++ } else if (right_block == next_block) {
++ __ b(cond, chunk_->GetAssemblyLabel(left_block), cr);
++ } else {
++ __ b(cond, chunk_->GetAssemblyLabel(left_block), cr);
++ __ b(chunk_->GetAssemblyLabel(right_block));
++ }
++}
++
++
++void LCodeGen::DoBranch(LBranch* instr) {
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ Representation r = instr->hydrogen()->value()->representation();
++ if (r.IsInteger32()) {
++ Register reg = ToRegister(instr->value());
++ __ cmpi(reg, Operand::Zero());
++ EmitBranch(true_block, false_block, ne);
++ } else if (r.IsDouble()) {
++ DoubleRegister reg = ToDoubleRegister(instr->value());
++ Register scratch = scratch0();
++
++ // Test the double value. Zero and NaN are false.
++ uint crBits = (1 << (31 - Assembler::encode_crbit(cr7, CR_EQ)) |
++ 1 << (31 - Assembler::encode_crbit(cr7, CR_FU)));
++ __ fcmpu(reg, kDoubleRegZero, cr7);
++ __ mfcr(scratch);
++ __ andi(scratch, scratch, Operand(crBits));
++ EmitBranch(true_block, false_block, eq, cr0);
++ } else {
++ ASSERT(r.IsTagged());
++ Register reg = ToRegister(instr->value());
++ HType type = instr->hydrogen()->value()->type();
++ if (type.IsBoolean()) {
++ __ CompareRoot(reg, Heap::kTrueValueRootIndex);
++ EmitBranch(true_block, false_block, eq);
++ } else if (type.IsSmi()) {
++ __ cmpi(reg, Operand::Zero());
++ EmitBranch(true_block, false_block, ne);
++ } else {
++ Label* true_label = chunk_->GetAssemblyLabel(true_block);
++ Label* false_label = chunk_->GetAssemblyLabel(false_block);
++
++ ToBooleanStub::Types expected = instr->hydrogen()->expected_input_types();
++ // Avoid deopts in the case where we've never executed this path before.
++ if (expected.IsEmpty()) expected = ToBooleanStub::all_types();
++
++ if (expected.Contains(ToBooleanStub::UNDEFINED)) {
++ // undefined -> false.
++ __ CompareRoot(reg, Heap::kUndefinedValueRootIndex);
++ __ beq(false_label);
++ }
++ if (expected.Contains(ToBooleanStub::BOOLEAN)) {
++ // Boolean -> its value.
++ __ CompareRoot(reg, Heap::kTrueValueRootIndex);
++ __ beq(true_label);
++ __ CompareRoot(reg, Heap::kFalseValueRootIndex);
++ __ beq(false_label);
++ }
++ if (expected.Contains(ToBooleanStub::NULL_TYPE)) {
++ // 'null' -> false.
++ __ CompareRoot(reg, Heap::kNullValueRootIndex);
++ __ beq(false_label);
++ }
++
++ if (expected.Contains(ToBooleanStub::SMI)) {
++ // Smis: 0 -> false, all other -> true.
++ __ cmpi(reg, Operand::Zero());
++ __ beq(false_label);
++ __ JumpIfSmi(reg, true_label);
++ } else if (expected.NeedsMap()) {
++ // If we need a map later and have a Smi -> deopt.
++ __ TestIfSmi(reg, r0);
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ }
++
++ const Register map = scratch0();
++ if (expected.NeedsMap()) {
++ __ LoadP(map, FieldMemOperand(reg, HeapObject::kMapOffset));
++
++ if (expected.CanBeUndetectable()) {
++ // Undetectable -> false.
++ __ lbz(ip, FieldMemOperand(map, Map::kBitFieldOffset));
++ __ TestBit(ip, Map::kIsUndetectable, r0);
++ __ bne(false_label, cr0);
++ }
++ }
++
++ if (expected.Contains(ToBooleanStub::SPEC_OBJECT)) {
++ // spec object -> true.
++ __ CompareInstanceType(map, ip, FIRST_SPEC_OBJECT_TYPE);
++ __ bge(true_label);
++ }
++
++ if (expected.Contains(ToBooleanStub::STRING)) {
++ // String value -> false iff empty.
++ Label not_string;
++ __ CompareInstanceType(map, ip, FIRST_NONSTRING_TYPE);
++ __ bge(¬_string);
++ __ LoadP(ip, FieldMemOperand(reg, String::kLengthOffset));
++ __ cmpi(ip, Operand::Zero());
++ __ bne(true_label);
++ __ b(false_label);
++ __ bind(¬_string);
++ }
++
++ if (expected.Contains(ToBooleanStub::HEAP_NUMBER)) {
++ // heap number -> false iff +0, -0, or NaN.
++ DoubleRegister dbl_scratch = double_scratch0();
++ Label not_heap_number;
++ __ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
++ __ bne(¬_heap_number);
++ __ lfd(dbl_scratch, FieldMemOperand(reg, HeapNumber::kValueOffset));
++ __ fcmpu(dbl_scratch, kDoubleRegZero);
++ __ bunordered(false_label); // NaN -> false.
++ __ beq(false_label); // +0, -0 -> false.
++ __ b(true_label);
++ __ bind(¬_heap_number);
++ }
++
++ // We've seen something for the first time -> deopt.
++ DeoptimizeIf(al, instr->environment());
++ }
++ }
++}
++
++
++void LCodeGen::EmitGoto(int block) {
++ block = chunk_->LookupDestination(block);
++ int next_block = GetNextEmittedBlock(current_block_);
++ if (block != next_block) {
++ __ b(chunk_->GetAssemblyLabel(block));
++ }
++}
++
++
++void LCodeGen::DoGoto(LGoto* instr) {
++ EmitGoto(instr->block_id());
++}
++
++
++Condition LCodeGen::TokenToCondition(Token::Value op) {
++ Condition cond = kNoCondition;
++ switch (op) {
++ case Token::EQ:
++ case Token::EQ_STRICT:
++ cond = eq;
++ break;
++ case Token::LT:
++ cond = lt;
++ break;
++ case Token::GT:
++ cond = gt;
++ break;
++ case Token::LTE:
++ cond = le;
++ break;
++ case Token::GTE:
++ cond = ge;
++ break;
++ case Token::IN:
++ case Token::INSTANCEOF:
++ default:
++ UNREACHABLE();
++ }
++ return cond;
++}
++
++
++void LCodeGen::DoCmpIDAndBranch(LCmpIDAndBranch* instr) {
++ LOperand* left = instr->left();
++ LOperand* right = instr->right();
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ Condition cond = TokenToCondition(instr->op());
++
++ if (left->IsConstantOperand() && right->IsConstantOperand()) {
++ // We can statically evaluate the comparison.
++ double left_val = ToDouble(LConstantOperand::cast(left));
++ double right_val = ToDouble(LConstantOperand::cast(right));
++ int next_block =
++ EvalComparison(instr->op(), left_val, right_val) ? true_block
++ : false_block;
++ EmitGoto(next_block);
++ } else {
++ if (instr->is_double()) {
++ // Compare left and right operands as doubles and load the
++ // resulting flags into the normal status register.
++ __ fcmpu(ToDoubleRegister(left), ToDoubleRegister(right));
++ // If a NaN is involved, i.e. the result is unordered,
++ // jump to false block label.
++ __ bunordered(chunk_->GetAssemblyLabel(false_block));
++ } else {
++ if (right->IsConstantOperand()) {
++ __ Cmpi(ToRegister(left),
++ Operand(ToInteger32(LConstantOperand::cast(right))), r0);
++ } else if (left->IsConstantOperand()) {
++ __ Cmpi(ToRegister(right),
++ Operand(ToInteger32(LConstantOperand::cast(left))), r0);
++ // We transposed the operands. Reverse the condition.
++ cond = ReverseCondition(cond);
++ } else {
++ __ cmp(ToRegister(left), ToRegister(right));
++ }
++ }
++ EmitBranch(true_block, false_block, cond);
++ }
++}
++
++
++void LCodeGen::DoCmpObjectEqAndBranch(LCmpObjectEqAndBranch* instr) {
++ Register left = ToRegister(instr->left());
++ Register right = ToRegister(instr->right());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++
++ __ cmp(left, right);
++ EmitBranch(true_block, false_block, eq);
++}
++
++
++void LCodeGen::DoCmpConstantEqAndBranch(LCmpConstantEqAndBranch* instr) {
++ Register left = ToRegister(instr->left());
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ __ Cmpi(left, Operand(instr->hydrogen()->right()), r0);
++ EmitBranch(true_block, false_block, eq);
++}
++
++
++void LCodeGen::DoIsNilAndBranch(LIsNilAndBranch* instr) {
++ Register scratch = scratch0();
++ Register reg = ToRegister(instr->value());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ // If the expression is known to be untagged or a smi, then it's definitely
++ // not null, and it can't be a an undetectable object.
++ if (instr->hydrogen()->representation().IsSpecialization() ||
++ instr->hydrogen()->type().IsSmi()) {
++ EmitGoto(false_block);
++ return;
++ }
++
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ Heap::RootListIndex nil_value = instr->nil() == kNullValue ?
++ Heap::kNullValueRootIndex :
++ Heap::kUndefinedValueRootIndex;
++ __ LoadRoot(ip, nil_value);
++ __ cmp(reg, ip);
++ if (instr->kind() == kStrictEquality) {
++ EmitBranch(true_block, false_block, eq);
++ } else {
++ Heap::RootListIndex other_nil_value = instr->nil() == kNullValue ?
++ Heap::kUndefinedValueRootIndex :
++ Heap::kNullValueRootIndex;
++ Label* true_label = chunk_->GetAssemblyLabel(true_block);
++ Label* false_label = chunk_->GetAssemblyLabel(false_block);
++ __ beq(true_label);
++ __ LoadRoot(ip, other_nil_value);
++ __ cmp(reg, ip);
++ __ beq(true_label);
++ __ JumpIfSmi(reg, false_label);
++ // Check for undetectable objects by looking in the bit field in
++ // the map. The object has already been smi checked.
++ __ LoadP(scratch, FieldMemOperand(reg, HeapObject::kMapOffset));
++ __ lbz(scratch, FieldMemOperand(scratch, Map::kBitFieldOffset));
++ __ TestBit(scratch, Map::kIsUndetectable, r0);
++ EmitBranch(true_block, false_block, ne, cr0);
++ }
++}
++
++
++Condition LCodeGen::EmitIsObject(Register input,
++ Register temp1,
++ Label* is_not_object,
++ Label* is_object) {
++ Register temp2 = scratch0();
++ __ JumpIfSmi(input, is_not_object);
++
++ __ LoadRoot(temp2, Heap::kNullValueRootIndex);
++ __ cmp(input, temp2);
++ __ beq(is_object);
++
++ // Load map.
++ __ LoadP(temp1, FieldMemOperand(input, HeapObject::kMapOffset));
++ // Undetectable objects behave like undefined.
++ __ lbz(temp2, FieldMemOperand(temp1, Map::kBitFieldOffset));
++ __ TestBit(temp2, Map::kIsUndetectable, r0);
++ __ bne(is_not_object, cr0);
++
++ // Load instance type and check that it is in object type range.
++ __ lbz(temp2, FieldMemOperand(temp1, Map::kInstanceTypeOffset));
++ __ cmpi(temp2, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ __ blt(is_not_object);
++ __ cmpi(temp2, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ return le;
++}
++
++
++void LCodeGen::DoIsObjectAndBranch(LIsObjectAndBranch* instr) {
++ Register reg = ToRegister(instr->value());
++ Register temp1 = ToRegister(instr->temp());
++
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++ Label* true_label = chunk_->GetAssemblyLabel(true_block);
++ Label* false_label = chunk_->GetAssemblyLabel(false_block);
++
++ Condition true_cond =
++ EmitIsObject(reg, temp1, false_label, true_label);
++
++ EmitBranch(true_block, false_block, true_cond);
++}
++
++
++Condition LCodeGen::EmitIsString(Register input,
++ Register temp1,
++ Label* is_not_string) {
++ __ JumpIfSmi(input, is_not_string);
++ __ CompareObjectType(input, temp1, temp1, FIRST_NONSTRING_TYPE);
++
++ return lt;
++}
++
++
++void LCodeGen::DoIsStringAndBranch(LIsStringAndBranch* instr) {
++ Register reg = ToRegister(instr->value());
++ Register temp1 = ToRegister(instr->temp());
++
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++ Label* false_label = chunk_->GetAssemblyLabel(false_block);
++
++ Condition true_cond =
++ EmitIsString(reg, temp1, false_label);
++
++ EmitBranch(true_block, false_block, true_cond);
++}
++
++
++void LCodeGen::DoIsSmiAndBranch(LIsSmiAndBranch* instr) {
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ Register input_reg = EmitLoadRegister(instr->value(), ip);
++ __ TestIfSmi(input_reg, r0);
++ EmitBranch(true_block, false_block, eq, cr0);
++}
++
++
++void LCodeGen::DoIsUndetectableAndBranch(LIsUndetectableAndBranch* instr) {
++ Register input = ToRegister(instr->value());
++ Register temp = ToRegister(instr->temp());
++
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ __ JumpIfSmi(input, chunk_->GetAssemblyLabel(false_block));
++ __ LoadP(temp, FieldMemOperand(input, HeapObject::kMapOffset));
++ __ lbz(temp, FieldMemOperand(temp, Map::kBitFieldOffset));
++ __ TestBit(temp, Map::kIsUndetectable, r0);
++ EmitBranch(true_block, false_block, ne, cr0);
++}
++
++
++static Condition ComputeCompareCondition(Token::Value op) {
++ switch (op) {
++ case Token::EQ_STRICT:
++ case Token::EQ:
++ return eq;
++ case Token::LT:
++ return lt;
++ case Token::GT:
++ return gt;
++ case Token::LTE:
++ return le;
++ case Token::GTE:
++ return ge;
++ default:
++ UNREACHABLE();
++ return kNoCondition;
++ }
++}
++
++
++void LCodeGen::DoStringCompareAndBranch(LStringCompareAndBranch* instr) {
++ Token::Value op = instr->op();
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ Handle<Code> ic = CompareIC::GetUninitialized(op);
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++ // This instruction also signals no smi code inlined
++ __ cmpi(r3, Operand::Zero());
++
++ Condition condition = ComputeCompareCondition(op);
++
++ EmitBranch(true_block, false_block, condition);
++}
++
++
++static InstanceType TestType(HHasInstanceTypeAndBranch* instr) {
++ InstanceType from = instr->from();
++ InstanceType to = instr->to();
++ if (from == FIRST_TYPE) return to;
++ ASSERT(from == to || to == LAST_TYPE);
++ return from;
++}
++
++
++static Condition BranchCondition(HHasInstanceTypeAndBranch* instr) {
++ InstanceType from = instr->from();
++ InstanceType to = instr->to();
++ if (from == to) return eq;
++ if (to == LAST_TYPE) return ge;
++ if (from == FIRST_TYPE) return le;
++ UNREACHABLE();
++ return eq;
++}
++
++
++void LCodeGen::DoHasInstanceTypeAndBranch(LHasInstanceTypeAndBranch* instr) {
++ Register scratch = scratch0();
++ Register input = ToRegister(instr->value());
++
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ Label* false_label = chunk_->GetAssemblyLabel(false_block);
++
++ __ JumpIfSmi(input, false_label);
++
++ __ CompareObjectType(input, scratch, scratch, TestType(instr->hydrogen()));
++ EmitBranch(true_block, false_block, BranchCondition(instr->hydrogen()));
++}
++
++
++void LCodeGen::DoGetCachedArrayIndex(LGetCachedArrayIndex* instr) {
++ Register input = ToRegister(instr->value());
++ Register result = ToRegister(instr->result());
++
++ __ AssertString(input);
++
++ __ lwz(result, FieldMemOperand(input, String::kHashFieldOffset));
++ __ IndexFromHash(result, result);
++}
++
++
++void LCodeGen::DoHasCachedArrayIndexAndBranch(
++ LHasCachedArrayIndexAndBranch* instr) {
++ Register input = ToRegister(instr->value());
++ Register scratch = scratch0();
++
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ __ lwz(scratch,
++ FieldMemOperand(input, String::kHashFieldOffset));
++ __ mov(r0, Operand(String::kContainsCachedArrayIndexMask));
++ __ and_(r0, scratch, r0, SetRC);
++ EmitBranch(true_block, false_block, eq, cr0);
++}
++
++
++// Branches to a label or falls through with the answer in flags. Trashes
++// the temp registers, but not the input.
++void LCodeGen::EmitClassOfTest(Label* is_true,
++ Label* is_false,
++ Handle<String>class_name,
++ Register input,
++ Register temp,
++ Register temp2) {
++ ASSERT(!input.is(temp));
++ ASSERT(!input.is(temp2));
++ ASSERT(!temp.is(temp2));
++
++ __ JumpIfSmi(input, is_false);
++
++ if (class_name->IsEqualTo(CStrVector("Function"))) {
++ // Assuming the following assertions, we can use the same compares to test
++ // for both being a function type and being in the object type range.
++ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
++ STATIC_ASSERT(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE ==
++ FIRST_SPEC_OBJECT_TYPE + 1);
++ STATIC_ASSERT(LAST_NONCALLABLE_SPEC_OBJECT_TYPE ==
++ LAST_SPEC_OBJECT_TYPE - 1);
++ STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
++ __ CompareObjectType(input, temp, temp2, FIRST_SPEC_OBJECT_TYPE);
++ __ blt(is_false);
++ __ beq(is_true);
++ __ cmpi(temp2, Operand(LAST_SPEC_OBJECT_TYPE));
++ __ beq(is_true);
++ } else {
++ // Faster code path to avoid two compares: subtract lower bound from the
++ // actual type and do a signed compare with the width of the type range.
++ __ LoadP(temp, FieldMemOperand(input, HeapObject::kMapOffset));
++ __ lbz(temp2, FieldMemOperand(temp, Map::kInstanceTypeOffset));
++ __ subi(temp2, temp2, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ __ cmpi(temp2, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE -
++ FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ __ bgt(is_false);
++ }
++
++ // Now we are in the FIRST-LAST_NONCALLABLE_SPEC_OBJECT_TYPE range.
++ // Check if the constructor in the map is a function.
++ __ LoadP(temp, FieldMemOperand(temp, Map::kConstructorOffset));
++
++ // Objects with a non-function constructor have class 'Object'.
++ __ CompareObjectType(temp, temp2, temp2, JS_FUNCTION_TYPE);
++ if (class_name->IsEqualTo(CStrVector("Object"))) {
++ __ bne(is_true);
++ } else {
++ __ bne(is_false);
++ }
++
++ // temp now contains the constructor function. Grab the
++ // instance class name from there.
++ __ LoadP(temp, FieldMemOperand(temp, JSFunction::kSharedFunctionInfoOffset));
++ __ LoadP(temp, FieldMemOperand(temp,
++ SharedFunctionInfo::kInstanceClassNameOffset));
++ // The class name we are testing against is a symbol because it's a literal.
++ // The name in the constructor is a symbol because of the way the context is
++ // booted. This routine isn't expected to work for random API-created
++ // classes and it doesn't have to because you can't access it with natives
++ // syntax. Since both sides are symbols it is sufficient to use an identity
++ // comparison.
++ __ Cmpi(temp, Operand(class_name), r0);
++ // End with the answer in flags.
++}
++
++
++void LCodeGen::DoClassOfTestAndBranch(LClassOfTestAndBranch* instr) {
++ Register input = ToRegister(instr->value());
++ Register temp = scratch0();
++ Register temp2 = ToRegister(instr->temp());
++ Handle<String> class_name = instr->hydrogen()->class_name();
++
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ Label* true_label = chunk_->GetAssemblyLabel(true_block);
++ Label* false_label = chunk_->GetAssemblyLabel(false_block);
++
++ EmitClassOfTest(true_label, false_label, class_name, input, temp, temp2);
++
++ EmitBranch(true_block, false_block, eq);
++}
++
++
++void LCodeGen::DoCmpMapAndBranch(LCmpMapAndBranch* instr) {
++ Register reg = ToRegister(instr->value());
++ Register temp = ToRegister(instr->temp());
++ int true_block = instr->true_block_id();
++ int false_block = instr->false_block_id();
++
++ __ LoadP(temp, FieldMemOperand(reg, HeapObject::kMapOffset));
++ __ Cmpi(temp, Operand(instr->map()), r0);
++ EmitBranch(true_block, false_block, eq);
++}
++
++
++void LCodeGen::DoInstanceOf(LInstanceOf* instr) {
++ ASSERT(ToRegister(instr->left()).is(r3)); // Object is in r3.
++ ASSERT(ToRegister(instr->right()).is(r4)); // Function is in r4.
++
++ InstanceofStub stub(InstanceofStub::kArgsInRegisters);
++ Label equal, done;
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++
++ __ cmpi(r3, Operand::Zero());
++ __ beq(&equal);
++ __ mov(r3, Operand(factory()->false_value()));
++ __ b(&done);
++
++ __ bind(&equal);
++ __ mov(r3, Operand(factory()->true_value()));
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr) {
++ class DeferredInstanceOfKnownGlobal: public LDeferredCode {
++ public:
++ DeferredInstanceOfKnownGlobal(LCodeGen* codegen,
++ LInstanceOfKnownGlobal* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() {
++ codegen()->DoDeferredInstanceOfKnownGlobal(instr_, &map_check_);
++ }
++ virtual LInstruction* instr() { return instr_; }
++ Label* map_check() { return &map_check_; }
++ private:
++ LInstanceOfKnownGlobal* instr_;
++ Label map_check_;
++ };
++
++ DeferredInstanceOfKnownGlobal* deferred;
++ deferred = new(zone()) DeferredInstanceOfKnownGlobal(this, instr);
++
++ Label done, false_result;
++ Register object = ToRegister(instr->value());
++ Register temp = ToRegister(instr->temp());
++ Register result = ToRegister(instr->result());
++
++ ASSERT(object.is(r3));
++ ASSERT(result.is(r3));
++
++ // A Smi is not instance of anything.
++ __ JumpIfSmi(object, &false_result);
++
++ // This is the inlined call site instanceof cache. The two occurences of the
++ // hole value will be patched to the last map/result pair generated by the
++ // instanceof stub.
++ Label cache_miss;
++ Register map = temp;
++ __ LoadP(map, FieldMemOperand(object, HeapObject::kMapOffset));
++ {
++ // Block constant pool emission to ensure the positions of instructions are
++ // as expected by the patcher. See InstanceofStub::Generate().
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
++ __ bind(deferred->map_check()); // Label for calculating code patching.
++ // We use Factory::the_hole_value() on purpose instead of loading from the
++ // root array to force relocation to be able to later patch with
++ // the cached map.
++ Handle<JSGlobalPropertyCell> cell =
++ factory()->NewJSGlobalPropertyCell(factory()->the_hole_value());
++ __ mov(ip, Operand(Handle<Object>(cell)));
++ __ LoadP(ip, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
++ __ cmp(map, ip);
++ __ bne(&cache_miss);
++ // We use Factory::the_hole_value() on purpose instead of loading from the
++ // root array to force relocation to be able to later patch
++ // with true or false.
++ __ mov(result, Operand(factory()->the_hole_value()));
++ }
++ __ b(&done);
++
++ // The inlined call site cache did not match. Check null and string before
++ // calling the deferred code.
++ __ bind(&cache_miss);
++ // Null is not instance of anything.
++ __ LoadRoot(ip, Heap::kNullValueRootIndex);
++ __ cmp(object, ip);
++ __ beq(&false_result);
++
++ // String values is not instance of anything.
++ Condition is_string = masm_->IsObjectStringType(object, temp);
++ __ b(is_string, &false_result, cr0);
++
++ // Go to the deferred code.
++ __ b(deferred->entry());
++
++ __ bind(&false_result);
++ __ LoadRoot(result, Heap::kFalseValueRootIndex);
++
++ // Here result has either true or false. Deferred code also produces true or
++ // false object.
++ __ bind(deferred->exit());
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
++ Label* map_check) {
++ Register result = ToRegister(instr->result());
++ ASSERT(result.is(r3));
++
++ InstanceofStub::Flags flags = InstanceofStub::kNoFlags;
++ flags = static_cast<InstanceofStub::Flags>(
++ flags | InstanceofStub::kArgsInRegisters);
++ flags = static_cast<InstanceofStub::Flags>(
++ flags | InstanceofStub::kCallSiteInlineCheck);
++ flags = static_cast<InstanceofStub::Flags>(
++ flags | InstanceofStub::kReturnTrueFalseObject);
++ InstanceofStub stub(flags);
++
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++
++ // Get the temp register reserved by the instruction. This needs to be r7 as
++ // its slot of the pushing of safepoint registers is used to communicate the
++ // offset to the location of the map check.
++ Register temp = ToRegister(instr->temp());
++ ASSERT(temp.is(r7));
++ __ LoadHeapObject(InstanceofStub::right(), instr->function());
++#if V8_TARGET_ARCH_PPC64
++ static const int kAdditionalDelta = 13;
++#else
++ static const int kAdditionalDelta = 7;
++#endif
++ int delta = masm_->InstructionsGeneratedSince(map_check) + kAdditionalDelta;
++ Label before_push_delta;
++ __ bind(&before_push_delta);
++ {
++ Assembler::BlockTrampolinePoolScope block_trampoline_pool(masm_);
++ __ mov(temp, Operand(delta * Instruction::kInstrSize));
++ __ StoreToSafepointRegisterSlot(temp, temp);
++ }
++ CallCodeGeneric(stub.GetCode(),
++ RelocInfo::CODE_TARGET,
++ instr,
++ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
++ ASSERT(delta == masm_->InstructionsGeneratedSince(map_check));
++ LEnvironment* env = instr->GetDeferredLazyDeoptimizationEnvironment();
++ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
++ // Put the result value into the result register slot and
++ // restore all registers.
++ __ StoreToSafepointRegisterSlot(result, result);
++}
++
++
++void LCodeGen::DoCmpT(LCmpT* instr) {
++ Token::Value op = instr->op();
++
++ Handle<Code> ic = CompareIC::GetUninitialized(op);
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++ // This instruction also signals no smi code inlined
++ __ cmpi(r3, Operand::Zero());
++
++ Condition condition = ComputeCompareCondition(op);
++ Label true_value, done;
++
++ __ b(condition, &true_value);
++
++ __ LoadRoot(ToRegister(instr->result()), Heap::kFalseValueRootIndex);
++ __ b(&done);
++
++ __ bind(&true_value);
++ __ LoadRoot(ToRegister(instr->result()), Heap::kTrueValueRootIndex);
++
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoReturn(LReturn* instr) {
++ if (FLAG_trace) {
++ // Push the return value on the stack as the parameter.
++ // Runtime::TraceExit returns its parameter in r3.
++ __ push(r3);
++ __ CallRuntime(Runtime::kTraceExit, 1);
++ }
++ int32_t sp_delta = (GetParameterCount() + 1) * kPointerSize;
++ __ mr(sp, fp);
++ __ Pop(r0, fp);
++ __ mtlr(r0);
++ __ addi(sp, sp, Operand(sp_delta));
++ __ blr();
++}
++
++
++void LCodeGen::DoLoadGlobalCell(LLoadGlobalCell* instr) {
++ Register result = ToRegister(instr->result());
++ __ mov(ip, Operand(Handle<Object>(instr->hydrogen()->cell())));
++ __ LoadP(result, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
++ if (instr->hydrogen()->RequiresHoleCheck()) {
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(result, ip);
++ DeoptimizeIf(eq, instr->environment());
++ }
++}
++
++
++void LCodeGen::DoLoadGlobalGeneric(LLoadGlobalGeneric* instr) {
++ ASSERT(ToRegister(instr->global_object()).is(r3));
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ __ mov(r5, Operand(instr->name()));
++ RelocInfo::Mode mode = instr->for_typeof() ? RelocInfo::CODE_TARGET
++ : RelocInfo::CODE_TARGET_CONTEXT;
++ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
++ CallCode(ic, mode, instr);
++}
++
++
++void LCodeGen::DoStoreGlobalCell(LStoreGlobalCell* instr) {
++ Register value = ToRegister(instr->value());
++ Register cell = scratch0();
++
++ // Load the cell.
++ __ mov(cell, Operand(instr->hydrogen()->cell()));
++
++ // If the cell we are storing to contains the hole it could have
++ // been deleted from the property dictionary. In that case, we need
++ // to update the property details in the property dictionary to mark
++ // it as no longer deleted.
++ if (instr->hydrogen()->RequiresHoleCheck()) {
++ // We use a temp to check the payload (CompareRoot might clobber ip).
++ Register payload = ToRegister(instr->temp());
++ __ LoadP(payload,
++ FieldMemOperand(cell, JSGlobalPropertyCell::kValueOffset));
++ __ CompareRoot(payload, Heap::kTheHoleValueRootIndex);
++ DeoptimizeIf(eq, instr->environment());
++ }
++
++ // Store the value.
++ __ StoreP(value, FieldMemOperand(cell, JSGlobalPropertyCell::kValueOffset),
++ r0);
++ // Cells are always rescanned, so no write barrier here.
++}
++
++
++void LCodeGen::DoStoreGlobalGeneric(LStoreGlobalGeneric* instr) {
++ ASSERT(ToRegister(instr->global_object()).is(r4));
++ ASSERT(ToRegister(instr->value()).is(r3));
++
++ __ mov(r5, Operand(instr->name()));
++ Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode)
++ ? isolate()->builtins()->StoreIC_Initialize_Strict()
++ : isolate()->builtins()->StoreIC_Initialize();
++ CallCode(ic, RelocInfo::CODE_TARGET_CONTEXT, instr);
++}
++
++
++void LCodeGen::DoLoadContextSlot(LLoadContextSlot* instr) {
++ Register context = ToRegister(instr->context());
++ Register result = ToRegister(instr->result());
++ __ LoadP(result, ContextOperand(context, instr->slot_index()));
++ if (instr->hydrogen()->RequiresHoleCheck()) {
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(result, ip);
++ if (instr->hydrogen()->DeoptimizesOnHole()) {
++ DeoptimizeIf(eq, instr->environment());
++ } else {
++ Label skip;
++ __ bne(&skip);
++ __ mov(result, Operand(factory()->undefined_value()));
++ __ bind(&skip);
++ }
++ }
++}
++
++
++void LCodeGen::DoStoreContextSlot(LStoreContextSlot* instr) {
++ Register context = ToRegister(instr->context());
++ Register value = ToRegister(instr->value());
++ Register scratch = scratch0();
++ MemOperand target = ContextOperand(context, instr->slot_index());
++
++ Label skip_assignment;
++
++ if (instr->hydrogen()->RequiresHoleCheck()) {
++ __ LoadP(scratch, target);
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(scratch, ip);
++ if (instr->hydrogen()->DeoptimizesOnHole()) {
++ DeoptimizeIf(eq, instr->environment());
++ } else {
++ __ bne(&skip_assignment);
++ }
++ }
++
++ __ StoreP(value, target, r0);
++ if (instr->hydrogen()->NeedsWriteBarrier()) {
++ HType type = instr->hydrogen()->value()->type();
++ SmiCheck check_needed =
++ type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
++ __ RecordWriteContextSlot(context,
++ target.offset(),
++ value,
++ scratch,
++ kLRHasBeenSaved,
++ kSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ check_needed);
++ }
++
++ __ bind(&skip_assignment);
++}
++
++
++void LCodeGen::DoLoadNamedField(LLoadNamedField* instr) {
++ Register object = ToRegister(instr->object());
++ Register result = ToRegister(instr->result());
++ if (instr->hydrogen()->is_in_object()) {
++ __ LoadP(result, FieldMemOperand(object, instr->hydrogen()->offset()));
++ } else {
++ __ LoadP(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
++ __ LoadP(result, FieldMemOperand(result, instr->hydrogen()->offset()));
++ }
++}
++
++
++void LCodeGen::EmitLoadFieldOrConstantFunction(Register result,
++ Register object,
++ Handle<Map> type,
++ Handle<String> name,
++ LEnvironment* env) {
++ LookupResult lookup(isolate());
++ type->LookupDescriptor(NULL, *name, &lookup);
++ ASSERT(lookup.IsFound() || lookup.IsCacheable());
++ if (lookup.IsField()) {
++ int index = lookup.GetLocalFieldIndexFromMap(*type);
++ int offset = index * kPointerSize;
++ if (index < 0) {
++ // Negative property indices are in-object properties, indexed
++ // from the end of the fixed part of the object.
++ __ LoadP(result, FieldMemOperand(object, offset + type->instance_size()));
++ } else {
++ // Non-negative property indices are in the properties array.
++ __ LoadP(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
++ __ LoadP(result,
++ FieldMemOperand(result, offset + FixedArray::kHeaderSize));
++ }
++ } else if (lookup.IsConstantFunction()) {
++ Handle<JSFunction> function(lookup.GetConstantFunctionFromMap(*type));
++ __ LoadHeapObject(result, function);
++ } else {
++ // Negative lookup.
++ // Check prototypes.
++ Handle<HeapObject> current(HeapObject::cast((*type)->prototype()));
++ Heap* heap = type->GetHeap();
++ while (*current != heap->null_value()) {
++ __ LoadHeapObject(result, current);
++ __ LoadP(result, FieldMemOperand(result, HeapObject::kMapOffset));
++ __ Cmpi(result, Operand(Handle<Map>(current->map())), r0);
++ DeoptimizeIf(ne, env);
++ current =
++
Handle<HeapObject>(HeapObject::cast(current->map()->prototype()));
++ }
++ __ LoadRoot(result, Heap::kUndefinedValueRootIndex);
++ }
++}
++
++
++void LCodeGen::DoLoadNamedFieldPolymorphic(LLoadNamedFieldPolymorphic* instr) {
++ Register object = ToRegister(instr->object());
++ Register result = ToRegister(instr->result());
++ Register object_map = scratch0();
++
++ int map_count = instr->hydrogen()->types()->length();
++ bool need_generic = instr->hydrogen()->need_generic();
++
++ if (map_count == 0 && !need_generic) {
++ DeoptimizeIf(al, instr->environment());
++ return;
++ }
++ Handle<String> name = instr->hydrogen()->name();
++ Label done;
++ __ LoadP(object_map, FieldMemOperand(object, HeapObject::kMapOffset));
++ for (int i = 0; i < map_count; ++i) {
++ bool last = (i == map_count - 1);
++ Handle<Map> map = instr->hydrogen()->types()->at(i);
++ Label check_passed;
++ __ CompareMap(
++ object_map, map, &check_passed, ALLOW_ELEMENT_TRANSITION_MAPS);
++ if (last && !need_generic) {
++ DeoptimizeIf(ne, instr->environment());
++ __ bind(&check_passed);
++ EmitLoadFieldOrConstantFunction(
++ result, object, map, name, instr->environment());
++ } else {
++ Label next;
++ __ bne(&next);
++ __ bind(&check_passed);
++ EmitLoadFieldOrConstantFunction(
++ result, object, map, name, instr->environment());
++ __ b(&done);
++ __ bind(&next);
++ }
++ }
++ if (need_generic) {
++ __ mov(r5, Operand(name));
++ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++ }
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoLoadNamedGeneric(LLoadNamedGeneric* instr) {
++ ASSERT(ToRegister(instr->object()).is(r3));
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ // Name is always in r5.
++ __ mov(r5, Operand(instr->name()));
++ Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize();
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoLoadFunctionPrototype(LLoadFunctionPrototype* instr) {
++ Register scratch = scratch0();
++ Register function = ToRegister(instr->function());
++ Register result = ToRegister(instr->result());
++
++ // Check that the function really is a function. Load map into the
++ // result register.
++ __ CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE);
++ DeoptimizeIf(ne, instr->environment());
++
++ // Make sure that the function has an instance prototype.
++ Label non_instance;
++ __ lbz(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
++ __ TestBit(scratch, Map::kHasNonInstancePrototype, r0);
++ __ bne(&non_instance, cr0);
++
++ // Get the prototype or initial map from the function.
++ __ LoadP(result,
++ FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
++
++ // Check that the function has a prototype or an initial map.
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(result, ip);
++ DeoptimizeIf(eq, instr->environment());
++
++ // If the function does not have an initial map, we're done.
++ Label done;
++ __ CompareObjectType(result, scratch, scratch, MAP_TYPE);
++ __ bne(&done);
++
++ // Get the prototype from the initial map.
++ __ LoadP(result, FieldMemOperand(result, Map::kPrototypeOffset));
++ __ b(&done);
++
++ // Non-instance prototype: Fetch prototype from constructor field
++ // in initial map.
++ __ bind(&non_instance);
++ __ LoadP(result, FieldMemOperand(result, Map::kConstructorOffset));
++
++ // All done.
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoLoadElements(LLoadElements* instr) {
++ Register result = ToRegister(instr->result());
++ Register input = ToRegister(instr->object());
++ Register scratch = scratch0();
++
++ __ LoadP(result, FieldMemOperand(input, JSObject::kElementsOffset));
++ if (FLAG_debug_code) {
++ Label done, fail;
++ __ LoadP(scratch, FieldMemOperand(result, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
++ __ cmp(scratch, ip);
++ __ beq(&done);
++ __ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
++ __ cmp(scratch, ip);
++ __ beq(&done);
++ // |scratch| still contains |input|'s map.
++ __ lbz(scratch, FieldMemOperand(scratch, Map::kBitField2Offset));
++ __ ExtractBitMask(scratch, scratch, Map::kElementsKindMask);
++ __ cmpi(scratch, Operand(GetInitialFastElementsKind()));
++ __ blt(&fail);
++ __ cmpi(scratch, Operand(TERMINAL_FAST_ELEMENTS_KIND));
++ __ ble(&done);
++ __ cmpi(scratch, Operand(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND));
++ __ blt(&fail);
++ __ cmpi(scratch, Operand(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND));
++ __ ble(&done);
++ __ bind(&fail);
++ __ Abort("Check for fast or external elements failed.");
++ __ bind(&done);
++ }
++}
++
++
++void LCodeGen::DoLoadExternalArrayPointer(
++ LLoadExternalArrayPointer* instr) {
++ Register to_reg = ToRegister(instr->result());
++ Register from_reg = ToRegister(instr->object());
++ __ LoadP(to_reg, FieldMemOperand(from_reg,
++ ExternalArray::kExternalPointerOffset));
++}
++
++
++void LCodeGen::DoAccessArgumentsAt(LAccessArgumentsAt* instr) {
++ Register arguments = ToRegister(instr->arguments());
++ Register length = ToRegister(instr->length());
++ Register index = ToRegister(instr->index());
++ Register result = ToRegister(instr->result());
++
++ // There are two words between the frame pointer and the last argument.
++ // Subtracting from length accounts for one of them add one more.
++ __ sub(length, length, index);
++ __ addi(length, length, Operand(1));
++ __ ShiftLeftImm(r0, length, Operand(kPointerSizeLog2));
++ __ LoadPX(result, MemOperand(arguments, r0));
++}
++
++
++void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) {
++ Register elements = ToRegister(instr->elements());
++ Register result = ToRegister(instr->result());
++ Register scratch = scratch0();
++ Register store_base = scratch;
++ int offset = 0;
++
++ if (instr->key()->IsConstantOperand()) {
++ LConstantOperand* const_operand = LConstantOperand::cast(instr->key());
++ offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) +
++ instr->additional_index());
++ store_base = elements;
++ } else {
++ Register key = EmitLoadRegister(instr->key(), scratch0());
++ // Even though the HLoadKeyedFastElement instruction forces the input
++ // representation for the key to be an integer, the input gets replaced
++ // during bound check elimination with the index argument to the bounds
++ // check, which can be tagged, so that case must be handled here, too.
++ if (instr->hydrogen()->key()->representation().IsTagged()) {
++ __ SmiToPtrArrayOffset(r0, key);
++ } else {
++ __ ShiftLeftImm(r0, key, Operand(kPointerSizeLog2));
++ }
++ __ add(scratch, elements, r0);
++ offset = FixedArray::OffsetOfElementAt(instr->additional_index());
++ }
++ __ LoadP(result, FieldMemOperand(store_base, offset));
++
++ // Check for the hole value.
++ if (instr->hydrogen()->RequiresHoleCheck()) {
++ if (IsFastSmiElementsKind(instr->hydrogen()->elements_kind())) {
++ __ TestIfSmi(result, r0);
++ DeoptimizeIf(ne, instr->environment(), cr0);
++ } else {
++ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
++ __ cmp(result, scratch);
++ DeoptimizeIf(eq, instr->environment());
++ }
++ }
++}
++
++
++void LCodeGen::DoLoadKeyedFastDoubleElement(
++ LLoadKeyedFastDoubleElement* instr) {
++ Register elements = ToRegister(instr->elements());
++ bool key_is_constant = instr->key()->IsConstantOperand();
++ Register key = no_reg;
++ DwVfpRegister result = ToDoubleRegister(instr->result());
++ Register scratch = scratch0();
++
++ int element_size_shift = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS);
++ bool key_is_tagged = instr->hydrogen()->key()->representation().IsTagged();
++ int constant_key = 0;
++ int address_offset = 0;
++ if (key_is_constant) {
++ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
++ if (constant_key & 0xF0000000) {
++ Abort("array index constant value too big.");
++ }
++ } else {
++ key = ToRegister(instr->key());
++ }
++
++ if (key_is_constant) {
++ __ Add(elements, elements,
++ (FixedDoubleArray::kHeaderSize - kHeapObjectTag) +
++ ((constant_key + instr->additional_index()) << element_size_shift),
++ r0);
++ } else {
++ __ IndexToArrayOffset(r0, key, element_size_shift, key_is_tagged);
++ __ add(elements, elements, r0);
++ address_offset = (FixedDoubleArray::kHeaderSize - kHeapObjectTag) +
++ (instr->additional_index() << element_size_shift);
++
++ if (!is_int16((address_offset))) {
++ __ mov(r0, Operand(address_offset));
++ __ add(elements, elements, r0);
++ address_offset = 0;
++ }
++ }
++
++ if (instr->hydrogen()->RequiresHoleCheck()) {
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ if (address_offset) {
++ if (is_int16(address_offset + sizeof(kHoleNanLower32))) {
++ __ lwz(scratch, MemOperand(elements,
++ address_offset + sizeof(kHoleNanLower32)));
++ } else {
++ __ li(r0, Operand(address_offset));
++ __ add(scratch, elements, r0);
++ __ lwz(scratch, MemOperand(scratch, sizeof(kHoleNanLower32)));
++ }
++ } else {
++ __ lwz(scratch, MemOperand(elements, sizeof(kHoleNanLower32)));
++ }
++#else
++ __ lwz(scratch, MemOperand(elements, address_offset));
++#endif
++ __ Cmpi(scratch, Operand(kHoleNanUpper32), r0);
++ DeoptimizeIf(eq, instr->environment());
++ }
++
++ __ lfd(result, MemOperand(elements, address_offset));
++}
++
++
++MemOperand LCodeGen::PrepareKeyedOperand(Register key,
++ Register base,
++ bool key_is_constant,
++ bool key_is_tagged,
++ int constant_key,
++ int element_size_shift,
++ int additional_index,
++ int additional_offset) {
++ Register scratch = scratch0();
++
++ ASSERT(!(key_is_constant && key_is_tagged));
++
++ if (key_is_constant) {
++ return MemOperand(base,
++ (constant_key << element_size_shift) + additional_offset);
++ }
++
++ bool needs_shift = (element_size_shift != (key_is_tagged ?
++ kSmiTagSize + kSmiShiftSize : 0));
++
++ if (!(additional_index || needs_shift)) {
++ return MemOperand(base, key);
++ }
++
++ if (additional_index) {
++ if (key_is_tagged) {
++#if V8_TARGET_ARCH_PPC64
++ // more efficient to just untag
++ __ SmiUntag(scratch, key);
++ key_is_tagged = false;
++ needs_shift = (element_size_shift != 0);
++ key = scratch;
++#else
++ additional_index <<= kSmiTagSize + kSmiShiftSize;
++#endif
++ }
++
++ __ Add(scratch, key, additional_index, r0);
++ key = scratch;
++ }
++
++ if (needs_shift) {
++ __ IndexToArrayOffset(scratch, key, element_size_shift, key_is_tagged);
++ }
++
++ return MemOperand(base, scratch);
++}
++
++
++void LCodeGen::DoLoadKeyedSpecializedArrayElement(
++ LLoadKeyedSpecializedArrayElement* instr) {
++ Register external_pointer = ToRegister(instr->external_pointer());
++ Register key = no_reg;
++ ElementsKind elements_kind = instr->elements_kind();
++ bool key_is_constant = instr->key()->IsConstantOperand();
++ int constant_key = 0;
++ if (key_is_constant) {
++ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
++ if (constant_key & 0xF0000000) {
++ Abort("array index constant value too big.");
++ }
++ } else {
++ key = ToRegister(instr->key());
++ }
++ int element_size_shift = ElementsKindToShiftSize(elements_kind);
++ bool key_is_tagged = instr->hydrogen()->key()->representation().IsTagged();
++ int additional_offset = instr->additional_index() << element_size_shift;
++
++ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS ||
++ elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
++ DwVfpRegister result = ToDoubleRegister(instr->result());
++ if (key_is_constant) {
++ __ Add(scratch0(), external_pointer,
++ constant_key << element_size_shift,
++ r0);
++ } else {
++ __ IndexToArrayOffset(r0, key, element_size_shift, key_is_tagged);
++ __ add(scratch0(), external_pointer, r0);
++ }
++ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
++ __ lfs(result, MemOperand(scratch0(), additional_offset));
++ } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS
++ __ lfd(result, MemOperand(scratch0(), additional_offset));
++ }
++ } else {
++ Register result = ToRegister(instr->result());
++ MemOperand mem_operand = PrepareKeyedOperand(
++ key, external_pointer, key_is_constant, key_is_tagged, constant_key,
++ element_size_shift, instr->additional_index(), additional_offset);
++ switch (elements_kind) {
++ case EXTERNAL_BYTE_ELEMENTS:
++ if (key_is_constant) {
++ __ LoadByte(result, mem_operand, r0);
++ } else {
++ __ lbzx(result, mem_operand);
++ }
++ __ extsb(result, result);
++ break;
++ case EXTERNAL_PIXEL_ELEMENTS:
++ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
++ if (key_is_constant) {
++ __ LoadByte(result, mem_operand, r0);
++ } else {
++ __ lbzx(result, mem_operand);
++ }
++ break;
++ case EXTERNAL_SHORT_ELEMENTS:
++ if (key_is_constant) {
++ __ LoadHalfWord(result, mem_operand, r0);
++ } else {
++ __ lhzx(result, mem_operand);
++ }
++ __ extsh(result, result);
++ break;
++ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
++ if (key_is_constant) {
++ __ LoadHalfWord(result, mem_operand, r0);
++ } else {
++ __ lhzx(result, mem_operand);
++ }
++ break;
++ case EXTERNAL_INT_ELEMENTS:
++ if (key_is_constant) {
++ __ LoadWord(result, mem_operand, r0);
++ } else {
++ __ lwzx(result, mem_operand);
++ }
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(result, result);
++#endif
++ break;
++ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
++ if (key_is_constant) {
++ __ LoadWord(result, mem_operand, r0);
++ } else {
++ __ lwzx(result, mem_operand);
++ }
++ if (!instr->hydrogen()->CheckFlag(HInstruction::kUint32)) {
++ __ lis(r0, Operand(SIGN_EXT_IMM16(0x8000)));
++ __ cmpl(result, r0);
++ DeoptimizeIf(ge, instr->environment());
++ }
++ break;
++ case EXTERNAL_FLOAT_ELEMENTS:
++ case EXTERNAL_DOUBLE_ELEMENTS:
++ case FAST_HOLEY_DOUBLE_ELEMENTS:
++ case FAST_HOLEY_ELEMENTS:
++ case FAST_HOLEY_SMI_ELEMENTS:
++ case FAST_DOUBLE_ELEMENTS:
++ case FAST_ELEMENTS:
++ case FAST_SMI_ELEMENTS:
++ case DICTIONARY_ELEMENTS:
++ case NON_STRICT_ARGUMENTS_ELEMENTS:
++ UNREACHABLE();
++ break;
++ }
++ }
++}
++
++
++void LCodeGen::DoLoadKeyedGeneric(LLoadKeyedGeneric* instr) {
++ ASSERT(ToRegister(instr->object()).is(r4));
++ ASSERT(ToRegister(instr->key()).is(r3));
++
++ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Initialize();
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoArgumentsElements(LArgumentsElements* instr) {
++ Register scratch = scratch0();
++ Register result = ToRegister(instr->result());
++
++ if (instr->hydrogen()->from_inlined()) {
++ __ subi(result, sp, Operand(2 * kPointerSize));
++ } else {
++ // Check if the calling frame is an arguments adaptor frame.
++ Label done, adapted;
++ __ LoadP(scratch, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++ __ LoadP(result,
++ MemOperand(scratch, StandardFrameConstants::kContextOffset));
++ __ CmpSmiLiteral(result, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++
++ // Result is the frame pointer for the frame if not adapted and for the real
++ // frame below the adaptor frame if adapted.
++ __ beq(&adapted);
++ __ mr(result, fp);
++ __ b(&done);
++
++ __ bind(&adapted);
++ __ mr(result, scratch);
++ __ bind(&done);
++ }
++}
++
++
++void LCodeGen::DoArgumentsLength(LArgumentsLength* instr) {
++ Register elem = ToRegister(instr->elements());
++ Register result = ToRegister(instr->result());
++
++ Label done;
++
++ // If no arguments adaptor frame the number of arguments is fixed.
++ __ cmp(fp, elem);
++ __ mov(result, Operand(scope()->num_parameters()));
++ __ beq(&done);
++
++ // Arguments adaptor frame present. Get argument length from there.
++ __ LoadP(result, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++ __ LoadP(result,
++ MemOperand(result, ArgumentsAdaptorFrameConstants::kLengthOffset));
++ __ SmiUntag(result);
++
++ // Argument length is in result register.
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoWrapReceiver(LWrapReceiver* instr) {
++ Register receiver = ToRegister(instr->receiver());
++ Register function = ToRegister(instr->function());
++ Register scratch = scratch0();
++
++ // If the receiver is null or undefined, we have to pass the global
++ // object as a receiver to normal functions. Values have to be
++ // passed unchanged to builtins and strict-mode functions.
++ Label global_object, receiver_ok;
++
++ // Do not transform the receiver to object for strict mode
++ // functions.
++ __ LoadP(scratch,
++ FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
++ __ lwz(scratch,
++ FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
++ __ TestBit(scratch,
++#if V8_TARGET_ARCH_PPC64
++ SharedFunctionInfo::kStrictModeFunction,
++#else
++ SharedFunctionInfo::kStrictModeFunction + kSmiTagSize,
++#endif
++ r0);
++ __ bne(&receiver_ok, cr0);
++
++ // Do not transform the receiver to object for builtins.
++ __ TestBit(scratch,
++#if V8_TARGET_ARCH_PPC64
++ SharedFunctionInfo::kNative,
++#else
++ SharedFunctionInfo::kNative + kSmiTagSize,
++#endif
++ r0);
++ __ bne(&receiver_ok, cr0);
++
++ // Normal function. Replace undefined or null with global receiver.
++ __ LoadRoot(scratch, Heap::kNullValueRootIndex);
++ __ cmp(receiver, scratch);
++ __ beq(&global_object);
++ __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
++ __ cmp(receiver, scratch);
++ __ beq(&global_object);
++
++ // Deoptimize if the receiver is not a JS object.
++ __ TestIfSmi(receiver, r0);
++ DeoptimizeIf(eq, instr->environment(), cr0);
++ __ CompareObjectType(receiver, scratch, scratch, FIRST_SPEC_OBJECT_TYPE);
++ DeoptimizeIf(lt, instr->environment());
++ __ b(&receiver_ok);
++
++ __ bind(&global_object);
++ __ LoadP(receiver, GlobalObjectOperand());
++ __ LoadP(receiver,
++ FieldMemOperand(receiver, JSGlobalObject::kGlobalReceiverOffset));
++ __ bind(&receiver_ok);
++}
++
++
++void LCodeGen::DoApplyArguments(LApplyArguments* instr) {
++ Register receiver = ToRegister(instr->receiver());
++ Register function = ToRegister(instr->function());
++ Register length = ToRegister(instr->length());
++ Register elements = ToRegister(instr->elements());
++ Register scratch = scratch0();
++ ASSERT(receiver.is(r3)); // Used for parameter count.
++ ASSERT(function.is(r4)); // Required by InvokeFunction.
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ // Copy the arguments to this function possibly from the
++ // adaptor frame below it.
++ const uint32_t kArgumentsLimit = 1 * KB;
++ __ cmpli(length, Operand(kArgumentsLimit));
++ DeoptimizeIf(gt, instr->environment());
++
++ // Push the receiver and use the register to keep the original
++ // number of arguments.
++ __ push(receiver);
++ __ mr(receiver, length);
++ // The arguments are at a one pointer size offset from elements.
++ __ addi(elements, elements, Operand(1 * kPointerSize));
++
++ // Loop through the arguments pushing them onto the execution
++ // stack.
++ Label invoke, loop;
++ // length is a small non-negative integer, due to the test above.
++ __ cmpi(length, Operand::Zero());
++ __ beq(&invoke);
++ __ mtctr(length);
++ __ bind(&loop);
++ __ ShiftLeftImm(r0, length, Operand(kPointerSizeLog2));
++ __ LoadPX(scratch, MemOperand(elements, r0));
++ __ push(scratch);
++ __ addi(length, length, Operand(-1));
++ __ bdnz(&loop);
++
++ __ bind(&invoke);
++ ASSERT(instr->HasPointerMap());
++ LPointerMap* pointers = instr->pointer_map();
++ RecordPosition(pointers->position());
++ SafepointGenerator safepoint_generator(
++ this, pointers, Safepoint::kLazyDeopt);
++ // The number of arguments is stored in receiver which is r3, as expected
++ // by InvokeFunction.
++ ParameterCount actual(receiver);
++ __ InvokeFunction(function, actual, CALL_FUNCTION,
++ safepoint_generator, CALL_AS_METHOD);
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++}
++
++
++void LCodeGen::DoPushArgument(LPushArgument* instr) {
++ LOperand* argument = instr->value();
++ if (argument->IsDoubleRegister() || argument->IsDoubleStackSlot()) {
++ Abort("DoPushArgument not implemented for double type.");
++ } else {
++ Register argument_reg = EmitLoadRegister(argument, ip);
++ __ push(argument_reg);
++ }
++}
++
++
++void LCodeGen::DoDrop(LDrop* instr) {
++ __ Drop(instr->count());
++}
++
++
++void LCodeGen::DoThisFunction(LThisFunction* instr) {
++ Register result = ToRegister(instr->result());
++ __ LoadP(result, MemOperand(fp, JavaScriptFrameConstants::kFunctionOffset));
++}
++
++
++void LCodeGen::DoContext(LContext* instr) {
++ Register result = ToRegister(instr->result());
++ __ mr(result, cp);
++}
++
++
++void LCodeGen::DoOuterContext(LOuterContext* instr) {
++ Register context = ToRegister(instr->context());
++ Register result = ToRegister(instr->result());
++ __ LoadP(result,
++ MemOperand(context, Context::SlotOffset(Context::PREVIOUS_INDEX)));
++}
++
++
++void LCodeGen::DoDeclareGlobals(LDeclareGlobals* instr) {
++ __ push(cp); // The context is the first argument.
++ __ LoadHeapObject(scratch0(), instr->hydrogen()->pairs());
++ __ push(scratch0());
++ __ LoadSmiLiteral(scratch0(), Smi::FromInt(instr->hydrogen()->flags()));
++ __ push(scratch0());
++ CallRuntime(Runtime::kDeclareGlobals, 3, instr);
++}
++
++
++void LCodeGen::DoGlobalObject(LGlobalObject* instr) {
++ Register result = ToRegister(instr->result());
++ __ LoadP(result, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
++}
++
++
++void LCodeGen::DoGlobalReceiver(LGlobalReceiver* instr) {
++ Register global = ToRegister(instr->global_object());
++ Register result = ToRegister(instr->result());
++ __ LoadP(result,
++ FieldMemOperand(global, GlobalObject::kGlobalReceiverOffset));
++}
++
++
++void LCodeGen::CallKnownFunction(Handle<JSFunction> function,
++ int arity,
++ LInstruction* instr,
++ CallKind call_kind,
++ R4State r4_state) {
++ bool can_invoke_directly = !function->NeedsArgumentsAdaption() ||
++ function->shared()->formal_parameter_count() == arity;
++
++ LPointerMap* pointers = instr->pointer_map();
++ RecordPosition(pointers->position());
++
++ if (can_invoke_directly) {
++ if (r4_state == R4_UNINITIALIZED) {
++ __ LoadHeapObject(r4, function);
++ }
++
++ // Change context.
++ __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++
++ // Set r3 to arguments count if adaption is not needed. Assumes that r3
++ // is available to write to at this point.
++ if (!function->NeedsArgumentsAdaption()) {
++ __ mov(r3, Operand(arity));
++ }
++
++ // Invoke function.
++ __ SetCallKind(r8, call_kind);
++ if (*function == *info()->closure()) {
++ __ CallSelf();
++ } else {
++ __ LoadP(ip, FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
++ __ Call(ip);
++ }
++
++ // Set up deoptimization.
++ RecordSafepointWithLazyDeopt(instr, RECORD_SIMPLE_SAFEPOINT);
++ } else {
++ SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt);
++ ParameterCount count(arity);
++ __ InvokeFunction(function, count, CALL_FUNCTION, generator, call_kind);
++ }
++
++ // Restore context.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++}
++
++
++void LCodeGen::DoCallConstantFunction(LCallConstantFunction* instr) {
++ ASSERT(ToRegister(instr->result()).is(r3));
++ CallKnownFunction(instr->function(),
++ instr->arity(),
++ instr,
++ CALL_AS_METHOD,
++ R4_UNINITIALIZED);
++}
++
++
++void LCodeGen::DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr) {
++ Register input = ToRegister(instr->value());
++ Register result = ToRegister(instr->result());
++ Register scratch = scratch0();
++
++ // Deoptimize if not a heap number.
++ __ LoadP(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
++ __ cmp(scratch, ip);
++ DeoptimizeIf(ne, instr->environment());
++
++ Label done;
++ Register exponent = scratch0();
++ scratch = no_reg;
++ __ lwz(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset));
++ // Check the sign of the argument. If the argument is positive, just
++ // return it.
++ __ TestSignBit32(exponent, r0);
++ // Move the input to the result if necessary.
++ __ Move(result, input);
++ __ beq(&done, cr0);
++
++ // Input is negative. Reverse its sign.
++ // Preserve the value of all registers.
++ {
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++
++ // Registers were saved at the safepoint, so we can use
++ // many scratch registers.
++ Register tmp1 = input.is(r4) ? r3 : r4;
++ Register tmp2 = input.is(r5) ? r3 : r5;
++ Register tmp3 = input.is(r6) ? r3 : r6;
++ Register tmp4 = input.is(r7) ? r3 : r7;
++
++ // exponent: floating point exponent value.
++
++ Label allocated, slow;
++ __ LoadRoot(tmp4, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(tmp1, tmp2, tmp3, tmp4, &slow);
++ __ b(&allocated);
++
++ // Slow case: Call the runtime system to do the number allocation.
++ __ bind(&slow);
++
++ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
++ // Set the pointer to the new heap number in tmp.
++ if (!tmp1.is(r3)) __ mr(tmp1, r3);
++ // Restore input_reg after call to runtime.
++ __ LoadFromSafepointRegisterSlot(input, input);
++ __ lwz(exponent, FieldMemOperand(input, HeapNumber::kExponentOffset));
++
++ __ bind(&allocated);
++ // exponent: floating point exponent value.
++ // tmp1: allocated heap number.
++ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
++ __ clrlwi(exponent, exponent, Operand(1)); // clear sign bit
++ __ stw(exponent, FieldMemOperand(tmp1, HeapNumber::kExponentOffset));
++ __ lwz(tmp2, FieldMemOperand(input, HeapNumber::kMantissaOffset));
++ __ stw(tmp2, FieldMemOperand(tmp1, HeapNumber::kMantissaOffset));
++
++ __ StoreToSafepointRegisterSlot(tmp1, result);
++ }
++
++ __ bind(&done);
++}
++
++
++void LCodeGen::EmitIntegerMathAbs(LUnaryMathOperation* instr) {
++ Register input = ToRegister(instr->value());
++ Register result = ToRegister(instr->result());
++ Label done;
++ __ cmpi(input, Operand::Zero());
++ __ Move(result, input);
++ __ bge(&done);
++ __ li(r0, Operand::Zero()); // clear xer
++ __ mtxer(r0);
++ __ neg(result, result, SetOE, SetRC);
++ // Deoptimize on overflow.
++ DeoptimizeIf(overflow, instr->environment(), cr0);
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoMathAbs(LUnaryMathOperation* instr) {
++ // Class for deferred case.
++ class DeferredMathAbsTaggedHeapNumber: public LDeferredCode {
++ public:
++ DeferredMathAbsTaggedHeapNumber(LCodeGen* codegen,
++ LUnaryMathOperation* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() {
++ codegen()->DoDeferredMathAbsTaggedHeapNumber(instr_);
++ }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LUnaryMathOperation* instr_;
++ };
++
++ Representation r = instr->hydrogen()->value()->representation();
++ if (r.IsDouble()) {
++ DwVfpRegister input = ToDoubleRegister(instr->value());
++ DwVfpRegister result = ToDoubleRegister(instr->result());
++ __ fabs(result, input);
++ } else if (r.IsInteger32()) {
++ EmitIntegerMathAbs(instr);
++ } else {
++ // Representation is tagged.
++ DeferredMathAbsTaggedHeapNumber* deferred =
++ new(zone()) DeferredMathAbsTaggedHeapNumber(this, instr);
++ Register input = ToRegister(instr->value());
++ // Smi check.
++ __ JumpIfNotSmi(input, deferred->entry());
++ // If smi, handle it directly.
++ EmitIntegerMathAbs(instr);
++ __ bind(deferred->exit());
++ }
++}
++
++
++void LCodeGen::DoMathFloor(LUnaryMathOperation* instr) {
++ DoubleRegister input = ToDoubleRegister(instr->value());
++ Register result = ToRegister(instr->result());
++ Register scratch = scratch0();
++
++ __ EmitVFPTruncate(kRoundToMinusInf,
++ result,
++ input,
++ scratch,
++ double_scratch0());
++ DeoptimizeIf(ne, instr->environment());
++
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ // Test for -0.
++ Label done;
++ __ cmpi(result, Operand::Zero());
++ __ bne(&done);
++ // Move high word to scrach and test sign bit
++ __ subi(sp, sp, Operand(8));
++ __ stfd(input, MemOperand(sp));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(scratch, MemOperand(sp, 4));
++#else
++ __ lwz(scratch, MemOperand(sp, 0));
++#endif
++ __ addi(sp, sp, Operand(8));
++ __ TestSignBit32(scratch, r0);
++ DeoptimizeIf(ne, instr->environment(), cr0);
++ __ bind(&done);
++ }
++}
++
++
++void LCodeGen::DoMathRound(LUnaryMathOperation* instr) {
++ DoubleRegister input = ToDoubleRegister(instr->value());
++ Register result = ToRegister(instr->result());
++ DwVfpRegister double_scratch1 = ToDoubleRegister(instr->temp());
++ Register scratch = scratch0();
++ Label done, check_sign_on_zero, skip1, skip2;
++
++ // Extract exponent bits.
++ __ subi(sp, sp, Operand(8));
++ __ stfd(input, MemOperand(sp));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(result, MemOperand(sp, 4));
++#else
++ __ lwz(result, MemOperand(sp, 0));
++#endif
++ __ addi(sp, sp, Operand(8));
++ __ ExtractBitMask(scratch, result, HeapNumber::kExponentMask);
++
++ // If the number is in ]-0.5, +0.5[, the result is +/- 0.
++ __ cmpi(scratch, Operand(HeapNumber::kExponentBias - 2));
++ __ bgt(&skip1);
++ __ li(result, Operand::Zero());
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ __ b(&check_sign_on_zero);
++ } else {
++ __ b(&done);
++ }
++
++ // The following conversion will not work with numbers
++ // outside of ]-2^32, 2^32[.
++ __ bind(&skip1);
++ __ cmpi(scratch, Operand(HeapNumber::kExponentBias + 32));
++ DeoptimizeIf(ge, instr->environment());
++
++ __ LoadDoubleLiteral(double_scratch0(), 0.5, scratch);
++ __ fadd(double_scratch0(), input, double_scratch0());
++
++ // Save the original sign for later comparison.
++ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
++ __ clrrwi(scratch, result, Operand(31));
++
++ // Check sign of the result: if the sign changed, the input
++ // value was in ]0.5, 0[ and the result should be -0.
++ __ subi(sp, sp, Operand(8));
++ __ stfd(double_scratch0(), MemOperand(sp, 0));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(result, MemOperand(sp, 4));
++#else
++ __ lwz(result, MemOperand(sp, 0));
++#endif
++ __ addi(sp, sp, Operand(8));
++ __ xor_(result, result, scratch, SetRC);
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ DeoptimizeIf(lt, instr->environment(), cr0);
++ } else {
++ __ bge(&skip2);
++ __ li(result, Operand::Zero());
++ __ b(&done);
++ __ bind(&skip2);
++ }
++
++ __ EmitVFPTruncate(kRoundToMinusInf,
++ result,
++ double_scratch0(),
++ scratch,
++ double_scratch1);
++ DeoptimizeIf(ne, instr->environment());
++
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ // Test for -0.
++ __ cmpi(result, Operand::Zero());
++ __ bne(&done);
++ __ bind(&check_sign_on_zero);
++ // Move high word to scrach and test sign bit
++ __ subi(sp, sp, Operand(8));
++ __ stfd(input, MemOperand(sp));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(scratch, MemOperand(sp, 4));
++#else
++ __ lwz(scratch, MemOperand(sp, 0));
++#endif
++ __ addi(sp, sp, Operand(8));
++ __ TestSignBit32(scratch, r0);
++ DeoptimizeIf(ne, instr->environment(), cr0);
++ }
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoMathSqrt(LUnaryMathOperation* instr) {
++ DoubleRegister input = ToDoubleRegister(instr->value());
++ DoubleRegister result = ToDoubleRegister(instr->result());
++ __ fsqrt(result, input);
++}
++
++
++void LCodeGen::DoMathPowHalf(LUnaryMathOperation* instr) {
++ DoubleRegister input = ToDoubleRegister(instr->value());
++ DoubleRegister result = ToDoubleRegister(instr->result());
++ DoubleRegister temp = ToDoubleRegister(instr->temp());
++
++ // Note that according to ECMA-262 15.8.2.13:
++ // Math.pow(-Infinity, 0.5) == Infinity
++ // Math.sqrt(-Infinity) == NaN
++ Label skip, done;
++
++ __ LoadDoubleLiteral(temp, -V8_INFINITY, scratch0());
++ __ fcmpu(input, temp);
++ __ bne(&skip);
++ __ fneg(result, temp);
++ __ b(&done);
++
++ // Add +0 to convert -0 to +0.
++ __ bind(&skip);
++ __ fadd(result, input, kDoubleRegZero);
++ __ fsqrt(result, result);
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoPower(LPower* instr) {
++ Representation exponent_type = instr->hydrogen()->right()->representation();
++ // Having marked this as a call, we can use any registers.
++ // Just make sure that the input/output registers are the expected ones.
++ ASSERT(!instr->right()->IsDoubleRegister() ||
++ ToDoubleRegister(instr->right()).is(d2));
++ ASSERT(!instr->right()->IsRegister() ||
++ ToRegister(instr->right()).is(r5));
++ ASSERT(ToDoubleRegister(instr->left()).is(d1));
++ ASSERT(ToDoubleRegister(instr->result()).is(d3));
++
++ if (exponent_type.IsTagged()) {
++ Label no_deopt;
++ __ JumpIfSmi(r5, &no_deopt);
++ __ LoadP(r10, FieldMemOperand(r5, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
++ __ cmp(r10, ip);
++ DeoptimizeIf(ne, instr->environment());
++ __ bind(&no_deopt);
++ MathPowStub stub(MathPowStub::TAGGED);
++ __ CallStub(&stub);
++ } else if (exponent_type.IsInteger32()) {
++ MathPowStub stub(MathPowStub::INTEGER);
++ __ CallStub(&stub);
++ } else {
++ ASSERT(exponent_type.IsDouble());
++ MathPowStub stub(MathPowStub::DOUBLE);
++ __ CallStub(&stub);
++ }
++}
++
++
++void LCodeGen::DoRandom(LRandom* instr) {
++ class DeferredDoRandom: public LDeferredCode {
++ public:
++ DeferredDoRandom(LCodeGen* codegen, LRandom* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() { codegen()->DoDeferredRandom(instr_); }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LRandom* instr_;
++ };
++
++ DeferredDoRandom* deferred = new(zone()) DeferredDoRandom(this, instr);
++
++ // Having marked this instruction as a call we can use any
++ // registers.
++ ASSERT(ToDoubleRegister(instr->result()).is(d7));
++ ASSERT(ToRegister(instr->global_object()).is(r3));
++
++ static const int kSeedSize = sizeof(uint32_t);
++#ifndef V8_TARGET_ARCH_PPC64 // todo fix (currently fails on 64bit)
++ STATIC_ASSERT(kPointerSize == kSeedSize);
++#endif
++
++ __ LoadP(r3, FieldMemOperand(r3, GlobalObject::kNativeContextOffset));
++ static const int kRandomSeedOffset =
++ FixedArray::kHeaderSize + Context::RANDOM_SEED_INDEX * kPointerSize;
++ __ LoadP(r5, FieldMemOperand(r3, kRandomSeedOffset));
++ // r5: FixedArray of the native context's random seeds
++
++ // Load state[0].
++ __ lwz(r4, FieldMemOperand(r5, ByteArray::kHeaderSize));
++ __ cmpi(r4, Operand::Zero());
++ __ beq(deferred->entry());
++ // Load state[1].
++ __ lwz(r3, FieldMemOperand(r5, ByteArray::kHeaderSize + kSeedSize));
++ // r4: state[0].
++ // r3: state[1].
++
++ // state[0] = 18273 * (state[0] & 0xFFFF) + (state[0] >> 16)
++ __ andi(r6, r4, Operand(0xFFFF));
++ __ li(r7, Operand(18273));
++ __ Mul(r6, r6, r7);
++ __ srwi(r4, r4, Operand(16));
++ __ add(r4, r6, r4);
++ // Save state[0].
++ __ stw(r4, FieldMemOperand(r5, ByteArray::kHeaderSize));
++
++ // state[1] = 36969 * (state[1] & 0xFFFF) + (state[1] >> 16)
++ __ andi(r6, r3, Operand(0xFFFF));
++ __ mov(r7, Operand(36969));
++ __ Mul(r6, r6, r7);
++ __ srwi(r3, r3, Operand(16));
++ __ add(r3, r6, r3);
++ // Save state[1].
++ __ stw(r3, FieldMemOperand(r5, ByteArray::kHeaderSize + kSeedSize));
++
++ // Random bit pattern = (state[0] << 14) + (state[1] & 0x3FFFF)
++ __ ExtractBitMask(r3, r3, 0x3FFFF);
++ __ slwi(r0, r4, Operand(14));
++ __ add(r3, r3, r0);
++
++ __ bind(deferred->exit());
++
++ // Allocate temp stack space to for double
++ __ addi(sp, sp, Operand(-8));
++
++ // 0x41300000 is the top half of 1.0 x 2^20 as a double.
++ __ lis(r4, Operand(0x4130));
++
++ // Move 0x41300000xxxxxxxx (x = random bits) to VFP.
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(r3, MemOperand(sp, 0));
++ __ stw(r4, MemOperand(sp, 4));
++#else
++ __ stw(r4, MemOperand(sp, 0));
++ __ stw(r3, MemOperand(sp, 4));
++#endif
++ __ lfd(d7, MemOperand(sp, 0));
++
++ // Move 0x4130000000000000 to VFP.
++ __ li(r3, Operand::Zero());
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(r3, MemOperand(sp, 0));
++ __ stw(r4, MemOperand(sp, 4));
++#else
++ __ stw(r4, MemOperand(sp, 0));
++ __ stw(r3, MemOperand(sp, 4));
++#endif
++ __ lfd(d8, MemOperand(sp, 0));
++
++ __ addi(sp, sp, Operand(8));
++
++ // Subtract and store the result in the heap number.
++ __ fsub(d7, d7, d8);
++}
++
++
++void LCodeGen::DoDeferredRandom(LRandom* instr) {
++ __ PrepareCallCFunction(1, scratch0());
++ __ CallCFunction(ExternalReference::random_uint32_function(isolate()), 1);
++ // Return value is in r3.
++}
++
++
++void LCodeGen::DoMathLog(LUnaryMathOperation* instr) {
++ ASSERT(ToDoubleRegister(instr->result()).is(d2));
++ TranscendentalCacheStub stub(TranscendentalCache::LOG,
++ TranscendentalCacheStub::UNTAGGED);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoMathTan(LUnaryMathOperation* instr) {
++ ASSERT(ToDoubleRegister(instr->result()).is(d2));
++ TranscendentalCacheStub stub(TranscendentalCache::TAN,
++ TranscendentalCacheStub::UNTAGGED);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoMathCos(LUnaryMathOperation* instr) {
++ ASSERT(ToDoubleRegister(instr->result()).is(d2));
++ TranscendentalCacheStub stub(TranscendentalCache::COS,
++ TranscendentalCacheStub::UNTAGGED);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoMathSin(LUnaryMathOperation* instr) {
++ ASSERT(ToDoubleRegister(instr->result()).is(d2));
++ TranscendentalCacheStub stub(TranscendentalCache::SIN,
++ TranscendentalCacheStub::UNTAGGED);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoUnaryMathOperation(LUnaryMathOperation* instr) {
++ switch (instr->op()) {
++ case kMathAbs:
++ DoMathAbs(instr);
++ break;
++ case kMathFloor:
++ DoMathFloor(instr);
++ break;
++ case kMathRound:
++ DoMathRound(instr);
++ break;
++ case kMathSqrt:
++ DoMathSqrt(instr);
++ break;
++ case kMathPowHalf:
++ DoMathPowHalf(instr);
++ break;
++ case kMathCos:
++ DoMathCos(instr);
++ break;
++ case kMathSin:
++ DoMathSin(instr);
++ break;
++ case kMathTan:
++ DoMathTan(instr);
++ break;
++ case kMathLog:
++ DoMathLog(instr);
++ break;
++ default:
++ Abort("Unimplemented type of LUnaryMathOperation.");
++ UNREACHABLE();
++ }
++}
++
++
++void LCodeGen::DoInvokeFunction(LInvokeFunction* instr) {
++ ASSERT(ToRegister(instr->function()).is(r4));
++ ASSERT(instr->HasPointerMap());
++
++ if (instr->known_function().is_null()) {
++ LPointerMap* pointers = instr->pointer_map();
++ RecordPosition(pointers->position());
++ SafepointGenerator generator(this, pointers, Safepoint::kLazyDeopt);
++ ParameterCount count(instr->arity());
++ __ InvokeFunction(r4, count, CALL_FUNCTION, generator, CALL_AS_METHOD);
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ } else {
++ CallKnownFunction(instr->known_function(),
++ instr->arity(),
++ instr,
++ CALL_AS_METHOD,
++ R4_CONTAINS_TARGET);
++ }
++}
++
++
++void LCodeGen::DoCallKeyed(LCallKeyed* instr) {
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ int arity = instr->arity();
++ Handle<Code> ic =
++ isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++}
++
++
++void LCodeGen::DoCallNamed(LCallNamed* instr) {
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ int arity = instr->arity();
++ RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
++ Handle<Code> ic =
++ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
++ __ mov(r5, Operand(instr->name()));
++ CallCode(ic, mode, instr);
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++}
++
++
++void LCodeGen::DoCallFunction(LCallFunction* instr) {
++ ASSERT(ToRegister(instr->function()).is(r4));
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ int arity = instr->arity();
++ CallFunctionStub stub(arity, NO_CALL_FUNCTION_FLAGS);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++}
++
++
++void LCodeGen::DoCallGlobal(LCallGlobal* instr) {
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ int arity = instr->arity();
++ RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
++ Handle<Code> ic =
++ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
++ __ mov(r5, Operand(instr->name()));
++ CallCode(ic, mode, instr);
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++}
++
++
++void LCodeGen::DoCallKnownGlobal(LCallKnownGlobal* instr) {
++ ASSERT(ToRegister(instr->result()).is(r3));
++ CallKnownFunction(instr->target(),
++ instr->arity(),
++ instr,
++ CALL_AS_FUNCTION,
++ R4_UNINITIALIZED);
++}
++
++
++void LCodeGen::DoCallNew(LCallNew* instr) {
++ ASSERT(ToRegister(instr->constructor()).is(r4));
++ ASSERT(ToRegister(instr->result()).is(r3));
++
++ CallConstructStub stub(NO_CALL_FUNCTION_FLAGS);
++ __ mov(r3, Operand(instr->arity()));
++ CallCode(stub.GetCode(), RelocInfo::CONSTRUCT_CALL, instr);
++}
++
++
++void LCodeGen::DoCallRuntime(LCallRuntime* instr) {
++ CallRuntime(instr->function(), instr->arity(), instr);
++}
++
++
++void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
++ Register object = ToRegister(instr->object());
++ Register value = ToRegister(instr->value());
++ Register scratch = scratch0();
++ int offset = instr->offset();
++
++ ASSERT(!object.is(value));
++
++ if (!instr->transition().is_null()) {
++ __ mov(scratch, Operand(instr->transition()));
++ __ StoreP(scratch, FieldMemOperand(object, HeapObject::kMapOffset), r0);
++ if (instr->hydrogen()->NeedsWriteBarrierForMap()) {
++ Register temp = ToRegister(instr->temp());
++ // Update the write barrier for the map field.
++ __ RecordWriteField(object,
++ HeapObject::kMapOffset,
++ scratch,
++ temp,
++ kLRHasBeenSaved,
++ kSaveFPRegs,
++ OMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ }
++ }
++
++ // Do the store.
++ HType type = instr->hydrogen()->value()->type();
++ SmiCheck check_needed =
++ type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
++ if (instr->is_in_object()) {
++ __ StoreP(value, FieldMemOperand(object, offset), r0);
++ if (instr->hydrogen()->NeedsWriteBarrier()) {
++ // Update the write barrier for the object for in-object properties.
++ __ RecordWriteField(object,
++ offset,
++ value,
++ scratch,
++ kLRHasBeenSaved,
++ kSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ check_needed);
++ }
++ } else {
++ __ LoadP(scratch, FieldMemOperand(object, JSObject::kPropertiesOffset));
++ __ StoreP(value, FieldMemOperand(scratch, offset), r0);
++ if (instr->hydrogen()->NeedsWriteBarrier()) {
++ // Update the write barrier for the properties array.
++ // object is used as a scratch register.
++ __ RecordWriteField(scratch,
++ offset,
++ value,
++ object,
++ kLRHasBeenSaved,
++ kSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ check_needed);
++ }
++ }
++}
++
++
++void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) {
++ ASSERT(ToRegister(instr->object()).is(r4));
++ ASSERT(ToRegister(instr->value()).is(r3));
++
++ // Name is always in r5.
++ __ mov(r5, Operand(instr->name()));
++ Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode)
++ ? isolate()->builtins()->StoreIC_Initialize_Strict()
++ : isolate()->builtins()->StoreIC_Initialize();
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DeoptIfTaggedButNotSmi(LEnvironment* environment,
++ HValue* value,
++ LOperand* operand) {
++ if (value->representation().IsTagged() && !value->type().IsSmi()) {
++ if (operand->IsRegister()) {
++ __ TestIfSmi(ToRegister(operand), r0);
++ } else {
++ __ mov(ip, ToOperand(operand));
++ __ TestIfSmi(ip, r0);
++ }
++ DeoptimizeIf(ne, environment, cr0);
++ }
++}
++
++
++void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) {
++ DeoptIfTaggedButNotSmi(instr->environment(),
++ instr->hydrogen()->length(),
++ instr->length());
++ DeoptIfTaggedButNotSmi(instr->environment(),
++ instr->hydrogen()->index(),
++ instr->index());
++ if (instr->index()->IsConstantOperand()) {
++ int constant_index =
++ ToInteger32(LConstantOperand::cast(instr->index()));
++ if (instr->hydrogen()->length()->representation().IsTagged()) {
++ __ LoadSmiLiteral(ip, Smi::FromInt(constant_index));
++ } else {
++ __ mov(ip, Operand(constant_index));
++ }
++ __ cmpl(ip, ToRegister(instr->length()));
++ } else {
++ __ cmpl(ToRegister(instr->index()), ToRegister(instr->length()));
++ }
++ DeoptimizeIf(ge, instr->environment());
++}
++
++
++void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) {
++ Register value = ToRegister(instr->value());
++ Register elements = ToRegister(instr->object());
++ Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) :
no_reg;
++ Register scratch = scratch0();
++ Register store_base = scratch;
++ int offset = 0;
++
++ // Do the store.
++ if (instr->key()->IsConstantOperand()) {
++ ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
++ LConstantOperand* const_operand = LConstantOperand::cast(instr->key());
++ offset = FixedArray::OffsetOfElementAt(ToInteger32(const_operand) +
++ instr->additional_index());
++ store_base = elements;
++ } else {
++ // Even though the HLoadKeyedFastElement instruction forces the input
++ // representation for the key to be an integer, the input gets replaced
++ // during bound check elimination with the index argument to the bounds
++ // check, which can be tagged, so that case must be handled here, too.
++ if (instr->hydrogen()->key()->representation().IsTagged()) {
++ __ SmiToPtrArrayOffset(scratch, key);
++ } else {
++ __ ShiftLeftImm(scratch, key, Operand(kPointerSizeLog2));
++ }
++ __ add(scratch, elements, scratch);
++ offset = FixedArray::OffsetOfElementAt(instr->additional_index());
++ }
++ __ StoreP(value, FieldMemOperand(store_base, offset), r0);
++
++ if (instr->hydrogen()->NeedsWriteBarrier()) {
++ HType type = instr->hydrogen()->value()->type();
++ SmiCheck check_needed =
++ type.IsHeapObject() ? OMIT_SMI_CHECK : INLINE_SMI_CHECK;
++ // Compute address of modified element and store it into key register.
++ __ Add(key, store_base, offset - kHeapObjectTag, r0);
++ __ RecordWrite(elements,
++ key,
++ value,
++ kLRHasBeenSaved,
++ kSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ check_needed);
++ }
++}
++
++
++void LCodeGen::DoStoreKeyedFastDoubleElement(
++ LStoreKeyedFastDoubleElement* instr) {
++ DwVfpRegister value = ToDoubleRegister(instr->value());
++ Register elements = ToRegister(instr->elements());
++ Register key = no_reg;
++ Register scratch = scratch0();
++ bool key_is_constant = instr->key()->IsConstantOperand();
++ int constant_key = 0;
++ Label no_canonicalization, done;
++
++ // Calculate the effective address of the slot in the array to store the
++ // double value.
++ if (key_is_constant) {
++ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
++ if (constant_key & 0xF0000000) {
++ Abort("array index constant value too big.");
++ }
++ } else {
++ key = ToRegister(instr->key());
++ }
++ int element_size_shift = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS);
++ bool key_is_tagged = instr->hydrogen()->key()->representation().IsTagged();
++ int dst_offset = instr->additional_index() << element_size_shift;
++ if (key_is_constant) {
++ __ Add(scratch, elements,
++ (constant_key << element_size_shift) +
++ FixedDoubleArray::kHeaderSize - kHeapObjectTag,
++ r0);
++ } else {
++ __ IndexToArrayOffset(scratch, key, element_size_shift, key_is_tagged);
++ __ add(scratch, elements, scratch);
++ __ addi(scratch, scratch,
++ Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag));
++ }
++
++ if (instr->NeedsCanonicalization()) {
++ // Check for NaN. All NaNs must be canonicalized.
++ __ fcmpu(value, value);
++ // Only load canonical NaN if the comparison above set unordered.
++ __ bordered(&no_canonicalization);
++
++ uint64_t nan_int64 = BitCast<uint64_t>(
++ FixedDoubleArray::canonical_not_the_hole_nan_as_double());
++ __ mov(r0, Operand(static_cast<intptr_t>(nan_int64)));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(r0, MemOperand(scratch, dst_offset));
++#else
++ __ stw(r0, MemOperand(scratch, dst_offset + 4));
++#endif
++ __ mov(r0, Operand(static_cast<intptr_t>(nan_int64 >> 32)));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ stw(r0, MemOperand(scratch, dst_offset + 4));
++#else
++ __ stw(r0, MemOperand(scratch, dst_offset));
++#endif
++ __ b(&done);
++ }
++
++ __ bind(&no_canonicalization);
++ __ stfd(value, MemOperand(scratch, dst_offset));
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoStoreKeyedSpecializedArrayElement(
++ LStoreKeyedSpecializedArrayElement* instr) {
++ Register external_pointer = ToRegister(instr->external_pointer());
++ Register key = no_reg;
++ ElementsKind elements_kind = instr->elements_kind();
++ bool key_is_constant = instr->key()->IsConstantOperand();
++ int constant_key = 0;
++ if (key_is_constant) {
++ constant_key = ToInteger32(LConstantOperand::cast(instr->key()));
++ if (constant_key & 0xF0000000) {
++ Abort("array index constant value too big.");
++ }
++ } else {
++ key = ToRegister(instr->key());
++ }
++ int element_size_shift = ElementsKindToShiftSize(elements_kind);
++ bool key_is_tagged = instr->hydrogen()->key()->representation().IsTagged();
++ int additional_offset = instr->additional_index() << element_size_shift;
++
++ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS ||
++ elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
++ DwVfpRegister value(ToDoubleRegister(instr->value()));
++ if (key_is_constant) {
++ __ Add(scratch0(), external_pointer,
++ constant_key << element_size_shift,
++ r0);
++ } else {
++ __ IndexToArrayOffset(r0, key, element_size_shift, key_is_tagged);
++ __ add(scratch0(), external_pointer, r0);
++ }
++ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
++ __ frsp(double_scratch0(), value);
++ __ stfs(double_scratch0(), MemOperand(scratch0(), additional_offset));
++ } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS
++ __ stfd(value, MemOperand(scratch0(), additional_offset));
++ }
++ } else {
++ Register value(ToRegister(instr->value()));
++ MemOperand mem_operand = PrepareKeyedOperand(
++ key, external_pointer, key_is_constant, key_is_tagged, constant_key,
++ element_size_shift, instr->additional_index(), additional_offset);
++ switch (elements_kind) {
++ case EXTERNAL_PIXEL_ELEMENTS:
++ case EXTERNAL_BYTE_ELEMENTS:
++ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
++ if (key_is_constant) {
++ __ StoreByte(value, mem_operand, r0);
++ } else {
++ __ stbx(value, mem_operand);
++ }
++ break;
++ case EXTERNAL_SHORT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
++ if (key_is_constant) {
++ __ StoreHalfWord(value, mem_operand, r0);
++ } else {
++ __ sthx(value, mem_operand);
++ }
++ break;
++ case EXTERNAL_INT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
++ if (key_is_constant) {
++ __ StoreWord(value, mem_operand, r0);
++ } else {
++ __ stwx(value, mem_operand);
++ }
++ break;
++ case EXTERNAL_FLOAT_ELEMENTS:
++ case EXTERNAL_DOUBLE_ELEMENTS:
++ case FAST_DOUBLE_ELEMENTS:
++ case FAST_ELEMENTS:
++ case FAST_SMI_ELEMENTS:
++ case FAST_HOLEY_DOUBLE_ELEMENTS:
++ case FAST_HOLEY_ELEMENTS:
++ case FAST_HOLEY_SMI_ELEMENTS:
++ case DICTIONARY_ELEMENTS:
++ case NON_STRICT_ARGUMENTS_ELEMENTS:
++ UNREACHABLE();
++ break;
++ }
++ }
++}
++
++
++void LCodeGen::DoStoreKeyedGeneric(LStoreKeyedGeneric* instr) {
++ ASSERT(ToRegister(instr->object()).is(r5));
++ ASSERT(ToRegister(instr->key()).is(r4));
++ ASSERT(ToRegister(instr->value()).is(r3));
++
++ Handle<Code> ic = (instr->strict_mode_flag() == kStrictMode)
++ ? isolate()->builtins()->KeyedStoreIC_Initialize_Strict()
++ : isolate()->builtins()->KeyedStoreIC_Initialize();
++ CallCode(ic, RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoTransitionElementsKind(LTransitionElementsKind* instr) {
++ Register object_reg = ToRegister(instr->object());
++ Register new_map_reg = ToRegister(instr->new_map_temp());
++ Register scratch = scratch0();
++
++ Handle<Map> from_map = instr->original_map();
++ Handle<Map> to_map = instr->transitioned_map();
++ ElementsKind from_kind = from_map->elements_kind();
++ ElementsKind to_kind = to_map->elements_kind();
++
++ Label not_applicable;
++ __ LoadP(scratch, FieldMemOperand(object_reg, HeapObject::kMapOffset));
++ __ Cmpi(scratch, Operand(from_map), r0);
++ __ bne(¬_applicable);
++ __ mov(new_map_reg, Operand(to_map));
++
++ if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
++ __ StoreP(new_map_reg, FieldMemOperand(object_reg, HeapObject::kMapOffset),
++ r0);
++ // Write barrier.
++ __ RecordWriteField(object_reg, HeapObject::kMapOffset, new_map_reg,
++ scratch, kLRHasBeenSaved, kDontSaveFPRegs);
++ } else if (IsFastSmiElementsKind(from_kind) &&
++ IsFastDoubleElementsKind(to_kind)) {
++ Register fixed_object_reg = ToRegister(instr->temp());
++ ASSERT(fixed_object_reg.is(r5));
++ ASSERT(new_map_reg.is(r6));
++ __ mr(fixed_object_reg, object_reg);
++ CallCode(isolate()->builtins()->TransitionElementsSmiToDouble(),
++ RelocInfo::CODE_TARGET, instr);
++ } else if (IsFastDoubleElementsKind(from_kind) &&
++ IsFastObjectElementsKind(to_kind)) {
++ Register fixed_object_reg = ToRegister(instr->temp());
++ ASSERT(fixed_object_reg.is(r5));
++ ASSERT(new_map_reg.is(r6));
++ __ mr(fixed_object_reg, object_reg);
++ CallCode(isolate()->builtins()->TransitionElementsDoubleToObject(),
++ RelocInfo::CODE_TARGET, instr);
++ } else {
++ UNREACHABLE();
++ }
++ __ bind(¬_applicable);
++}
++
++
++void LCodeGen::DoStringAdd(LStringAdd* instr) {
++ __ push(ToRegister(instr->left()));
++ __ push(ToRegister(instr->right()));
++ StringAddStub stub(NO_STRING_CHECK_IN_STUB);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++}
++
++
++void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) {
++ class DeferredStringCharCodeAt: public LDeferredCode {
++ public:
++ DeferredStringCharCodeAt(LCodeGen* codegen, LStringCharCodeAt* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() { codegen()->DoDeferredStringCharCodeAt(instr_); }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LStringCharCodeAt* instr_;
++ };
++
++ DeferredStringCharCodeAt* deferred =
++ new(zone()) DeferredStringCharCodeAt(this, instr);
++
++ StringCharLoadGenerator::Generate(masm(),
++ ToRegister(instr->string()),
++ ToRegister(instr->index()),
++ ToRegister(instr->result()),
++ deferred->entry());
++ __ bind(deferred->exit());
++}
++
++
++void LCodeGen::DoDeferredStringCharCodeAt(LStringCharCodeAt* instr) {
++ Register string = ToRegister(instr->string());
++ Register result = ToRegister(instr->result());
++ Register scratch = scratch0();
++
++ // TODO(3095996): Get rid of this. For now, we need to make the
++ // result register contain a valid pointer because it is already
++ // contained in the register pointer map.
++ __ li(result, Operand::Zero());
++
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++ __ push(string);
++ // Push the index as a smi. This is safe because of the checks in
++ // DoStringCharCodeAt above.
++ if (instr->index()->IsConstantOperand()) {
++ int const_index = ToInteger32(LConstantOperand::cast(instr->index()));
++ __ LoadSmiLiteral(scratch, Smi::FromInt(const_index));
++ __ push(scratch);
++ } else {
++ Register index = ToRegister(instr->index());
++ __ SmiTag(index);
++ __ push(index);
++ }
++ CallRuntimeFromDeferred(Runtime::kStringCharCodeAt, 2, instr);
++ __ AssertSmi(r3);
++ __ SmiUntag(r3);
++ __ StoreToSafepointRegisterSlot(r3, result);
++}
++
++
++void LCodeGen::DoStringCharFromCode(LStringCharFromCode* instr) {
++ class DeferredStringCharFromCode: public LDeferredCode {
++ public:
++ DeferredStringCharFromCode(LCodeGen* codegen, LStringCharFromCode* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() { codegen()->DoDeferredStringCharFromCode(instr_); }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LStringCharFromCode* instr_;
++ };
++
++ DeferredStringCharFromCode* deferred =
++ new(zone()) DeferredStringCharFromCode(this, instr);
++
++ ASSERT(instr->hydrogen()->value()->representation().IsInteger32());
++ Register char_code = ToRegister(instr->char_code());
++ Register result = ToRegister(instr->result());
++ ASSERT(!char_code.is(result));
++
++ __ cmpli(char_code, Operand(String::kMaxAsciiCharCode));
++ __ bgt(deferred->entry());
++ __ LoadRoot(result, Heap::kSingleCharacterStringCacheRootIndex);
++ __ ShiftLeftImm(r0, char_code, Operand(kPointerSizeLog2));
++ __ add(result, result, r0);
++ __ LoadP(result, FieldMemOperand(result, FixedArray::kHeaderSize));
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ cmp(result, ip);
++ __ beq(deferred->entry());
++ __ bind(deferred->exit());
++}
++
++
++void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
++ Register char_code = ToRegister(instr->char_code());
++ Register result = ToRegister(instr->result());
++
++ // TODO(3095996): Get rid of this. For now, we need to make the
++ // result register contain a valid pointer because it is already
++ // contained in the register pointer map.
++ __ li(result, Operand::Zero());
++
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++ __ SmiTag(char_code);
++ __ push(char_code);
++ CallRuntimeFromDeferred(Runtime::kCharFromCode, 1, instr);
++ __ StoreToSafepointRegisterSlot(r3, result);
++}
++
++
++void LCodeGen::DoStringLength(LStringLength* instr) {
++ Register string = ToRegister(instr->string());
++ Register result = ToRegister(instr->result());
++ __ LoadP(result, FieldMemOperand(string, String::kLengthOffset));
++}
++
++
++void LCodeGen::DoInteger32ToDouble(LInteger32ToDouble* instr) {
++ LOperand* input = instr->value();
++ ASSERT(input->IsRegister() || input->IsStackSlot());
++ LOperand* output = instr->result();
++ ASSERT(output->IsDoubleRegister());
++ if (input->IsStackSlot()) {
++ Register scratch = scratch0();
++ __ LoadP(scratch, ToMemOperand(input));
++ FloatingPointHelper::ConvertIntToDouble(masm(), scratch,
++ ToDoubleRegister(output));
++ } else {
++ FloatingPointHelper::ConvertIntToDouble(masm(), ToRegister(input),
++ ToDoubleRegister(output));
++ }
++}
++
++
++void LCodeGen::DoUint32ToDouble(LUint32ToDouble* instr) {
++ LOperand* input = instr->value();
++ LOperand* output = instr->result();
++ FloatingPointHelper::ConvertUnsignedIntToDouble(masm(), ToRegister(input),
++ ToDoubleRegister(output));
++}
++
++
++void LCodeGen::DoNumberTagI(LNumberTagI* instr) {
++ class DeferredNumberTagI: public LDeferredCode {
++ public:
++ DeferredNumberTagI(LCodeGen* codegen, LNumberTagI* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() {
++ codegen()->DoDeferredNumberTagI(instr_,
++ instr_->value(),
++ SIGNED_INT32);
++ }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LNumberTagI* instr_;
++ };
++
++ Register src = ToRegister(instr->value());
++ Register dst = ToRegister(instr->result());
++
++ DeferredNumberTagI* deferred = new(zone()) DeferredNumberTagI(this, instr);
++#if V8_TARGET_ARCH_PPC64
++ __ SmiTag(dst, src);
++#else
++ __ SmiTagCheckOverflow(dst, src, r0);
++ __ BranchOnOverflow(deferred->entry());
++#endif
++ __ bind(deferred->exit());
++}
++
++
++void LCodeGen::DoNumberTagU(LNumberTagU* instr) {
++ class DeferredNumberTagU: public LDeferredCode {
++ public:
++ DeferredNumberTagU(LCodeGen* codegen, LNumberTagU* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() {
++ codegen()->DoDeferredNumberTagI(instr_,
++ instr_->value(),
++ UNSIGNED_INT32);
++ }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LNumberTagU* instr_;
++ };
++
++ LOperand* input = instr->value();
++ ASSERT(input->IsRegister() && input->Equals(instr->result()));
++ Register reg = ToRegister(input);
++
++ DeferredNumberTagU* deferred = new(zone()) DeferredNumberTagU(this, instr);
++ __ Cmpli(reg, Operand(Smi::kMaxValue), r0);
++ __ bgt(deferred->entry());
++ __ SmiTag(reg, reg);
++ __ bind(deferred->exit());
++}
++
++
++void LCodeGen::DoDeferredNumberTagI(LInstruction* instr,
++ LOperand* value,
++ IntegerSignedness signedness) {
++ Label slow;
++ Register src = ToRegister(value);
++ Register dst = ToRegister(instr->result());
++ DoubleRegister dbl_scratch = double_scratch0();
++
++ // Preserve the value of all registers.
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++
++ Label done;
++ if (signedness == SIGNED_INT32) {
++ // There was overflow, so bits 30 and 31 of the original integer
++ // disagree. Try to allocate a heap number in new space and store
++ // the value in there. If that fails, call the runtime system.
++ if (dst.is(src)) {
++ __ SmiUntag(src, dst);
++ __ xoris(src, src, Operand(HeapNumber::kSignMask >> 16));
++ }
++ FloatingPointHelper::ConvertIntToDouble(masm(), src, dbl_scratch);
++ } else {
++ FloatingPointHelper::ConvertUnsignedIntToDouble(masm(), src, dbl_scratch);
++ }
++
++ if (FLAG_inline_new) {
++ __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r8, r6, r7, r9, &slow);
++ __ Move(dst, r8);
++ __ b(&done);
++ }
++
++ // Slow case: Call the runtime system to do the number allocation.
++ __ bind(&slow);
++
++ // TODO(3095996): Put a valid pointer value in the stack slot where the result
++ // register is stored, as this register is in the pointer map, but contains an
++ // integer value.
++ __ li(ip, Operand::Zero());
++ __ StoreToSafepointRegisterSlot(ip, dst);
++ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
++ __ Move(dst, r3);
++
++ // Done. Put the value in dbl_scratch into the value of the allocated heap
++ // number.
++ __ bind(&done);
++ __ stfd(dbl_scratch, FieldMemOperand(dst, HeapNumber::kValueOffset));
++ __ StoreToSafepointRegisterSlot(dst, dst);
++}
++
++
++void LCodeGen::DoNumberTagD(LNumberTagD* instr) {
++ class DeferredNumberTagD: public LDeferredCode {
++ public:
++ DeferredNumberTagD(LCodeGen* codegen, LNumberTagD* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() { codegen()->DoDeferredNumberTagD(instr_); }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LNumberTagD* instr_;
++ };
++
++ DoubleRegister input_reg = ToDoubleRegister(instr->value());
++ Register scratch = scratch0();
++ Register reg = ToRegister(instr->result());
++ Register temp1 = ToRegister(instr->temp());
++ Register temp2 = ToRegister(instr->temp2());
++
++ DeferredNumberTagD* deferred = new(zone()) DeferredNumberTagD(this, instr);
++ if (FLAG_inline_new) {
++ __ LoadRoot(scratch, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(reg, temp1, temp2, scratch, deferred->entry());
++ } else {
++ __ b(deferred->entry());
++ }
++ __ bind(deferred->exit());
++ __ stfd(input_reg, FieldMemOperand(reg, HeapNumber::kValueOffset));
++}
++
++
++void LCodeGen::DoDeferredNumberTagD(LNumberTagD* instr) {
++ // TODO(3095996): Get rid of this. For now, we need to make the
++ // result register contain a valid pointer because it is already
++ // contained in the register pointer map.
++ Register reg = ToRegister(instr->result());
++ __ li(reg, Operand::Zero());
++
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++ CallRuntimeFromDeferred(Runtime::kAllocateHeapNumber, 0, instr);
++ __ StoreToSafepointRegisterSlot(r3, reg);
++}
++
++
++void LCodeGen::DoSmiTag(LSmiTag* instr) {
++ ASSERT(!instr->hydrogen_value()->CheckFlag(HValue::kCanOverflow));
++ __ SmiTag(ToRegister(instr->result()), ToRegister(instr->value()));
++}
++
++
++void LCodeGen::DoSmiUntag(LSmiUntag* instr) {
++ Register scratch = scratch0();
++ Register input = ToRegister(instr->value());
++ Register result = ToRegister(instr->result());
++ if (instr->needs_check()) {
++ STATIC_ASSERT(kHeapObjectTag == 1);
++ // If the input is a HeapObject, value of scratch won't be zero.
++ __ andi(scratch, input, Operand(kHeapObjectTag));
++ __ SmiUntag(result, input);
++ DeoptimizeIf(ne, instr->environment(), cr0);
++ } else {
++ __ SmiUntag(result, input);
++ }
++}
++
++
++void LCodeGen::EmitNumberUntagD(Register input_reg,
++ DoubleRegister result_reg,
++ bool deoptimize_on_undefined,
++ bool deoptimize_on_minus_zero,
++ LEnvironment* env) {
++ Register scratch = scratch0();
++ ASSERT(!result_reg.is(double_scratch0()));
++
++ Label load_smi, heap_number, done;
++
++ // Smi check.
++ __ UntagAndJumpIfSmi(scratch, input_reg, &load_smi);
++
++ // Heap number map check.
++ __ LoadP(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
++ __ cmp(scratch, ip);
++ if (deoptimize_on_undefined) {
++ DeoptimizeIf(ne, env);
++ } else {
++ Label heap_number;
++ __ beq(&heap_number);
++
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ cmp(input_reg, ip);
++ DeoptimizeIf(ne, env);
++
++ // Convert undefined to NaN.
++ __ LoadRoot(ip, Heap::kNanValueRootIndex);
++ __ lfd(result_reg, FieldMemOperand(ip, HeapNumber::kValueOffset));
++ __ b(&done);
++
++ __ bind(&heap_number);
++ }
++ // Heap number to double register conversion.
++ __ lfd(result_reg, FieldMemOperand(input_reg, HeapNumber::kValueOffset));
++ if (deoptimize_on_minus_zero) {
++ __ subi(sp, sp, Operand(8));
++ __ stfd(result_reg, MemOperand(sp));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(ip, MemOperand(sp, 0));
++ __ lwz(scratch, MemOperand(sp, 4));
++#else
++ __ lwz(ip, MemOperand(sp, 4));
++ __ lwz(scratch, MemOperand(sp, 0));
++#endif
++ __ addi(sp, sp, Operand(8));
++
++ __ cmpi(ip, Operand::Zero());
++ __ bne(&done);
++ __ Cmpi(scratch, Operand(HeapNumber::kSignMask), r0);
++ DeoptimizeIf(eq, env);
++ }
++ __ b(&done);
++
++ // Smi to double register conversion
++ __ bind(&load_smi);
++ // scratch: untagged value of input_reg
++ FloatingPointHelper::ConvertIntToDouble(masm(), scratch, result_reg);
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoDeferredTaggedToI(LTaggedToI* instr) {
++ Register input_reg = ToRegister(instr->value());
++ Register scratch1 = scratch0();
++ Register scratch2 = ToRegister(instr->temp());
++ DwVfpRegister double_scratch = double_scratch0();
++ DwVfpRegister double_scratch2 = ToDoubleRegister(instr->temp3());
++
++ ASSERT(!scratch1.is(input_reg) && !scratch1.is(scratch2));
++ ASSERT(!scratch2.is(input_reg) && !scratch2.is(scratch1));
++
++ Label done;
++
++ // Heap number map check.
++ __ LoadP(scratch1, FieldMemOperand(input_reg, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
++ __ cmp(scratch1, ip);
++
++ if (instr->truncating()) {
++ Register scratch3 = ToRegister(instr->temp2());
++ ASSERT(!scratch3.is(input_reg) &&
++ !scratch3.is(scratch1) &&
++ !scratch3.is(scratch2));
++ // Performs a truncating conversion of a floating point number as used by
++ // the JS bitwise operations.
++ Label heap_number;
++ __ beq(&heap_number);
++ // Check for undefined. Undefined is converted to zero for truncating
++ // conversions.
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ cmp(input_reg, ip);
++ DeoptimizeIf(ne, instr->environment());
++ __ li(input_reg, Operand::Zero());
++ __ b(&done);
++
++ __ bind(&heap_number);
++ __ lfd(double_scratch2,
++ FieldMemOperand(input_reg, HeapNumber::kValueOffset));
++
++ __ EmitECMATruncate(input_reg,
++ double_scratch2,
++ double_scratch,
++ scratch1,
++ scratch2,
++ scratch3);
++
++ } else {
++ // Deoptimize if we don't have a heap number.
++ DeoptimizeIf(ne, instr->environment());
++
++ __ lfd(double_scratch,
++ FieldMemOperand(input_reg, HeapNumber::kValueOffset));
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ // preserve heap number pointer in scratch2 for minus zero check below
++ __ mr(scratch2, input_reg);
++ }
++ __ EmitVFPTruncate(kRoundToZero,
++ input_reg,
++ double_scratch,
++ scratch1,
++ double_scratch2,
++ kCheckForInexactConversion);
++ DeoptimizeIf(ne, instr->environment());
++
++ if (instr->hydrogen()->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ __ cmpi(input_reg, Operand::Zero());
++ __ bne(&done);
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(scratch1, FieldMemOperand(scratch2, HeapNumber::kValueOffset + 4));
++#else
++ __ lwz(scratch1, FieldMemOperand(scratch2, HeapNumber::kValueOffset));
++#endif
++ __ TestSignBit32(scratch1, r0);
++ DeoptimizeIf(ne, instr->environment(), cr0);
++ }
++ }
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoTaggedToI(LTaggedToI* instr) {
++ class DeferredTaggedToI: public LDeferredCode {
++ public:
++ DeferredTaggedToI(LCodeGen* codegen, LTaggedToI* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() { codegen()->DoDeferredTaggedToI(instr_); }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LTaggedToI* instr_;
++ };
++
++ LOperand* input = instr->value();
++ ASSERT(input->IsRegister());
++ ASSERT(input->Equals(instr->result()));
++
++ Register input_reg = ToRegister(input);
++
++ DeferredTaggedToI* deferred = new(zone()) DeferredTaggedToI(this, instr);
++
++ // Branch to deferred code if the input is a HeapObject.
++ __ JumpIfNotSmi(input_reg, deferred->entry());
++
++ __ SmiUntag(input_reg);
++ __ bind(deferred->exit());
++}
++
++
++void LCodeGen::DoNumberUntagD(LNumberUntagD* instr) {
++ LOperand* input = instr->value();
++ ASSERT(input->IsRegister());
++ LOperand* result = instr->result();
++ ASSERT(result->IsDoubleRegister());
++
++ Register input_reg = ToRegister(input);
++ DoubleRegister result_reg = ToDoubleRegister(result);
++
++ EmitNumberUntagD(input_reg, result_reg,
++ instr->hydrogen()->deoptimize_on_undefined(),
++ instr->hydrogen()->deoptimize_on_minus_zero(),
++ instr->environment());
++}
++
++
++void LCodeGen::DoDoubleToI(LDoubleToI* instr) {
++ Register result_reg = ToRegister(instr->result());
++ Register scratch1 = scratch0();
++ Register scratch2 = ToRegister(instr->temp());
++ DwVfpRegister double_input = ToDoubleRegister(instr->value());
++
++ Label done;
++
++ if (instr->truncating()) {
++ Register scratch3 = ToRegister(instr->temp2());
++ DwVfpRegister double_scratch = double_scratch0();
++ __ EmitECMATruncate(result_reg,
++ double_input,
++ double_scratch,
++ scratch1,
++ scratch2,
++ scratch3);
++ } else {
++ DwVfpRegister double_scratch = double_scratch0();
++ __ EmitVFPTruncate(kRoundToMinusInf,
++ result_reg,
++ double_input,
++ scratch1,
++ double_scratch,
++ kCheckForInexactConversion);
++
++ // Deoptimize if we had a vfp invalid exception,
++ // including inexact operation.
++ DeoptimizeIf(ne, instr->environment());
++ }
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoCheckSmi(LCheckSmi* instr) {
++ LOperand* input = instr->value();
++ __ TestIfSmi(ToRegister(input), r0);
++ DeoptimizeIf(ne, instr->environment(), cr0);
++}
++
++
++void LCodeGen::DoCheckNonSmi(LCheckNonSmi* instr) {
++ LOperand* input = instr->value();
++ __ TestIfSmi(ToRegister(input), r0);
++ DeoptimizeIf(eq, instr->environment(), cr0);
++}
++
++
++void LCodeGen::DoCheckInstanceType(LCheckInstanceType* instr) {
++ Register input = ToRegister(instr->value());
++ Register scratch = scratch0();
++
++ __ LoadP(scratch, FieldMemOperand(input, HeapObject::kMapOffset));
++ __ lbz(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
++
++ if (instr->hydrogen()->is_interval_check()) {
++ InstanceType first;
++ InstanceType last;
++ instr->hydrogen()->GetCheckInterval(&first, &last);
++
++ __ cmpli(scratch, Operand(first));
++
++ // If there is only one type in the interval check for equality.
++ if (first == last) {
++ DeoptimizeIf(ne, instr->environment());
++ } else {
++ DeoptimizeIf(lt, instr->environment());
++ // Omit check for the last type.
++ if (last != LAST_TYPE) {
++ __ cmpli(scratch, Operand(last));
++ DeoptimizeIf(gt, instr->environment());
++ }
++ }
++ } else {
++ uint8_t mask;
++ uint8_t tag;
++ instr->hydrogen()->GetCheckMaskAndTag(&mask, &tag);
++
++ if (IsPowerOf2(mask)) {
++ ASSERT(tag == 0 || IsPowerOf2(tag));
++ __ andi(r0, scratch, Operand(mask));
++ DeoptimizeIf(tag == 0 ? ne : eq, instr->environment(), cr0);
++ } else {
++ __ andi(scratch, scratch, Operand(mask));
++ __ cmpi(scratch, Operand(tag));
++ DeoptimizeIf(ne, instr->environment());
++ }
++ }
++}
++
++
++void LCodeGen::DoCheckFunction(LCheckFunction* instr) {
++ Register reg = ToRegister(instr->value());
++ Handle<JSFunction> target = instr->hydrogen()->target();
++ if (isolate()->heap()->InNewSpace(*target)) {
++ Register reg = ToRegister(instr->value());
++ Handle<JSGlobalPropertyCell> cell =
++ isolate()->factory()->NewJSGlobalPropertyCell(target);
++ __ mov(ip, Operand(Handle<Object>(cell)));
++ __ LoadP(ip, FieldMemOperand(ip, JSGlobalPropertyCell::kValueOffset));
++ __ cmp(reg, ip);
++ } else {
++ __ Cmpi(reg, Operand(target), r0);
++ }
++ DeoptimizeIf(ne, instr->environment());
++}
++
++
++void LCodeGen::DoCheckMapCommon(Register reg,
++ Register scratch,
++ Handle<Map> map,
++ CompareMapMode mode,
++ LEnvironment* env) {
++ Label success;
++ __ CompareMap(reg, scratch, map, &success, mode);
++ DeoptimizeIf(ne, env);
++ __ bind(&success);
++}
++
++
++void LCodeGen::DoCheckMaps(LCheckMaps* instr) {
++ Register scratch = scratch0();
++ LOperand* input = instr->value();
++ ASSERT(input->IsRegister());
++ Register reg = ToRegister(input);
++
++ Label success;
++ SmallMapList* map_set = instr->hydrogen()->map_set();
++ for (int i = 0; i < map_set->length() - 1; i++) {
++ Handle<Map> map = map_set->at(i);
++ __ CompareMap(reg, scratch, map, &success, REQUIRE_EXACT_MAP);
++ __ beq(&success);
++ }
++ Handle<Map> map = map_set->last();
++ DoCheckMapCommon(reg, scratch, map, REQUIRE_EXACT_MAP, instr->environment());
++ __ bind(&success);
++}
++
++
++void LCodeGen::DoClampDToUint8(LClampDToUint8* instr) {
++ DoubleRegister value_reg = ToDoubleRegister(instr->unclamped());
++ Register result_reg = ToRegister(instr->result());
++ DoubleRegister temp_reg = ToDoubleRegister(instr->temp());
++ __ ClampDoubleToUint8(result_reg, value_reg, temp_reg, double_scratch0());
++}
++
++
++void LCodeGen::DoClampIToUint8(LClampIToUint8* instr) {
++ Register unclamped_reg = ToRegister(instr->unclamped());
++ Register result_reg = ToRegister(instr->result());
++ __ ClampUint8(result_reg, unclamped_reg);
++}
++
++
++void LCodeGen::DoClampTToUint8(LClampTToUint8* instr) {
++ Register scratch = scratch0();
++ Register input_reg = ToRegister(instr->unclamped());
++ Register result_reg = ToRegister(instr->result());
++ DoubleRegister temp_reg1 = ToDoubleRegister(instr->temp1());
++ DoubleRegister temp_reg2 = ToDoubleRegister(instr->temp2());
++ Label is_smi, done, heap_number;
++
++ // Both smi and heap number cases are handled.
++ __ UntagAndJumpIfSmi(result_reg, input_reg, &is_smi);
++
++ // Check for heap number
++ __ LoadP(scratch, FieldMemOperand(input_reg, HeapObject::kMapOffset));
++ __ Cmpi(scratch, Operand(factory()->heap_number_map()), r0);
++ __ beq(&heap_number);
++
++ // Check for undefined. Undefined is converted to zero for clamping
++ // conversions.
++ __ Cmpi(input_reg, Operand(factory()->undefined_value()), r0);
++ DeoptimizeIf(ne, instr->environment());
++ __ li(result_reg, Operand::Zero());
++ __ b(&done);
++
++ // Heap number
++ __ bind(&heap_number);
++ __ lfd(double_scratch0(), FieldMemOperand(input_reg,
++ HeapNumber::kValueOffset));
++ __ ClampDoubleToUint8(result_reg, double_scratch0(), temp_reg1, temp_reg2);
++ __ b(&done);
++
++ // smi
++ __ bind(&is_smi);
++ __ ClampUint8(result_reg, result_reg);
++
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoCheckPrototypeMaps(LCheckPrototypeMaps* instr) {
++ Register temp1 = ToRegister(instr->temp());
++ Register temp2 = ToRegister(instr->temp2());
++
++ Handle<JSObject> holder = instr->holder();
++ Handle<JSObject> current_prototype = instr->prototype();
++
++ // Load prototype object.
++ __ LoadHeapObject(temp1, current_prototype);
++
++ // Check prototype maps up to the holder.
++ while (!current_prototype.is_identical_to(holder)) {
++ DoCheckMapCommon(temp1, temp2,
++ Handle<Map>(current_prototype->map()),
++ ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment());
++ current_prototype =
++ Handle<JSObject>(JSObject::cast(current_prototype->GetPrototype()));
++ // Load next prototype object.
++ __ LoadHeapObject(temp1, current_prototype);
++ }
++
++ // Check the holder map.
++ DoCheckMapCommon(temp1, temp2,
++ Handle<Map>(current_prototype->map()),
++ ALLOW_ELEMENT_TRANSITION_MAPS, instr->environment());
++ DeoptimizeIf(ne, instr->environment());
++}
++
++
++void LCodeGen::DoAllocateObject(LAllocateObject* instr) {
++ class DeferredAllocateObject: public LDeferredCode {
++ public:
++ DeferredAllocateObject(LCodeGen* codegen, LAllocateObject* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() { codegen()->DoDeferredAllocateObject(instr_); }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LAllocateObject* instr_;
++ };
++
++ DeferredAllocateObject* deferred =
++ new(zone()) DeferredAllocateObject(this, instr);
++
++ Register result = ToRegister(instr->result());
++ Register scratch = ToRegister(instr->temp());
++ Register scratch2 = ToRegister(instr->temp2());
++ Handle<JSFunction> constructor = instr->hydrogen()->constructor();
++ Handle<Map> initial_map(constructor->initial_map());
++ int instance_size = initial_map->instance_size();
++ ASSERT(initial_map->pre_allocated_property_fields() +
++ initial_map->unused_property_fields() -
++ initial_map->inobject_properties() == 0);
++
++ // Allocate memory for the object. The initial map might change when
++ // the constructor's prototype changes, but instance size and property
++ // counts remain unchanged (if slack tracking finished).
++ ASSERT(!constructor->shared()->IsInobjectSlackTrackingInProgress());
++ __ AllocateInNewSpace(instance_size,
++ result,
++ scratch,
++ scratch2,
++ deferred->entry(),
++ TAG_OBJECT);
++
++ __ bind(deferred->exit());
++ if (FLAG_debug_code) {
++ Label is_in_new_space;
++ __ JumpIfInNewSpace(result, scratch, &is_in_new_space);
++ __ Abort("Allocated object is not in new-space");
++ __ bind(&is_in_new_space);
++ }
++
++ // Load the initial map.
++ Register map = scratch;
++ __ LoadHeapObject(map, constructor);
++ __ LoadP(map, FieldMemOperand(map, JSFunction::kPrototypeOrInitialMapOffset));
++
++ // Initialize map and fields of the newly allocated object.
++ ASSERT(initial_map->instance_type() == JS_OBJECT_TYPE);
++ __ StoreP(map, FieldMemOperand(result, JSObject::kMapOffset), r0);
++ __ LoadRoot(scratch, Heap::kEmptyFixedArrayRootIndex);
++ __ StoreP(scratch, FieldMemOperand(result, JSObject::kElementsOffset), r0);
++ __ StoreP(scratch, FieldMemOperand(result, JSObject::kPropertiesOffset), r0);
++ if (initial_map->inobject_properties() != 0) {
++ __ LoadRoot(scratch, Heap::kUndefinedValueRootIndex);
++ for (int i = 0; i < initial_map->inobject_properties(); i++) {
++ int property_offset = JSObject::kHeaderSize + i * kPointerSize;
++ __ StoreP(scratch, FieldMemOperand(result, property_offset), r0);
++ }
++ }
++}
++
++
++void LCodeGen::DoDeferredAllocateObject(LAllocateObject* instr) {
++ Register result = ToRegister(instr->result());
++ Handle<JSFunction> constructor = instr->hydrogen()->constructor();
++ Handle<Map> initial_map(constructor->initial_map());
++ int instance_size = initial_map->instance_size();
++
++ // TODO(3095996): Get rid of this. For now, we need to make the
++ // result register contain a valid pointer because it is already
++ // contained in the register pointer map.
++ __ li(result, Operand::Zero());
++
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++ __ LoadSmiLiteral(r3, Smi::FromInt(instance_size));
++ __ push(r3);
++ CallRuntimeFromDeferred(Runtime::kAllocateInNewSpace, 1, instr);
++ __ StoreToSafepointRegisterSlot(r3, result);
++}
++
++
++void LCodeGen::DoArrayLiteral(LArrayLiteral* instr) {
++ Handle<FixedArray>
literals(instr->environment()->closure()->literals());
++ ElementsKind boilerplate_elements_kind =
++ instr->hydrogen()->boilerplate_elements_kind();
++
++ // Deopt if the array literal boilerplate ElementsKind is of a type different
++ // than the expected one. The check isn't necessary if the boilerplate has
++ // already been converted to TERMINAL_FAST_ELEMENTS_KIND.
++ if (CanTransitionToMoreGeneralFastElementsKind(
++ boilerplate_elements_kind, true)) {
++ __ LoadHeapObject(r4, instr->hydrogen()->boilerplate_object());
++ // Load map into r5.
++ __ LoadP(r5, FieldMemOperand(r4, HeapObject::kMapOffset));
++ // Load the map's "bit field 2".
++ __ lbz(r5, FieldMemOperand(r5, Map::kBitField2Offset));
++ // Retrieve elements_kind from bit field 2.
++ __ ExtractBitMask(r5, r5, Map::kElementsKindMask);
++ __ Cmpi(r5, Operand(boilerplate_elements_kind), r0);
++ DeoptimizeIf(ne, instr->environment());
++ }
++
++ // Set up the parameters to the stub/runtime call.
++ __ LoadHeapObject(r6, literals);
++ __ LoadSmiLiteral(r5, Smi::FromInt(instr->hydrogen()->literal_index()));
++ // Boilerplate already exists, constant elements are never accessed.
++ // Pass an empty fixed array.
++ __ mov(r4, Operand(isolate()->factory()->empty_fixed_array()));
++ __ Push(r6, r5, r4);
++
++ // Pick the right runtime function or stub to call.
++ int length = instr->hydrogen()->length();
++ if (instr->hydrogen()->IsCopyOnWrite()) {
++ ASSERT(instr->hydrogen()->depth() == 1);
++ FastCloneShallowArrayStub::Mode mode =
++ FastCloneShallowArrayStub::COPY_ON_WRITE_ELEMENTS;
++ FastCloneShallowArrayStub stub(mode, length);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ } else if (instr->hydrogen()->depth() > 1) {
++ CallRuntime(Runtime::kCreateArrayLiteral, 3, instr);
++ } else if (length > FastCloneShallowArrayStub::kMaximumClonedLength) {
++ CallRuntime(Runtime::kCreateArrayLiteralShallow, 3, instr);
++ } else {
++ FastCloneShallowArrayStub::Mode mode =
++ boilerplate_elements_kind == FAST_DOUBLE_ELEMENTS
++ ? FastCloneShallowArrayStub::CLONE_DOUBLE_ELEMENTS
++ : FastCloneShallowArrayStub::CLONE_ELEMENTS;
++ FastCloneShallowArrayStub stub(mode, length);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ }
++}
++
++
++void LCodeGen::EmitDeepCopy(Handle<JSObject> object,
++ Register result,
++ Register source,
++ int* offset) {
++ ASSERT(!source.is(r5));
++ ASSERT(!result.is(r5));
++
++ // Only elements backing stores for non-COW arrays need to be copied.
++ Handle<FixedArrayBase> elements(object->elements());
++ bool has_elements = elements->length() > 0 &&
++ elements->map() != isolate()->heap()->fixed_cow_array_map();
++
++ // Increase the offset so that subsequent objects end up right after
++ // this object and its backing store.
++ int object_offset = *offset;
++ int object_size = object->map()->instance_size();
++ int elements_offset = *offset + object_size;
++ int elements_size = has_elements ? elements->Size() : 0;
++ *offset += object_size + elements_size;
++
++ // Copy object header.
++ ASSERT(object->properties()->length() == 0);
++ int inobject_properties = object->map()->inobject_properties();
++ int header_size = object_size - inobject_properties * kPointerSize;
++ for (int i = 0; i < header_size; i += kPointerSize) {
++ if (has_elements && i == JSObject::kElementsOffset) {
++ __ Add(r5, result, elements_offset, r0);
++ } else {
++ __ LoadP(r5, FieldMemOperand(source, i));
++ }
++ __ StoreP(r5, FieldMemOperand(result, object_offset + i), r0);
++ }
++
++ // Copy in-object properties.
++ for (int i = 0; i < inobject_properties; i++) {
++ int total_offset = object_offset + object->GetInObjectPropertyOffset(i);
++ Handle<Object> value =
Handle<Object>(object->InObjectPropertyAt(i));
++ if (value->IsJSObject()) {
++ Handle<JSObject> value_object = Handle<JSObject>::cast(value);
++ __ Add(r5, result, *offset, r0);
++ __ StoreP(r5, FieldMemOperand(result, total_offset), r0);
++ __ LoadHeapObject(source, value_object);
++ EmitDeepCopy(value_object, result, source, offset);
++ } else if (value->IsHeapObject()) {
++ __ LoadHeapObject(r5, Handle<HeapObject>::cast(value));
++ __ StoreP(r5, FieldMemOperand(result, total_offset), r0);
++ } else {
++ __ mov(r5, Operand(value));
++ __ StoreP(r5, FieldMemOperand(result, total_offset), r0);
++ }
++ }
++
++ if (has_elements) {
++ // Copy elements backing store header.
++ __ LoadHeapObject(source, elements);
++ for (int i = 0; i < FixedArray::kHeaderSize; i += kPointerSize) {
++ __ LoadP(r5, FieldMemOperand(source, i));
++ __ StoreP(r5, FieldMemOperand(result, elements_offset + i), r0);
++ }
++
++ // Copy elements backing store content.
++ int elements_length = has_elements ? elements->length() : 0;
++ if (elements->IsFixedDoubleArray()) {
++ Handle<FixedDoubleArray> double_array =
++ Handle<FixedDoubleArray>::cast(elements);
++ for (int i = 0; i < elements_length; i++) {
++ int64_t value = double_array->get_representation(i);
++ int32_t value_low = static_cast<int32_t>(value & 0xFFFFFFFF);
++ int32_t value_high = static_cast<int32_t>(value >> 32);
++ int total_offset =
++ elements_offset + FixedDoubleArray::OffsetOfElementAt(i);
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ mov(r5, Operand(value_low));
++ __ stw(r5, FieldMemOperand(result, total_offset));
++ __ mov(r5, Operand(value_high));
++ __ stw(r5, FieldMemOperand(result, total_offset + 4));
++#else
++ __ mov(r5, Operand(value_high));
++ __ stw(r5, FieldMemOperand(result, total_offset));
++ __ mov(r5, Operand(value_low));
++ __ stw(r5, FieldMemOperand(result, total_offset + 4));
++#endif
++ }
++ } else if (elements->IsFixedArray()) {
++ Handle<FixedArray> fast_elements =
Handle<FixedArray>::cast(elements);
++ for (int i = 0; i < elements_length; i++) {
++ int total_offset = elements_offset + FixedArray::OffsetOfElementAt(i);
++ Handle<Object> value(fast_elements->get(i));
++ if (value->IsJSObject()) {
++ Handle<JSObject> value_object = Handle<JSObject>::cast(value);
++ __ Add(r5, result, *offset, r0);
++ __ StoreP(r5, FieldMemOperand(result, total_offset), r0);
++ __ LoadHeapObject(source, value_object);
++ EmitDeepCopy(value_object, result, source, offset);
++ } else if (value->IsHeapObject()) {
++ __ LoadHeapObject(r5, Handle<HeapObject>::cast(value));
++ __ StoreP(r5, FieldMemOperand(result, total_offset), r0);
++ } else {
++ __ mov(r5, Operand(value));
++ __ StoreP(r5, FieldMemOperand(result, total_offset), r0);
++ }
++ }
++ } else {
++ UNREACHABLE();
++ }
++ }
++}
++
++
++void LCodeGen::DoFastLiteral(LFastLiteral* instr) {
++ int size = instr->hydrogen()->total_size();
++ ElementsKind boilerplate_elements_kind =
++ instr->hydrogen()->boilerplate()->GetElementsKind();
++
++ // Deopt if the array literal boilerplate ElementsKind is of a type different
++ // than the expected one. The check isn't necessary if the boilerplate has
++ // already been converted to TERMINAL_FAST_ELEMENTS_KIND.
++ if (CanTransitionToMoreGeneralFastElementsKind(
++ boilerplate_elements_kind, true)) {
++ __ LoadHeapObject(r4, instr->hydrogen()->boilerplate());
++ // Load map into r5.
++ __ LoadP(r5, FieldMemOperand(r4, HeapObject::kMapOffset));
++ // Load the map's "bit field 2".
++ __ lbz(r5, FieldMemOperand(r5, Map::kBitField2Offset));
++ // Retrieve elements_kind from bit field 2.
++ __ ExtractBitMask(r5, r5, Map::kElementsKindMask);
++ __ Cmpi(r5, Operand(boilerplate_elements_kind), r0);
++ DeoptimizeIf(ne, instr->environment());
++ }
++
++ // Allocate all objects that are part of the literal in one big
++ // allocation. This avoids multiple limit checks.
++ Label allocated, runtime_allocate;
++ __ AllocateInNewSpace(size, r3, r5, r6, &runtime_allocate, TAG_OBJECT);
++ __ b(&allocated);
++
++ __ bind(&runtime_allocate);
++ __ LoadSmiLiteral(r3, Smi::FromInt(size));
++ __ push(r3);
++ CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
++
++ __ bind(&allocated);
++ int offset = 0;
++ __ LoadHeapObject(r4, instr->hydrogen()->boilerplate());
++ EmitDeepCopy(instr->hydrogen()->boilerplate(), r3, r4, &offset);
++ ASSERT_EQ(size, offset);
++}
++
++
++void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) {
++ Handle<FixedArray>
literals(instr->environment()->closure()->literals());
++ Handle<FixedArray> constant_properties =
++ instr->hydrogen()->constant_properties();
++
++ // Set up the parameters to the stub/runtime call.
++ __ LoadHeapObject(r7, literals);
++ __ LoadSmiLiteral(r6, Smi::FromInt(instr->hydrogen()->literal_index()));
++ __ mov(r5, Operand(constant_properties));
++ int flags = instr->hydrogen()->fast_elements()
++ ? ObjectLiteral::kFastElements
++ : ObjectLiteral::kNoFlags;
++ __ LoadSmiLiteral(r4, Smi::FromInt(flags));
++ __ Push(r7, r6, r5, r4);
++
++ // Pick the right runtime function or stub to call.
++ int properties_count = constant_properties->length() / 2;
++ if (instr->hydrogen()->depth() > 1) {
++ CallRuntime(Runtime::kCreateObjectLiteral, 4, instr);
++ } else if (flags != ObjectLiteral::kFastElements ||
++ properties_count > FastCloneShallowObjectStub::kMaximumClonedProperties) {
++ CallRuntime(Runtime::kCreateObjectLiteralShallow, 4, instr);
++ } else {
++ FastCloneShallowObjectStub stub(properties_count);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ }
++}
++
++
++void LCodeGen::DoToFastProperties(LToFastProperties* instr) {
++ ASSERT(ToRegister(instr->value()).is(r3));
++ __ push(r3);
++ CallRuntime(Runtime::kToFastProperties, 1, instr);
++}
++
++
++void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) {
++ Label materialized;
++ // Registers will be used as follows:
++ // r10 = literals array.
++ // r4 = regexp literal.
++ // r3 = regexp literal clone.
++ // r5 and r7-r9 are used as temporaries.
++ int literal_offset =
++ FixedArray::OffsetOfElementAt(instr->hydrogen()->literal_index());
++ __ LoadHeapObject(r10, instr->hydrogen()->literals());
++ __ LoadP(r4, FieldMemOperand(r10, literal_offset));
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ cmp(r4, ip);
++ __ bne(&materialized);
++
++ // Create regexp literal using runtime function
++ // Result will be in r3.
++ __ LoadSmiLiteral(r9, Smi::FromInt(instr->hydrogen()->literal_index()));
++ __ mov(r8, Operand(instr->hydrogen()->pattern()));
++ __ mov(r7, Operand(instr->hydrogen()->flags()));
++ __ Push(r10, r9, r8, r7);
++ CallRuntime(Runtime::kMaterializeRegExpLiteral, 4, instr);
++ __ mr(r4, r3);
++
++ __ bind(&materialized);
++ int size = JSRegExp::kSize + JSRegExp::kInObjectFieldCount * kPointerSize;
++ Label allocated, runtime_allocate;
++
++ __ AllocateInNewSpace(size, r3, r5, r6, &runtime_allocate, TAG_OBJECT);
++ __ b(&allocated);
++
++ __ bind(&runtime_allocate);
++ __ LoadSmiLiteral(r3, Smi::FromInt(size));
++ __ Push(r4, r3);
++ CallRuntime(Runtime::kAllocateInNewSpace, 1, instr);
++ __ pop(r4);
++
++ __ bind(&allocated);
++ // Copy the content into the newly allocated memory.
++ // (Unroll copy loop once for better throughput).
++ for (int i = 0; i < size - kPointerSize; i += 2 * kPointerSize) {
++ __ LoadP(r6, FieldMemOperand(r4, i));
++ __ LoadP(r5, FieldMemOperand(r4, i + kPointerSize));
++ __ StoreP(r6, FieldMemOperand(r3, i), r0);
++ __ StoreP(r5, FieldMemOperand(r3, i + kPointerSize), r0);
++ }
++ if ((size % (2 * kPointerSize)) != 0) {
++ __ LoadP(r6, FieldMemOperand(r4, size - kPointerSize));
++ __ StoreP(r6, FieldMemOperand(r3, size - kPointerSize), r0);
++ }
++}
++
++
++void LCodeGen::DoFunctionLiteral(LFunctionLiteral* instr) {
++ // Use the fast case closure allocation code that allocates in new
++ // space for nested functions that don't need literals cloning.
++ Handle<SharedFunctionInfo> shared_info = instr->shared_info();
++ bool pretenure = instr->hydrogen()->pretenure();
++ if (!pretenure && shared_info->num_literals() == 0) {
++ FastNewClosureStub stub(shared_info->language_mode());
++ __ mov(r4, Operand(shared_info));
++ __ push(r4);
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ } else {
++ __ mov(r5, Operand(shared_info));
++ __ mov(r4, Operand(pretenure
++ ? factory()->true_value()
++ : factory()->false_value()));
++ __ Push(cp, r5, r4);
++ CallRuntime(Runtime::kNewClosure, 3, instr);
++ }
++}
++
++
++void LCodeGen::DoTypeof(LTypeof* instr) {
++ Register input = ToRegister(instr->value());
++ __ push(input);
++ CallRuntime(Runtime::kTypeof, 1, instr);
++}
++
++
++void LCodeGen::DoTypeofIsAndBranch(LTypeofIsAndBranch* instr) {
++ Register input = ToRegister(instr->value());
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++ Label* true_label = chunk_->GetAssemblyLabel(true_block);
++ Label* false_label = chunk_->GetAssemblyLabel(false_block);
++
++ Condition final_branch_condition = EmitTypeofIs(true_label,
++ false_label,
++ input,
++ instr->type_literal());
++ if (final_branch_condition != kNoCondition) {
++ EmitBranch(true_block, false_block, final_branch_condition);
++ }
++}
++
++
++Condition LCodeGen::EmitTypeofIs(Label* true_label,
++ Label* false_label,
++ Register input,
++ Handle<String> type_name) {
++ Condition final_branch_condition = kNoCondition;
++ Register scratch = scratch0();
++ if (type_name->Equals(heap()->number_symbol())) {
++ __ JumpIfSmi(input, true_label);
++ __ LoadP(input, FieldMemOperand(input, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
++ __ cmp(input, ip);
++ final_branch_condition = eq;
++
++ } else if (type_name->Equals(heap()->string_symbol())) {
++ __ JumpIfSmi(input, false_label);
++ __ CompareObjectType(input, input, scratch, FIRST_NONSTRING_TYPE);
++ __ bge(false_label);
++ __ lbz(ip, FieldMemOperand(input, Map::kBitFieldOffset));
++ __ ExtractBit(r0, ip, Map::kIsUndetectable);
++ __ cmpi(r0, Operand::Zero());
++ final_branch_condition = eq;
++
++ } else if (type_name->Equals(heap()->boolean_symbol())) {
++ __ CompareRoot(input, Heap::kTrueValueRootIndex);
++ __ beq(true_label);
++ __ CompareRoot(input, Heap::kFalseValueRootIndex);
++ final_branch_condition = eq;
++
++ } else if (FLAG_harmony_typeof &&
type_name->Equals(heap()->null_symbol())) {
++ __ CompareRoot(input, Heap::kNullValueRootIndex);
++ final_branch_condition = eq;
++
++ } else if (type_name->Equals(heap()->undefined_symbol())) {
++ __ CompareRoot(input, Heap::kUndefinedValueRootIndex);
++ __ beq(true_label);
++ __ JumpIfSmi(input, false_label);
++ // Check for undetectable objects => true.
++ __ LoadP(input, FieldMemOperand(input, HeapObject::kMapOffset));
++ __ lbz(ip, FieldMemOperand(input, Map::kBitFieldOffset));
++ __ ExtractBit(r0, ip, Map::kIsUndetectable);
++ __ cmpi(r0, Operand::Zero());
++ final_branch_condition = ne;
++
++ } else if (type_name->Equals(heap()->function_symbol())) {
++ STATIC_ASSERT(NUM_OF_CALLABLE_SPEC_OBJECT_TYPES == 2);
++ __ JumpIfSmi(input, false_label);
++ __ CompareObjectType(input, scratch, input, JS_FUNCTION_TYPE);
++ __ beq(true_label);
++ __ cmpi(input, Operand(JS_FUNCTION_PROXY_TYPE));
++ final_branch_condition = eq;
++
++ } else if (type_name->Equals(heap()->object_symbol())) {
++ __ JumpIfSmi(input, false_label);
++ if (!FLAG_harmony_typeof) {
++ __ CompareRoot(input, Heap::kNullValueRootIndex);
++ __ beq(true_label);
++ }
++ __ CompareObjectType(input, input, scratch,
++ FIRST_NONCALLABLE_SPEC_OBJECT_TYPE);
++ __ blt(false_label);
++ __ CompareInstanceType(input, scratch, LAST_NONCALLABLE_SPEC_OBJECT_TYPE);
++ __ bgt(false_label);
++ // Check for undetectable objects => false.
++ __ lbz(ip, FieldMemOperand(input, Map::kBitFieldOffset));
++ __ ExtractBit(r0, ip, Map::kIsUndetectable);
++ __ cmpi(r0, Operand::Zero());
++ final_branch_condition = eq;
++
++ } else {
++ __ b(false_label);
++ }
++
++ return final_branch_condition;
++}
++
++
++void LCodeGen::DoIsConstructCallAndBranch(LIsConstructCallAndBranch* instr) {
++ Register temp1 = ToRegister(instr->temp());
++ int true_block = chunk_->LookupDestination(instr->true_block_id());
++ int false_block = chunk_->LookupDestination(instr->false_block_id());
++
++ EmitIsConstructCall(temp1, scratch0());
++ EmitBranch(true_block, false_block, eq);
++}
++
++
++void LCodeGen::EmitIsConstructCall(Register temp1, Register temp2) {
++ ASSERT(!temp1.is(temp2));
++ // Get the frame pointer for the calling frame.
++ __ LoadP(temp1, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
++
++ // Skip the arguments adaptor frame if it exists.
++ Label check_frame_marker;
++ __ LoadP(temp2, MemOperand(temp1, StandardFrameConstants::kContextOffset));
++ __ CmpSmiLiteral(temp2, Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR), r0);
++ __ bne(&check_frame_marker);
++ __ LoadP(temp1, MemOperand(temp1, StandardFrameConstants::kCallerFPOffset));
++
++ // Check the marker in the calling frame.
++ __ bind(&check_frame_marker);
++ __ LoadP(temp1, MemOperand(temp1, StandardFrameConstants::kMarkerOffset));
++ __ CmpSmiLiteral(temp1, Smi::FromInt(StackFrame::CONSTRUCT), r0);
++}
++
++
++void LCodeGen::EnsureSpaceForLazyDeopt() {
++ // Ensure that we have enough space after the previous lazy-bailout
++ // instruction for patching the code here.
++ int current_pc = masm()->pc_offset();
++ int patch_size = Deoptimizer::patch_size();
++ if (current_pc < last_lazy_deopt_pc_ + patch_size) {
++ int padding_size = last_lazy_deopt_pc_ + patch_size - current_pc;
++ ASSERT_EQ(0, padding_size % Assembler::kInstrSize);
++ while (padding_size > 0) {
++ __ nop();
++ padding_size -= Assembler::kInstrSize;
++ }
++ }
++ last_lazy_deopt_pc_ = masm()->pc_offset();
++}
++
++
++void LCodeGen::DoLazyBailout(LLazyBailout* instr) {
++ EnsureSpaceForLazyDeopt();
++ ASSERT(instr->HasEnvironment());
++ LEnvironment* env = instr->environment();
++ RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt);
++ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
++}
++
++
++void LCodeGen::DoDeoptimize(LDeoptimize* instr) {
++ DeoptimizeIf(al, instr->environment());
++}
++
++
++void LCodeGen::DoDeleteProperty(LDeleteProperty* instr) {
++ Register object = ToRegister(instr->object());
++ Register key = ToRegister(instr->key());
++ Register strict = scratch0();
++ __ LoadSmiLiteral(strict, Smi::FromInt(strict_mode_flag()));
++ __ Push(object, key, strict);
++ ASSERT(instr->HasPointerMap());
++ LPointerMap* pointers = instr->pointer_map();
++ RecordPosition(pointers->position());
++ SafepointGenerator safepoint_generator(
++ this, pointers, Safepoint::kLazyDeopt);
++ __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION, safepoint_generator);
++}
++
++
++void LCodeGen::DoIn(LIn* instr) {
++ Register obj = ToRegister(instr->object());
++ Register key = ToRegister(instr->key());
++ __ Push(key, obj);
++ ASSERT(instr->HasPointerMap());
++ LPointerMap* pointers = instr->pointer_map();
++ RecordPosition(pointers->position());
++ SafepointGenerator safepoint_generator(this, pointers, Safepoint::kLazyDeopt);
++ __ InvokeBuiltin(Builtins::IN, CALL_FUNCTION, safepoint_generator);
++}
++
++
++void LCodeGen::DoDeferredStackCheck(LStackCheck* instr) {
++ PushSafepointRegistersScope scope(this, Safepoint::kWithRegisters);
++ __ CallRuntimeSaveDoubles(Runtime::kStackGuard);
++ RecordSafepointWithLazyDeopt(
++ instr, RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS);
++ ASSERT(instr->HasEnvironment());
++ LEnvironment* env = instr->environment();
++ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
++}
++
++
++void LCodeGen::DoStackCheck(LStackCheck* instr) {
++ class DeferredStackCheck: public LDeferredCode {
++ public:
++ DeferredStackCheck(LCodeGen* codegen, LStackCheck* instr)
++ : LDeferredCode(codegen), instr_(instr) { }
++ virtual void Generate() { codegen()->DoDeferredStackCheck(instr_); }
++ virtual LInstruction* instr() { return instr_; }
++ private:
++ LStackCheck* instr_;
++ };
++
++ ASSERT(instr->HasEnvironment());
++ LEnvironment* env = instr->environment();
++ // There is no LLazyBailout instruction for stack-checks. We have to
++ // prepare for lazy deoptimization explicitly here.
++ if (instr->hydrogen()->is_function_entry()) {
++ // Perform stack overflow check.
++ Label done;
++ __ LoadRoot(ip, Heap::kStackLimitRootIndex);
++ __ cmpl(sp, ip);
++ __ bge(&done);
++ StackCheckStub stub;
++ CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
++ EnsureSpaceForLazyDeopt();
++ __ bind(&done);
++ RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt);
++ safepoints_.RecordLazyDeoptimizationIndex(env->deoptimization_index());
++ } else {
++ ASSERT(instr->hydrogen()->is_backwards_branch());
++ // Perform stack overflow check if this goto needs it before jumping.
++ DeferredStackCheck* deferred_stack_check =
++ new(zone()) DeferredStackCheck(this, instr);
++ __ LoadRoot(ip, Heap::kStackLimitRootIndex);
++ __ cmpl(sp, ip);
++ __ blt(deferred_stack_check->entry());
++ EnsureSpaceForLazyDeopt();
++ __ bind(instr->done_label());
++ deferred_stack_check->SetExit(instr->done_label());
++ RegisterEnvironmentForDeoptimization(env, Safepoint::kLazyDeopt);
++ // Don't record a deoptimization index for the safepoint here.
++ // This will be done explicitly when emitting call and the safepoint in
++ // the deferred code.
++ }
++}
++
++
++void LCodeGen::DoOsrEntry(LOsrEntry* instr) {
++ // This is a pseudo-instruction that ensures that the environment here is
++ // properly registered for deoptimization and records the assembler's PC
++ // offset.
++ LEnvironment* environment = instr->environment();
++ environment->SetSpilledRegisters(instr->SpilledRegisterArray(),
++ instr->SpilledDoubleRegisterArray());
++
++ // If the environment were already registered, we would have no way of
++ // backpatching it with the spill slot operands.
++ ASSERT(!environment->HasBeenRegistered());
++ RegisterEnvironmentForDeoptimization(environment, Safepoint::kNoLazyDeopt);
++ ASSERT(osr_pc_offset_ == -1);
++ osr_pc_offset_ = masm()->pc_offset();
++}
++
++
++void LCodeGen::DoForInPrepareMap(LForInPrepareMap* instr) {
++ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
++ __ cmp(r3, ip);
++ DeoptimizeIf(eq, instr->environment());
++
++ Register null_value = r8;
++ __ LoadRoot(null_value, Heap::kNullValueRootIndex);
++ __ cmp(r3, null_value);
++ DeoptimizeIf(eq, instr->environment());
++
++ __ TestIfSmi(r3, r0);
++ DeoptimizeIf(eq, instr->environment(), cr0);
++
++ STATIC_ASSERT(FIRST_JS_PROXY_TYPE == FIRST_SPEC_OBJECT_TYPE);
++ __ CompareObjectType(r3, r4, r4, LAST_JS_PROXY_TYPE);
++ DeoptimizeIf(le, instr->environment());
++
++ Label use_cache, call_runtime;
++ __ CheckEnumCache(null_value, &call_runtime);
++
++ __ LoadP(r3, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ b(&use_cache);
++
++ // Get the set of properties to enumerate.
++ __ bind(&call_runtime);
++ __ push(r3);
++ CallRuntime(Runtime::kGetPropertyNamesFast, 1, instr);
++
++ __ LoadP(r4, FieldMemOperand(r3, HeapObject::kMapOffset));
++ __ LoadRoot(ip, Heap::kMetaMapRootIndex);
++ __ cmp(r4, ip);
++ DeoptimizeIf(ne, instr->environment());
++ __ bind(&use_cache);
++}
++
++
++void LCodeGen::DoForInCacheArray(LForInCacheArray* instr) {
++ Register map = ToRegister(instr->map());
++ Register result = ToRegister(instr->result());
++ Label load_cache, done;
++ __ EnumLength(result, map);
++ __ CmpSmiLiteral(result, Smi::FromInt(0), r0);
++ __ bne(&load_cache);
++ __ mov(result, Operand(isolate()->factory()->empty_fixed_array()));
++ __ b(&done);
++
++ __ bind(&load_cache);
++ __ LoadInstanceDescriptors(map, result);
++ __ LoadP(result,
++ FieldMemOperand(result, DescriptorArray::kEnumCacheOffset));
++ __ LoadP(result,
++ FieldMemOperand(result, FixedArray::SizeFor(instr->idx())));
++ __ cmpi(result, Operand::Zero());
++ DeoptimizeIf(eq, instr->environment());
++
++ __ bind(&done);
++}
++
++
++void LCodeGen::DoCheckMapValue(LCheckMapValue* instr) {
++ Register object = ToRegister(instr->value());
++ Register map = ToRegister(instr->map());
++ __ LoadP(scratch0(), FieldMemOperand(object, HeapObject::kMapOffset));
++ __ cmp(map, scratch0());
++ DeoptimizeIf(ne, instr->environment());
++}
++
++
++void LCodeGen::DoLoadFieldByIndex(LLoadFieldByIndex* instr) {
++ Register object = ToRegister(instr->object());
++ Register index = ToRegister(instr->index());
++ Register result = ToRegister(instr->result());
++ Register scratch = scratch0();
++
++ Label out_of_object, done;
++ __ cmpi(index, Operand::Zero());
++ __ blt(&out_of_object);
++
++ __ SmiToPtrArrayOffset(r0, index);
++ __ add(scratch, object, r0);
++ __ LoadP(result, FieldMemOperand(scratch, JSObject::kHeaderSize));
++
++ __ b(&done);
++
++ __ bind(&out_of_object);
++ __ LoadP(result, FieldMemOperand(object, JSObject::kPropertiesOffset));
++ // Index is equal to negated out of object property index plus 1.
++ __ SmiToPtrArrayOffset(r0, index);
++ __ sub(scratch, result, r0);
++ __ LoadP(result, FieldMemOperand(scratch,
++ FixedArray::kHeaderSize - kPointerSize));
++ __ bind(&done);
++}
++
++
++#undef __
++
++} } // namespace v8::internal
+diff -up v8-3.14.5.10/src/ppc/lithium-codegen-ppc.h.ppc
v8-3.14.5.10/src/ppc/lithium-codegen-ppc.h
+--- v8-3.14.5.10/src/ppc/lithium-codegen-ppc.h.ppc 2016-06-07 14:15:45.997392967 -0400
++++ v8-3.14.5.10/src/ppc/lithium-codegen-ppc.h 2016-06-07 14:15:45.997392967 -0400
+@@ -0,0 +1,479 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_LITHIUM_CODEGEN_PPC_H_
++#define V8_PPC_LITHIUM_CODEGEN_PPC_H_
++
++#include "ppc/lithium-ppc.h"
++#include "ppc/lithium-gap-resolver-ppc.h"
++#include "deoptimizer.h"
++#include "safepoint-table.h"
++#include "scopes.h"
++
++namespace v8 {
++namespace internal {
++
++// Forward declarations.
++class LDeferredCode;
++class SafepointGenerator;
++
++class LCodeGen BASE_EMBEDDED {
++ public:
++ LCodeGen(LChunk* chunk, MacroAssembler* assembler, CompilationInfo* info)
++ : zone_(info->zone()),
++ chunk_(static_cast<LPlatformChunk*>(chunk)),
++ masm_(assembler),
++ info_(info),
++ current_block_(-1),
++ current_instruction_(-1),
++ instructions_(chunk->instructions()),
++ deoptimizations_(4, info->zone()),
++ deopt_jump_table_(4, info->zone()),
++ deoptimization_literals_(8, info->zone()),
++ inlined_function_count_(0),
++ scope_(info->scope()),
++ status_(UNUSED),
++ translations_(info->zone()),
++ deferred_(8, info->zone()),
++ osr_pc_offset_(-1),
++ last_lazy_deopt_pc_(0),
++ safepoints_(info->zone()),
++ resolver_(this),
++ expected_safepoint_kind_(Safepoint::kSimple) {
++ PopulateDeoptimizationLiteralsWithInlinedFunctions();
++ }
++
++
++ // Simple accessors.
++ MacroAssembler* masm() const { return masm_; }
++ CompilationInfo* info() const { return info_; }
++ Isolate* isolate() const { return info_->isolate(); }
++ Factory* factory() const { return isolate()->factory(); }
++ Heap* heap() const { return isolate()->heap(); }
++ Zone* zone() const { return zone_; }
++
++ // Support for converting LOperands to assembler types.
++ // LOperand must be a register.
++ Register ToRegister(LOperand* op) const;
++
++ // LOperand is loaded into scratch, unless already a register.
++ Register EmitLoadRegister(LOperand* op, Register scratch);
++
++ // LOperand must be a double register.
++ DoubleRegister ToDoubleRegister(LOperand* op) const;
++
++ /*
++ // LOperand is loaded into dbl_scratch, unless already a double register.
++ DoubleRegister EmitLoadDoubleRegister(LOperand* op,
++ SwVfpRegister flt_scratch,
++ DoubleRegister dbl_scratch);
++ */
++
++ int ToInteger32(LConstantOperand* op) const;
++ double ToDouble(LConstantOperand* op) const;
++ Operand ToOperand(LOperand* op);
++ MemOperand ToMemOperand(LOperand* op) const;
++ // Returns a MemOperand pointing to the high word of a DoubleStackSlot.
++ MemOperand ToHighMemOperand(LOperand* op) const;
++
++ bool IsInteger32(LConstantOperand* op) const;
++ Handle<Object> ToHandle(LConstantOperand* op) const;
++
++ // Try to generate code for the entire chunk, but it may fail if the
++ // chunk contains constructs we cannot handle. Returns true if the
++ // code generation attempt succeeded.
++ bool GenerateCode();
++
++ // Finish the code by setting stack height, safepoint, and bailout
++ // information on it.
++ void FinishCode(Handle<Code> code);
++
++ // Deferred code support.
++ void DoDeferredNumberTagD(LNumberTagD* instr);
++
++ enum IntegerSignedness { SIGNED_INT32, UNSIGNED_INT32 };
++ void DoDeferredNumberTagI(LInstruction* instr,
++ LOperand* value,
++ IntegerSignedness signedness);
++
++ void DoDeferredTaggedToI(LTaggedToI* instr);
++ void DoDeferredMathAbsTaggedHeapNumber(LUnaryMathOperation* instr);
++ void DoDeferredStackCheck(LStackCheck* instr);
++ void DoDeferredRandom(LRandom* instr);
++ void DoDeferredStringCharCodeAt(LStringCharCodeAt* instr);
++ void DoDeferredStringCharFromCode(LStringCharFromCode* instr);
++ void DoDeferredAllocateObject(LAllocateObject* instr);
++ void DoDeferredInstanceOfKnownGlobal(LInstanceOfKnownGlobal* instr,
++ Label* map_check);
++
++ void DoCheckMapCommon(Register reg, Register scratch, Handle<Map> map,
++ CompareMapMode mode, LEnvironment* env);
++
++ // Parallel move support.
++ void DoParallelMove(LParallelMove* move);
++ void DoGap(LGap* instr);
++
++ MemOperand PrepareKeyedOperand(Register key,
++ Register base,
++ bool key_is_constant,
++ bool key_is_tagged,
++ int constant_key,
++ int element_size_shift,
++ int additional_index,
++ int additional_offset);
++
++ // Emit frame translation commands for an environment.
++ void WriteTranslation(LEnvironment* environment,
++ Translation* translation,
++ int* arguments_index,
++ int* arguments_count);
++
++ // Declare methods that deal with the individual node types.
++#define DECLARE_DO(type) void Do##type(L##type* node);
++ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
++#undef DECLARE_DO
++
++ private:
++ enum Status {
++ UNUSED,
++ GENERATING,
++ DONE,
++ ABORTED
++ };
++
++ bool is_unused() const { return status_ == UNUSED; }
++ bool is_generating() const { return status_ == GENERATING; }
++ bool is_done() const { return status_ == DONE; }
++ bool is_aborted() const { return status_ == ABORTED; }
++
++ StrictModeFlag strict_mode_flag() const {
++ return info()->is_classic_mode() ? kNonStrictMode : kStrictMode;
++ }
++
++ LPlatformChunk* chunk() const { return chunk_; }
++ Scope* scope() const { return scope_; }
++ HGraph* graph() const { return chunk_->graph(); }
++
++ Register scratch0() { return r11; }
++ DwVfpRegister double_scratch0() { return kScratchDoubleReg; }
++
++ int GetNextEmittedBlock(int block);
++ LInstruction* GetNextInstruction();
++
++ void EmitClassOfTest(Label* if_true,
++ Label* if_false,
++ Handle<String> class_name,
++ Register input,
++ Register temporary,
++ Register temporary2);
++
++ int GetStackSlotCount() const { return chunk()->spill_slot_count(); }
++ int GetParameterCount() const { return scope()->num_parameters(); }
++
++ void Abort(const char* reason);
++ void Comment(const char* format, ...);
++
++ void AddDeferredCode(LDeferredCode* code) { deferred_.Add(code, zone()); }
++
++ // Code generation passes. Returns true if code generation should
++ // continue.
++ bool GeneratePrologue();
++ bool GenerateBody();
++ bool GenerateDeferredCode();
++ bool GenerateDeoptJumpTable();
++ bool GenerateSafepointTable();
++
++ enum SafepointMode {
++ RECORD_SIMPLE_SAFEPOINT,
++ RECORD_SAFEPOINT_WITH_REGISTERS_AND_NO_ARGUMENTS
++ };
++
++ void CallCode(Handle<Code> code,
++ RelocInfo::Mode mode,
++ LInstruction* instr);
++
++ void CallCodeGeneric(Handle<Code> code,
++ RelocInfo::Mode mode,
++ LInstruction* instr,
++ SafepointMode safepoint_mode);
++
++ void CallRuntime(const Runtime::Function* function,
++ int num_arguments,
++ LInstruction* instr);
++
++ void CallRuntime(Runtime::FunctionId id,
++ int num_arguments,
++ LInstruction* instr) {
++ const Runtime::Function* function = Runtime::FunctionForId(id);
++ CallRuntime(function, num_arguments, instr);
++ }
++
++ void CallRuntimeFromDeferred(Runtime::FunctionId id,
++ int argc,
++ LInstruction* instr);
++
++ enum R4State {
++ R4_UNINITIALIZED,
++ R4_CONTAINS_TARGET
++ };
++
++ // Generate a direct call to a known function. Expects the function
++ // to be in r4.
++ void CallKnownFunction(Handle<JSFunction> function,
++ int arity,
++ LInstruction* instr,
++ CallKind call_kind,
++ R4State r4_state);
++
++ void LoadHeapObject(Register result, Handle<HeapObject> object);
++
++ void RecordSafepointWithLazyDeopt(LInstruction* instr,
++ SafepointMode safepoint_mode);
++
++ void RegisterEnvironmentForDeoptimization(LEnvironment* environment,
++ Safepoint::DeoptMode mode);
++ void DeoptimizeIf(Condition cc, LEnvironment* environment,
++ CRegister cr = cr7);
++
++ void AddToTranslation(Translation* translation,
++ LOperand* op,
++ bool is_tagged,
++ bool is_uint32,
++ int arguments_index,
++ int arguments_count);
++ void PopulateDeoptimizationData(Handle<Code> code);
++ int DefineDeoptimizationLiteral(Handle<Object> literal);
++
++ void PopulateDeoptimizationLiteralsWithInlinedFunctions();
++
++ Register ToRegister(int index) const;
++ DoubleRegister ToDoubleRegister(int index) const;
++
++ // Specific math operations - used from DoUnaryMathOperation.
++ void EmitIntegerMathAbs(LUnaryMathOperation* instr);
++ void DoMathAbs(LUnaryMathOperation* instr);
++ void DoMathFloor(LUnaryMathOperation* instr);
++ void DoMathRound(LUnaryMathOperation* instr);
++ void DoMathSqrt(LUnaryMathOperation* instr);
++ void DoMathPowHalf(LUnaryMathOperation* instr);
++ void DoMathLog(LUnaryMathOperation* instr);
++ void DoMathTan(LUnaryMathOperation* instr);
++ void DoMathCos(LUnaryMathOperation* instr);
++ void DoMathSin(LUnaryMathOperation* instr);
++
++ // Support for recording safepoint and position information.
++ void RecordSafepoint(LPointerMap* pointers,
++ Safepoint::Kind kind,
++ int arguments,
++ Safepoint::DeoptMode mode);
++ void RecordSafepoint(LPointerMap* pointers, Safepoint::DeoptMode mode);
++ void RecordSafepoint(Safepoint::DeoptMode mode);
++ void RecordSafepointWithRegisters(LPointerMap* pointers,
++ int arguments,
++ Safepoint::DeoptMode mode);
++ void RecordPosition(int position);
++
++ static Condition TokenToCondition(Token::Value op);
++ void EmitGoto(int block);
++ void EmitBranch(int left_block, int right_block, Condition cc,
++ CRegister cr = cr7);
++ void EmitNumberUntagD(Register input,
++ DoubleRegister result,
++ bool deoptimize_on_undefined,
++ bool deoptimize_on_minus_zero,
++ LEnvironment* env);
++
++ void DeoptIfTaggedButNotSmi(LEnvironment* environment,
++ HValue* value,
++ LOperand* operand);
++
++ // Emits optimized code for typeof x == "y". Modifies input register.
++ // Returns the condition on which a final split to
++ // true and false label should be made, to optimize fallthrough.
++ Condition EmitTypeofIs(Label* true_label,
++ Label* false_label,
++ Register input,
++ Handle<String> type_name);
++
++ // Emits optimized code for %_IsObject(x). Preserves input register.
++ // Returns the condition on which a final split to
++ // true and false label should be made, to optimize fallthrough.
++ Condition EmitIsObject(Register input,
++ Register temp1,
++ Label* is_not_object,
++ Label* is_object);
++
++ // Emits optimized code for %_IsString(x). Preserves input register.
++ // Returns the condition on which a final split to
++ // true and false label should be made, to optimize fallthrough.
++ Condition EmitIsString(Register input,
++ Register temp1,
++ Label* is_not_string);
++
++ // Emits optimized code for %_IsConstructCall().
++ // Caller should branch on equal condition.
++ void EmitIsConstructCall(Register temp1, Register temp2);
++
++ void EmitLoadFieldOrConstantFunction(Register result,
++ Register object,
++ Handle<Map> type,
++ Handle<String> name,
++ LEnvironment* env);
++
++ // Emits optimized code to deep-copy the contents of statically known
++ // object graphs (e.g. object literal boilerplate).
++ void EmitDeepCopy(Handle<JSObject> object,
++ Register result,
++ Register source,
++ int* offset);
++
++ // Emit optimized code for integer division.
++ // Inputs are signed.
++ // All registers are clobbered.
++ // If 'remainder' is no_reg, it is not computed.
++ void EmitSignedIntegerDivisionByConstant(Register result,
++ Register dividend,
++ int32_t divisor,
++ Register remainder,
++ Register scratch,
++ LEnvironment* environment);
++
++ struct JumpTableEntry {
++ explicit inline JumpTableEntry(Address entry)
++ : label(),
++ address(entry) { }
++ Label label;
++ Address address;
++ };
++
++ void EnsureSpaceForLazyDeopt();
++
++ Zone* zone_;
++ LPlatformChunk* const chunk_;
++ MacroAssembler* const masm_;
++ CompilationInfo* const info_;
++
++ int current_block_;
++ int current_instruction_;
++ const ZoneList<LInstruction*>* instructions_;
++ ZoneList<LEnvironment*> deoptimizations_;
++ ZoneList<JumpTableEntry> deopt_jump_table_;
++ ZoneList<Handle<Object> > deoptimization_literals_;
++ int inlined_function_count_;
++ Scope* const scope_;
++ Status status_;
++ TranslationBuffer translations_;
++ ZoneList<LDeferredCode*> deferred_;
++ int osr_pc_offset_;
++ int last_lazy_deopt_pc_;
++
++ // Builder that keeps track of safepoints in the code. The table
++ // itself is emitted at the end of the generated code.
++ SafepointTableBuilder safepoints_;
++
++ // Compiler from a set of parallel moves to a sequential list of moves.
++ LGapResolver resolver_;
++
++ Safepoint::Kind expected_safepoint_kind_;
++
++ class PushSafepointRegistersScope BASE_EMBEDDED {
++ public:
++ PushSafepointRegistersScope(LCodeGen* codegen,
++ Safepoint::Kind kind)
++ : codegen_(codegen) {
++ ASSERT(codegen_->expected_safepoint_kind_ == Safepoint::kSimple);
++ codegen_->expected_safepoint_kind_ = kind;
++
++ switch (codegen_->expected_safepoint_kind_) {
++ case Safepoint::kWithRegisters:
++ codegen_->masm_->PushSafepointRegisters();
++ break;
++ default:
++ UNREACHABLE();
++ }
++ }
++
++ ~PushSafepointRegistersScope() {
++ Safepoint::Kind kind = codegen_->expected_safepoint_kind_;
++ ASSERT((kind & Safepoint::kWithRegisters) != 0);
++ switch (kind) {
++ case Safepoint::kWithRegisters:
++ codegen_->masm_->PopSafepointRegisters();
++ break;
++ default:
++ UNREACHABLE();
++ }
++ codegen_->expected_safepoint_kind_ = Safepoint::kSimple;
++ }
++
++ private:
++ LCodeGen* codegen_;
++ };
++
++ friend class LDeferredCode;
++ friend class LEnvironment;
++ friend class SafepointGenerator;
++ DISALLOW_COPY_AND_ASSIGN(LCodeGen);
++};
++
++
++class LDeferredCode: public ZoneObject {
++ public:
++ explicit LDeferredCode(LCodeGen* codegen)
++ : codegen_(codegen),
++ external_exit_(NULL),
++ instruction_index_(codegen->current_instruction_) {
++ codegen->AddDeferredCode(this);
++ }
++
++ virtual ~LDeferredCode() { }
++ virtual void Generate() = 0;
++ virtual LInstruction* instr() = 0;
++
++ void SetExit(Label* exit) { external_exit_ = exit; }
++ Label* entry() { return &entry_; }
++ Label* exit() { return external_exit_ != NULL ? external_exit_ : &exit_; }
++ int instruction_index() const { return instruction_index_; }
++
++ protected:
++ LCodeGen* codegen() const { return codegen_; }
++ MacroAssembler* masm() const { return codegen_->masm(); }
++
++ private:
++ LCodeGen* codegen_;
++ Label entry_;
++ Label exit_;
++ Label* external_exit_;
++ int instruction_index_;
++};
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_LITHIUM_CODEGEN_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc.ppc
v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc
+--- v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc.ppc 2016-06-07 14:15:45.997392967
-0400
++++ v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.cc 2016-06-07 14:15:45.997392967 -0400
+@@ -0,0 +1,311 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#include "ppc/lithium-gap-resolver-ppc.h"
++#include "ppc/lithium-codegen-ppc.h"
++
++namespace v8 {
++namespace internal {
++
++static const Register kSavedValueRegister = { 11 };
++
++LGapResolver::LGapResolver(LCodeGen* owner)
++ : cgen_(owner), moves_(32, owner->zone()), root_index_(0), in_cycle_(false),
++ saved_destination_(NULL) { }
++
++
++void LGapResolver::Resolve(LParallelMove* parallel_move) {
++ ASSERT(moves_.is_empty());
++ // Build up a worklist of moves.
++ BuildInitialMoveList(parallel_move);
++
++ for (int i = 0; i < moves_.length(); ++i) {
++ LMoveOperands move = moves_[i];
++ // Skip constants to perform them last. They don't block other moves
++ // and skipping such moves with register destinations keeps those
++ // registers free for the whole algorithm.
++ if (!move.IsEliminated() && !move.source()->IsConstantOperand()) {
++ root_index_ = i; // Any cycle is found when by reaching this move again.
++ PerformMove(i);
++ if (in_cycle_) {
++ RestoreValue();
++ }
++ }
++ }
++
++ // Perform the moves with constant sources.
++ for (int i = 0; i < moves_.length(); ++i) {
++ if (!moves_[i].IsEliminated()) {
++ ASSERT(moves_[i].source()->IsConstantOperand());
++ EmitMove(i);
++ }
++ }
++
++ moves_.Rewind(0);
++}
++
++
++void LGapResolver::BuildInitialMoveList(LParallelMove* parallel_move) {
++ // Perform a linear sweep of the moves to add them to the initial list of
++ // moves to perform, ignoring any move that is redundant (the source is
++ // the same as the destination, the destination is ignored and
++ // unallocated, or the move was already eliminated).
++ const ZoneList<LMoveOperands>* moves = parallel_move->move_operands();
++ for (int i = 0; i < moves->length(); ++i) {
++ LMoveOperands move = moves->at(i);
++ if (!move.IsRedundant()) moves_.Add(move, cgen_->zone());
++ }
++ Verify();
++}
++
++
++void LGapResolver::PerformMove(int index) {
++ // Each call to this function performs a move and deletes it from the move
++ // graph. We first recursively perform any move blocking this one. We
++ // mark a move as "pending" on entry to PerformMove in order to detect
++ // cycles in the move graph.
++
++ // We can only find a cycle, when doing a depth-first traversal of moves,
++ // be encountering the starting move again. So by spilling the source of
++ // the starting move, we break the cycle. All moves are then unblocked,
++ // and the starting move is completed by writing the spilled value to
++ // its destination. All other moves from the spilled source have been
++ // completed prior to breaking the cycle.
++ // An additional complication is that moves to MemOperands with large
++ // offsets (more than 1K or 4K) require us to spill this spilled value to
++ // the stack, to free up the register.
++ ASSERT(!moves_[index].IsPending());
++ ASSERT(!moves_[index].IsRedundant());
++
++ // Clear this move's destination to indicate a pending move. The actual
++ // destination is saved in a stack allocated local. Multiple moves can
++ // be pending because this function is recursive.
++ ASSERT(moves_[index].source() != NULL); // Or else it will look eliminated.
++ LOperand* destination = moves_[index].destination();
++ moves_[index].set_destination(NULL);
++
++ // Perform a depth-first traversal of the move graph to resolve
++ // dependencies. Any unperformed, unpending move with a source the same
++ // as this one's destination blocks this one so recursively perform all
++ // such moves.
++ for (int i = 0; i < moves_.length(); ++i) {
++ LMoveOperands other_move = moves_[i];
++ if (other_move.Blocks(destination) && !other_move.IsPending()) {
++ PerformMove(i);
++ // If there is a blocking, pending move it must be moves_[root_index_]
++ // and all other moves with the same source as moves_[root_index_] are
++ // sucessfully executed (because they are cycle-free) by this loop.
++ }
++ }
++
++ // We are about to resolve this move and don't need it marked as
++ // pending, so restore its destination.
++ moves_[index].set_destination(destination);
++
++ // The move may be blocked on a pending move, which must be the starting move.
++ // In this case, we have a cycle, and we save the source of this move to
++ // a scratch register to break it.
++ LMoveOperands other_move = moves_[root_index_];
++ if (other_move.Blocks(destination)) {
++ ASSERT(other_move.IsPending());
++ BreakCycle(index);
++ return;
++ }
++
++ // This move is no longer blocked.
++ EmitMove(index);
++}
++
++
++void LGapResolver::Verify() {
++#ifdef ENABLE_SLOW_ASSERTS
++ // No operand should be the destination for more than one move.
++ for (int i = 0; i < moves_.length(); ++i) {
++ LOperand* destination = moves_[i].destination();
++ for (int j = i + 1; j < moves_.length(); ++j) {
++ SLOW_ASSERT(!destination->Equals(moves_[j].destination()));
++ }
++ }
++#endif
++}
++
++#define __ ACCESS_MASM(cgen_->masm())
++
++void LGapResolver::BreakCycle(int index) {
++ // We save in a register the value that should end up in the source of
++ // moves_[root_index]. After performing all moves in the tree rooted
++ // in that move, we save the value to that source.
++ ASSERT(moves_[index].destination()->Equals(moves_[root_index_].source()));
++ ASSERT(!in_cycle_);
++ in_cycle_ = true;
++ LOperand* source = moves_[index].source();
++ saved_destination_ = moves_[index].destination();
++ if (source->IsRegister()) {
++ __ mr(kSavedValueRegister, cgen_->ToRegister(source));
++ } else if (source->IsStackSlot()) {
++ __ LoadP(kSavedValueRegister, cgen_->ToMemOperand(source));
++ } else if (source->IsDoubleRegister()) {
++ __ fmr(kScratchDoubleReg, cgen_->ToDoubleRegister(source));
++ } else if (source->IsDoubleStackSlot()) {
++ __ lfd(kScratchDoubleReg, cgen_->ToMemOperand(source));
++ } else {
++ UNREACHABLE();
++ }
++ // This move will be done by restoring the saved value to the destination.
++ moves_[index].Eliminate();
++}
++
++
++void LGapResolver::RestoreValue() {
++ ASSERT(in_cycle_);
++ ASSERT(saved_destination_ != NULL);
++
++ // Spilled value is in kSavedValueRegister or kSavedDoubleValueRegister.
++ if (saved_destination_->IsRegister()) {
++ __ mr(cgen_->ToRegister(saved_destination_), kSavedValueRegister);
++ } else if (saved_destination_->IsStackSlot()) {
++ __ StoreP(kSavedValueRegister, cgen_->ToMemOperand(saved_destination_));
++ } else if (saved_destination_->IsDoubleRegister()) {
++ __ fmr(cgen_->ToDoubleRegister(saved_destination_), kScratchDoubleReg);
++ } else if (saved_destination_->IsDoubleStackSlot()) {
++ __ stfd(kScratchDoubleReg, cgen_->ToMemOperand(saved_destination_));
++ } else {
++ UNREACHABLE();
++ }
++
++ in_cycle_ = false;
++ saved_destination_ = NULL;
++}
++
++
++void LGapResolver::EmitMove(int index) {
++ LOperand* source = moves_[index].source();
++ LOperand* destination = moves_[index].destination();
++
++ // Dispatch on the source and destination operand kinds. Not all
++ // combinations are possible.
++
++ if (source->IsRegister()) {
++ Register source_register = cgen_->ToRegister(source);
++ if (destination->IsRegister()) {
++ __ mr(cgen_->ToRegister(destination), source_register);
++ } else {
++ ASSERT(destination->IsStackSlot());
++ __ StoreP(source_register, cgen_->ToMemOperand(destination));
++ }
++
++ } else if (source->IsStackSlot()) {
++ MemOperand source_operand = cgen_->ToMemOperand(source);
++ if (destination->IsRegister()) {
++ __ LoadP(cgen_->ToRegister(destination), source_operand);
++ } else {
++ ASSERT(destination->IsStackSlot());
++ MemOperand destination_operand = cgen_->ToMemOperand(destination);
++ if (in_cycle_) {
++ __ LoadP(ip, source_operand);
++ __ StoreP(ip, destination_operand);
++ } else {
++ __ LoadP(kSavedValueRegister, source_operand);
++ __ StoreP(kSavedValueRegister, destination_operand);
++ }
++ }
++
++ } else if (source->IsConstantOperand()) {
++ LConstantOperand* constant_source = LConstantOperand::cast(source);
++ if (destination->IsRegister()) {
++ Register dst = cgen_->ToRegister(destination);
++ if (cgen_->IsInteger32(constant_source)) {
++ __ LoadIntLiteral(dst, cgen_->ToInteger32(constant_source));
++ } else {
++ __ LoadObject(dst, cgen_->ToHandle(constant_source));
++ }
++ } else {
++ ASSERT(destination->IsStackSlot());
++ ASSERT(!in_cycle_); // Constant moves happen after all cycles are gone.
++ if (cgen_->IsInteger32(constant_source)) {
++ __ LoadIntLiteral(kSavedValueRegister,
++ cgen_->ToInteger32(constant_source));
++ } else {
++ __ LoadObject(kSavedValueRegister,
++ cgen_->ToHandle(constant_source));
++ }
++ __ StoreP(kSavedValueRegister, cgen_->ToMemOperand(destination));
++ }
++
++ } else if (source->IsDoubleRegister()) {
++ DoubleRegister source_register = cgen_->ToDoubleRegister(source);
++ if (destination->IsDoubleRegister()) {
++ __ fmr(cgen_->ToDoubleRegister(destination), source_register);
++ } else {
++ ASSERT(destination->IsDoubleStackSlot());
++ __ stfd(source_register, cgen_->ToMemOperand(destination));
++ }
++
++ } else if (source->IsDoubleStackSlot()) {
++ MemOperand source_operand = cgen_->ToMemOperand(source);
++ if (destination->IsDoubleRegister()) {
++ __ lfd(cgen_->ToDoubleRegister(destination), source_operand);
++ } else {
++ ASSERT(destination->IsDoubleStackSlot());
++ MemOperand destination_operand = cgen_->ToMemOperand(destination);
++ if (in_cycle_) {
++ // kSavedDoubleValueRegister was used to break the cycle,
++ // but kSavedValueRegister is free.
++#if V8_TARGET_ARCH_PPC64
++ __ ld(kSavedValueRegister, source_operand);
++ __ std(kSavedValueRegister, destination_operand);
++#else
++ MemOperand source_high_operand =
++ cgen_->ToHighMemOperand(source);
++ MemOperand destination_high_operand =
++ cgen_->ToHighMemOperand(destination);
++ __ lwz(kSavedValueRegister, source_operand);
++ __ stw(kSavedValueRegister, destination_operand);
++ __ lwz(kSavedValueRegister, source_high_operand);
++ __ stw(kSavedValueRegister, destination_high_operand);
++#endif
++ } else {
++ __ lfd(kScratchDoubleReg, source_operand);
++ __ stfd(kScratchDoubleReg, destination_operand);
++ }
++ }
++ } else {
++ UNREACHABLE();
++ }
++
++ moves_[index].Eliminate();
++}
++
++
++#undef __
++
++} } // namespace v8::internal
+diff -up v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.h.ppc
v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.h
+--- v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.h.ppc 2016-06-07 14:15:45.997392967
-0400
++++ v8-3.14.5.10/src/ppc/lithium-gap-resolver-ppc.h 2016-06-07 14:15:45.997392967 -0400
+@@ -0,0 +1,86 @@
++// Copyright 2011 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_LITHIUM_GAP_RESOLVER_PPC_H_
++#define V8_PPC_LITHIUM_GAP_RESOLVER_PPC_H_
++
++#include "v8.h"
++
++#include "lithium.h"
++
++namespace v8 {
++namespace internal {
++
++class LCodeGen;
++class LGapResolver;
++
++class LGapResolver BASE_EMBEDDED {
++ public:
++ explicit LGapResolver(LCodeGen* owner);
++
++ // Resolve a set of parallel moves, emitting assembler instructions.
++ void Resolve(LParallelMove* parallel_move);
++
++ private:
++ // Build the initial list of moves.
++ void BuildInitialMoveList(LParallelMove* parallel_move);
++
++ // Perform the move at the moves_ index in question (possibly requiring
++ // other moves to satisfy dependencies).
++ void PerformMove(int index);
++
++ // If a cycle is found in the series of moves, save the blocking value to
++ // a scratch register. The cycle must be found by hitting the root of the
++ // depth-first search.
++ void BreakCycle(int index);
++
++ // After a cycle has been resolved, restore the value from the scratch
++ // register to its proper destination.
++ void RestoreValue();
++
++ // Emit a move and remove it from the move graph.
++ void EmitMove(int index);
++
++ // Verify the move list before performing moves.
++ void Verify();
++
++ LCodeGen* cgen_;
++
++ // List of moves not yet resolved.
++ ZoneList<LMoveOperands> moves_;
++
++ int root_index_;
++ bool in_cycle_;
++ LOperand* saved_destination_;
++};
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_LITHIUM_GAP_RESOLVER_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/lithium-ppc.cc.ppc v8-3.14.5.10/src/ppc/lithium-ppc.cc
+--- v8-3.14.5.10/src/ppc/lithium-ppc.cc.ppc 2016-06-07 14:15:45.998392961 -0400
++++ v8-3.14.5.10/src/ppc/lithium-ppc.cc 2016-06-07 14:15:45.998392961 -0400
+@@ -0,0 +1,2323 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#include "lithium-allocator-inl.h"
++#include "ppc/lithium-ppc.h"
++#include "ppc/lithium-codegen-ppc.h"
++
++namespace v8 {
++namespace internal {
++
++#define DEFINE_COMPILE(type) \
++ void L##type::CompileToNative(LCodeGen* generator) { \
++ generator->Do##type(this); \
++ }
++LITHIUM_CONCRETE_INSTRUCTION_LIST(DEFINE_COMPILE)
++#undef DEFINE_COMPILE
++
++LOsrEntry::LOsrEntry() {
++ for (int i = 0; i < Register::kNumAllocatableRegisters; ++i) {
++ register_spills_[i] = NULL;
++ }
++ for (int i = 0; i < DoubleRegister::kNumAllocatableRegisters; ++i) {
++ double_register_spills_[i] = NULL;
++ }
++}
++
++
++void LOsrEntry::MarkSpilledRegister(int allocation_index,
++ LOperand* spill_operand) {
++ ASSERT(spill_operand->IsStackSlot());
++ ASSERT(register_spills_[allocation_index] == NULL);
++ register_spills_[allocation_index] = spill_operand;
++}
++
++
++#ifdef DEBUG
++void LInstruction::VerifyCall() {
++ // Call instructions can use only fixed registers as temporaries and
++ // outputs because all registers are blocked by the calling convention.
++ // Inputs operands must use a fixed register or use-at-start policy or
++ // a non-register policy.
++ ASSERT(Output() == NULL ||
++ LUnallocated::cast(Output())->HasFixedPolicy() ||
++ !LUnallocated::cast(Output())->HasRegisterPolicy());
++ for (UseIterator it(this); !it.Done(); it.Advance()) {
++ LUnallocated* operand = LUnallocated::cast(it.Current());
++ ASSERT(operand->HasFixedPolicy() ||
++ operand->IsUsedAtStart());
++ }
++ for (TempIterator it(this); !it.Done(); it.Advance()) {
++ LUnallocated* operand = LUnallocated::cast(it.Current());
++ ASSERT(operand->HasFixedPolicy() ||!operand->HasRegisterPolicy());
++ }
++}
++#endif
++
++
++void LOsrEntry::MarkSpilledDoubleRegister(int allocation_index,
++ LOperand* spill_operand) {
++ ASSERT(spill_operand->IsDoubleStackSlot());
++ ASSERT(double_register_spills_[allocation_index] == NULL);
++ double_register_spills_[allocation_index] = spill_operand;
++}
++
++
++void LInstruction::PrintTo(StringStream* stream) {
++ stream->Add("%s ", this->Mnemonic());
++
++ PrintOutputOperandTo(stream);
++
++ PrintDataTo(stream);
++
++ if (HasEnvironment()) {
++ stream->Add(" ");
++ environment()->PrintTo(stream);
++ }
++
++ if (HasPointerMap()) {
++ stream->Add(" ");
++ pointer_map()->PrintTo(stream);
++ }
++}
++
++
++void LInstruction::PrintDataTo(StringStream* stream) {
++ stream->Add("= ");
++ for (int i = 0; i < InputCount(); i++) {
++ if (i > 0) stream->Add(" ");
++ InputAt(i)->PrintTo(stream);
++ }
++}
++
++
++void LInstruction::PrintOutputOperandTo(StringStream* stream) {
++ if (HasResult()) result()->PrintTo(stream);
++}
++
++
++void LLabel::PrintDataTo(StringStream* stream) {
++ LGap::PrintDataTo(stream);
++ LLabel* rep = replacement();
++ if (rep != NULL) {
++ stream->Add(" Dead block replaced with B%d", rep->block_id());
++ }
++}
++
++
++bool LGap::IsRedundant() const {
++ for (int i = 0; i < 4; i++) {
++ if (parallel_moves_[i] != NULL && !parallel_moves_[i]->IsRedundant()) {
++ return false;
++ }
++ }
++
++ return true;
++}
++
++
++void LGap::PrintDataTo(StringStream* stream) {
++ for (int i = 0; i < 4; i++) {
++ stream->Add("(");
++ if (parallel_moves_[i] != NULL) {
++ parallel_moves_[i]->PrintDataTo(stream);
++ }
++ stream->Add(") ");
++ }
++}
++
++
++const char* LArithmeticD::Mnemonic() const {
++ switch (op()) {
++ case Token::ADD: return "add-d";
++ case Token::SUB: return "sub-d";
++ case Token::MUL: return "mul-d";
++ case Token::DIV: return "div-d";
++ case Token::MOD: return "mod-d";
++ default:
++ UNREACHABLE();
++ return NULL;
++ }
++}
++
++
++const char* LArithmeticT::Mnemonic() const {
++ switch (op()) {
++ case Token::ADD: return "add-t";
++ case Token::SUB: return "sub-t";
++ case Token::MUL: return "mul-t";
++ case Token::MOD: return "mod-t";
++ case Token::DIV: return "div-t";
++ case Token::BIT_AND: return "bit-and-t";
++ case Token::BIT_OR: return "bit-or-t";
++ case Token::BIT_XOR: return "bit-xor-t";
++ case Token::SHL: return "shl-t";
++ case Token::SAR: return "sar-t";
++ case Token::SHR: return "shr-t";
++ default:
++ UNREACHABLE();
++ return NULL;
++ }
++}
++
++
++void LGoto::PrintDataTo(StringStream* stream) {
++ stream->Add("B%d", block_id());
++}
++
++
++void LBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("B%d | B%d on ", true_block_id(), false_block_id());
++ value()->PrintTo(stream);
++}
++
++
++void LCmpIDAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if ");
++ left()->PrintTo(stream);
++ stream->Add(" %s ", Token::String(op()));
++ right()->PrintTo(stream);
++ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LIsNilAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if ");
++ value()->PrintTo(stream);
++ stream->Add(kind() == kStrictEquality ? " === " : " == ");
++ stream->Add(nil() == kNullValue ? "null" : "undefined");
++ stream->Add(" then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LIsObjectAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if is_object(");
++ value()->PrintTo(stream);
++ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LIsStringAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if is_string(");
++ value()->PrintTo(stream);
++ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LIsSmiAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if is_smi(");
++ value()->PrintTo(stream);
++ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LIsUndetectableAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if is_undetectable(");
++ value()->PrintTo(stream);
++ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LStringCompareAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if string_compare(");
++ left()->PrintTo(stream);
++ right()->PrintTo(stream);
++ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LHasInstanceTypeAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if has_instance_type(");
++ value()->PrintTo(stream);
++ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LHasCachedArrayIndexAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if has_cached_array_index(");
++ value()->PrintTo(stream);
++ stream->Add(") then B%d else B%d", true_block_id(), false_block_id());
++}
++
++
++void LClassOfTestAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if class_of_test(");
++ value()->PrintTo(stream);
++ stream->Add(", \"%o\") then B%d else B%d",
++ *hydrogen()->class_name(),
++ true_block_id(),
++ false_block_id());
++}
++
++
++void LTypeofIsAndBranch::PrintDataTo(StringStream* stream) {
++ stream->Add("if typeof ");
++ value()->PrintTo(stream);
++ stream->Add(" == \"%s\" then B%d else B%d",
++ *hydrogen()->type_literal()->ToCString(),
++ true_block_id(), false_block_id());
++}
++
++
++void LCallConstantFunction::PrintDataTo(StringStream* stream) {
++ stream->Add("#%d / ", arity());
++}
++
++
++void LUnaryMathOperation::PrintDataTo(StringStream* stream) {
++ stream->Add("/%s ", hydrogen()->OpName());
++ value()->PrintTo(stream);
++}
++
++
++void LLoadContextSlot::PrintDataTo(StringStream* stream) {
++ context()->PrintTo(stream);
++ stream->Add("[%d]", slot_index());
++}
++
++
++void LStoreContextSlot::PrintDataTo(StringStream* stream) {
++ context()->PrintTo(stream);
++ stream->Add("[%d] <- ", slot_index());
++ value()->PrintTo(stream);
++}
++
++
++void LInvokeFunction::PrintDataTo(StringStream* stream) {
++ stream->Add("= ");
++ function()->PrintTo(stream);
++ stream->Add(" #%d / ", arity());
++}
++
++
++void LCallKeyed::PrintDataTo(StringStream* stream) {
++ stream->Add("[r5] #%d / ", arity());
++}
++
++
++void LCallNamed::PrintDataTo(StringStream* stream) {
++ SmartArrayPointer<char> name_string = name()->ToCString();
++ stream->Add("%s #%d / ", *name_string, arity());
++}
++
++
++void LCallGlobal::PrintDataTo(StringStream* stream) {
++ SmartArrayPointer<char> name_string = name()->ToCString();
++ stream->Add("%s #%d / ", *name_string, arity());
++}
++
++
++void LCallKnownGlobal::PrintDataTo(StringStream* stream) {
++ stream->Add("#%d / ", arity());
++}
++
++
++void LCallNew::PrintDataTo(StringStream* stream) {
++ stream->Add("= ");
++ constructor()->PrintTo(stream);
++ stream->Add(" #%d / ", arity());
++}
++
++
++void LAccessArgumentsAt::PrintDataTo(StringStream* stream) {
++ arguments()->PrintTo(stream);
++ stream->Add(" length ");
++ length()->PrintTo(stream);
++ stream->Add(" index ");
++ index()->PrintTo(stream);
++}
++
++
++void LStoreNamedField::PrintDataTo(StringStream* stream) {
++ object()->PrintTo(stream);
++ stream->Add(".");
++ stream->Add(*String::cast(*name())->ToCString());
++ stream->Add(" <- ");
++ value()->PrintTo(stream);
++}
++
++
++void LStoreNamedGeneric::PrintDataTo(StringStream* stream) {
++ object()->PrintTo(stream);
++ stream->Add(".");
++ stream->Add(*String::cast(*name())->ToCString());
++ stream->Add(" <- ");
++ value()->PrintTo(stream);
++}
++
++
++void LStoreKeyedFastElement::PrintDataTo(StringStream* stream) {
++ object()->PrintTo(stream);
++ stream->Add("[");
++ key()->PrintTo(stream);
++ stream->Add("] <- ");
++ value()->PrintTo(stream);
++}
++
++
++void LStoreKeyedFastDoubleElement::PrintDataTo(StringStream* stream) {
++ elements()->PrintTo(stream);
++ stream->Add("[");
++ key()->PrintTo(stream);
++ stream->Add("] <- ");
++ value()->PrintTo(stream);
++}
++
++
++void LStoreKeyedGeneric::PrintDataTo(StringStream* stream) {
++ object()->PrintTo(stream);
++ stream->Add("[");
++ key()->PrintTo(stream);
++ stream->Add("] <- ");
++ value()->PrintTo(stream);
++}
++
++
++void LTransitionElementsKind::PrintDataTo(StringStream* stream) {
++ object()->PrintTo(stream);
++ stream->Add(" %p -> %p", *original_map(), *transitioned_map());
++}
++
++
++int LPlatformChunk::GetNextSpillIndex(bool is_double) {
++ // Skip a slot if for a double-width slot.
++ if (is_double) spill_slot_count_++;
++ return spill_slot_count_++;
++}
++
++
++LOperand* LPlatformChunk::GetNextSpillSlot(bool is_double) {
++ int index = GetNextSpillIndex(is_double);
++ if (is_double) {
++ return LDoubleStackSlot::Create(index, zone());
++ } else {
++ return LStackSlot::Create(index, zone());
++ }
++}
++
++
++LPlatformChunk* LChunkBuilder::Build() {
++ ASSERT(is_unused());
++ chunk_ = new(zone()) LPlatformChunk(info(), graph());
++ HPhase phase("L_Building chunk", chunk_);
++ status_ = BUILDING;
++ const ZoneList<HBasicBlock*>* blocks = graph()->blocks();
++ for (int i = 0; i < blocks->length(); i++) {
++ HBasicBlock* next = NULL;
++ if (i < blocks->length() - 1) next = blocks->at(i + 1);
++ DoBasicBlock(blocks->at(i), next);
++ if (is_aborted()) return NULL;
++ }
++ status_ = DONE;
++ return chunk_;
++}
++
++
++void LChunkBuilder::Abort(const char* reason) {
++ info()->set_bailout_reason(reason);
++ status_ = ABORTED;
++}
++
++
++LUnallocated* LChunkBuilder::ToUnallocated(Register reg) {
++ return new(zone()) LUnallocated(LUnallocated::FIXED_REGISTER,
++ Register::ToAllocationIndex(reg));
++}
++
++
++LUnallocated* LChunkBuilder::ToUnallocated(DoubleRegister reg) {
++ return new(zone()) LUnallocated(LUnallocated::FIXED_DOUBLE_REGISTER,
++ DoubleRegister::ToAllocationIndex(reg));
++}
++
++
++LOperand* LChunkBuilder::UseFixed(HValue* value, Register fixed_register) {
++ return Use(value, ToUnallocated(fixed_register));
++}
++
++
++LOperand* LChunkBuilder::UseFixedDouble(HValue* value, DoubleRegister reg) {
++ return Use(value, ToUnallocated(reg));
++}
++
++
++LOperand* LChunkBuilder::UseRegister(HValue* value) {
++ return Use(value, new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
++}
++
++
++LOperand* LChunkBuilder::UseRegisterAtStart(HValue* value) {
++ return Use(value,
++ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER,
++ LUnallocated::USED_AT_START));
++}
++
++
++LOperand* LChunkBuilder::UseTempRegister(HValue* value) {
++ return Use(value, new(zone()) LUnallocated(LUnallocated::WRITABLE_REGISTER));
++}
++
++
++LOperand* LChunkBuilder::Use(HValue* value) {
++ return Use(value, new(zone()) LUnallocated(LUnallocated::NONE));
++}
++
++
++LOperand* LChunkBuilder::UseAtStart(HValue* value) {
++ return Use(value, new(zone()) LUnallocated(LUnallocated::NONE,
++ LUnallocated::USED_AT_START));
++}
++
++
++LOperand* LChunkBuilder::UseOrConstant(HValue* value) {
++ return value->IsConstant()
++ ? chunk_->DefineConstantOperand(HConstant::cast(value))
++ : Use(value);
++}
++
++
++LOperand* LChunkBuilder::UseOrConstantAtStart(HValue* value) {
++ return value->IsConstant()
++ ? chunk_->DefineConstantOperand(HConstant::cast(value))
++ : UseAtStart(value);
++}
++
++
++LOperand* LChunkBuilder::UseRegisterOrConstant(HValue* value) {
++ return value->IsConstant()
++ ? chunk_->DefineConstantOperand(HConstant::cast(value))
++ : UseRegister(value);
++}
++
++
++LOperand* LChunkBuilder::UseRegisterOrConstantAtStart(HValue* value) {
++ return value->IsConstant()
++ ? chunk_->DefineConstantOperand(HConstant::cast(value))
++ : UseRegisterAtStart(value);
++}
++
++
++LOperand* LChunkBuilder::UseAny(HValue* value) {
++ return value->IsConstant()
++ ? chunk_->DefineConstantOperand(HConstant::cast(value))
++ : Use(value, new(zone()) LUnallocated(LUnallocated::ANY));
++}
++
++
++LOperand* LChunkBuilder::Use(HValue* value, LUnallocated* operand) {
++ if (value->EmitAtUses()) {
++ HInstruction* instr = HInstruction::cast(value);
++ VisitInstruction(instr);
++ }
++ operand->set_virtual_register(value->id());
++ return operand;
++}
++
++
++template<int I, int T>
++LInstruction* LChunkBuilder::Define(LTemplateInstruction<1, I, T>* instr,
++ LUnallocated* result) {
++ result->set_virtual_register(current_instruction_->id());
++ instr->set_result(result);
++ return instr;
++}
++
++
++template<int I, int T>
++LInstruction* LChunkBuilder::DefineAsRegister(
++ LTemplateInstruction<1, I, T>* instr) {
++ return Define(instr,
++ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER));
++}
++
++
++template<int I, int T>
++LInstruction* LChunkBuilder::DefineAsSpilled(
++ LTemplateInstruction<1, I, T>* instr, int index) {
++ return Define(instr,
++ new(zone()) LUnallocated(LUnallocated::FIXED_SLOT, index));
++}
++
++
++template<int I, int T>
++LInstruction* LChunkBuilder::DefineSameAsFirst(
++ LTemplateInstruction<1, I, T>* instr) {
++ return Define(instr,
++ new(zone()) LUnallocated(LUnallocated::SAME_AS_FIRST_INPUT));
++}
++
++
++template<int I, int T>
++LInstruction* LChunkBuilder::DefineFixed(
++ LTemplateInstruction<1, I, T>* instr, Register reg) {
++ return Define(instr, ToUnallocated(reg));
++}
++
++
++template<int I, int T>
++LInstruction* LChunkBuilder::DefineFixedDouble(
++ LTemplateInstruction<1, I, T>* instr, DoubleRegister reg) {
++ return Define(instr, ToUnallocated(reg));
++}
++
++
++LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) {
++ HEnvironment* hydrogen_env = current_block_->last_environment();
++ int argument_index_accumulator = 0;
++ instr->set_environment(CreateEnvironment(hydrogen_env,
++ &argument_index_accumulator));
++ return instr;
++}
++
++
++LInstruction* LChunkBuilder::MarkAsCall(LInstruction* instr,
++ HInstruction* hinstr,
++ CanDeoptimize can_deoptimize) {
++#ifdef DEBUG
++ instr->VerifyCall();
++#endif
++ instr->MarkAsCall();
++ instr = AssignPointerMap(instr);
++
++ if (hinstr->HasObservableSideEffects()) {
++ ASSERT(hinstr->next()->IsSimulate());
++ HSimulate* sim = HSimulate::cast(hinstr->next());
++ ASSERT(instruction_pending_deoptimization_environment_ == NULL);
++ ASSERT(pending_deoptimization_ast_id_.IsNone());
++ instruction_pending_deoptimization_environment_ = instr;
++ pending_deoptimization_ast_id_ = sim->ast_id();
++ }
++
++ // If instruction does not have side-effects lazy deoptimization
++ // after the call will try to deoptimize to the point before the call.
++ // Thus we still need to attach environment to this call even if
++ // call sequence can not deoptimize eagerly.
++ bool needs_environment =
++ (can_deoptimize == CAN_DEOPTIMIZE_EAGERLY) ||
++ !hinstr->HasObservableSideEffects();
++ if (needs_environment && !instr->HasEnvironment()) {
++ instr = AssignEnvironment(instr);
++ }
++
++ return instr;
++}
++
++
++LInstruction* LChunkBuilder::AssignPointerMap(LInstruction* instr) {
++ ASSERT(!instr->HasPointerMap());
++ instr->set_pointer_map(new(zone()) LPointerMap(position_, zone()));
++ return instr;
++}
++
++
++LUnallocated* LChunkBuilder::TempRegister() {
++ LUnallocated* operand =
++ new(zone()) LUnallocated(LUnallocated::MUST_HAVE_REGISTER);
++ operand->set_virtual_register(allocator_->GetVirtualRegister());
++ if (!allocator_->AllocationOk()) Abort("Not enough virtual registers.");
++ return operand;
++}
++
++
++LOperand* LChunkBuilder::FixedTemp(Register reg) {
++ LUnallocated* operand = ToUnallocated(reg);
++ ASSERT(operand->HasFixedPolicy());
++ return operand;
++}
++
++
++LOperand* LChunkBuilder::FixedTemp(DoubleRegister reg) {
++ LUnallocated* operand = ToUnallocated(reg);
++ ASSERT(operand->HasFixedPolicy());
++ return operand;
++}
++
++
++LInstruction* LChunkBuilder::DoBlockEntry(HBlockEntry* instr) {
++ return new(zone()) LLabel(instr->block());
++}
++
++
++LInstruction* LChunkBuilder::DoSoftDeoptimize(HSoftDeoptimize* instr) {
++ return AssignEnvironment(new(zone()) LDeoptimize);
++}
++
++
++LInstruction* LChunkBuilder::DoDeoptimize(HDeoptimize* instr) {
++ return AssignEnvironment(new(zone()) LDeoptimize);
++}
++
++
++LInstruction* LChunkBuilder::DoShift(Token::Value op,
++ HBitwiseBinaryOperation* instr) {
++ if (instr->representation().IsTagged()) {
++ ASSERT(instr->left()->representation().IsTagged());
++ ASSERT(instr->right()->representation().IsTagged());
++
++ LOperand* left = UseFixed(instr->left(), r4);
++ LOperand* right = UseFixed(instr->right(), r3);
++ LArithmeticT* result = new(zone()) LArithmeticT(op, left, right);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++ }
++
++ ASSERT(instr->representation().IsInteger32());
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++ LOperand* left = UseRegisterAtStart(instr->left());
++
++ HValue* right_value = instr->right();
++ LOperand* right = NULL;
++ int constant_value = 0;
++ if (right_value->IsConstant()) {
++ HConstant* constant = HConstant::cast(right_value);
++ right = chunk_->DefineConstantOperand(constant);
++ constant_value = constant->Integer32Value() & 0x1f;
++ } else {
++ right = UseRegisterAtStart(right_value);
++ }
++
++ // Shift operations can only deoptimize if we do a logical shift
++ // by 0 and the result cannot be truncated to int32.
++ bool does_deopt = false;
++ if (op == Token::SHR && constant_value == 0) {
++ if (FLAG_opt_safe_uint32_operations) {
++ does_deopt = !instr->CheckFlag(HInstruction::kUint32);
++ } else {
++ for (HUseIterator it(instr->uses()); !it.Done(); it.Advance()) {
++ if (!it.value()->CheckFlag(HValue::kTruncatingToInt32)) {
++ does_deopt = true;
++ break;
++ }
++ }
++ }
++ }
++
++ LInstruction* result =
++ DefineAsRegister(new(zone()) LShiftI(op, left, right, does_deopt));
++ return does_deopt ? AssignEnvironment(result) : result;
++}
++
++
++LInstruction* LChunkBuilder::DoArithmeticD(Token::Value op,
++ HArithmeticBinaryOperation* instr) {
++ ASSERT(instr->representation().IsDouble());
++ ASSERT(instr->left()->representation().IsDouble());
++ ASSERT(instr->right()->representation().IsDouble());
++ ASSERT(op != Token::MOD);
++ LOperand* left = UseRegisterAtStart(instr->left());
++ LOperand* right = UseRegisterAtStart(instr->right());
++ LArithmeticD* result = new(zone()) LArithmeticD(op, left, right);
++ return DefineAsRegister(result);
++}
++
++
++LInstruction* LChunkBuilder::DoArithmeticT(Token::Value op,
++ HArithmeticBinaryOperation* instr) {
++ ASSERT(op == Token::ADD ||
++ op == Token::DIV ||
++ op == Token::MOD ||
++ op == Token::MUL ||
++ op == Token::SUB);
++ HValue* left = instr->left();
++ HValue* right = instr->right();
++ ASSERT(left->representation().IsTagged());
++ ASSERT(right->representation().IsTagged());
++ LOperand* left_operand = UseFixed(left, r4);
++ LOperand* right_operand = UseFixed(right, r3);
++ LArithmeticT* result =
++ new(zone()) LArithmeticT(op, left_operand, right_operand);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++void LChunkBuilder::DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block) {
++ ASSERT(is_building());
++ current_block_ = block;
++ next_block_ = next_block;
++ if (block->IsStartBlock()) {
++ block->UpdateEnvironment(graph_->start_environment());
++ argument_count_ = 0;
++ } else if (block->predecessors()->length() == 1) {
++ // We have a single predecessor => copy environment and outgoing
++ // argument count from the predecessor.
++ ASSERT(block->phis()->length() == 0);
++ HBasicBlock* pred = block->predecessors()->at(0);
++ HEnvironment* last_environment = pred->last_environment();
++ ASSERT(last_environment != NULL);
++ // Only copy the environment, if it is later used again.
++ if (pred->end()->SecondSuccessor() == NULL) {
++ ASSERT(pred->end()->FirstSuccessor() == block);
++ } else {
++ if (pred->end()->FirstSuccessor()->block_id() > block->block_id()
||
++ pred->end()->SecondSuccessor()->block_id() > block->block_id())
{
++ last_environment = last_environment->Copy();
++ }
++ }
++ block->UpdateEnvironment(last_environment);
++ ASSERT(pred->argument_count() >= 0);
++ argument_count_ = pred->argument_count();
++ } else {
++ // We are at a state join => process phis.
++ HBasicBlock* pred = block->predecessors()->at(0);
++ // No need to copy the environment, it cannot be used later.
++ HEnvironment* last_environment = pred->last_environment();
++ for (int i = 0; i < block->phis()->length(); ++i) {
++ HPhi* phi = block->phis()->at(i);
++ last_environment->SetValueAt(phi->merged_index(), phi);
++ }
++ for (int i = 0; i < block->deleted_phis()->length(); ++i) {
++ last_environment->SetValueAt(block->deleted_phis()->at(i),
++ graph_->GetConstantUndefined());
++ }
++ block->UpdateEnvironment(last_environment);
++ // Pick up the outgoing argument count of one of the predecessors.
++ argument_count_ = pred->argument_count();
++ }
++ HInstruction* current = block->first();
++ int start = chunk_->instructions()->length();
++ while (current != NULL && !is_aborted()) {
++ // Code for constants in registers is generated lazily.
++ if (!current->EmitAtUses()) {
++ VisitInstruction(current);
++ }
++ current = current->next();
++ }
++ int end = chunk_->instructions()->length() - 1;
++ if (end >= start) {
++ block->set_first_instruction_index(start);
++ block->set_last_instruction_index(end);
++ }
++ block->set_argument_count(argument_count_);
++ next_block_ = NULL;
++ current_block_ = NULL;
++}
++
++
++void LChunkBuilder::VisitInstruction(HInstruction* current) {
++ HInstruction* old_current = current_instruction_;
++ current_instruction_ = current;
++ if (current->has_position()) position_ = current->position();
++ LInstruction* instr = current->CompileToLithium(this);
++
++ if (instr != NULL) {
++ if (FLAG_stress_pointer_maps && !instr->HasPointerMap()) {
++ instr = AssignPointerMap(instr);
++ }
++ if (FLAG_stress_environments && !instr->HasEnvironment()) {
++ instr = AssignEnvironment(instr);
++ }
++ instr->set_hydrogen_value(current);
++ chunk_->AddInstruction(instr, current_block_);
++ }
++ current_instruction_ = old_current;
++}
++
++
++LEnvironment* LChunkBuilder::CreateEnvironment(
++ HEnvironment* hydrogen_env,
++ int* argument_index_accumulator) {
++ if (hydrogen_env == NULL) return NULL;
++
++ LEnvironment* outer =
++ CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator);
++ BailoutId ast_id = hydrogen_env->ast_id();
++ ASSERT(!ast_id.IsNone() ||
++ hydrogen_env->frame_type() != JS_FUNCTION);
++ int value_count = hydrogen_env->length();
++ LEnvironment* result = new(zone()) LEnvironment(
++ hydrogen_env->closure(),
++ hydrogen_env->frame_type(),
++ ast_id,
++ hydrogen_env->parameter_count(),
++ argument_count_,
++ value_count,
++ outer,
++ hydrogen_env->entry(),
++ zone());
++ int argument_index = *argument_index_accumulator;
++ for (int i = 0; i < value_count; ++i) {
++ if (hydrogen_env->is_special_index(i)) continue;
++
++ HValue* value = hydrogen_env->values()->at(i);
++ LOperand* op = NULL;
++ if (value->IsArgumentsObject()) {
++ op = NULL;
++ } else if (value->IsPushArgument()) {
++ op = new(zone()) LArgument(argument_index++);
++ } else {
++ op = UseAny(value);
++ }
++ result->AddValue(op,
++ value->representation(),
++ value->CheckFlag(HInstruction::kUint32));
++ }
++
++ if (hydrogen_env->frame_type() == JS_FUNCTION) {
++ *argument_index_accumulator = argument_index;
++ }
++
++ return result;
++}
++
++
++LInstruction* LChunkBuilder::DoGoto(HGoto* instr) {
++ return new(zone()) LGoto(instr->FirstSuccessor()->block_id());
++}
++
++
++LInstruction* LChunkBuilder::DoBranch(HBranch* instr) {
++ HValue* value = instr->value();
++ if (value->EmitAtUses()) {
++ HBasicBlock* successor = HConstant::cast(value)->ToBoolean()
++ ? instr->FirstSuccessor()
++ : instr->SecondSuccessor();
++ return new(zone()) LGoto(successor->block_id());
++ }
++
++ LBranch* result = new(zone()) LBranch(UseRegister(value));
++ // Tagged values that are not known smis or booleans require a
++ // deoptimization environment.
++ Representation rep = value->representation();
++ HType type = value->type();
++ if (rep.IsTagged() && !type.IsSmi() && !type.IsBoolean()) {
++ return AssignEnvironment(result);
++ }
++ return result;
++}
++
++
++
++LInstruction* LChunkBuilder::DoCompareMap(HCompareMap* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ LOperand* value = UseRegisterAtStart(instr->value());
++ LOperand* temp = TempRegister();
++ return new(zone()) LCmpMapAndBranch(value, temp);
++}
++
++
++LInstruction* LChunkBuilder::DoArgumentsLength(HArgumentsLength* instr) {
++ LOperand* value = UseRegister(instr->value());
++ return DefineAsRegister(new(zone()) LArgumentsLength(value));
++}
++
++
++LInstruction* LChunkBuilder::DoArgumentsElements(HArgumentsElements* elems) {
++ return DefineAsRegister(new(zone()) LArgumentsElements);
++}
++
++
++LInstruction* LChunkBuilder::DoInstanceOf(HInstanceOf* instr) {
++ LInstanceOf* result =
++ new(zone()) LInstanceOf(UseFixed(instr->left(), r3),
++ UseFixed(instr->right(), r4));
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoInstanceOfKnownGlobal(
++ HInstanceOfKnownGlobal* instr) {
++ LInstanceOfKnownGlobal* result =
++ new(zone()) LInstanceOfKnownGlobal(UseFixed(instr->left(), r3),
++ FixedTemp(r7));
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoWrapReceiver(HWrapReceiver* instr) {
++ LOperand* receiver = UseRegisterAtStart(instr->receiver());
++ LOperand* function = UseRegisterAtStart(instr->function());
++ LWrapReceiver* result = new(zone()) LWrapReceiver(receiver, function);
++ return AssignEnvironment(DefineSameAsFirst(result));
++}
++
++
++LInstruction* LChunkBuilder::DoApplyArguments(HApplyArguments* instr) {
++ LOperand* function = UseFixed(instr->function(), r4);
++ LOperand* receiver = UseFixed(instr->receiver(), r3);
++ LOperand* length = UseFixed(instr->length(), r5);
++ LOperand* elements = UseFixed(instr->elements(), r6);
++ LApplyArguments* result = new(zone()) LApplyArguments(function,
++ receiver,
++ length,
++ elements);
++ return MarkAsCall(DefineFixed(result, r3), instr, CAN_DEOPTIMIZE_EAGERLY);
++}
++
++
++LInstruction* LChunkBuilder::DoPushArgument(HPushArgument* instr) {
++ ++argument_count_;
++ LOperand* argument = Use(instr->argument());
++ return new(zone()) LPushArgument(argument);
++}
++
++
++LInstruction* LChunkBuilder::DoThisFunction(HThisFunction* instr) {
++ return instr->HasNoUses()
++ ? NULL
++ : DefineAsRegister(new(zone()) LThisFunction);
++}
++
++
++LInstruction* LChunkBuilder::DoContext(HContext* instr) {
++ return instr->HasNoUses() ? NULL : DefineAsRegister(new(zone()) LContext);
++}
++
++
++LInstruction* LChunkBuilder::DoOuterContext(HOuterContext* instr) {
++ LOperand* context = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LOuterContext(context));
++}
++
++
++LInstruction* LChunkBuilder::DoDeclareGlobals(HDeclareGlobals* instr) {
++ return MarkAsCall(new(zone()) LDeclareGlobals, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoGlobalObject(HGlobalObject* instr) {
++ LOperand* context = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LGlobalObject(context));
++}
++
++
++LInstruction* LChunkBuilder::DoGlobalReceiver(HGlobalReceiver* instr) {
++ LOperand* global_object = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LGlobalReceiver(global_object));
++}
++
++
++LInstruction* LChunkBuilder::DoCallConstantFunction(
++ HCallConstantFunction* instr) {
++ argument_count_ -= instr->argument_count();
++ return MarkAsCall(DefineFixed(new(zone()) LCallConstantFunction, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoInvokeFunction(HInvokeFunction* instr) {
++ LOperand* function = UseFixed(instr->function(), r4);
++ argument_count_ -= instr->argument_count();
++ LInvokeFunction* result = new(zone()) LInvokeFunction(function);
++ return MarkAsCall(DefineFixed(result, r3), instr, CANNOT_DEOPTIMIZE_EAGERLY);
++}
++
++
++LInstruction* LChunkBuilder::DoUnaryMathOperation(HUnaryMathOperation* instr) {
++ BuiltinFunctionId op = instr->op();
++ if (op == kMathLog || op == kMathSin || op == kMathCos || op == kMathTan) {
++ LOperand* input = UseFixedDouble(instr->value(), d2);
++ LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(input, NULL);
++ return MarkAsCall(DefineFixedDouble(result, d2), instr);
++ } else if (op == kMathPowHalf) {
++ LOperand* input = UseFixedDouble(instr->value(), d2);
++ LOperand* temp = FixedTemp(d3);
++ LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(input, temp);
++ return DefineFixedDouble(result, d2);
++ } else {
++ LOperand* input = UseRegisterAtStart(instr->value());
++
++ LOperand* temp = (op == kMathRound) ? FixedTemp(d3) : NULL;
++ LUnaryMathOperation* result = new(zone()) LUnaryMathOperation(input, temp);
++ switch (op) {
++ case kMathAbs:
++ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
++ case kMathFloor:
++ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
++ case kMathSqrt:
++ return DefineAsRegister(result);
++ case kMathRound:
++ return AssignEnvironment(DefineAsRegister(result));
++ default:
++ UNREACHABLE();
++ return NULL;
++ }
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoCallKeyed(HCallKeyed* instr) {
++ ASSERT(instr->key()->representation().IsTagged());
++ argument_count_ -= instr->argument_count();
++ LOperand* key = UseFixed(instr->key(), r5);
++ return MarkAsCall(DefineFixed(new(zone()) LCallKeyed(key), r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCallNamed(HCallNamed* instr) {
++ argument_count_ -= instr->argument_count();
++ return MarkAsCall(DefineFixed(new(zone()) LCallNamed, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCallGlobal(HCallGlobal* instr) {
++ argument_count_ -= instr->argument_count();
++ return MarkAsCall(DefineFixed(new(zone()) LCallGlobal, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCallKnownGlobal(HCallKnownGlobal* instr) {
++ argument_count_ -= instr->argument_count();
++ return MarkAsCall(DefineFixed(new(zone()) LCallKnownGlobal, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCallNew(HCallNew* instr) {
++ LOperand* constructor = UseFixed(instr->constructor(), r4);
++ argument_count_ -= instr->argument_count();
++ LCallNew* result = new(zone()) LCallNew(constructor);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCallFunction(HCallFunction* instr) {
++ LOperand* function = UseFixed(instr->function(), r4);
++ argument_count_ -= instr->argument_count();
++ return MarkAsCall(DefineFixed(new(zone()) LCallFunction(function), r3),
++ instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCallRuntime(HCallRuntime* instr) {
++ argument_count_ -= instr->argument_count();
++ return MarkAsCall(DefineFixed(new(zone()) LCallRuntime, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoShr(HShr* instr) {
++ return DoShift(Token::SHR, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoSar(HSar* instr) {
++ return DoShift(Token::SAR, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoShl(HShl* instr) {
++ return DoShift(Token::SHL, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoBitwise(HBitwise* instr) {
++ if (instr->representation().IsInteger32()) {
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++
++ LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand());
++ LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand());
++ return DefineAsRegister(new(zone()) LBitI(left, right));
++ } else {
++ ASSERT(instr->representation().IsTagged());
++ ASSERT(instr->left()->representation().IsTagged());
++ ASSERT(instr->right()->representation().IsTagged());
++
++ LOperand* left = UseFixed(instr->left(), r4);
++ LOperand* right = UseFixed(instr->right(), r3);
++ LArithmeticT* result = new(zone()) LArithmeticT(instr->op(), left, right);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoBitNot(HBitNot* instr) {
++ ASSERT(instr->value()->representation().IsInteger32());
++ ASSERT(instr->representation().IsInteger32());
++ if (instr->HasNoUses()) return NULL;
++ LOperand* value = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LBitNotI(value));
++}
++
++
++LInstruction* LChunkBuilder::DoDiv(HDiv* instr) {
++ if (instr->representation().IsDouble()) {
++ return DoArithmeticD(Token::DIV, instr);
++ } else if (instr->representation().IsInteger32()) {
++ // TODO(1042) The fixed register allocation
++ // is needed because we call TypeRecordingBinaryOpStub from
++ // the generated code, which requires registers r3
++ // and r4 to be used. We should remove that
++ // when we provide a native implementation.
++ LOperand* dividend = UseFixed(instr->left(), r3);
++ LOperand* divisor = UseFixed(instr->right(), r4);
++ return AssignEnvironment(AssignPointerMap(
++ DefineFixed(new(zone()) LDivI(dividend, divisor), r3)));
++ } else {
++ return DoArithmeticT(Token::DIV, instr);
++ }
++}
++
++
++bool LChunkBuilder::HasMagicNumberForDivisor(int32_t divisor) {
++ uint32_t divisor_abs = abs(divisor);
++ // Dividing by 0, 1, and powers of 2 is easy.
++ // Note that IsPowerOf2(0) returns true;
++ ASSERT(IsPowerOf2(0) == true);
++ if (IsPowerOf2(divisor_abs)) return true;
++
++ // We have magic numbers for a few specific divisors.
++ // Details and proofs can be found in:
++ // - Hacker's Delight, Henry S. Warren, Jr.
++ // - The PowerPC Compiler Writers Guide
++ // and probably many others.
++ //
++ // We handle
++ // <divisor with magic numbers> * <power of 2>
++ // but not
++ // <divisor with magic numbers> * <other divisor with magic numbers>
++ int32_t power_of_2_factor =
++ CompilerIntrinsics::CountTrailingZeros(divisor_abs);
++ DivMagicNumbers magic_numbers =
++ DivMagicNumberFor(divisor_abs >> power_of_2_factor);
++ if (magic_numbers.M != InvalidDivMagicNumber.M) return true;
++
++ return false;
++}
++
++
++HValue* LChunkBuilder::SimplifiedDividendForMathFloorOfDiv(HValue* dividend) {
++ // A value with an integer representation does not need to be transformed.
++ if (dividend->representation().IsInteger32()) {
++ return dividend;
++ // A change from an integer32 can be replaced by the integer32 value.
++ } else if (dividend->IsChange() &&
++ HChange::cast(dividend)->from().IsInteger32()) {
++ return HChange::cast(dividend)->value();
++ }
++ return NULL;
++}
++
++
++HValue* LChunkBuilder::SimplifiedDivisorForMathFloorOfDiv(HValue* divisor) {
++ if (divisor->IsConstant() &&
++ HConstant::cast(divisor)->HasInteger32Value()) {
++ HConstant* constant_val = HConstant::cast(divisor);
++ return constant_val->CopyToRepresentation(Representation::Integer32(),
++ divisor->block()->zone());
++ }
++ return NULL;
++}
++
++LInstruction* LChunkBuilder::DoMathFloorOfDiv(HMathFloorOfDiv* instr) {
++ HValue* right = instr->right();
++ ASSERT(right->IsConstant() &&
HConstant::cast(right)->HasInteger32Value());
++ LOperand* divisor = chunk_->DefineConstantOperand(HConstant::cast(right));
++ int32_t divisor_si = HConstant::cast(right)->Integer32Value();
++ if (divisor_si == 0) {
++ LOperand* dividend = UseRegister(instr->left());
++ return AssignEnvironment(DefineAsRegister(
++ new(zone()) LMathFloorOfDiv(dividend, divisor, NULL)));
++ } else if (IsPowerOf2(abs(divisor_si))) {
++ LOperand* dividend = UseRegisterAtStart(instr->left());
++ LInstruction* result = DefineAsRegister(
++ new(zone()) LMathFloorOfDiv(dividend, divisor, NULL));
++ return divisor_si < 0 ? AssignEnvironment(result) : result;
++ } else {
++ LOperand* dividend = UseRegisterAtStart(instr->left());
++ LOperand* temp = TempRegister();
++ LInstruction* result = DefineAsRegister(
++ new(zone()) LMathFloorOfDiv(dividend, divisor, temp));
++ return divisor_si < 0 ? AssignEnvironment(result) : result;
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoMod(HMod* instr) {
++ if (instr->representation().IsInteger32()) {
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++
++ LModI* mod;
++ if (instr->HasPowerOf2Divisor()) {
++ ASSERT(!instr->CheckFlag(HValue::kCanBeDivByZero));
++ LOperand* value = UseRegisterAtStart(instr->left());
++ mod = new(zone()) LModI(value, UseOrConstant(instr->right()));
++ } else {
++ LOperand* dividend = UseRegister(instr->left());
++ LOperand* divisor = UseRegister(instr->right());
++ mod = new(zone()) LModI(dividend,
++ divisor,
++ TempRegister(),
++ FixedTemp(d10),
++ FixedTemp(d11));
++ }
++
++ if (instr->CheckFlag(HValue::kBailoutOnMinusZero) ||
++ instr->CheckFlag(HValue::kCanBeDivByZero)) {
++ return AssignEnvironment(DefineAsRegister(mod));
++ } else {
++ return DefineAsRegister(mod);
++ }
++ } else if (instr->representation().IsTagged()) {
++ return DoArithmeticT(Token::MOD, instr);
++ } else {
++ ASSERT(instr->representation().IsDouble());
++ // We call a C function for double modulo. It can't trigger a GC.
++ // We need to use fixed result register for the call.
++ // TODO(fschneider): Allow any register as input registers.
++ LOperand* left = UseFixedDouble(instr->left(), d1);
++ LOperand* right = UseFixedDouble(instr->right(), d2);
++ LArithmeticD* result = new(zone()) LArithmeticD(Token::MOD, left, right);
++ return MarkAsCall(DefineFixedDouble(result, d1), instr);
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoMul(HMul* instr) {
++ if (instr->representation().IsInteger32()) {
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++ LOperand* left;
++ LOperand* right = UseOrConstant(instr->MostConstantOperand());
++ LOperand* temp = NULL;
++ if (instr->CheckFlag(HValue::kBailoutOnMinusZero) &&
++ (instr->CheckFlag(HValue::kCanOverflow) ||
++ !right->IsConstantOperand())) {
++ left = UseRegister(instr->LeastConstantOperand());
++ temp = TempRegister();
++ } else {
++ left = UseRegisterAtStart(instr->LeastConstantOperand());
++ }
++ LMulI* mul = new(zone()) LMulI(left, right, temp);
++ if (instr->CheckFlag(HValue::kCanOverflow) ||
++ instr->CheckFlag(HValue::kBailoutOnMinusZero)) {
++ AssignEnvironment(mul);
++ }
++ return DefineAsRegister(mul);
++
++ } else if (instr->representation().IsDouble()) {
++ return DoArithmeticD(Token::MUL, instr);
++
++ } else {
++ return DoArithmeticT(Token::MUL, instr);
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoSub(HSub* instr) {
++ if (instr->representation().IsInteger32()) {
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++ LOperand* left = UseRegisterAtStart(instr->left());
++ LOperand* right = UseOrConstantAtStart(instr->right());
++ LSubI* sub = new(zone()) LSubI(left, right);
++ LInstruction* result = DefineAsRegister(sub);
++ if (instr->CheckFlag(HValue::kCanOverflow)) {
++ result = AssignEnvironment(result);
++ }
++ return result;
++ } else if (instr->representation().IsDouble()) {
++ return DoArithmeticD(Token::SUB, instr);
++ } else {
++ return DoArithmeticT(Token::SUB, instr);
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoAdd(HAdd* instr) {
++ if (instr->representation().IsInteger32()) {
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++ LOperand* left = UseRegisterAtStart(instr->LeastConstantOperand());
++ LOperand* right = UseOrConstantAtStart(instr->MostConstantOperand());
++ LAddI* add = new(zone()) LAddI(left, right);
++ LInstruction* result = DefineAsRegister(add);
++ if (instr->CheckFlag(HValue::kCanOverflow)) {
++ result = AssignEnvironment(result);
++ }
++ return result;
++ } else if (instr->representation().IsDouble()) {
++ return DoArithmeticD(Token::ADD, instr);
++ } else {
++ ASSERT(instr->representation().IsTagged());
++ return DoArithmeticT(Token::ADD, instr);
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoMathMinMax(HMathMinMax* instr) {
++ LOperand* left = NULL;
++ LOperand* right = NULL;
++ if (instr->representation().IsInteger32()) {
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++ left = UseRegisterAtStart(instr->LeastConstantOperand());
++ right = UseOrConstantAtStart(instr->MostConstantOperand());
++ } else {
++ ASSERT(instr->representation().IsDouble());
++ ASSERT(instr->left()->representation().IsDouble());
++ ASSERT(instr->right()->representation().IsDouble());
++ left = UseRegisterAtStart(instr->left());
++ right = UseRegisterAtStart(instr->right());
++ }
++ return DefineAsRegister(new(zone()) LMathMinMax(left, right));
++}
++
++
++LInstruction* LChunkBuilder::DoPower(HPower* instr) {
++ ASSERT(instr->representation().IsDouble());
++ // We call a C function for double power. It can't trigger a GC.
++ // We need to use fixed result register for the call.
++ Representation exponent_type = instr->right()->representation();
++ ASSERT(instr->left()->representation().IsDouble());
++ LOperand* left = UseFixedDouble(instr->left(), d1);
++ LOperand* right = exponent_type.IsDouble() ?
++ UseFixedDouble(instr->right(), d2) :
++ UseFixed(instr->right(), r5);
++ LPower* result = new(zone()) LPower(left, right);
++ return MarkAsCall(DefineFixedDouble(result, d3),
++ instr,
++ CAN_DEOPTIMIZE_EAGERLY);
++}
++
++
++LInstruction* LChunkBuilder::DoRandom(HRandom* instr) {
++ ASSERT(instr->representation().IsDouble());
++ ASSERT(instr->global_object()->representation().IsTagged());
++ LOperand* global_object = UseFixed(instr->global_object(), r3);
++ LRandom* result = new(zone()) LRandom(global_object);
++ return MarkAsCall(DefineFixedDouble(result, d7), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCompareGeneric(HCompareGeneric* instr) {
++ ASSERT(instr->left()->representation().IsTagged());
++ ASSERT(instr->right()->representation().IsTagged());
++ LOperand* left = UseFixed(instr->left(), r4);
++ LOperand* right = UseFixed(instr->right(), r3);
++ LCmpT* result = new(zone()) LCmpT(left, right);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoCompareIDAndBranch(
++ HCompareIDAndBranch* instr) {
++ Representation r = instr->GetInputRepresentation();
++ if (r.IsInteger32()) {
++ ASSERT(instr->left()->representation().IsInteger32());
++ ASSERT(instr->right()->representation().IsInteger32());
++ LOperand* left = UseRegisterOrConstantAtStart(instr->left());
++ LOperand* right = UseRegisterOrConstantAtStart(instr->right());
++ return new(zone()) LCmpIDAndBranch(left, right);
++ } else {
++ ASSERT(r.IsDouble());
++ ASSERT(instr->left()->representation().IsDouble());
++ ASSERT(instr->right()->representation().IsDouble());
++ LOperand* left = UseRegisterAtStart(instr->left());
++ LOperand* right = UseRegisterAtStart(instr->right());
++ return new(zone()) LCmpIDAndBranch(left, right);
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoCompareObjectEqAndBranch(
++ HCompareObjectEqAndBranch* instr) {
++ LOperand* left = UseRegisterAtStart(instr->left());
++ LOperand* right = UseRegisterAtStart(instr->right());
++ return new(zone()) LCmpObjectEqAndBranch(left, right);
++}
++
++
++LInstruction* LChunkBuilder::DoCompareConstantEqAndBranch(
++ HCompareConstantEqAndBranch* instr) {
++ LOperand* value = UseRegisterAtStart(instr->value());
++ return new(zone()) LCmpConstantEqAndBranch(value);
++}
++
++
++LInstruction* LChunkBuilder::DoIsNilAndBranch(HIsNilAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ return new(zone()) LIsNilAndBranch(UseRegisterAtStart(instr->value()));
++}
++
++
++LInstruction* LChunkBuilder::DoIsObjectAndBranch(HIsObjectAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ LOperand* value = UseRegisterAtStart(instr->value());
++ LOperand* temp = TempRegister();
++ return new(zone()) LIsObjectAndBranch(value, temp);
++}
++
++
++LInstruction* LChunkBuilder::DoIsStringAndBranch(HIsStringAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ LOperand* value = UseRegisterAtStart(instr->value());
++ LOperand* temp = TempRegister();
++ return new(zone()) LIsStringAndBranch(value, temp);
++}
++
++
++LInstruction* LChunkBuilder::DoIsSmiAndBranch(HIsSmiAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ return new(zone()) LIsSmiAndBranch(Use(instr->value()));
++}
++
++
++LInstruction* LChunkBuilder::DoIsUndetectableAndBranch(
++ HIsUndetectableAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ LOperand* value = UseRegisterAtStart(instr->value());
++ return new(zone()) LIsUndetectableAndBranch(value, TempRegister());
++}
++
++
++LInstruction* LChunkBuilder::DoStringCompareAndBranch(
++ HStringCompareAndBranch* instr) {
++ ASSERT(instr->left()->representation().IsTagged());
++ ASSERT(instr->right()->representation().IsTagged());
++ LOperand* left = UseFixed(instr->left(), r4);
++ LOperand* right = UseFixed(instr->right(), r3);
++ LStringCompareAndBranch* result =
++ new(zone()) LStringCompareAndBranch(left, right);
++ return MarkAsCall(result, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoHasInstanceTypeAndBranch(
++ HHasInstanceTypeAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ LOperand* value = UseRegisterAtStart(instr->value());
++ return new(zone()) LHasInstanceTypeAndBranch(value);
++}
++
++
++LInstruction* LChunkBuilder::DoGetCachedArrayIndex(
++ HGetCachedArrayIndex* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ LOperand* value = UseRegisterAtStart(instr->value());
++
++ return DefineAsRegister(new(zone()) LGetCachedArrayIndex(value));
++}
++
++
++LInstruction* LChunkBuilder::DoHasCachedArrayIndexAndBranch(
++ HHasCachedArrayIndexAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ return new(zone()) LHasCachedArrayIndexAndBranch(
++ UseRegisterAtStart(instr->value()));
++}
++
++
++LInstruction* LChunkBuilder::DoClassOfTestAndBranch(
++ HClassOfTestAndBranch* instr) {
++ ASSERT(instr->value()->representation().IsTagged());
++ LOperand* value = UseRegister(instr->value());
++ return new(zone()) LClassOfTestAndBranch(value, TempRegister());
++}
++
++
++LInstruction* LChunkBuilder::DoJSArrayLength(HJSArrayLength* instr) {
++ LOperand* array = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LJSArrayLength(array));
++}
++
++
++LInstruction* LChunkBuilder::DoFixedArrayBaseLength(
++ HFixedArrayBaseLength* instr) {
++ LOperand* array = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LFixedArrayBaseLength(array));
++}
++
++
++LInstruction* LChunkBuilder::DoMapEnumLength(HMapEnumLength* instr) {
++ LOperand* map = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LMapEnumLength(map));
++}
++
++
++LInstruction* LChunkBuilder::DoElementsKind(HElementsKind* instr) {
++ LOperand* object = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LElementsKind(object));
++}
++
++
++LInstruction* LChunkBuilder::DoValueOf(HValueOf* instr) {
++ LOperand* object = UseRegister(instr->value());
++ LValueOf* result = new(zone()) LValueOf(object, TempRegister());
++ return DefineAsRegister(result);
++}
++
++
++LInstruction* LChunkBuilder::DoDateField(HDateField* instr) {
++ LOperand* object = UseFixed(instr->value(), r3);
++ LDateField* result =
++ new(zone()) LDateField(object, FixedTemp(r4), instr->index());
++ return MarkAsCall(DefineFixed(result, r3), instr, CAN_DEOPTIMIZE_EAGERLY);
++}
++
++
++LInstruction* LChunkBuilder::DoBoundsCheck(HBoundsCheck* instr) {
++ LOperand* value = UseRegisterOrConstantAtStart(instr->index());
++ LOperand* length = UseRegister(instr->length());
++ return AssignEnvironment(new(zone()) LBoundsCheck(value, length));
++}
++
++
++LInstruction* LChunkBuilder::DoAbnormalExit(HAbnormalExit* instr) {
++ // The control instruction marking the end of a block that completed
++ // abruptly (e.g., threw an exception). There is nothing specific to do.
++ return NULL;
++}
++
++
++LInstruction* LChunkBuilder::DoThrow(HThrow* instr) {
++ LOperand* value = UseFixed(instr->value(), r3);
++ return MarkAsCall(new(zone()) LThrow(value), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoUseConst(HUseConst* instr) {
++ return NULL;
++}
++
++
++LInstruction* LChunkBuilder::DoForceRepresentation(HForceRepresentation* bad) {
++ // All HForceRepresentation instructions should be eliminated in the
++ // representation change phase of Hydrogen.
++ UNREACHABLE();
++ return NULL;
++}
++
++
++LInstruction* LChunkBuilder::DoChange(HChange* instr) {
++ Representation from = instr->from();
++ Representation to = instr->to();
++ if (from.IsTagged()) {
++ if (to.IsDouble()) {
++ LOperand* value = UseRegister(instr->value());
++ LNumberUntagD* res = new(zone()) LNumberUntagD(value);
++ return AssignEnvironment(DefineAsRegister(res));
++ } else {
++ ASSERT(to.IsInteger32());
++ LOperand* value = UseRegisterAtStart(instr->value());
++ LInstruction* res = NULL;
++ if (instr->value()->type().IsSmi()) {
++ res = DefineAsRegister(new(zone()) LSmiUntag(value, false));
++ } else {
++ LOperand* temp1 = TempRegister();
++ LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister()
++ : NULL;
++ LOperand* temp3 = FixedTemp(d11);
++ res = DefineSameAsFirst(new(zone()) LTaggedToI(value,
++ temp1,
++ temp2,
++ temp3));
++ res = AssignEnvironment(res);
++ }
++ return res;
++ }
++ } else if (from.IsDouble()) {
++ if (to.IsTagged()) {
++ LOperand* value = UseRegister(instr->value());
++ LOperand* temp1 = TempRegister();
++ LOperand* temp2 = TempRegister();
++
++ // Make sure that the temp and result_temp registers are
++ // different.
++ LUnallocated* result_temp = TempRegister();
++ LNumberTagD* result = new(zone()) LNumberTagD(value, temp1, temp2);
++ Define(result, result_temp);
++ return AssignPointerMap(result);
++ } else {
++ ASSERT(to.IsInteger32());
++ LOperand* value = UseRegister(instr->value());
++ LOperand* temp1 = TempRegister();
++ LOperand* temp2 = instr->CanTruncateToInt32() ? TempRegister() : NULL;
++ LDoubleToI* res = new(zone()) LDoubleToI(value, temp1, temp2);
++ return AssignEnvironment(DefineAsRegister(res));
++ }
++ } else if (from.IsInteger32()) {
++ if (to.IsTagged()) {
++ HValue* val = instr->value();
++ LOperand* value = UseRegisterAtStart(val);
++ if (val->CheckFlag(HInstruction::kUint32)) {
++ LNumberTagU* result = new(zone()) LNumberTagU(value);
++ return AssignEnvironment(AssignPointerMap(DefineSameAsFirst(result)));
++ } else if (val->HasRange() && val->range()->IsInSmiRange()) {
++ return DefineAsRegister(new(zone()) LSmiTag(value));
++ } else {
++ LNumberTagI* result = new(zone()) LNumberTagI(value);
++ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
++ }
++ } else {
++ ASSERT(to.IsDouble());
++ if (instr->value()->CheckFlag(HInstruction::kUint32)) {
++ return DefineAsRegister(
++ new(zone()) LUint32ToDouble(UseRegister(instr->value())));
++ } else {
++ return DefineAsRegister(
++ new(zone()) LInteger32ToDouble(Use(instr->value())));
++ }
++ }
++ }
++ UNREACHABLE();
++ return NULL;
++}
++
++
++LInstruction* LChunkBuilder::DoCheckNonSmi(HCheckNonSmi* instr) {
++ LOperand* value = UseRegisterAtStart(instr->value());
++ return AssignEnvironment(new(zone()) LCheckNonSmi(value));
++}
++
++
++LInstruction* LChunkBuilder::DoCheckInstanceType(HCheckInstanceType* instr) {
++ LOperand* value = UseRegisterAtStart(instr->value());
++ LInstruction* result = new(zone()) LCheckInstanceType(value);
++ return AssignEnvironment(result);
++}
++
++
++LInstruction* LChunkBuilder::DoCheckPrototypeMaps(HCheckPrototypeMaps* instr) {
++ LOperand* temp1 = TempRegister();
++ LOperand* temp2 = TempRegister();
++ LInstruction* result = new(zone()) LCheckPrototypeMaps(temp1, temp2);
++ return AssignEnvironment(result);
++}
++
++
++LInstruction* LChunkBuilder::DoCheckSmi(HCheckSmi* instr) {
++ LOperand* value = UseRegisterAtStart(instr->value());
++ return AssignEnvironment(new(zone()) LCheckSmi(value));
++}
++
++
++LInstruction* LChunkBuilder::DoCheckFunction(HCheckFunction* instr) {
++ LOperand* value = UseRegisterAtStart(instr->value());
++ return AssignEnvironment(new(zone()) LCheckFunction(value));
++}
++
++
++LInstruction* LChunkBuilder::DoCheckMaps(HCheckMaps* instr) {
++ LOperand* value = UseRegisterAtStart(instr->value());
++ LInstruction* result = new(zone()) LCheckMaps(value);
++ return AssignEnvironment(result);
++}
++
++
++LInstruction* LChunkBuilder::DoClampToUint8(HClampToUint8* instr) {
++ HValue* value = instr->value();
++ Representation input_rep = value->representation();
++ LOperand* reg = UseRegister(value);
++ if (input_rep.IsDouble()) {
++ return DefineAsRegister(new(zone()) LClampDToUint8(reg, FixedTemp(d11)));
++ } else if (input_rep.IsInteger32()) {
++ return DefineAsRegister(new(zone()) LClampIToUint8(reg));
++ } else {
++ ASSERT(input_rep.IsTagged());
++ // Register allocator doesn't (yet) support allocation of double
++ // temps. Reserve d1 explicitly.
++ LClampTToUint8* result = new(zone()) LClampTToUint8(reg, FixedTemp(d10),
++ FixedTemp(d11));
++ return AssignEnvironment(DefineAsRegister(result));
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoReturn(HReturn* instr) {
++ return new(zone()) LReturn(UseFixed(instr->value(), r3));
++}
++
++
++LInstruction* LChunkBuilder::DoConstant(HConstant* instr) {
++ Representation r = instr->representation();
++ if (r.IsInteger32()) {
++ return DefineAsRegister(new(zone()) LConstantI);
++ } else if (r.IsDouble()) {
++ return DefineAsRegister(new(zone()) LConstantD);
++ } else if (r.IsTagged()) {
++ return DefineAsRegister(new(zone()) LConstantT);
++ } else {
++ UNREACHABLE();
++ return NULL;
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoLoadGlobalCell(HLoadGlobalCell* instr) {
++ LLoadGlobalCell* result = new(zone()) LLoadGlobalCell;
++ return instr->RequiresHoleCheck()
++ ? AssignEnvironment(DefineAsRegister(result))
++ : DefineAsRegister(result);
++}
++
++
++LInstruction* LChunkBuilder::DoLoadGlobalGeneric(HLoadGlobalGeneric* instr) {
++ LOperand* global_object = UseFixed(instr->global_object(), r3);
++ LLoadGlobalGeneric* result = new(zone()) LLoadGlobalGeneric(global_object);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoStoreGlobalCell(HStoreGlobalCell* instr) {
++ LOperand* value = UseRegister(instr->value());
++ // Use a temp to check the value in the cell in the case where we perform
++ // a hole check.
++ return instr->RequiresHoleCheck()
++ ? AssignEnvironment(new(zone()) LStoreGlobalCell(value, TempRegister()))
++ : new(zone()) LStoreGlobalCell(value, NULL);
++}
++
++
++LInstruction* LChunkBuilder::DoStoreGlobalGeneric(HStoreGlobalGeneric* instr) {
++ LOperand* global_object = UseFixed(instr->global_object(), r4);
++ LOperand* value = UseFixed(instr->value(), r3);
++ LStoreGlobalGeneric* result =
++ new(zone()) LStoreGlobalGeneric(global_object, value);
++ return MarkAsCall(result, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoLoadContextSlot(HLoadContextSlot* instr) {
++ LOperand* context = UseRegisterAtStart(instr->value());
++ LInstruction* result =
++ DefineAsRegister(new(zone()) LLoadContextSlot(context));
++ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
++}
++
++
++LInstruction* LChunkBuilder::DoStoreContextSlot(HStoreContextSlot* instr) {
++ LOperand* context;
++ LOperand* value;
++ if (instr->NeedsWriteBarrier()) {
++ context = UseTempRegister(instr->context());
++ value = UseTempRegister(instr->value());
++ } else {
++ context = UseRegister(instr->context());
++ value = UseRegister(instr->value());
++ }
++ LInstruction* result = new(zone()) LStoreContextSlot(context, value);
++ return instr->RequiresHoleCheck() ? AssignEnvironment(result) : result;
++}
++
++
++LInstruction* LChunkBuilder::DoLoadNamedField(HLoadNamedField* instr) {
++ return DefineAsRegister(
++ new(zone()) LLoadNamedField(UseRegisterAtStart(instr->object())));
++}
++
++
++LInstruction* LChunkBuilder::DoLoadNamedFieldPolymorphic(
++ HLoadNamedFieldPolymorphic* instr) {
++ ASSERT(instr->representation().IsTagged());
++ if (instr->need_generic()) {
++ LOperand* obj = UseFixed(instr->object(), r3);
++ LLoadNamedFieldPolymorphic* result =
++ new(zone()) LLoadNamedFieldPolymorphic(obj);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++ } else {
++ LOperand* obj = UseRegisterAtStart(instr->object());
++ LLoadNamedFieldPolymorphic* result =
++ new(zone()) LLoadNamedFieldPolymorphic(obj);
++ return AssignEnvironment(DefineAsRegister(result));
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoLoadNamedGeneric(HLoadNamedGeneric* instr) {
++ LOperand* object = UseFixed(instr->object(), r3);
++ LInstruction* result = DefineFixed(new(zone()) LLoadNamedGeneric(object), r3);
++ return MarkAsCall(result, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoLoadFunctionPrototype(
++ HLoadFunctionPrototype* instr) {
++ return AssignEnvironment(DefineAsRegister(
++ new(zone()) LLoadFunctionPrototype(UseRegister(instr->function()))));
++}
++
++
++LInstruction* LChunkBuilder::DoLoadElements(HLoadElements* instr) {
++ LOperand* input = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LLoadElements(input));
++}
++
++
++LInstruction* LChunkBuilder::DoLoadExternalArrayPointer(
++ HLoadExternalArrayPointer* instr) {
++ LOperand* input = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LLoadExternalArrayPointer(input));
++}
++
++
++LInstruction* LChunkBuilder::DoLoadKeyedFastElement(
++ HLoadKeyedFastElement* instr) {
++ ASSERT(instr->representation().IsTagged());
++ ASSERT(instr->key()->representation().IsInteger32() ||
++ instr->key()->representation().IsTagged());
++ LOperand* obj = UseRegisterAtStart(instr->object());
++ LOperand* key = UseRegisterOrConstantAtStart(instr->key());
++ LLoadKeyedFastElement* result = new(zone()) LLoadKeyedFastElement(obj, key);
++ if (instr->RequiresHoleCheck()) AssignEnvironment(result);
++ return DefineAsRegister(result);
++}
++
++
++LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement(
++ HLoadKeyedFastDoubleElement* instr) {
++ ASSERT(instr->representation().IsDouble());
++ ASSERT(instr->key()->representation().IsInteger32() ||
++ instr->key()->representation().IsTagged());
++ LOperand* elements = UseTempRegister(instr->elements());
++ LOperand* key = UseRegisterOrConstantAtStart(instr->key());
++ LLoadKeyedFastDoubleElement* result =
++ new(zone()) LLoadKeyedFastDoubleElement(elements, key);
++ return AssignEnvironment(DefineAsRegister(result));
++}
++
++
++LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement(
++ HLoadKeyedSpecializedArrayElement* instr) {
++ ElementsKind elements_kind = instr->elements_kind();
++ ASSERT(
++ (instr->representation().IsInteger32() &&
++ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
++ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
++ (instr->representation().IsDouble() &&
++ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
++ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
++ ASSERT(instr->key()->representation().IsInteger32() ||
++ instr->key()->representation().IsTagged());
++ LOperand* external_pointer = UseRegister(instr->external_pointer());
++ LOperand* key = UseRegisterOrConstant(instr->key());
++ LLoadKeyedSpecializedArrayElement* result =
++ new(zone()) LLoadKeyedSpecializedArrayElement(external_pointer, key);
++ LInstruction* load_instr = DefineAsRegister(result);
++ // An unsigned int array load might overflow and cause a deopt, make sure it
++ // has an environment.
++ return (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) ?
++ AssignEnvironment(load_instr) : load_instr;
++}
++
++
++LInstruction* LChunkBuilder::DoLoadKeyedGeneric(HLoadKeyedGeneric* instr) {
++ LOperand* object = UseFixed(instr->object(), r4);
++ LOperand* key = UseFixed(instr->key(), r3);
++
++ LInstruction* result =
++ DefineFixed(new(zone()) LLoadKeyedGeneric(object, key), r3);
++ return MarkAsCall(result, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoStoreKeyedFastElement(
++ HStoreKeyedFastElement* instr) {
++ bool needs_write_barrier = instr->NeedsWriteBarrier();
++ ASSERT(instr->value()->representation().IsTagged());
++ ASSERT(instr->object()->representation().IsTagged());
++ ASSERT(instr->key()->representation().IsInteger32() ||
++ instr->key()->representation().IsTagged());
++
++ LOperand* obj = UseTempRegister(instr->object());
++ LOperand* val = needs_write_barrier
++ ? UseTempRegister(instr->value())
++ : UseRegisterAtStart(instr->value());
++ LOperand* key = needs_write_barrier
++ ? UseTempRegister(instr->key())
++ : UseRegisterOrConstantAtStart(instr->key());
++ return new(zone()) LStoreKeyedFastElement(obj, key, val);
++}
++
++
++LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement(
++ HStoreKeyedFastDoubleElement* instr) {
++ ASSERT(instr->value()->representation().IsDouble());
++ ASSERT(instr->elements()->representation().IsTagged());
++ ASSERT(instr->key()->representation().IsInteger32() ||
++ instr->key()->representation().IsTagged());
++
++ LOperand* elements = UseRegisterAtStart(instr->elements());
++ LOperand* val = UseTempRegister(instr->value());
++ LOperand* key = UseRegisterOrConstantAtStart(instr->key());
++
++ return new(zone()) LStoreKeyedFastDoubleElement(elements, key, val);
++}
++
++
++LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement(
++ HStoreKeyedSpecializedArrayElement* instr) {
++ ElementsKind elements_kind = instr->elements_kind();
++ ASSERT(
++ (instr->value()->representation().IsInteger32() &&
++ (elements_kind != EXTERNAL_FLOAT_ELEMENTS) &&
++ (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) ||
++ (instr->value()->representation().IsDouble() &&
++ ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) ||
++ (elements_kind == EXTERNAL_DOUBLE_ELEMENTS))));
++ ASSERT(instr->external_pointer()->representation().IsExternal());
++ ASSERT(instr->key()->representation().IsInteger32() ||
++ instr->key()->representation().IsTagged());
++
++ LOperand* external_pointer = UseRegister(instr->external_pointer());
++ bool val_is_temp_register =
++ elements_kind == EXTERNAL_PIXEL_ELEMENTS ||
++ elements_kind == EXTERNAL_FLOAT_ELEMENTS;
++ LOperand* val = val_is_temp_register
++ ? UseTempRegister(instr->value())
++ : UseRegister(instr->value());
++ LOperand* key = UseRegisterOrConstant(instr->key());
++
++ return new(zone()) LStoreKeyedSpecializedArrayElement(external_pointer,
++ key,
++ val);
++}
++
++
++LInstruction* LChunkBuilder::DoStoreKeyedGeneric(HStoreKeyedGeneric* instr) {
++ LOperand* obj = UseFixed(instr->object(), r5);
++ LOperand* key = UseFixed(instr->key(), r4);
++ LOperand* val = UseFixed(instr->value(), r3);
++
++ ASSERT(instr->object()->representation().IsTagged());
++ ASSERT(instr->key()->representation().IsTagged());
++ ASSERT(instr->value()->representation().IsTagged());
++
++ return MarkAsCall(new(zone()) LStoreKeyedGeneric(obj, key, val), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoTransitionElementsKind(
++ HTransitionElementsKind* instr) {
++ ElementsKind from_kind = instr->original_map()->elements_kind();
++ ElementsKind to_kind = instr->transitioned_map()->elements_kind();
++ if (IsSimpleMapChangeTransition(from_kind, to_kind)) {
++ LOperand* object = UseRegister(instr->object());
++ LOperand* new_map_reg = TempRegister();
++ LTransitionElementsKind* result =
++ new(zone()) LTransitionElementsKind(object, new_map_reg, NULL);
++ return DefineSameAsFirst(result);
++ } else {
++ LOperand* object = UseFixed(instr->object(), r3);
++ LOperand* fixed_object_reg = FixedTemp(r5);
++ LOperand* new_map_reg = FixedTemp(r6);
++ LTransitionElementsKind* result =
++ new(zone()) LTransitionElementsKind(object,
++ new_map_reg,
++ fixed_object_reg);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoStoreNamedField(HStoreNamedField* instr) {
++ bool needs_write_barrier = instr->NeedsWriteBarrier();
++ bool needs_write_barrier_for_map = !instr->transition().is_null() &&
++ instr->NeedsWriteBarrierForMap();
++
++ LOperand* obj;
++ if (needs_write_barrier) {
++ obj = instr->is_in_object()
++ ? UseRegister(instr->object())
++ : UseTempRegister(instr->object());
++ } else {
++ obj = needs_write_barrier_for_map
++ ? UseRegister(instr->object())
++ : UseRegisterAtStart(instr->object());
++ }
++
++ LOperand* val = needs_write_barrier
++ ? UseTempRegister(instr->value())
++ : UseRegister(instr->value());
++
++ // We need a temporary register for write barrier of the map field.
++ LOperand* temp = needs_write_barrier_for_map ? TempRegister() : NULL;
++
++ return new(zone()) LStoreNamedField(obj, val, temp);
++}
++
++
++LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
++ LOperand* obj = UseFixed(instr->object(), r4);
++ LOperand* val = UseFixed(instr->value(), r3);
++
++ LInstruction* result = new(zone()) LStoreNamedGeneric(obj, val);
++ return MarkAsCall(result, instr);
++}
++
++
++LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
++ LOperand* left = UseRegisterAtStart(instr->left());
++ LOperand* right = UseRegisterAtStart(instr->right());
++ return MarkAsCall(DefineFixed(new(zone()) LStringAdd(left, right), r3),
++ instr);
++}
++
++
++LInstruction* LChunkBuilder::DoStringCharCodeAt(HStringCharCodeAt* instr) {
++ LOperand* string = UseTempRegister(instr->string());
++ LOperand* index = UseTempRegister(instr->index());
++ LStringCharCodeAt* result = new(zone()) LStringCharCodeAt(string, index);
++ return AssignEnvironment(AssignPointerMap(DefineAsRegister(result)));
++}
++
++
++LInstruction* LChunkBuilder::DoStringCharFromCode(HStringCharFromCode* instr) {
++ LOperand* char_code = UseRegister(instr->value());
++ LStringCharFromCode* result = new(zone()) LStringCharFromCode(char_code);
++ return AssignPointerMap(DefineAsRegister(result));
++}
++
++
++LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) {
++ LOperand* string = UseRegisterAtStart(instr->value());
++ return DefineAsRegister(new(zone()) LStringLength(string));
++}
++
++
++LInstruction* LChunkBuilder::DoAllocateObject(HAllocateObject* instr) {
++ LAllocateObject* result =
++ new(zone()) LAllocateObject(TempRegister(), TempRegister());
++ return AssignPointerMap(DefineAsRegister(result));
++}
++
++
++LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) {
++ return MarkAsCall(DefineFixed(new(zone()) LFastLiteral, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) {
++ return MarkAsCall(DefineFixed(new(zone()) LArrayLiteral, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) {
++ return MarkAsCall(DefineFixed(new(zone()) LObjectLiteral, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoRegExpLiteral(HRegExpLiteral* instr) {
++ return MarkAsCall(DefineFixed(new(zone()) LRegExpLiteral, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoFunctionLiteral(HFunctionLiteral* instr) {
++ return MarkAsCall(DefineFixed(new(zone()) LFunctionLiteral, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoDeleteProperty(HDeleteProperty* instr) {
++ LOperand* object = UseFixed(instr->object(), r3);
++ LOperand* key = UseFixed(instr->key(), r4);
++ LDeleteProperty* result = new(zone()) LDeleteProperty(object, key);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoOsrEntry(HOsrEntry* instr) {
++ ASSERT(argument_count_ == 0);
++ allocator_->MarkAsOsrEntry();
++ current_block_->last_environment()->set_ast_id(instr->ast_id());
++ return AssignEnvironment(new(zone()) LOsrEntry);
++}
++
++
++LInstruction* LChunkBuilder::DoParameter(HParameter* instr) {
++ int spill_index = chunk()->GetParameterStackSlot(instr->index());
++ return DefineAsSpilled(new(zone()) LParameter, spill_index);
++}
++
++
++LInstruction* LChunkBuilder::DoUnknownOSRValue(HUnknownOSRValue* instr) {
++ int spill_index = chunk()->GetNextSpillIndex(false); // Not double-width.
++ if (spill_index > LUnallocated::kMaxFixedIndex) {
++ Abort("Too many spill slots needed for OSR");
++ spill_index = 0;
++ }
++ return DefineAsSpilled(new(zone()) LUnknownOSRValue, spill_index);
++}
++
++
++LInstruction* LChunkBuilder::DoCallStub(HCallStub* instr) {
++ argument_count_ -= instr->argument_count();
++ return MarkAsCall(DefineFixed(new(zone()) LCallStub, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoArgumentsObject(HArgumentsObject* instr) {
++ // There are no real uses of the arguments object.
++ // arguments.length and element access are supported directly on
++ // stack arguments, and any real arguments object use causes a bailout.
++ // So this value is never used.
++ return NULL;
++}
++
++
++LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) {
++ LOperand* args = UseRegister(instr->arguments());
++ LOperand* length = UseTempRegister(instr->length());
++ LOperand* index = UseRegister(instr->index());
++ return DefineAsRegister(new(zone()) LAccessArgumentsAt(args, length, index));
++}
++
++
++LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) {
++ LOperand* object = UseFixed(instr->value(), r3);
++ LToFastProperties* result = new(zone()) LToFastProperties(object);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) {
++ LTypeof* result = new(zone()) LTypeof(UseFixed(instr->value(), r3));
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoTypeofIsAndBranch(HTypeofIsAndBranch* instr) {
++ return new(zone()) LTypeofIsAndBranch(UseTempRegister(instr->value()));
++}
++
++
++LInstruction* LChunkBuilder::DoIsConstructCallAndBranch(
++ HIsConstructCallAndBranch* instr) {
++ return new(zone()) LIsConstructCallAndBranch(TempRegister());
++}
++
++
++LInstruction* LChunkBuilder::DoSimulate(HSimulate* instr) {
++ HEnvironment* env = current_block_->last_environment();
++ ASSERT(env != NULL);
++
++ env->set_ast_id(instr->ast_id());
++
++ env->Drop(instr->pop_count());
++ for (int i = 0; i < instr->values()->length(); ++i) {
++ HValue* value = instr->values()->at(i);
++ if (instr->HasAssignedIndexAt(i)) {
++ env->Bind(instr->GetAssignedIndexAt(i), value);
++ } else {
++ env->Push(value);
++ }
++ }
++
++ // If there is an instruction pending deoptimization environment create a
++ // lazy bailout instruction to capture the environment.
++ if (pending_deoptimization_ast_id_ == instr->ast_id()) {
++ LInstruction* result = new(zone()) LLazyBailout;
++ result = AssignEnvironment(result);
++ // Store the lazy deopt environment with the instruction if needed. Right
++ // now it is only used for LInstanceOfKnownGlobal.
++ instruction_pending_deoptimization_environment_->
++ SetDeferredLazyDeoptimizationEnvironment(result->environment());
++ instruction_pending_deoptimization_environment_ = NULL;
++ pending_deoptimization_ast_id_ = BailoutId::None();
++ return result;
++ }
++
++ return NULL;
++}
++
++
++LInstruction* LChunkBuilder::DoStackCheck(HStackCheck* instr) {
++ if (instr->is_function_entry()) {
++ return MarkAsCall(new(zone()) LStackCheck, instr);
++ } else {
++ ASSERT(instr->is_backwards_branch());
++ return AssignEnvironment(AssignPointerMap(new(zone()) LStackCheck));
++ }
++}
++
++
++LInstruction* LChunkBuilder::DoEnterInlined(HEnterInlined* instr) {
++ HEnvironment* outer = current_block_->last_environment();
++ HConstant* undefined = graph()->GetConstantUndefined();
++ HEnvironment* inner = outer->CopyForInlining(instr->closure(),
++ instr->arguments_count(),
++ instr->function(),
++ undefined,
++ instr->call_kind(),
++ instr->inlining_kind());
++ if (instr->arguments_var() != NULL) {
++ inner->Bind(instr->arguments_var(), graph()->GetArgumentsObject());
++ }
++ inner->set_entry(instr);
++ current_block_->UpdateEnvironment(inner);
++ chunk_->AddInlinedClosure(instr->closure());
++ return NULL;
++}
++
++
++LInstruction* LChunkBuilder::DoLeaveInlined(HLeaveInlined* instr) {
++ LInstruction* pop = NULL;
++
++ HEnvironment* env = current_block_->last_environment();
++
++ if (env->entry()->arguments_pushed()) {
++ int argument_count = env->arguments_environment()->parameter_count();
++ pop = new(zone()) LDrop(argument_count);
++ argument_count_ -= argument_count;
++ }
++
++ HEnvironment* outer = current_block_->last_environment()->
++ DiscardInlined(false);
++ current_block_->UpdateEnvironment(outer);
++
++ return pop;
++}
++
++
++LInstruction* LChunkBuilder::DoIn(HIn* instr) {
++ LOperand* key = UseRegisterAtStart(instr->key());
++ LOperand* object = UseRegisterAtStart(instr->object());
++ LIn* result = new(zone()) LIn(key, object);
++ return MarkAsCall(DefineFixed(result, r3), instr);
++}
++
++
++LInstruction* LChunkBuilder::DoForInPrepareMap(HForInPrepareMap* instr) {
++ LOperand* object = UseFixed(instr->enumerable(), r3);
++ LForInPrepareMap* result = new(zone()) LForInPrepareMap(object);
++ return MarkAsCall(DefineFixed(result, r3), instr, CAN_DEOPTIMIZE_EAGERLY);
++}
++
++
++LInstruction* LChunkBuilder::DoForInCacheArray(HForInCacheArray* instr) {
++ LOperand* map = UseRegister(instr->map());
++ return AssignEnvironment(DefineAsRegister(new(zone()) LForInCacheArray(map)));
++}
++
++
++LInstruction* LChunkBuilder::DoCheckMapValue(HCheckMapValue* instr) {
++ LOperand* value = UseRegisterAtStart(instr->value());
++ LOperand* map = UseRegisterAtStart(instr->map());
++ return AssignEnvironment(new(zone()) LCheckMapValue(value, map));
++}
++
++
++LInstruction* LChunkBuilder::DoLoadFieldByIndex(HLoadFieldByIndex* instr) {
++ LOperand* object = UseRegister(instr->object());
++ LOperand* index = UseRegister(instr->index());
++ return DefineAsRegister(new(zone()) LLoadFieldByIndex(object, index));
++}
++
++
++} } // namespace v8::internal
+diff -up v8-3.14.5.10/src/ppc/lithium-ppc.h.ppc v8-3.14.5.10/src/ppc/lithium-ppc.h
+--- v8-3.14.5.10/src/ppc/lithium-ppc.h.ppc 2016-06-07 14:15:45.999392955 -0400
++++ v8-3.14.5.10/src/ppc/lithium-ppc.h 2016-06-07 14:15:45.999392955 -0400
+@@ -0,0 +1,2620 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_LITHIUM_PPC_H_
++#define V8_PPC_LITHIUM_PPC_H_
++
++#include "hydrogen.h"
++#include "lithium-allocator.h"
++#include "lithium.h"
++#include "safepoint-table.h"
++#include "utils.h"
++
++namespace v8 {
++namespace internal {
++
++// Forward declarations.
++class LCodeGen;
++
++#define LITHIUM_ALL_INSTRUCTION_LIST(V) \
++ V(ControlInstruction) \
++ V(Call) \
++ LITHIUM_CONCRETE_INSTRUCTION_LIST(V)
++
++
++#define LITHIUM_CONCRETE_INSTRUCTION_LIST(V) \
++ V(AccessArgumentsAt) \
++ V(AddI) \
++ V(AllocateObject) \
++ V(ApplyArguments) \
++ V(ArgumentsElements) \
++ V(ArgumentsLength) \
++ V(ArithmeticD) \
++ V(ArithmeticT) \
++ V(ArrayLiteral) \
++ V(BitI) \
++ V(BitNotI) \
++ V(BoundsCheck) \
++ V(Branch) \
++ V(CallConstantFunction) \
++ V(CallFunction) \
++ V(CallGlobal) \
++ V(CallKeyed) \
++ V(CallKnownGlobal) \
++ V(CallNamed) \
++ V(CallNew) \
++ V(CallRuntime) \
++ V(CallStub) \
++ V(CheckFunction) \
++ V(CheckInstanceType) \
++ V(CheckNonSmi) \
++ V(CheckMaps) \
++ V(CheckPrototypeMaps) \
++ V(CheckSmi) \
++ V(ClampDToUint8) \
++ V(ClampIToUint8) \
++ V(ClampTToUint8) \
++ V(ClassOfTestAndBranch) \
++ V(CmpConstantEqAndBranch) \
++ V(CmpIDAndBranch) \
++ V(CmpObjectEqAndBranch) \
++ V(CmpMapAndBranch) \
++ V(CmpT) \
++ V(ConstantD) \
++ V(ConstantI) \
++ V(ConstantT) \
++ V(Context) \
++ V(DeclareGlobals) \
++ V(DeleteProperty) \
++ V(Deoptimize) \
++ V(DivI) \
++ V(DoubleToI) \
++ V(ElementsKind) \
++ V(FastLiteral) \
++ V(FixedArrayBaseLength) \
++ V(FunctionLiteral) \
++ V(GetCachedArrayIndex) \
++ V(GlobalObject) \
++ V(GlobalReceiver) \
++ V(Goto) \
++ V(HasCachedArrayIndexAndBranch) \
++ V(HasInstanceTypeAndBranch) \
++ V(In) \
++ V(InstanceOf) \
++ V(InstanceOfKnownGlobal) \
++ V(InstructionGap) \
++ V(Integer32ToDouble) \
++ V(Uint32ToDouble) \
++ V(InvokeFunction) \
++ V(IsConstructCallAndBranch) \
++ V(IsNilAndBranch) \
++ V(IsObjectAndBranch) \
++ V(IsStringAndBranch) \
++ V(IsSmiAndBranch) \
++ V(IsUndetectableAndBranch) \
++ V(JSArrayLength) \
++ V(Label) \
++ V(LazyBailout) \
++ V(LoadContextSlot) \
++ V(LoadElements) \
++ V(LoadExternalArrayPointer) \
++ V(LoadFunctionPrototype) \
++ V(LoadGlobalCell) \
++ V(LoadGlobalGeneric) \
++ V(LoadKeyedFastDoubleElement) \
++ V(LoadKeyedFastElement) \
++ V(LoadKeyedGeneric) \
++ V(LoadKeyedSpecializedArrayElement) \
++ V(LoadNamedField) \
++ V(LoadNamedFieldPolymorphic) \
++ V(LoadNamedGeneric) \
++ V(MapEnumLength) \
++ V(MathFloorOfDiv) \
++ V(MathMinMax) \
++ V(ModI) \
++ V(MulI) \
++ V(NumberTagD) \
++ V(NumberTagI) \
++ V(NumberTagU) \
++ V(NumberUntagD) \
++ V(ObjectLiteral) \
++ V(OsrEntry) \
++ V(OuterContext) \
++ V(Parameter) \
++ V(Power) \
++ V(PushArgument) \
++ V(Random) \
++ V(RegExpLiteral) \
++ V(Return) \
++ V(ShiftI) \
++ V(SmiTag) \
++ V(SmiUntag) \
++ V(StackCheck) \
++ V(StoreContextSlot) \
++ V(StoreGlobalCell) \
++ V(StoreGlobalGeneric) \
++ V(StoreKeyedFastDoubleElement) \
++ V(StoreKeyedFastElement) \
++ V(StoreKeyedGeneric) \
++ V(StoreKeyedSpecializedArrayElement) \
++ V(StoreNamedField) \
++ V(StoreNamedGeneric) \
++ V(StringAdd) \
++ V(StringCharCodeAt) \
++ V(StringCharFromCode) \
++ V(StringCompareAndBranch) \
++ V(StringLength) \
++ V(SubI) \
++ V(TaggedToI) \
++ V(ThisFunction) \
++ V(Throw) \
++ V(ToFastProperties) \
++ V(TransitionElementsKind) \
++ V(Typeof) \
++ V(TypeofIsAndBranch) \
++ V(UnaryMathOperation) \
++ V(UnknownOSRValue) \
++ V(ValueOf) \
++ V(ForInPrepareMap) \
++ V(ForInCacheArray) \
++ V(CheckMapValue) \
++ V(LoadFieldByIndex) \
++ V(DateField) \
++ V(WrapReceiver) \
++ V(Drop)
++
++
++#define DECLARE_CONCRETE_INSTRUCTION(type, mnemonic) \
++ virtual Opcode opcode() const { return LInstruction::k##type; } \
++ virtual void CompileToNative(LCodeGen* generator); \
++ virtual const char* Mnemonic() const { return mnemonic; } \
++ static L##type* cast(LInstruction* instr) { \
++ ASSERT(instr->Is##type()); \
++ return reinterpret_cast<L##type*>(instr); \
++ }
++
++
++#define DECLARE_HYDROGEN_ACCESSOR(type) \
++ H##type* hydrogen() const { \
++ return H##type::cast(hydrogen_value()); \
++ }
++
++
++class LInstruction: public ZoneObject {
++ public:
++ LInstruction()
++ : environment_(NULL),
++ hydrogen_value_(NULL),
++ is_call_(false) { }
++ virtual ~LInstruction() { }
++
++ virtual void CompileToNative(LCodeGen* generator) = 0;
++ virtual const char* Mnemonic() const = 0;
++ virtual void PrintTo(StringStream* stream);
++ virtual void PrintDataTo(StringStream* stream);
++ virtual void PrintOutputOperandTo(StringStream* stream);
++
++ enum Opcode {
++ // Declare a unique enum value for each instruction.
++#define DECLARE_OPCODE(type) k##type,
++ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_OPCODE)
++ kNumberOfInstructions
++#undef DECLARE_OPCODE
++ };
++
++ virtual Opcode opcode() const = 0;
++
++ // Declare non-virtual type testers for all leaf IR classes.
++#define DECLARE_PREDICATE(type) \
++ bool Is##type() const { return opcode() == k##type; }
++ LITHIUM_CONCRETE_INSTRUCTION_LIST(DECLARE_PREDICATE)
++#undef DECLARE_PREDICATE
++
++ // Declare virtual predicates for instructions that don't have
++ // an opcode.
++ virtual bool IsGap() const { return false; }
++
++ virtual bool IsControl() const { return false; }
++
++ void set_environment(LEnvironment* env) { environment_ = env; }
++ LEnvironment* environment() const { return environment_; }
++ bool HasEnvironment() const { return environment_ != NULL; }
++
++ void set_pointer_map(LPointerMap* p) { pointer_map_.set(p); }
++ LPointerMap* pointer_map() const { return pointer_map_.get(); }
++ bool HasPointerMap() const { return pointer_map_.is_set(); }
++
++ void set_hydrogen_value(HValue* value) { hydrogen_value_ = value; }
++ HValue* hydrogen_value() const { return hydrogen_value_; }
++
++ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) { }
++
++ void MarkAsCall() { is_call_ = true; }
++
++ // Interface to the register allocator and iterators.
++ bool IsMarkedAsCall() const { return is_call_; }
++
++ virtual bool HasResult() const = 0;
++ virtual LOperand* result() = 0;
++
++ LOperand* FirstInput() { return InputAt(0); }
++ LOperand* Output() { return HasResult() ? result() : NULL; }
++
++#ifdef DEBUG
++ void VerifyCall();
++#endif
++
++ private:
++ // Iterator support.
++ friend class InputIterator;
++ virtual int InputCount() = 0;
++ virtual LOperand* InputAt(int i) = 0;
++
++ friend class TempIterator;
++ virtual int TempCount() = 0;
++ virtual LOperand* TempAt(int i) = 0;
++
++ LEnvironment* environment_;
++ SetOncePointer<LPointerMap> pointer_map_;
++ HValue* hydrogen_value_;
++ bool is_call_;
++};
++
++
++// R = number of result operands (0 or 1).
++// I = number of input operands.
++// T = number of temporary operands.
++template<int R, int I, int T>
++class LTemplateInstruction: public LInstruction {
++ public:
++ // Allow 0 or 1 output operands.
++ STATIC_ASSERT(R == 0 || R == 1);
++ virtual bool HasResult() const { return R != 0; }
++ void set_result(LOperand* operand) { results_[0] = operand; }
++ LOperand* result() { return results_[0]; }
++
++ protected:
++ EmbeddedContainer<LOperand*, R> results_;
++ EmbeddedContainer<LOperand*, I> inputs_;
++ EmbeddedContainer<LOperand*, T> temps_;
++
++ private:
++ virtual int InputCount() { return I; }
++ virtual LOperand* InputAt(int i) { return inputs_[i]; }
++
++ virtual int TempCount() { return T; }
++ virtual LOperand* TempAt(int i) { return temps_[i]; }
++};
++
++
++class LGap: public LTemplateInstruction<0, 0, 0> {
++ public:
++ explicit LGap(HBasicBlock* block)
++ : block_(block) {
++ parallel_moves_[BEFORE] = NULL;
++ parallel_moves_[START] = NULL;
++ parallel_moves_[END] = NULL;
++ parallel_moves_[AFTER] = NULL;
++ }
++
++ // Can't use the DECLARE-macro here because of sub-classes.
++ virtual bool IsGap() const { return true; }
++ virtual void PrintDataTo(StringStream* stream);
++ static LGap* cast(LInstruction* instr) {
++ ASSERT(instr->IsGap());
++ return reinterpret_cast<LGap*>(instr);
++ }
++
++ bool IsRedundant() const;
++
++ HBasicBlock* block() const { return block_; }
++
++ enum InnerPosition {
++ BEFORE,
++ START,
++ END,
++ AFTER,
++ FIRST_INNER_POSITION = BEFORE,
++ LAST_INNER_POSITION = AFTER
++ };
++
++ LParallelMove* GetOrCreateParallelMove(InnerPosition pos, Zone* zone) {
++ if (parallel_moves_[pos] == NULL) {
++ parallel_moves_[pos] = new(zone) LParallelMove(zone);
++ }
++ return parallel_moves_[pos];
++ }
++
++ LParallelMove* GetParallelMove(InnerPosition pos) {
++ return parallel_moves_[pos];
++ }
++
++ private:
++ LParallelMove* parallel_moves_[LAST_INNER_POSITION + 1];
++ HBasicBlock* block_;
++};
++
++
++class LInstructionGap: public LGap {
++ public:
++ explicit LInstructionGap(HBasicBlock* block) : LGap(block) { }
++
++ DECLARE_CONCRETE_INSTRUCTION(InstructionGap, "gap")
++};
++
++
++class LGoto: public LTemplateInstruction<0, 0, 0> {
++ public:
++ explicit LGoto(int block_id) : block_id_(block_id) { }
++
++ DECLARE_CONCRETE_INSTRUCTION(Goto, "goto")
++ virtual void PrintDataTo(StringStream* stream);
++ virtual bool IsControl() const { return true; }
++
++ int block_id() const { return block_id_; }
++
++ private:
++ int block_id_;
++};
++
++
++class LLazyBailout: public LTemplateInstruction<0, 0, 0> {
++ public:
++ LLazyBailout() : gap_instructions_size_(0) { }
++
++ DECLARE_CONCRETE_INSTRUCTION(LazyBailout, "lazy-bailout")
++
++ void set_gap_instructions_size(int gap_instructions_size) {
++ gap_instructions_size_ = gap_instructions_size;
++ }
++ int gap_instructions_size() { return gap_instructions_size_; }
++
++ private:
++ int gap_instructions_size_;
++};
++
++
++class LDeoptimize: public LTemplateInstruction<0, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(Deoptimize, "deoptimize")
++};
++
++
++class LLabel: public LGap {
++ public:
++ explicit LLabel(HBasicBlock* block)
++ : LGap(block), replacement_(NULL) { }
++
++ DECLARE_CONCRETE_INSTRUCTION(Label, "label")
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ int block_id() const { return block()->block_id(); }
++ bool is_loop_header() const { return block()->IsLoopHeader(); }
++ Label* label() { return &label_; }
++ LLabel* replacement() const { return replacement_; }
++ void set_replacement(LLabel* label) { replacement_ = label; }
++ bool HasReplacement() const { return replacement_ != NULL; }
++
++ private:
++ Label label_;
++ LLabel* replacement_;
++};
++
++
++class LParameter: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(Parameter, "parameter")
++};
++
++
++class LCallStub: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(CallStub, "call-stub")
++ DECLARE_HYDROGEN_ACCESSOR(CallStub)
++
++ TranscendentalCache::Type transcendental_type() {
++ return hydrogen()->transcendental_type();
++ }
++};
++
++
++class LUnknownOSRValue: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(UnknownOSRValue, "unknown-osr-value")
++};
++
++
++template<int I, int T>
++class LControlInstruction: public LTemplateInstruction<0, I, T> {
++ public:
++ virtual bool IsControl() const { return true; }
++
++ int SuccessorCount() { return hydrogen()->SuccessorCount(); }
++ HBasicBlock* SuccessorAt(int i) { return hydrogen()->SuccessorAt(i); }
++ int true_block_id() { return hydrogen()->SuccessorAt(0)->block_id(); }
++ int false_block_id() { return hydrogen()->SuccessorAt(1)->block_id(); }
++
++ private:
++ HControlInstruction* hydrogen() {
++ return HControlInstruction::cast(this->hydrogen_value());
++ }
++};
++
++
++class LWrapReceiver: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LWrapReceiver(LOperand* receiver, LOperand* function) {
++ inputs_[0] = receiver;
++ inputs_[1] = function;
++ }
++
++ DECLARE_CONCRETE_INSTRUCTION(WrapReceiver, "wrap-receiver")
++
++ LOperand* receiver() { return inputs_[0]; }
++ LOperand* function() { return inputs_[1]; }
++};
++
++
++class LApplyArguments: public LTemplateInstruction<1, 4, 0> {
++ public:
++ LApplyArguments(LOperand* function,
++ LOperand* receiver,
++ LOperand* length,
++ LOperand* elements) {
++ inputs_[0] = function;
++ inputs_[1] = receiver;
++ inputs_[2] = length;
++ inputs_[3] = elements;
++ }
++
++ DECLARE_CONCRETE_INSTRUCTION(ApplyArguments, "apply-arguments")
++
++ LOperand* function() { return inputs_[0]; }
++ LOperand* receiver() { return inputs_[1]; }
++ LOperand* length() { return inputs_[2]; }
++ LOperand* elements() { return inputs_[3]; }
++};
++
++
++class LAccessArgumentsAt: public LTemplateInstruction<1, 3, 0> {
++ public:
++ LAccessArgumentsAt(LOperand* arguments, LOperand* length, LOperand* index) {
++ inputs_[0] = arguments;
++ inputs_[1] = length;
++ inputs_[2] = index;
++ }
++
++ DECLARE_CONCRETE_INSTRUCTION(AccessArgumentsAt, "access-arguments-at")
++
++ LOperand* arguments() { return inputs_[0]; }
++ LOperand* length() { return inputs_[1]; }
++ LOperand* index() { return inputs_[2]; }
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LArgumentsLength: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LArgumentsLength(LOperand* elements) {
++ inputs_[0] = elements;
++ }
++
++ LOperand* elements() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ArgumentsLength, "arguments-length")
++};
++
++
++class LArgumentsElements: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(ArgumentsElements, "arguments-elements")
++ DECLARE_HYDROGEN_ACCESSOR(ArgumentsElements)
++};
++
++
++class LModI: public LTemplateInstruction<1, 2, 3> {
++ public:
++ // Used when the right hand is a constant power of 2.
++ LModI(LOperand* left,
++ LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ temps_[0] = NULL;
++ temps_[1] = NULL;
++ temps_[2] = NULL;
++ }
++
++ // Used for the standard case.
++ LModI(LOperand* left,
++ LOperand* right,
++ LOperand* temp,
++ LOperand* temp2,
++ LOperand* temp3) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ temps_[0] = temp;
++ temps_[1] = temp2;
++ temps_[2] = temp3;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++ LOperand* temp() { return temps_[0]; }
++ LOperand* temp2() { return temps_[1]; }
++ LOperand* temp3() { return temps_[2]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ModI, "mod-i")
++ DECLARE_HYDROGEN_ACCESSOR(Mod)
++};
++
++
++class LDivI: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LDivI(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(DivI, "div-i")
++ DECLARE_HYDROGEN_ACCESSOR(Div)
++};
++
++
++class LMathFloorOfDiv: public LTemplateInstruction<1, 2, 1> {
++ public:
++ LMathFloorOfDiv(LOperand* left,
++ LOperand* right,
++ LOperand* temp = NULL) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ temps_[0] = temp;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(MathFloorOfDiv, "math-floor-of-div")
++ DECLARE_HYDROGEN_ACCESSOR(MathFloorOfDiv)
++};
++
++
++class LMulI: public LTemplateInstruction<1, 2, 1> {
++ public:
++ LMulI(LOperand* left, LOperand* right, LOperand* temp) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ temps_[0] = temp;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(MulI, "mul-i")
++ DECLARE_HYDROGEN_ACCESSOR(Mul)
++};
++
++
++class LCmpIDAndBranch: public LControlInstruction<2, 0> {
++ public:
++ LCmpIDAndBranch(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CmpIDAndBranch, "cmp-id-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(CompareIDAndBranch)
++
++ Token::Value op() const { return hydrogen()->token(); }
++ bool is_double() const {
++ return hydrogen()->GetInputRepresentation().IsDouble();
++ }
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LUnaryMathOperation: public LTemplateInstruction<1, 1, 1> {
++ public:
++ LUnaryMathOperation(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(UnaryMathOperation, "unary-math-operation")
++ DECLARE_HYDROGEN_ACCESSOR(UnaryMathOperation)
++
++ virtual void PrintDataTo(StringStream* stream);
++ BuiltinFunctionId op() const { return hydrogen()->op(); }
++};
++
++
++class LCmpObjectEqAndBranch: public LControlInstruction<2, 0> {
++ public:
++ LCmpObjectEqAndBranch(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CmpObjectEqAndBranch,
++ "cmp-object-eq-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(CompareObjectEqAndBranch)
++};
++
++
++class LCmpConstantEqAndBranch: public LControlInstruction<1, 0> {
++ public:
++ explicit LCmpConstantEqAndBranch(LOperand* left) {
++ inputs_[0] = left;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CmpConstantEqAndBranch,
++ "cmp-constant-eq-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(CompareConstantEqAndBranch)
++};
++
++
++class LIsNilAndBranch: public LControlInstruction<1, 0> {
++ public:
++ explicit LIsNilAndBranch(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(IsNilAndBranch, "is-nil-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(IsNilAndBranch)
++
++ EqualityKind kind() const { return hydrogen()->kind(); }
++ NilValue nil() const { return hydrogen()->nil(); }
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LIsObjectAndBranch: public LControlInstruction<1, 1> {
++ public:
++ LIsObjectAndBranch(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(IsObjectAndBranch, "is-object-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(IsObjectAndBranch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LIsStringAndBranch: public LControlInstruction<1, 1> {
++ public:
++ LIsStringAndBranch(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(IsStringAndBranch, "is-string-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(IsStringAndBranch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LIsSmiAndBranch: public LControlInstruction<1, 0> {
++ public:
++ explicit LIsSmiAndBranch(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(IsSmiAndBranch, "is-smi-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(IsSmiAndBranch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LIsUndetectableAndBranch: public LControlInstruction<1, 1> {
++ public:
++ explicit LIsUndetectableAndBranch(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(IsUndetectableAndBranch,
++ "is-undetectable-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(IsUndetectableAndBranch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LStringCompareAndBranch: public LControlInstruction<2, 0> {
++ public:
++ LStringCompareAndBranch(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StringCompareAndBranch,
++ "string-compare-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(StringCompareAndBranch)
++
++ Token::Value op() const { return hydrogen()->token(); }
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LHasInstanceTypeAndBranch: public LControlInstruction<1, 0> {
++ public:
++ explicit LHasInstanceTypeAndBranch(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(HasInstanceTypeAndBranch,
++ "has-instance-type-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(HasInstanceTypeAndBranch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LGetCachedArrayIndex: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LGetCachedArrayIndex(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(GetCachedArrayIndex, "get-cached-array-index")
++ DECLARE_HYDROGEN_ACCESSOR(GetCachedArrayIndex)
++};
++
++
++class LHasCachedArrayIndexAndBranch: public LControlInstruction<1, 0> {
++ public:
++ explicit LHasCachedArrayIndexAndBranch(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(HasCachedArrayIndexAndBranch,
++ "has-cached-array-index-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(HasCachedArrayIndexAndBranch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LClassOfTestAndBranch: public LControlInstruction<1, 1> {
++ public:
++ LClassOfTestAndBranch(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ClassOfTestAndBranch,
++ "class-of-test-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(ClassOfTestAndBranch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LCmpT: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LCmpT(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CmpT, "cmp-t")
++ DECLARE_HYDROGEN_ACCESSOR(CompareGeneric)
++
++ Token::Value op() const { return hydrogen()->token(); }
++};
++
++
++class LInstanceOf: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LInstanceOf(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(InstanceOf, "instance-of")
++};
++
++
++class LInstanceOfKnownGlobal: public LTemplateInstruction<1, 1, 1> {
++ public:
++ LInstanceOfKnownGlobal(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(InstanceOfKnownGlobal,
++ "instance-of-known-global")
++ DECLARE_HYDROGEN_ACCESSOR(InstanceOfKnownGlobal)
++
++ Handle<JSFunction> function() const { return hydrogen()->function(); }
++ LEnvironment* GetDeferredLazyDeoptimizationEnvironment() {
++ return lazy_deopt_env_;
++ }
++ virtual void SetDeferredLazyDeoptimizationEnvironment(LEnvironment* env) {
++ lazy_deopt_env_ = env;
++ }
++
++ private:
++ LEnvironment* lazy_deopt_env_;
++};
++
++
++class LBoundsCheck: public LTemplateInstruction<0, 2, 0> {
++ public:
++ LBoundsCheck(LOperand* index, LOperand* length) {
++ inputs_[0] = index;
++ inputs_[1] = length;
++ }
++
++ LOperand* index() { return inputs_[0]; }
++ LOperand* length() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(BoundsCheck, "bounds-check")
++ DECLARE_HYDROGEN_ACCESSOR(BoundsCheck)
++};
++
++
++class LBitI: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LBitI(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ Token::Value op() const { return hydrogen()->op(); }
++
++ DECLARE_CONCRETE_INSTRUCTION(BitI, "bit-i")
++ DECLARE_HYDROGEN_ACCESSOR(Bitwise)
++};
++
++
++class LShiftI: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LShiftI(Token::Value op, LOperand* left, LOperand* right, bool can_deopt)
++ : op_(op), can_deopt_(can_deopt) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ Token::Value op() const { return op_; }
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++ bool can_deopt() const { return can_deopt_; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ShiftI, "shift-i")
++
++ private:
++ Token::Value op_;
++ bool can_deopt_;
++};
++
++
++class LSubI: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LSubI(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(SubI, "sub-i")
++ DECLARE_HYDROGEN_ACCESSOR(Sub)
++};
++
++
++class LConstantI: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(ConstantI, "constant-i")
++ DECLARE_HYDROGEN_ACCESSOR(Constant)
++
++ int32_t value() const { return hydrogen()->Integer32Value(); }
++};
++
++
++class LConstantD: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(ConstantD, "constant-d")
++ DECLARE_HYDROGEN_ACCESSOR(Constant)
++
++ double value() const { return hydrogen()->DoubleValue(); }
++};
++
++
++class LConstantT: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(ConstantT, "constant-t")
++ DECLARE_HYDROGEN_ACCESSOR(Constant)
++
++ Handle<Object> value() const { return hydrogen()->handle(); }
++};
++
++
++class LBranch: public LControlInstruction<1, 0> {
++ public:
++ explicit LBranch(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Branch, "branch")
++ DECLARE_HYDROGEN_ACCESSOR(Branch)
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LCmpMapAndBranch: public LTemplateInstruction<0, 1, 1> {
++ public:
++ LCmpMapAndBranch(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CmpMapAndBranch, "cmp-map-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(CompareMap)
++
++ virtual bool IsControl() const { return true; }
++
++ Handle<Map> map() const { return hydrogen()->map(); }
++ int true_block_id() const {
++ return hydrogen()->FirstSuccessor()->block_id();
++ }
++ int false_block_id() const {
++ return hydrogen()->SecondSuccessor()->block_id();
++ }
++};
++
++
++class LJSArrayLength: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LJSArrayLength(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(JSArrayLength, "js-array-length")
++ DECLARE_HYDROGEN_ACCESSOR(JSArrayLength)
++};
++
++
++class LFixedArrayBaseLength: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LFixedArrayBaseLength(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(FixedArrayBaseLength,
++ "fixed-array-base-length")
++ DECLARE_HYDROGEN_ACCESSOR(FixedArrayBaseLength)
++};
++
++
++class LMapEnumLength: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LMapEnumLength(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(MapEnumLength, "map-enum-length")
++};
++
++
++class LElementsKind: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LElementsKind(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ElementsKind, "elements-kind")
++ DECLARE_HYDROGEN_ACCESSOR(ElementsKind)
++};
++
++
++class LValueOf: public LTemplateInstruction<1, 1, 1> {
++ public:
++ LValueOf(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "value-of")
++ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
++};
++
++
++class LDateField: public LTemplateInstruction<1, 1, 1> {
++ public:
++ LDateField(LOperand* date, LOperand* temp, Smi* index) : index_(index) {
++ inputs_[0] = date;
++ temps_[0] = temp;
++ }
++
++ LOperand* date() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++ Smi* index() const { return index_; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ValueOf, "date-field")
++ DECLARE_HYDROGEN_ACCESSOR(ValueOf)
++
++ private:
++ Smi* index_;
++};
++
++
++class LThrow: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LThrow(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Throw, "throw")
++};
++
++
++class LBitNotI: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LBitNotI(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(BitNotI, "bit-not-i")
++};
++
++
++class LAddI: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LAddI(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(AddI, "add-i")
++ DECLARE_HYDROGEN_ACCESSOR(Add)
++};
++
++
++class LMathMinMax: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LMathMinMax(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(MathMinMax, "min-max")
++ DECLARE_HYDROGEN_ACCESSOR(MathMinMax)
++};
++
++
++class LPower: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LPower(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Power, "power")
++ DECLARE_HYDROGEN_ACCESSOR(Power)
++};
++
++
++class LRandom: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LRandom(LOperand* global_object) {
++ inputs_[0] = global_object;
++ }
++
++ LOperand* global_object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Random, "random")
++ DECLARE_HYDROGEN_ACCESSOR(Random)
++};
++
++
++class LArithmeticD: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LArithmeticD(Token::Value op, LOperand* left, LOperand* right)
++ : op_(op) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ Token::Value op() const { return op_; }
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ virtual Opcode opcode() const { return LInstruction::kArithmeticD; }
++ virtual void CompileToNative(LCodeGen* generator);
++ virtual const char* Mnemonic() const;
++
++ private:
++ Token::Value op_;
++};
++
++
++class LArithmeticT: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LArithmeticT(Token::Value op, LOperand* left, LOperand* right)
++ : op_(op) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++ Token::Value op() const { return op_; }
++
++ virtual Opcode opcode() const { return LInstruction::kArithmeticT; }
++ virtual void CompileToNative(LCodeGen* generator);
++ virtual const char* Mnemonic() const;
++
++ private:
++ Token::Value op_;
++};
++
++
++class LReturn: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LReturn(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Return, "return")
++};
++
++
++class LLoadNamedField: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadNamedField(LOperand* object) {
++ inputs_[0] = object;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField, "load-named-field")
++ DECLARE_HYDROGEN_ACCESSOR(LoadNamedField)
++};
++
++
++class LLoadNamedFieldPolymorphic: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadNamedFieldPolymorphic(LOperand* object) {
++ inputs_[0] = object;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadNamedField,
"load-named-field-polymorphic")
++ DECLARE_HYDROGEN_ACCESSOR(LoadNamedFieldPolymorphic)
++};
++
++
++class LLoadNamedGeneric: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadNamedGeneric(LOperand* object) {
++ inputs_[0] = object;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadNamedGeneric, "load-named-generic")
++ DECLARE_HYDROGEN_ACCESSOR(LoadNamedGeneric)
++
++ Handle<Object> name() const { return hydrogen()->name(); }
++};
++
++
++class LLoadFunctionPrototype: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadFunctionPrototype(LOperand* function) {
++ inputs_[0] = function;
++ }
++
++ LOperand* function() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadFunctionPrototype,
"load-function-prototype")
++ DECLARE_HYDROGEN_ACCESSOR(LoadFunctionPrototype)
++};
++
++
++class LLoadElements: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadElements(LOperand* object) {
++ inputs_[0] = object;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadElements, "load-elements")
++};
++
++
++class LLoadExternalArrayPointer: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadExternalArrayPointer(LOperand* object) {
++ inputs_[0] = object;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadExternalArrayPointer,
++ "load-external-array-pointer")
++};
++
++
++class LLoadKeyedFastElement: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LLoadKeyedFastElement(LOperand* elements, LOperand* key) {
++ inputs_[0] = elements;
++ inputs_[1] = key;
++ }
++
++ LOperand* elements() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastElement,
"load-keyed-fast-element")
++ DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastElement)
++
++ uint32_t additional_index() const { return hydrogen()->index_offset(); }
++};
++
++
++class LLoadKeyedFastDoubleElement: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LLoadKeyedFastDoubleElement(LOperand* elements, LOperand* key) {
++ inputs_[0] = elements;
++ inputs_[1] = key;
++ }
++
++ LOperand* elements() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedFastDoubleElement,
++ "load-keyed-fast-double-element")
++ DECLARE_HYDROGEN_ACCESSOR(LoadKeyedFastDoubleElement)
++
++ uint32_t additional_index() const { return hydrogen()->index_offset(); }
++};
++
++
++class LLoadKeyedSpecializedArrayElement: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LLoadKeyedSpecializedArrayElement(LOperand* external_pointer, LOperand* key) {
++ inputs_[0] = external_pointer;
++ inputs_[1] = key;
++ }
++
++ LOperand* external_pointer() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement,
++ "load-keyed-specialized-array-element")
++ DECLARE_HYDROGEN_ACCESSOR(LoadKeyedSpecializedArrayElement)
++
++ ElementsKind elements_kind() const {
++ return hydrogen()->elements_kind();
++ }
++ uint32_t additional_index() const { return hydrogen()->index_offset(); }
++};
++
++
++class LLoadKeyedGeneric: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LLoadKeyedGeneric(LOperand* object, LOperand* key) {
++ inputs_[0] = object;
++ inputs_[1] = key;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadKeyedGeneric, "load-keyed-generic")
++};
++
++
++class LLoadGlobalCell: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalCell, "load-global-cell")
++ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalCell)
++};
++
++
++class LLoadGlobalGeneric: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadGlobalGeneric(LOperand* global_object) {
++ inputs_[0] = global_object;
++ }
++
++ LOperand* global_object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadGlobalGeneric, "load-global-generic")
++ DECLARE_HYDROGEN_ACCESSOR(LoadGlobalGeneric)
++
++ Handle<Object> name() const { return hydrogen()->name(); }
++ bool for_typeof() const { return hydrogen()->for_typeof(); }
++};
++
++
++class LStoreGlobalCell: public LTemplateInstruction<0, 1, 1> {
++ public:
++ LStoreGlobalCell(LOperand* value, LOperand* temp) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalCell, "store-global-cell")
++ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalCell)
++};
++
++
++class LStoreGlobalGeneric: public LTemplateInstruction<0, 2, 0> {
++ public:
++ explicit LStoreGlobalGeneric(LOperand* global_object,
++ LOperand* value) {
++ inputs_[0] = global_object;
++ inputs_[1] = value;
++ }
++
++ LOperand* global_object() { return inputs_[0]; }
++ LOperand* value() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreGlobalGeneric, "store-global-generic")
++ DECLARE_HYDROGEN_ACCESSOR(StoreGlobalGeneric)
++
++ Handle<Object> name() const { return hydrogen()->name(); }
++ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
++};
++
++
++class LLoadContextSlot: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LLoadContextSlot(LOperand* context) {
++ inputs_[0] = context;
++ }
++
++ LOperand* context() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadContextSlot, "load-context-slot")
++ DECLARE_HYDROGEN_ACCESSOR(LoadContextSlot)
++
++ int slot_index() { return hydrogen()->slot_index(); }
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LStoreContextSlot: public LTemplateInstruction<0, 2, 0> {
++ public:
++ LStoreContextSlot(LOperand* context, LOperand* value) {
++ inputs_[0] = context;
++ inputs_[1] = value;
++ }
++
++ LOperand* context() { return inputs_[0]; }
++ LOperand* value() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreContextSlot, "store-context-slot")
++ DECLARE_HYDROGEN_ACCESSOR(StoreContextSlot)
++
++ int slot_index() { return hydrogen()->slot_index(); }
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LPushArgument: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LPushArgument(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(PushArgument, "push-argument")
++};
++
++
++class LDrop: public LTemplateInstruction<0, 0, 0> {
++ public:
++ explicit LDrop(int count) : count_(count) { }
++
++ int count() const { return count_; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Drop, "drop")
++
++ private:
++ int count_;
++};
++
++
++class LThisFunction: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(ThisFunction, "this-function")
++ DECLARE_HYDROGEN_ACCESSOR(ThisFunction)
++};
++
++
++class LContext: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(Context, "context")
++};
++
++
++class LOuterContext: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LOuterContext(LOperand* context) {
++ inputs_[0] = context;
++ }
++
++ LOperand* context() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(OuterContext, "outer-context")
++};
++
++
++class LDeclareGlobals: public LTemplateInstruction<0, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(DeclareGlobals, "declare-globals")
++ DECLARE_HYDROGEN_ACCESSOR(DeclareGlobals)
++};
++
++
++class LGlobalObject: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LGlobalObject(LOperand* context) {
++ inputs_[0] = context;
++ }
++
++ DECLARE_CONCRETE_INSTRUCTION(GlobalObject, "global-object")
++
++ LOperand* context() { return inputs_[0]; }
++};
++
++
++class LGlobalReceiver: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LGlobalReceiver(LOperand* global_object) {
++ inputs_[0] = global_object;
++ }
++
++ LOperand* global_object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(GlobalReceiver, "global-receiver")
++};
++
++
++class LCallConstantFunction: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(CallConstantFunction,
"call-constant-function")
++ DECLARE_HYDROGEN_ACCESSOR(CallConstantFunction)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ Handle<JSFunction> function() { return hydrogen()->function(); }
++ int arity() const { return hydrogen()->argument_count() - 1; }
++};
++
++
++class LInvokeFunction: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LInvokeFunction(LOperand* function) {
++ inputs_[0] = function;
++ }
++
++ LOperand* function() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(InvokeFunction, "invoke-function")
++ DECLARE_HYDROGEN_ACCESSOR(InvokeFunction)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ int arity() const { return hydrogen()->argument_count() - 1; }
++ Handle<JSFunction> known_function() { return hydrogen()->known_function(); }
++};
++
++
++class LCallKeyed: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LCallKeyed(LOperand* key) {
++ inputs_[0] = key;
++ }
++
++ LOperand* key() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CallKeyed, "call-keyed")
++ DECLARE_HYDROGEN_ACCESSOR(CallKeyed)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ int arity() const { return hydrogen()->argument_count() - 1; }
++};
++
++
++
++class LCallNamed: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(CallNamed, "call-named")
++ DECLARE_HYDROGEN_ACCESSOR(CallNamed)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ Handle<String> name() const { return hydrogen()->name(); }
++ int arity() const { return hydrogen()->argument_count() - 1; }
++};
++
++
++class LCallFunction: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LCallFunction(LOperand* function) {
++ inputs_[0] = function;
++ }
++
++ LOperand* function() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CallFunction, "call-function")
++ DECLARE_HYDROGEN_ACCESSOR(CallFunction)
++
++ int arity() const { return hydrogen()->argument_count() - 1; }
++};
++
++
++class LCallGlobal: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(CallGlobal, "call-global")
++ DECLARE_HYDROGEN_ACCESSOR(CallGlobal)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ Handle<String> name() const {return hydrogen()->name(); }
++ int arity() const { return hydrogen()->argument_count() - 1; }
++};
++
++
++class LCallKnownGlobal: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(CallKnownGlobal, "call-known-global")
++ DECLARE_HYDROGEN_ACCESSOR(CallKnownGlobal)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ Handle<JSFunction> target() const { return hydrogen()->target(); }
++ int arity() const { return hydrogen()->argument_count() - 1; }
++};
++
++
++class LCallNew: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LCallNew(LOperand* constructor) {
++ inputs_[0] = constructor;
++ }
++
++ LOperand* constructor() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CallNew, "call-new")
++ DECLARE_HYDROGEN_ACCESSOR(CallNew)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ int arity() const { return hydrogen()->argument_count() - 1; }
++};
++
++
++class LCallRuntime: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(CallRuntime, "call-runtime")
++ DECLARE_HYDROGEN_ACCESSOR(CallRuntime)
++
++ const Runtime::Function* function() const { return hydrogen()->function(); }
++ int arity() const { return hydrogen()->argument_count(); }
++};
++
++
++class LInteger32ToDouble: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LInteger32ToDouble(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Integer32ToDouble, "int32-to-double")
++};
++
++
++class LUint32ToDouble: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LUint32ToDouble(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Uint32ToDouble, "uint32-to-double")
++};
++
++
++class LNumberTagI: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LNumberTagI(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(NumberTagI, "number-tag-i")
++};
++
++
++class LNumberTagU: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LNumberTagU(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(NumberTagU, "number-tag-u")
++};
++
++
++class LNumberTagD: public LTemplateInstruction<1, 1, 2> {
++ public:
++ LNumberTagD(LOperand* value, LOperand* temp, LOperand* temp2) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ temps_[1] = temp2;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++ LOperand* temp2() { return temps_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(NumberTagD, "number-tag-d")
++};
++
++
++// Sometimes truncating conversion from a tagged value to an int32.
++class LDoubleToI: public LTemplateInstruction<1, 1, 2> {
++ public:
++ LDoubleToI(LOperand* value, LOperand* temp, LOperand* temp2) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ temps_[1] = temp2;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++ LOperand* temp2() { return temps_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(DoubleToI, "double-to-i")
++ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
++
++ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
++};
++
++
++// Truncating conversion from a tagged value to an int32.
++class LTaggedToI: public LTemplateInstruction<1, 1, 3> {
++ public:
++ LTaggedToI(LOperand* value,
++ LOperand* temp,
++ LOperand* temp2,
++ LOperand* temp3) {
++ inputs_[0] = value;
++ temps_[0] = temp;
++ temps_[1] = temp2;
++ temps_[2] = temp3;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++ LOperand* temp2() { return temps_[1]; }
++ LOperand* temp3() { return temps_[2]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(TaggedToI, "tagged-to-i")
++ DECLARE_HYDROGEN_ACCESSOR(UnaryOperation)
++
++ bool truncating() { return hydrogen()->CanTruncateToInt32(); }
++};
++
++
++class LSmiTag: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LSmiTag(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(SmiTag, "smi-tag")
++};
++
++
++class LNumberUntagD: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LNumberUntagD(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(NumberUntagD, "double-untag")
++ DECLARE_HYDROGEN_ACCESSOR(Change)
++};
++
++
++class LSmiUntag: public LTemplateInstruction<1, 1, 0> {
++ public:
++ LSmiUntag(LOperand* value, bool needs_check)
++ : needs_check_(needs_check) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ bool needs_check() const { return needs_check_; }
++
++ DECLARE_CONCRETE_INSTRUCTION(SmiUntag, "smi-untag")
++
++ private:
++ bool needs_check_;
++};
++
++
++class LStoreNamedField: public LTemplateInstruction<0, 2, 1> {
++ public:
++ LStoreNamedField(LOperand* object, LOperand* value, LOperand* temp) {
++ inputs_[0] = object;
++ inputs_[1] = value;
++ temps_[0] = temp;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* value() { return inputs_[1]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreNamedField, "store-named-field")
++ DECLARE_HYDROGEN_ACCESSOR(StoreNamedField)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ Handle<Object> name() const { return hydrogen()->name(); }
++ bool is_in_object() { return hydrogen()->is_in_object(); }
++ int offset() { return hydrogen()->offset(); }
++ Handle<Map> transition() const { return hydrogen()->transition(); }
++};
++
++
++class LStoreNamedGeneric: public LTemplateInstruction<0, 2, 0> {
++ public:
++ LStoreNamedGeneric(LOperand* object, LOperand* value) {
++ inputs_[0] = object;
++ inputs_[1] = value;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* value() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreNamedGeneric, "store-named-generic")
++ DECLARE_HYDROGEN_ACCESSOR(StoreNamedGeneric)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ Handle<Object> name() const { return hydrogen()->name(); }
++ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
++};
++
++
++class LStoreKeyedFastElement: public LTemplateInstruction<0, 3, 0> {
++ public:
++ LStoreKeyedFastElement(LOperand* object, LOperand* key, LOperand* value) {
++ inputs_[0] = object;
++ inputs_[1] = key;
++ inputs_[2] = value;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++ LOperand* value() { return inputs_[2]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastElement,
++ "store-keyed-fast-element")
++ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastElement)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ uint32_t additional_index() const { return hydrogen()->index_offset(); }
++};
++
++
++class LStoreKeyedFastDoubleElement: public LTemplateInstruction<0, 3, 0> {
++ public:
++ LStoreKeyedFastDoubleElement(LOperand* elements,
++ LOperand* key,
++ LOperand* value) {
++ inputs_[0] = elements;
++ inputs_[1] = key;
++ inputs_[2] = value;
++ }
++
++ LOperand* elements() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++ LOperand* value() { return inputs_[2]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedFastDoubleElement,
++ "store-keyed-fast-double-element")
++ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedFastDoubleElement)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ uint32_t additional_index() const { return hydrogen()->index_offset(); }
++
++ bool NeedsCanonicalization() { return hydrogen()->NeedsCanonicalization(); }
++};
++
++
++class LStoreKeyedGeneric: public LTemplateInstruction<0, 3, 0> {
++ public:
++ LStoreKeyedGeneric(LOperand* obj, LOperand* key, LOperand* value) {
++ inputs_[0] = obj;
++ inputs_[1] = key;
++ inputs_[2] = value;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++ LOperand* value() { return inputs_[2]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedGeneric, "store-keyed-generic")
++ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedGeneric)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ StrictModeFlag strict_mode_flag() { return hydrogen()->strict_mode_flag(); }
++};
++
++class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> {
++ public:
++ LStoreKeyedSpecializedArrayElement(LOperand* external_pointer,
++ LOperand* key,
++ LOperand* value) {
++ inputs_[0] = external_pointer;
++ inputs_[1] = key;
++ inputs_[2] = value;
++ }
++
++ LOperand* external_pointer() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++ LOperand* value() { return inputs_[2]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StoreKeyedSpecializedArrayElement,
++ "store-keyed-specialized-array-element")
++ DECLARE_HYDROGEN_ACCESSOR(StoreKeyedSpecializedArrayElement)
++
++ ElementsKind elements_kind() const { return hydrogen()->elements_kind(); }
++ uint32_t additional_index() const { return hydrogen()->index_offset(); }
++};
++
++
++class LTransitionElementsKind: public LTemplateInstruction<1, 1, 2> {
++ public:
++ LTransitionElementsKind(LOperand* object,
++ LOperand* new_map_temp,
++ LOperand* temp) {
++ inputs_[0] = object;
++ temps_[0] = new_map_temp;
++ temps_[1] = temp;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* new_map_temp() { return temps_[0]; }
++ LOperand* temp() { return temps_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(TransitionElementsKind,
++ "transition-elements-kind")
++ DECLARE_HYDROGEN_ACCESSOR(TransitionElementsKind)
++
++ virtual void PrintDataTo(StringStream* stream);
++
++ Handle<Map> original_map() { return hydrogen()->original_map(); }
++ Handle<Map> transitioned_map() { return hydrogen()->transitioned_map(); }
++};
++
++
++class LStringAdd: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LStringAdd(LOperand* left, LOperand* right) {
++ inputs_[0] = left;
++ inputs_[1] = right;
++ }
++
++ LOperand* left() { return inputs_[0]; }
++ LOperand* right() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StringAdd, "string-add")
++ DECLARE_HYDROGEN_ACCESSOR(StringAdd)
++};
++
++
++
++class LStringCharCodeAt: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LStringCharCodeAt(LOperand* string, LOperand* index) {
++ inputs_[0] = string;
++ inputs_[1] = index;
++ }
++
++ LOperand* string() { return inputs_[0]; }
++ LOperand* index() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StringCharCodeAt, "string-char-code-at")
++ DECLARE_HYDROGEN_ACCESSOR(StringCharCodeAt)
++};
++
++
++class LStringCharFromCode: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LStringCharFromCode(LOperand* char_code) {
++ inputs_[0] = char_code;
++ }
++
++ LOperand* char_code() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StringCharFromCode, "string-char-from-code")
++ DECLARE_HYDROGEN_ACCESSOR(StringCharFromCode)
++};
++
++
++class LStringLength: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LStringLength(LOperand* string) {
++ inputs_[0] = string;
++ }
++
++ LOperand* string() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(StringLength, "string-length")
++ DECLARE_HYDROGEN_ACCESSOR(StringLength)
++};
++
++
++class LCheckFunction: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LCheckFunction(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CheckFunction, "check-function")
++ DECLARE_HYDROGEN_ACCESSOR(CheckFunction)
++};
++
++
++class LCheckInstanceType: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LCheckInstanceType(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CheckInstanceType, "check-instance-type")
++ DECLARE_HYDROGEN_ACCESSOR(CheckInstanceType)
++};
++
++
++class LCheckMaps: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LCheckMaps(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CheckMaps, "check-maps")
++ DECLARE_HYDROGEN_ACCESSOR(CheckMaps)
++};
++
++
++class LCheckPrototypeMaps: public LTemplateInstruction<0, 0, 2> {
++ public:
++ LCheckPrototypeMaps(LOperand* temp, LOperand* temp2) {
++ temps_[0] = temp;
++ temps_[1] = temp2;
++ }
++
++ LOperand* temp() { return temps_[0]; }
++ LOperand* temp2() { return temps_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CheckPrototypeMaps, "check-prototype-maps")
++ DECLARE_HYDROGEN_ACCESSOR(CheckPrototypeMaps)
++
++ Handle<JSObject> prototype() const { return hydrogen()->prototype(); }
++ Handle<JSObject> holder() const { return hydrogen()->holder(); }
++};
++
++
++class LCheckSmi: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LCheckSmi(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CheckSmi, "check-smi")
++};
++
++
++class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> {
++ public:
++ explicit LCheckNonSmi(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CheckNonSmi, "check-non-smi")
++};
++
++
++class LClampDToUint8: public LTemplateInstruction<1, 1, 1> {
++ public:
++ LClampDToUint8(LOperand* unclamped, LOperand* temp) {
++ inputs_[0] = unclamped;
++ temps_[0] = temp;
++ }
++
++ LOperand* unclamped() { return inputs_[0]; }
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ClampDToUint8, "clamp-d-to-uint8")
++};
++
++
++class LClampIToUint8: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LClampIToUint8(LOperand* unclamped) {
++ inputs_[0] = unclamped;
++ }
++
++ LOperand* unclamped() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ClampIToUint8, "clamp-i-to-uint8")
++};
++
++
++class LClampTToUint8: public LTemplateInstruction<1, 1, 2> {
++ public:
++ LClampTToUint8(LOperand* unclamped, LOperand* temp1, LOperand* temp2) {
++ inputs_[0] = unclamped;
++ temps_[0] = temp1;
++ temps_[1] = temp2;
++ }
++
++ LOperand* unclamped() { return inputs_[0]; }
++ LOperand* temp1() { return temps_[0]; }
++ LOperand* temp2() { return temps_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ClampTToUint8, "clamp-t-to-uint8")
++};
++
++
++class LAllocateObject: public LTemplateInstruction<1, 0, 2> {
++ public:
++ LAllocateObject(LOperand* temp, LOperand* temp2) {
++ temps_[0] = temp;
++ temps_[1] = temp2;
++ }
++
++ LOperand* temp() { return temps_[0]; }
++ LOperand* temp2() { return temps_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(AllocateObject, "allocate-object")
++ DECLARE_HYDROGEN_ACCESSOR(AllocateObject)
++};
++
++
++class LFastLiteral: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal")
++ DECLARE_HYDROGEN_ACCESSOR(FastLiteral)
++};
++
++
++class LArrayLiteral: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal")
++ DECLARE_HYDROGEN_ACCESSOR(ArrayLiteral)
++};
++
++
++class LObjectLiteral: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal")
++ DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral)
++};
++
++
++class LRegExpLiteral: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(RegExpLiteral, "regexp-literal")
++ DECLARE_HYDROGEN_ACCESSOR(RegExpLiteral)
++};
++
++
++class LFunctionLiteral: public LTemplateInstruction<1, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(FunctionLiteral, "function-literal")
++ DECLARE_HYDROGEN_ACCESSOR(FunctionLiteral)
++
++ Handle<SharedFunctionInfo> shared_info() { return hydrogen()->shared_info();
}
++};
++
++
++class LToFastProperties: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LToFastProperties(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties")
++ DECLARE_HYDROGEN_ACCESSOR(ToFastProperties)
++};
++
++
++class LTypeof: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LTypeof(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(Typeof, "typeof")
++};
++
++
++class LTypeofIsAndBranch: public LControlInstruction<1, 0> {
++ public:
++ explicit LTypeofIsAndBranch(LOperand* value) {
++ inputs_[0] = value;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(TypeofIsAndBranch, "typeof-is-and-branch")
++ DECLARE_HYDROGEN_ACCESSOR(TypeofIsAndBranch)
++
++ Handle<String> type_literal() { return hydrogen()->type_literal(); }
++
++ virtual void PrintDataTo(StringStream* stream);
++};
++
++
++class LIsConstructCallAndBranch: public LControlInstruction<0, 1> {
++ public:
++ explicit LIsConstructCallAndBranch(LOperand* temp) {
++ temps_[0] = temp;
++ }
++
++ LOperand* temp() { return temps_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(IsConstructCallAndBranch,
++ "is-construct-call-and-branch")
++};
++
++
++class LDeleteProperty: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LDeleteProperty(LOperand* object, LOperand* key) {
++ inputs_[0] = object;
++ inputs_[1] = key;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* key() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(DeleteProperty, "delete-property")
++};
++
++
++class LOsrEntry: public LTemplateInstruction<0, 0, 0> {
++ public:
++ LOsrEntry();
++
++ DECLARE_CONCRETE_INSTRUCTION(OsrEntry, "osr-entry")
++
++ LOperand** SpilledRegisterArray() { return register_spills_; }
++ LOperand** SpilledDoubleRegisterArray() { return double_register_spills_; }
++
++ void MarkSpilledRegister(int allocation_index, LOperand* spill_operand);
++ void MarkSpilledDoubleRegister(int allocation_index,
++ LOperand* spill_operand);
++
++ private:
++ // Arrays of spill slot operands for registers with an assigned spill
++ // slot, i.e., that must also be restored to the spill slot on OSR entry.
++ // NULL if the register has no assigned spill slot. Indexed by allocation
++ // index.
++ LOperand* register_spills_[Register::kNumAllocatableRegisters];
++ LOperand* double_register_spills_[DoubleRegister::kNumAllocatableRegisters];
++};
++
++
++class LStackCheck: public LTemplateInstruction<0, 0, 0> {
++ public:
++ DECLARE_CONCRETE_INSTRUCTION(StackCheck, "stack-check")
++ DECLARE_HYDROGEN_ACCESSOR(StackCheck)
++
++ Label* done_label() { return &done_label_; }
++
++ private:
++ Label done_label_;
++};
++
++
++class LIn: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LIn(LOperand* key, LOperand* object) {
++ inputs_[0] = key;
++ inputs_[1] = object;
++ }
++
++ LOperand* key() { return inputs_[0]; }
++ LOperand* object() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(In, "in")
++};
++
++
++class LForInPrepareMap: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LForInPrepareMap(LOperand* object) {
++ inputs_[0] = object;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ForInPrepareMap, "for-in-prepare-map")
++};
++
++
++class LForInCacheArray: public LTemplateInstruction<1, 1, 0> {
++ public:
++ explicit LForInCacheArray(LOperand* map) {
++ inputs_[0] = map;
++ }
++
++ LOperand* map() { return inputs_[0]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(ForInCacheArray, "for-in-cache-array")
++
++ int idx() {
++ return HForInCacheArray::cast(this->hydrogen_value())->idx();
++ }
++};
++
++
++class LCheckMapValue: public LTemplateInstruction<0, 2, 0> {
++ public:
++ LCheckMapValue(LOperand* value, LOperand* map) {
++ inputs_[0] = value;
++ inputs_[1] = map;
++ }
++
++ LOperand* value() { return inputs_[0]; }
++ LOperand* map() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(CheckMapValue, "check-map-value")
++};
++
++
++class LLoadFieldByIndex: public LTemplateInstruction<1, 2, 0> {
++ public:
++ LLoadFieldByIndex(LOperand* object, LOperand* index) {
++ inputs_[0] = object;
++ inputs_[1] = index;
++ }
++
++ LOperand* object() { return inputs_[0]; }
++ LOperand* index() { return inputs_[1]; }
++
++ DECLARE_CONCRETE_INSTRUCTION(LoadFieldByIndex, "load-field-by-index")
++};
++
++
++class LChunkBuilder;
++class LPlatformChunk: public LChunk {
++ public:
++ LPlatformChunk(CompilationInfo* info, HGraph* graph)
++ : LChunk(info, graph) { }
++
++ int GetNextSpillIndex(bool is_double);
++ LOperand* GetNextSpillSlot(bool is_double);
++};
++
++
++class LChunkBuilder BASE_EMBEDDED {
++ public:
++ LChunkBuilder(CompilationInfo* info, HGraph* graph, LAllocator* allocator)
++ : chunk_(NULL),
++ info_(info),
++ graph_(graph),
++ zone_(graph->zone()),
++ status_(UNUSED),
++ current_instruction_(NULL),
++ current_block_(NULL),
++ next_block_(NULL),
++ argument_count_(0),
++ allocator_(allocator),
++ position_(RelocInfo::kNoPosition),
++ instruction_pending_deoptimization_environment_(NULL),
++ pending_deoptimization_ast_id_(BailoutId::None()) { }
++
++ // Build the sequence for the graph.
++ LPlatformChunk* Build();
++
++ // Declare methods that deal with the individual node types.
++#define DECLARE_DO(type) LInstruction* Do##type(H##type* node);
++ HYDROGEN_CONCRETE_INSTRUCTION_LIST(DECLARE_DO)
++#undef DECLARE_DO
++
++ static bool HasMagicNumberForDivisor(int32_t divisor);
++ static HValue* SimplifiedDividendForMathFloorOfDiv(HValue* val);
++ static HValue* SimplifiedDivisorForMathFloorOfDiv(HValue* val);
++
++ private:
++ enum Status {
++ UNUSED,
++ BUILDING,
++ DONE,
++ ABORTED
++ };
++
++ LPlatformChunk* chunk() const { return chunk_; }
++ CompilationInfo* info() const { return info_; }
++ HGraph* graph() const { return graph_; }
++ Zone* zone() const { return zone_; }
++
++ bool is_unused() const { return status_ == UNUSED; }
++ bool is_building() const { return status_ == BUILDING; }
++ bool is_done() const { return status_ == DONE; }
++ bool is_aborted() const { return status_ == ABORTED; }
++
++ void Abort(const char* reason);
++
++ // Methods for getting operands for Use / Define / Temp.
++ LUnallocated* ToUnallocated(Register reg);
++ LUnallocated* ToUnallocated(DoubleRegister reg);
++
++ // Methods for setting up define-use relationships.
++ MUST_USE_RESULT LOperand* Use(HValue* value, LUnallocated* operand);
++ MUST_USE_RESULT LOperand* UseFixed(HValue* value, Register fixed_register);
++ MUST_USE_RESULT LOperand* UseFixedDouble(HValue* value,
++ DoubleRegister fixed_register);
++
++ // A value that is guaranteed to be allocated to a register.
++ // Operand created by UseRegister is guaranteed to be live until the end of
++ // instruction. This means that register allocator will not reuse it's
++ // register for any other operand inside instruction.
++ // Operand created by UseRegisterAtStart is guaranteed to be live only at
++ // instruction start. Register allocator is free to assign the same register
++ // to some other operand used inside instruction (i.e. temporary or
++ // output).
++ MUST_USE_RESULT LOperand* UseRegister(HValue* value);
++ MUST_USE_RESULT LOperand* UseRegisterAtStart(HValue* value);
++
++ // An input operand in a register that may be trashed.
++ MUST_USE_RESULT LOperand* UseTempRegister(HValue* value);
++
++ // An input operand in a register or stack slot.
++ MUST_USE_RESULT LOperand* Use(HValue* value);
++ MUST_USE_RESULT LOperand* UseAtStart(HValue* value);
++
++ // An input operand in a register, stack slot or a constant operand.
++ MUST_USE_RESULT LOperand* UseOrConstant(HValue* value);
++ MUST_USE_RESULT LOperand* UseOrConstantAtStart(HValue* value);
++
++ // An input operand in a register or a constant operand.
++ MUST_USE_RESULT LOperand* UseRegisterOrConstant(HValue* value);
++ MUST_USE_RESULT LOperand* UseRegisterOrConstantAtStart(HValue* value);
++
++ // An input operand in register, stack slot or a constant operand.
++ // Will not be moved to a register even if one is freely available.
++ MUST_USE_RESULT LOperand* UseAny(HValue* value);
++
++ // Temporary operand that must be in a register.
++ MUST_USE_RESULT LUnallocated* TempRegister();
++ MUST_USE_RESULT LOperand* FixedTemp(Register reg);
++ MUST_USE_RESULT LOperand* FixedTemp(DoubleRegister reg);
++
++ // Methods for setting up define-use relationships.
++ // Return the same instruction that they are passed.
++ template<int I, int T>
++ LInstruction* Define(LTemplateInstruction<1, I, T>* instr,
++ LUnallocated* result);
++ template<int I, int T>
++ LInstruction* DefineAsRegister(LTemplateInstruction<1, I, T>* instr);
++ template<int I, int T>
++ LInstruction* DefineAsSpilled(LTemplateInstruction<1, I, T>* instr,
++ int index);
++ template<int I, int T>
++ LInstruction* DefineSameAsFirst(LTemplateInstruction<1, I, T>* instr);
++ template<int I, int T>
++ LInstruction* DefineFixed(LTemplateInstruction<1, I, T>* instr,
++ Register reg);
++ template<int I, int T>
++ LInstruction* DefineFixedDouble(LTemplateInstruction<1, I, T>* instr,
++ DoubleRegister reg);
++ LInstruction* AssignEnvironment(LInstruction* instr);
++ LInstruction* AssignPointerMap(LInstruction* instr);
++
++ enum CanDeoptimize { CAN_DEOPTIMIZE_EAGERLY, CANNOT_DEOPTIMIZE_EAGERLY };
++
++ // By default we assume that instruction sequences generated for calls
++ // cannot deoptimize eagerly and we do not attach environment to this
++ // instruction.
++ LInstruction* MarkAsCall(
++ LInstruction* instr,
++ HInstruction* hinstr,
++ CanDeoptimize can_deoptimize = CANNOT_DEOPTIMIZE_EAGERLY);
++
++ LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env,
++ int* argument_index_accumulator);
++
++ void VisitInstruction(HInstruction* current);
++
++ void DoBasicBlock(HBasicBlock* block, HBasicBlock* next_block);
++ LInstruction* DoShift(Token::Value op, HBitwiseBinaryOperation* instr);
++ LInstruction* DoArithmeticD(Token::Value op,
++ HArithmeticBinaryOperation* instr);
++ LInstruction* DoArithmeticT(Token::Value op,
++ HArithmeticBinaryOperation* instr);
++
++ LPlatformChunk* chunk_;
++ CompilationInfo* info_;
++ HGraph* const graph_;
++ Zone* zone_;
++ Status status_;
++ HInstruction* current_instruction_;
++ HBasicBlock* current_block_;
++ HBasicBlock* next_block_;
++ int argument_count_;
++ LAllocator* allocator_;
++ int position_;
++ LInstruction* instruction_pending_deoptimization_environment_;
++ BailoutId pending_deoptimization_ast_id_;
++
++ DISALLOW_COPY_AND_ASSIGN(LChunkBuilder);
++};
++
++#undef DECLARE_HYDROGEN_ACCESSOR
++#undef DECLARE_CONCRETE_INSTRUCTION
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_LITHIUM_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc.ppc
v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc
+--- v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc.ppc 2016-06-07 14:15:46.001392943 -0400
++++ v8-3.14.5.10/src/ppc/macro-assembler-ppc.cc 2016-06-07 14:15:46.001392943 -0400
+@@ -0,0 +1,4460 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include <limits.h> // For LONG_MIN, LONG_MAX.
++#include <assert.h> // For assert
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "bootstrapper.h"
++#include "codegen.h"
++#include "debug.h"
++#include "runtime.h"
++
++namespace v8 {
++namespace internal {
++
++MacroAssembler::MacroAssembler(Isolate* arg_isolate, void* buffer, int size)
++ : Assembler(arg_isolate, buffer, size),
++ generating_stub_(false),
++ allow_stub_calls_(true),
++ has_frame_(false) {
++ if (isolate() != NULL) {
++ code_object_ = Handle<Object>(isolate()->heap()->undefined_value(),
++ isolate());
++ }
++}
++
++
++void MacroAssembler::Jump(Register target, Condition cond) {
++ ASSERT(cond == al);
++ mtctr(target);
++ bcr();
++}
++
++
++void MacroAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
++ Condition cond, CRegister cr) {
++ Label skip;
++
++ if (cond != al) b(NegateCondition(cond), &skip, cr);
++
++ ASSERT(rmode == RelocInfo::CODE_TARGET ||
++ rmode == RelocInfo::RUNTIME_ENTRY);
++
++ mov(r0, Operand(target, rmode));
++ mtctr(r0);
++ bcr();
++
++ bind(&skip);
++ // mov(pc, Operand(target, rmode), LeaveCC, cond);
++}
++
++
++void MacroAssembler::Jump(Address target, RelocInfo::Mode rmode,
++ Condition cond, CRegister cr) {
++ ASSERT(!RelocInfo::IsCodeTarget(rmode));
++ Jump(reinterpret_cast<intptr_t>(target), rmode, cond, cr);
++}
++
++
++void MacroAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode,
++ Condition cond) {
++ ASSERT(RelocInfo::IsCodeTarget(rmode));
++ // 'code' is always generated ppc code, never THUMB code
++ Jump(reinterpret_cast<intptr_t>(code.location()), rmode, cond);
++}
++
++
++int MacroAssembler::CallSize(Register target, Condition cond) {
++ return 2 * kInstrSize;
++}
++
++
++void MacroAssembler::Call(Register target, Condition cond) {
++ BlockTrampolinePoolScope block_trampoline_pool(this);
++ Label start;
++ bind(&start);
++ ASSERT(cond == al); // in prep of removal of condition
++
++ // Statement positions are expected to be recorded when the target
++ // address is loaded.
++ positions_recorder()->WriteRecordedPositions();
++
++ // branch via link register and set LK bit for return point
++ mtlr(target);
++ bclr(BA, SetLK);
++
++ ASSERT_EQ(CallSize(target, cond), SizeOfCodeGeneratedSince(&start));
++}
++
++
++int MacroAssembler::CallSize(
++ Address target, RelocInfo::Mode rmode, Condition cond) {
++ int size;
++ int movSize;
++
++#if 0 // Account for variable length Assembler::mov sequence.
++ intptr_t value = reinterpret_cast<intptr_t>(target);
++ if (is_int16(value) || (((value >> 16) << 16) == value)) {
++ movSize = 1;
++ } else {
++ movSize = 2;
++ }
++#else
++
++#if V8_TARGET_ARCH_PPC64
++ movSize = 5;
++#else
++ movSize = 2;
++#endif
++
++#endif
++
++ size = (2 + movSize) * kInstrSize;
++
++ return size;
++}
++
++
++int MacroAssembler::CallSizeNotPredictableCodeSize(
++ Address target, RelocInfo::Mode rmode, Condition cond) {
++ int size;
++ int movSize;
++
++#if 0 // Account for variable length Assembler::mov sequence.
++ intptr_t value = reinterpret_cast<intptr_t>(target);
++ if (is_int16(value) || (((value >> 16) << 16) == value)) {
++ movSize = 1;
++ } else {
++ movSize = 2;
++ }
++#else
++
++#if V8_TARGET_ARCH_PPC64
++ movSize = 5;
++#else
++ movSize = 2;
++#endif
++
++#endif
++
++ size = (2 + movSize) * kInstrSize;
++
++ return size;
++}
++
++
++void MacroAssembler::Call(Address target,
++ RelocInfo::Mode rmode,
++ Condition cond) {
++ BlockTrampolinePoolScope block_trampoline_pool(this);
++ ASSERT(cond == al);
++ Label start;
++ bind(&start);
++
++ // Statement positions are expected to be recorded when the target
++ // address is loaded.
++ positions_recorder()->WriteRecordedPositions();
++
++ // This can likely be optimized to make use of bc() with 24bit relative
++ //
++ // RecordRelocInfo(x.rmode_, x.imm_);
++ // bc( BA, .... offset, LKset);
++ //
++
++ mov(ip, Operand(reinterpret_cast<intptr_t>(target), rmode));
++ mtlr(ip);
++ bclr(BA, SetLK);
++
++#if V8_TARGET_ARCH_PPC64
++ ASSERT(kCallTargetAddressOffset == 7 * kInstrSize);
++#else
++ ASSERT(kCallTargetAddressOffset == 4 * kInstrSize);
++#endif
++ ASSERT_EQ(CallSize(target, rmode, cond), SizeOfCodeGeneratedSince(&start));
++}
++
++
++int MacroAssembler::CallSize(Handle<Code> code,
++ RelocInfo::Mode rmode,
++ TypeFeedbackId ast_id,
++ Condition cond) {
++ return CallSize(reinterpret_cast<Address>(code.location()), rmode, cond);
++}
++
++
++void MacroAssembler::Call(Handle<Code> code,
++ RelocInfo::Mode rmode,
++ TypeFeedbackId ast_id,
++ Condition cond) {
++ BlockTrampolinePoolScope block_trampoline_pool(this);
++ Label start;
++ bind(&start);
++ ASSERT(RelocInfo::IsCodeTarget(rmode));
++ if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) {
++ SetRecordedAstId(ast_id);
++ rmode = RelocInfo::CODE_TARGET_WITH_ID;
++ }
++ Call(reinterpret_cast<Address>(code.location()), rmode, cond);
++ ASSERT_EQ(CallSize(code, rmode, ast_id, cond),
++ SizeOfCodeGeneratedSince(&start));
++}
++
++
++void MacroAssembler::Ret(Condition cond) {
++ ASSERT(cond == al);
++ blr();
++}
++
++
++void MacroAssembler::Drop(int count, Condition cond) {
++ ASSERT(cond == al);
++ if (count > 0) {
++ Add(sp, sp, count * kPointerSize, r0);
++ }
++}
++
++
++void MacroAssembler::Ret(int drop, Condition cond) {
++ Drop(drop, cond);
++ Ret(cond);
++}
++
++void MacroAssembler::Call(Label* target) {
++ b(target, SetLK);
++}
++
++
++void MacroAssembler::Push(Handle<Object> handle) {
++ mov(ip, Operand(handle));
++ push(ip);
++}
++
++
++void MacroAssembler::Move(Register dst, Handle<Object> value) {
++ mov(dst, Operand(value));
++}
++
++
++void MacroAssembler::Move(Register dst, Register src, Condition cond) {
++ ASSERT(cond == al);
++ if (!dst.is(src)) {
++ mr(dst, src);
++ }
++}
++
++
++void MacroAssembler::Move(DoubleRegister dst, DoubleRegister src) {
++ if (!dst.is(src)) {
++ fmr(dst, src);
++ }
++}
++
++
++void MacroAssembler::MultiPush(RegList regs) {
++ int16_t num_to_push = NumberOfBitsSet(regs);
++ int16_t stack_offset = num_to_push * kPointerSize;
++
++ subi(sp, sp, Operand(stack_offset));
++ for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
++ if ((regs & (1 << i)) != 0) {
++ stack_offset -= kPointerSize;
++ StoreP(ToRegister(i), MemOperand(sp, stack_offset));
++ }
++ }
++}
++
++void MacroAssembler::MultiPop(RegList regs) {
++ int16_t stack_offset = 0;
++
++ for (int16_t i = 0; i < kNumRegisters; i++) {
++ if ((regs & (1 << i)) != 0) {
++ LoadP(ToRegister(i), MemOperand(sp, stack_offset));
++ stack_offset += kPointerSize;
++ }
++ }
++ addi(sp, sp, Operand(stack_offset));
++}
++
++
++void MacroAssembler::LoadRoot(Register destination,
++ Heap::RootListIndex index,
++ Condition cond) {
++ ASSERT(cond == al);
++ LoadP(destination, MemOperand(kRootRegister,
++ index << kPointerSizeLog2), r0);
++}
++
++
++void MacroAssembler::StoreRoot(Register source,
++ Heap::RootListIndex index,
++ Condition cond) {
++ ASSERT(cond == al);
++ StoreP(source, MemOperand(kRootRegister, index << kPointerSizeLog2), r0);
++}
++
++
++void MacroAssembler::LoadHeapObject(Register result,
++ Handle<HeapObject> object) {
++ if (isolate()->heap()->InNewSpace(*object)) {
++ Handle<JSGlobalPropertyCell> cell =
++ isolate()->factory()->NewJSGlobalPropertyCell(object);
++ mov(result, Operand(cell));
++ LoadP(result, FieldMemOperand(result, JSGlobalPropertyCell::kValueOffset));
++ } else {
++ mov(result, Operand(object));
++ }
++}
++
++
++void MacroAssembler::InNewSpace(Register object,
++ Register scratch,
++ Condition cond,
++ Label* branch) {
++ // N.B. scratch may be same register as object
++ ASSERT(cond == eq || cond == ne);
++ mov(r0, Operand(ExternalReference::new_space_mask(isolate())));
++ and_(scratch, object, r0);
++ mov(r0, Operand(ExternalReference::new_space_start(isolate())));
++ cmp(scratch, r0);
++ b(cond, branch);
++}
++
++
++void MacroAssembler::RecordWriteField(
++ Register object,
++ int offset,
++ Register value,
++ Register dst,
++ LinkRegisterStatus lr_status,
++ SaveFPRegsMode save_fp,
++ RememberedSetAction remembered_set_action,
++ SmiCheck smi_check) {
++ // First, check if a write barrier is even needed. The tests below
++ // catch stores of Smis.
++ Label done;
++
++ // Skip barrier if writing a smi.
++ if (smi_check == INLINE_SMI_CHECK) {
++ JumpIfSmi(value, &done);
++ }
++
++ // Although the object register is tagged, the offset is relative to the start
++ // of the object, so so offset must be a multiple of kPointerSize.
++ ASSERT(IsAligned(offset, kPointerSize));
++
++ addi(dst, object, Operand(offset - kHeapObjectTag));
++ if (emit_debug_code()) {
++ Label ok;
++ andi(r0, dst, Operand((1 << kPointerSizeLog2) - 1));
++ beq(&ok, cr0);
++ stop("Unaligned cell in write barrier");
++ bind(&ok);
++ }
++
++ RecordWrite(object,
++ dst,
++ value,
++ lr_status,
++ save_fp,
++ remembered_set_action,
++ OMIT_SMI_CHECK);
++
++ bind(&done);
++
++ // Clobber clobbered input registers when running with the debug-code flag
++ // turned on to provoke errors.
++ if (emit_debug_code()) {
++ mov(value, Operand(BitCast<intptr_t>(kZapValue + 4)));
++ mov(dst, Operand(BitCast<intptr_t>(kZapValue + 8)));
++ }
++}
++
++
++// Will clobber 4 registers: object, address, scratch, ip. The
++// register 'object' contains a heap object pointer. The heap object
++// tag is shifted away.
++void MacroAssembler::RecordWrite(Register object,
++ Register address,
++ Register value,
++ LinkRegisterStatus lr_status,
++ SaveFPRegsMode fp_mode,
++ RememberedSetAction remembered_set_action,
++ SmiCheck smi_check) {
++ // The compiled code assumes that record write doesn't change the
++ // context register, so we check that none of the clobbered
++ // registers are cp.
++ ASSERT(!address.is(cp) && !value.is(cp));
++
++ if (emit_debug_code()) {
++ LoadP(ip, MemOperand(address));
++ cmp(ip, value);
++ Check(eq, "Wrong address or value passed to RecordWrite");
++ }
++
++ Label done;
++
++ if (smi_check == INLINE_SMI_CHECK) {
++ JumpIfSmi(value, &done);
++ }
++
++ CheckPageFlag(value,
++ value, // Used as scratch.
++ MemoryChunk::kPointersToHereAreInterestingMask,
++ eq,
++ &done);
++ CheckPageFlag(object,
++ value, // Used as scratch.
++ MemoryChunk::kPointersFromHereAreInterestingMask,
++ eq,
++ &done);
++
++ // Record the actual write.
++ if (lr_status == kLRHasNotBeenSaved) {
++ mflr(r0);
++ push(r0);
++ }
++ RecordWriteStub stub(object, value, address, remembered_set_action, fp_mode);
++ CallStub(&stub);
++ if (lr_status == kLRHasNotBeenSaved) {
++ pop(r0);
++ mtlr(r0);
++ }
++
++ bind(&done);
++
++ // Clobber clobbered registers when running with the debug-code flag
++ // turned on to provoke errors.
++ if (emit_debug_code()) {
++ mov(address, Operand(BitCast<intptr_t>(kZapValue + 12)));
++ mov(value, Operand(BitCast<intptr_t>(kZapValue + 16)));
++ }
++}
++
++
++void MacroAssembler::RememberedSetHelper(Register object, // For debug tests.
++ Register address,
++ Register scratch,
++ SaveFPRegsMode fp_mode,
++ RememberedSetFinalAction and_then) {
++ Label done;
++ if (emit_debug_code()) {
++ Label ok;
++ JumpIfNotInNewSpace(object, scratch, &ok);
++ stop("Remembered set pointer is in new space");
++ bind(&ok);
++ }
++ // Load store buffer top.
++ ExternalReference store_buffer =
++ ExternalReference::store_buffer_top(isolate());
++ mov(ip, Operand(store_buffer));
++ LoadP(scratch, MemOperand(ip));
++ // Store pointer to buffer and increment buffer top.
++ StoreP(address, MemOperand(scratch));
++ addi(scratch, scratch, Operand(kPointerSize));
++ // Write back new top of buffer.
++ StoreP(scratch, MemOperand(ip));
++ // Call stub on end of buffer.
++ // Check for end of buffer.
++ mov(r0, Operand(StoreBuffer::kStoreBufferOverflowBit));
++ and_(r0, scratch, r0, SetRC);
++
++ if (and_then == kFallThroughAtEnd) {
++ beq(&done, cr0);
++ } else {
++ ASSERT(and_then == kReturnAtEnd);
++ beq(&done, cr0);
++ }
++ mflr(r0);
++ push(r0);
++ StoreBufferOverflowStub store_buffer_overflow =
++ StoreBufferOverflowStub(fp_mode);
++ CallStub(&store_buffer_overflow);
++ pop(r0);
++ mtlr(r0);
++ bind(&done);
++ if (and_then == kReturnAtEnd) {
++ Ret();
++ }
++}
++
++
++// Push and pop all registers that can hold pointers.
++void MacroAssembler::PushSafepointRegisters() {
++ // Safepoints expect a block of kNumSafepointRegisters values on the
++ // stack, so adjust the stack for unsaved registers.
++ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
++ ASSERT(num_unsaved >= 0);
++ if (num_unsaved > 0) {
++ subi(sp, sp, Operand(num_unsaved * kPointerSize));
++ }
++ MultiPush(kSafepointSavedRegisters);
++}
++
++
++void MacroAssembler::PopSafepointRegisters() {
++ const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters;
++ MultiPop(kSafepointSavedRegisters);
++ if (num_unsaved > 0) {
++ addi(sp, sp, Operand(num_unsaved * kPointerSize));
++ }
++}
++
++
++void MacroAssembler::StoreToSafepointRegisterSlot(Register src, Register dst) {
++ StoreP(src, SafepointRegisterSlot(dst));
++}
++
++
++void MacroAssembler::LoadFromSafepointRegisterSlot(Register dst, Register src) {
++ LoadP(dst, SafepointRegisterSlot(src));
++}
++
++
++int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
++ // The registers are pushed starting with the highest encoding,
++ // which means that lowest encodings are closest to the stack pointer.
++ RegList regs = kSafepointSavedRegisters;
++ int index = 0;
++
++ ASSERT(reg_code >= 0 && reg_code < kNumRegisters);
++
++ for (int16_t i = 0; i < reg_code; i++) {
++ if ((regs & (1 << i)) != 0) {
++ index++;
++ }
++ }
++
++ return index;
++}
++
++
++MemOperand MacroAssembler::SafepointRegisterSlot(Register reg) {
++ return MemOperand(sp, SafepointRegisterStackIndex(reg.code()) * kPointerSize);
++}
++
++
++MemOperand MacroAssembler::SafepointRegistersAndDoublesSlot(Register reg) {
++ // General purpose registers are pushed last on the stack.
++ int doubles_size = DwVfpRegister::kNumAllocatableRegisters * kDoubleSize;
++ int register_offset = SafepointRegisterStackIndex(reg.code()) * kPointerSize;
++ return MemOperand(sp, doubles_size + register_offset);
++}
++
++void MacroAssembler::EnterFrame(StackFrame::Type type) {
++ mflr(r0);
++ push(r0);
++ push(fp);
++ push(cp);
++ LoadSmiLiteral(r0, Smi::FromInt(type));
++ push(r0);
++ mov(r0, Operand(CodeObject()));
++ push(r0);
++ addi(fp, sp, Operand(3 * kPointerSize)); // Adjust FP to point to saved FP
++}
++
++
++void MacroAssembler::LeaveFrame(StackFrame::Type type) {
++ // Drop the execution stack down to the frame pointer and restore
++ // the caller frame pointer and return address.
++ mr(sp, fp);
++ LoadP(fp, MemOperand(sp));
++ LoadP(r0, MemOperand(sp, kPointerSize));
++ mtlr(r0);
++ addi(sp, sp, Operand(2*kPointerSize));
++}
++
++// ExitFrame layout (probably wrongish.. needs updating)
++//
++// SP -> previousSP
++// LK reserved
++// code
++// sp_on_exit (for debug?)
++// oldSP->prev SP
++// LK
++// <parameters on stack>
++
++// Prior to calling EnterExitFrame, we've got a bunch of parameters
++// on the stack that we need to wrap a real frame around.. so first
++// we reserve a slot for LK and push the previous SP which is captured
++// in the fp register (r31)
++// Then - we buy a new frame
++
++void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space) {
++ // Set up the frame structure on the stack.
++ ASSERT_EQ(2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement);
++ ASSERT_EQ(1 * kPointerSize, ExitFrameConstants::kCallerPCOffset);
++ ASSERT_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset);
++ ASSERT(stack_space > 0);
++
++ // This is an opportunity to build a frame to wrap
++ // all of the pushes that have happened inside of V8
++ // since we were called from C code
++
++ // replicate ARM frame - TODO make this more closely follow PPC ABI
++ mflr(r0);
++ Push(r0, fp);
++ mr(fp, sp);
++ // Reserve room for saved entry sp and code object.
++ subi(sp, sp, Operand(2 * kPointerSize));
++
++ if (emit_debug_code()) {
++ li(r8, Operand::Zero());
++ StoreP(r8, MemOperand(fp, ExitFrameConstants::kSPOffset));
++ }
++ mov(r8, Operand(CodeObject()));
++ StoreP(r8, MemOperand(fp, ExitFrameConstants::kCodeOffset));
++
++ // Save the frame pointer and the context in top.
++ mov(r8, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate())));
++ StoreP(fp, MemOperand(r8));
++ mov(r8, Operand(ExternalReference(Isolate::kContextAddress, isolate())));
++ StoreP(cp, MemOperand(r8));
++
++ // Optionally save all volatile double registers.
++ if (save_doubles) {
++ const int kNumRegs = DwVfpRegister::kNumVolatileRegisters;
++ subi(sp, sp, Operand(kNumRegs * kDoubleSize));
++ for (int i = 0; i < kNumRegs; i++) {
++ DwVfpRegister reg = DwVfpRegister::from_code(i);
++ stfd(reg, MemOperand(sp, i * kDoubleSize));
++ }
++ // Note that d0 will be accessible at
++ // fp - 2 * kPointerSize - kNumVolatileRegisters * kDoubleSize,
++ // since the sp slot and code slot were pushed after the fp.
++ }
++
++ // Allocate and align the frame preparing for calling the runtime
++ // function.
++ stack_space += kNumRequiredStackFrameSlots;
++ subi(sp, sp, Operand(stack_space * kPointerSize));
++ const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
++ if (frame_alignment > 0) {
++ ASSERT(frame_alignment == 8);
++ ClearRightImm(sp, sp, Operand(3)); // equivalent to &= -8
++ }
++
++ // Set the exit frame sp value to point just before the return address
++ // location.
++ addi(r8, sp, Operand((kStackFrameExtraParamSlot + 1) * kPointerSize));
++ StoreP(r8, MemOperand(fp, ExitFrameConstants::kSPOffset));
++}
++
++
++void MacroAssembler::InitializeNewString(Register string,
++ Register length,
++ Heap::RootListIndex map_index,
++ Register scratch1,
++ Register scratch2) {
++ SmiTag(scratch1, length);
++ LoadRoot(scratch2, map_index);
++ StoreP(scratch1, FieldMemOperand(string, String::kLengthOffset), r0);
++ li(scratch1, Operand(String::kEmptyHashField));
++ StoreP(scratch2, FieldMemOperand(string, HeapObject::kMapOffset), r0);
++ StoreP(scratch1, FieldMemOperand(string, String::kHashFieldSlot), r0);
++}
++
++
++int MacroAssembler::ActivationFrameAlignment() {
++#if !defined(USE_SIMULATOR)
++ // Running on the real platform. Use the alignment as mandated by the local
++ // environment.
++ // Note: This will break if we ever start generating snapshots on one PPC
++ // platform for another PPC platform with a different alignment.
++ return OS::ActivationFrameAlignment();
++#else // Simulated
++ // If we are using the simulator then we should always align to the expected
++ // alignment. As the simulator is used to generate snapshots we do not know
++ // if the target platform will need alignment, so this is controlled from a
++ // flag.
++ return FLAG_sim_stack_alignment;
++#endif
++}
++
++
++void MacroAssembler::LeaveExitFrame(bool save_doubles,
++ Register argument_count) {
++ // Optionally restore all double registers.
++ if (save_doubles) {
++ // Calculate the stack location of the saved doubles and restore them.
++ const int kNumRegs = DwVfpRegister::kNumVolatileRegisters;
++ const int offset = (2 * kPointerSize + kNumRegs * kDoubleSize);
++ addi(r6, fp, Operand(-offset));
++ for (int i = 0; i < kNumRegs; i++) {
++ DwVfpRegister reg = DwVfpRegister::from_code(i);
++ lfd(reg, MemOperand(r6, i * kDoubleSize));
++ }
++ }
++
++ // Clear top frame.
++ li(r6, Operand(0, RelocInfo::NONE));
++ mov(ip, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate())));
++ StoreP(r6, MemOperand(ip));
++
++ // Restore current context from top and clear it in debug mode.
++ mov(ip, Operand(ExternalReference(Isolate::kContextAddress, isolate())));
++ LoadP(cp, MemOperand(ip));
++#ifdef DEBUG
++ StoreP(r6, MemOperand(ip));
++#endif
++
++ // Tear down the exit frame, pop the arguments, and return.
++ mr(sp, fp);
++ pop(fp);
++ pop(r0);
++ mtlr(r0);
++
++ if (argument_count.is_valid()) {
++ ShiftLeftImm(argument_count, argument_count, Operand(kPointerSizeLog2));
++ add(sp, sp, argument_count);
++ }
++}
++
++void MacroAssembler::GetCFunctionDoubleResult(const DoubleRegister dst) {
++ fmr(dst, d1);
++}
++
++
++void MacroAssembler::SetCallKind(Register dst, CallKind call_kind) {
++ // This macro takes the dst register to make the code more readable
++ // at the call sites. However, the dst register has to be r8 to
++ // follow the calling convention which requires the call type to be
++ // in r8.
++ ASSERT(dst.is(r8));
++ if (call_kind == CALL_AS_FUNCTION) {
++ LoadSmiLiteral(dst, Smi::FromInt(1));
++ } else {
++ LoadSmiLiteral(dst, Smi::FromInt(0));
++ }
++}
++
++
++void MacroAssembler::InvokePrologue(const ParameterCount& expected,
++ const ParameterCount& actual,
++ Handle<Code> code_constant,
++ Register code_reg,
++ Label* done,
++ bool* definitely_mismatches,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind) {
++ bool definitely_matches = false;
++ *definitely_mismatches = false;
++ Label regular_invoke;
++
++ // Check whether the expected and actual arguments count match. If not,
++ // setup registers according to contract with ArgumentsAdaptorTrampoline:
++ // r3: actual arguments count
++ // r4: function (passed through to callee)
++ // r5: expected arguments count
++ // r6: callee code entry
++
++ // The code below is made a lot easier because the calling code already sets
++ // up actual and expected registers according to the contract if values are
++ // passed in registers.
++
++ // roohack - remove these 3 checks temporarily
++ // ASSERT(actual.is_immediate() || actual.reg().is(r3));
++ // ASSERT(expected.is_immediate() || expected.reg().is(r5));
++ // ASSERT((!code_constant.is_null() && code_reg.is(no_reg))
++ // || code_reg.is(r6));
++
++ if (expected.is_immediate()) {
++ ASSERT(actual.is_immediate());
++ if (expected.immediate() == actual.immediate()) {
++ definitely_matches = true;
++ } else {
++ mov(r3, Operand(actual.immediate()));
++ const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
++ if (expected.immediate() == sentinel) {
++ // Don't worry about adapting arguments for builtins that
++ // don't want that done. Skip adaption code by making it look
++ // like we have a match between expected and actual number of
++ // arguments.
++ definitely_matches = true;
++ } else {
++ *definitely_mismatches = true;
++ mov(r5, Operand(expected.immediate()));
++ }
++ }
++ } else {
++ if (actual.is_immediate()) {
++ cmpi(expected.reg(), Operand(actual.immediate()));
++ beq(®ular_invoke);
++ mov(r3, Operand(actual.immediate()));
++ } else {
++ cmp(expected.reg(), actual.reg());
++ beq(®ular_invoke);
++ }
++ }
++
++ if (!definitely_matches) {
++ if (!code_constant.is_null()) {
++ mov(r6, Operand(code_constant));
++ addi(r6, r6, Operand(Code::kHeaderSize - kHeapObjectTag));
++ }
++
++ Handle<Code> adaptor =
++ isolate()->builtins()->ArgumentsAdaptorTrampoline();
++ if (flag == CALL_FUNCTION) {
++ call_wrapper.BeforeCall(CallSize(adaptor));
++ SetCallKind(r8, call_kind);
++ Call(adaptor);
++ call_wrapper.AfterCall();
++ if (!*definitely_mismatches) {
++ b(done);
++ }
++ } else {
++ SetCallKind(r8, call_kind);
++ Jump(adaptor, RelocInfo::CODE_TARGET);
++ }
++ bind(®ular_invoke);
++ }
++}
++
++
++void MacroAssembler::InvokeCode(Register code,
++ const ParameterCount& expected,
++ const ParameterCount& actual,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind) {
++ // You can't call a function without a valid frame.
++ ASSERT(flag == JUMP_FUNCTION || has_frame());
++
++ Label done;
++ bool definitely_mismatches = false;
++ InvokePrologue(expected, actual, Handle<Code>::null(), code,
++ &done, &definitely_mismatches, flag,
++ call_wrapper, call_kind);
++ if (!definitely_mismatches) {
++ if (flag == CALL_FUNCTION) {
++ call_wrapper.BeforeCall(CallSize(code));
++ SetCallKind(r8, call_kind);
++ Call(code);
++ call_wrapper.AfterCall();
++ } else {
++ ASSERT(flag == JUMP_FUNCTION);
++ SetCallKind(r8, call_kind);
++ Jump(code);
++ }
++
++ // Continue here if InvokePrologue does handle the invocation due to
++ // mismatched parameter counts.
++ bind(&done);
++ }
++}
++
++
++void MacroAssembler::InvokeCode(Handle<Code> code,
++ const ParameterCount& expected,
++ const ParameterCount& actual,
++ RelocInfo::Mode rmode,
++ InvokeFlag flag,
++ CallKind call_kind) {
++ // You can't call a function without a valid frame.
++ ASSERT(flag == JUMP_FUNCTION || has_frame());
++
++ Label done;
++ bool definitely_mismatches = false;
++ InvokePrologue(expected, actual, code, no_reg,
++ &done, &definitely_mismatches, flag,
++ NullCallWrapper(), call_kind);
++ if (!definitely_mismatches) {
++ if (flag == CALL_FUNCTION) {
++ SetCallKind(r8, call_kind);
++ Call(code, rmode);
++ } else {
++ SetCallKind(r8, call_kind);
++ Jump(code, rmode);
++ }
++
++ // Continue here if InvokePrologue does handle the invocation due to
++ // mismatched parameter counts.
++ bind(&done);
++ }
++}
++
++
++void MacroAssembler::InvokeFunction(Register fun,
++ const ParameterCount& actual,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind) {
++ // You can't call a function without a valid frame.
++ ASSERT(flag == JUMP_FUNCTION || has_frame());
++
++ // Contract with called JS functions requires that function is passed in r4.
++ ASSERT(fun.is(r4));
++
++ Register expected_reg = r5;
++ Register code_reg = r6;
++
++ LoadP(code_reg, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++ LoadWordArith(expected_reg,
++ FieldMemOperand(code_reg,
++ SharedFunctionInfo::kFormalParameterCountOffset));
++#if !defined(V8_TARGET_ARCH_PPC64)
++ SmiUntag(expected_reg);
++#endif
++ LoadP(code_reg,
++ FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
++
++ ParameterCount expected(expected_reg);
++ InvokeCode(code_reg, expected, actual, flag, call_wrapper, call_kind);
++}
++
++
++void MacroAssembler::InvokeFunction(Handle<JSFunction> function,
++ const ParameterCount& actual,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind) {
++ // You can't call a function without a valid frame.
++ ASSERT(flag == JUMP_FUNCTION || has_frame());
++
++ // Get the function and setup the context.
++ LoadHeapObject(r4, function);
++ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++
++ ParameterCount expected(function->shared()->formal_parameter_count());
++ // We call indirectly through the code field in the function to
++ // allow recompilation to take effect without changing any of the
++ // call sites.
++ LoadP(r6, FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
++ InvokeCode(r6, expected, actual, flag, call_wrapper, call_kind);
++}
++
++
++void MacroAssembler::IsObjectJSObjectType(Register heap_object,
++ Register map,
++ Register scratch,
++ Label* fail) {
++ LoadP(map, FieldMemOperand(heap_object, HeapObject::kMapOffset));
++ IsInstanceJSObjectType(map, scratch, fail);
++}
++
++
++void MacroAssembler::IsInstanceJSObjectType(Register map,
++ Register scratch,
++ Label* fail) {
++ lbz(scratch, FieldMemOperand(map, Map::kInstanceTypeOffset));
++ cmpi(scratch, Operand(FIRST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ blt(fail);
++ cmpi(scratch, Operand(LAST_NONCALLABLE_SPEC_OBJECT_TYPE));
++ bgt(fail);
++}
++
++
++void MacroAssembler::IsObjectJSStringType(Register object,
++ Register scratch,
++ Label* fail) {
++ ASSERT(kNotStringTag != 0);
++
++ LoadP(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
++ lbz(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
++ andi(r0, scratch, Operand(kIsNotStringMask));
++ bne(fail, cr0);
++}
++
++
++#ifdef ENABLE_DEBUGGER_SUPPORT
++void MacroAssembler::DebugBreak() {
++ li(r3, Operand(0, RelocInfo::NONE));
++ mov(r4, Operand(ExternalReference(Runtime::kDebugBreak, isolate())));
++ CEntryStub ces(1);
++ ASSERT(AllowThisStubCall(&ces));
++ Call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
++}
++#endif
++
++
++void MacroAssembler::PushTryHandler(StackHandler::Kind kind,
++ int handler_index) {
++ // Adjust this code if not the case.
++ STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kStateSlot == 2 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize);
++
++ // For the JSEntry handler, we must preserve r1-r7, r0,r8-r15 are available.
++ // We want the stack to look like
++ // sp -> NextOffset
++ // CodeObject
++ // state
++ // context
++ // frame pointer
++
++ // Link the current handler as the next handler.
++ mov(r8, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
++ LoadP(r0, MemOperand(r8));
++ StorePU(r0, MemOperand(sp, -StackHandlerConstants::kSize));
++ // Set this new handler as the current one.
++ StoreP(sp, MemOperand(r8));
++
++ if (kind == StackHandler::JS_ENTRY) {
++ li(r8, Operand(0, RelocInfo::NONE)); // NULL frame pointer.
++ StoreP(r8, MemOperand(sp, StackHandlerConstants::kFPOffset));
++ LoadSmiLiteral(r8, Smi::FromInt(0)); // Indicates no context.
++ StoreP(r8, MemOperand(sp, StackHandlerConstants::kContextOffset));
++ } else {
++ // still not sure if fp is right
++ StoreP(fp, MemOperand(sp, StackHandlerConstants::kFPOffset));
++ StoreP(cp, MemOperand(sp, StackHandlerConstants::kContextOffset));
++ }
++ unsigned state =
++ StackHandler::IndexField::encode(handler_index) |
++ StackHandler::KindField::encode(kind);
++ LoadIntLiteral(r8, state);
++ StoreP(r8, MemOperand(sp, StackHandlerConstants::kStateSlot));
++ mov(r8, Operand(CodeObject()));
++ StoreP(r8, MemOperand(sp, StackHandlerConstants::kCodeOffset));
++}
++
++
++void MacroAssembler::PopTryHandler() {
++ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
++ pop(r4);
++ mov(ip, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
++ addi(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
++ StoreP(r4, MemOperand(ip));
++}
++
++// PPC - make use of ip as a temporary register
++void MacroAssembler::JumpToHandlerEntry() {
++ // Compute the handler entry address and jump to it. The handler table is
++ // a fixed array of (smi-tagged) code offsets.
++ // r3 = exception, r4 = code object, r5 = state.
++ LoadP(r6, FieldMemOperand(r4, Code::kHandlerTableOffset)); // Handler table.
++ addi(r6, r6, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ srwi(r5, r5, Operand(StackHandler::kKindWidth)); // Handler index.
++ slwi(ip, r5, Operand(kPointerSizeLog2));
++ add(ip, r6, ip);
++ LoadP(r5, MemOperand(ip)); // Smi-tagged offset.
++ addi(r4, r4, Operand(Code::kHeaderSize - kHeapObjectTag)); // Code start.
++ SmiUntag(ip, r5);
++ add(r0, r4, ip);
++ mtctr(r0);
++ bcr();
++}
++
++
++void MacroAssembler::Throw(Register value) {
++ // Adjust this code if not the case.
++ STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
++ STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kStateSlot == 2 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize);
++ Label skip;
++
++ // The exception is expected in r3.
++ if (!value.is(r3)) {
++ mr(r3, value);
++ }
++ // Drop the stack pointer to the top of the top handler.
++ mov(r6, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
++ LoadP(sp, MemOperand(r6));
++ // Restore the next handler.
++ pop(r5);
++ StoreP(r5, MemOperand(r6));
++
++ // Get the code object (r4) and state (r5). Restore the context and frame
++ // pointer.
++ pop(r4);
++ pop(r5);
++ pop(cp);
++ pop(fp);
++
++ // If the handler is a JS frame, restore the context to the frame.
++ // (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp
++ // or cp.
++ cmpi(cp, Operand::Zero());
++ beq(&skip);
++ StoreP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ bind(&skip);
++
++ JumpToHandlerEntry();
++}
++
++
++void MacroAssembler::ThrowUncatchable(Register value) {
++ // Adjust this code if not the case.
++ STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kStateSlot == 2 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize);
++ STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize);
++
++ // The exception is expected in r3.
++ if (!value.is(r3)) {
++ mr(r3, value);
++ }
++ // Drop the stack pointer to the top of the top stack handler.
++ mov(r6, Operand(ExternalReference(Isolate::kHandlerAddress, isolate())));
++ LoadP(sp, MemOperand(r6));
++
++ // Unwind the handlers until the ENTRY handler is found.
++ Label fetch_next, check_kind;
++ b(&check_kind);
++ bind(&fetch_next);
++ LoadP(sp, MemOperand(sp, StackHandlerConstants::kNextOffset));
++
++ bind(&check_kind);
++ STATIC_ASSERT(StackHandler::JS_ENTRY == 0);
++ LoadP(r5, MemOperand(sp, StackHandlerConstants::kStateSlot));
++ andi(r0, r5, Operand(StackHandler::KindField::kMask));
++ bne(&fetch_next, cr0);
++
++ // Set the top handler address to next handler past the top ENTRY handler.
++ pop(r5);
++ StoreP(r5, MemOperand(r6));
++ // Get the code object (r4) and state (r5). Clear the context and frame
++ // pointer (0 was saved in the handler).
++ pop(r4);
++ pop(r5);
++ pop(cp);
++ pop(fp);
++
++ JumpToHandlerEntry();
++}
++
++
++void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
++ Register scratch,
++ Label* miss) {
++ Label same_contexts;
++
++ ASSERT(!holder_reg.is(scratch));
++ ASSERT(!holder_reg.is(ip));
++ ASSERT(!scratch.is(ip));
++
++ // Load current lexical context from the stack frame.
++ LoadP(scratch, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ // In debug mode, make sure the lexical context is set.
++#ifdef DEBUG
++ cmpi(scratch, Operand(0, RelocInfo::NONE));
++ Check(ne, "we should not have an empty lexical context");
++#endif
++
++ // Load the native context of the current context.
++ int offset =
++ Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize;
++ LoadP(scratch, FieldMemOperand(scratch, offset));
++ LoadP(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset));
++
++ // Check the context is a native context.
++ if (emit_debug_code()) {
++ // TODO(119): avoid push(holder_reg)/pop(holder_reg)
++ // Cannot use ip as a temporary in this verification code. Due to the fact
++ // that ip is clobbered as part of cmp with an object Operand.
++ push(holder_reg); // Temporarily save holder on the stack.
++ // Read the first word and compare to the native_context_map.
++ LoadP(holder_reg, FieldMemOperand(scratch, HeapObject::kMapOffset));
++ LoadRoot(ip, Heap::kNativeContextMapRootIndex);
++ cmp(holder_reg, ip);
++ Check(eq, "JSGlobalObject::native_context should be a native context.");
++ pop(holder_reg); // Restore holder.
++ }
++
++ // Check if both contexts are the same.
++ LoadP(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
++ cmp(scratch, ip);
++ beq(&same_contexts);
++
++ // Check the context is a native context.
++ if (emit_debug_code()) {
++ // TODO(119): avoid push(holder_reg)/pop(holder_reg)
++ // Cannot use ip as a temporary in this verification code. Due to the fact
++ // that ip is clobbered as part of cmp with an object Operand.
++ push(holder_reg); // Temporarily save holder on the stack.
++ mr(holder_reg, ip); // Move ip to its holding place.
++ LoadRoot(ip, Heap::kNullValueRootIndex);
++ cmp(holder_reg, ip);
++ Check(ne, "JSGlobalProxy::context() should not be null.");
++
++ LoadP(holder_reg, FieldMemOperand(holder_reg, HeapObject::kMapOffset));
++ LoadRoot(ip, Heap::kNativeContextMapRootIndex);
++ cmp(holder_reg, ip);
++ Check(eq, "JSGlobalObject::native_context should be a native context.");
++ // Restore ip is not needed. ip is reloaded below.
++ pop(holder_reg); // Restore holder.
++ // Restore ip to holder's context.
++ LoadP(ip, FieldMemOperand(holder_reg, JSGlobalProxy::kNativeContextOffset));
++ }
++
++ // Check that the security token in the calling global object is
++ // compatible with the security token in the receiving global
++ // object.
++ int token_offset = Context::kHeaderSize +
++ Context::SECURITY_TOKEN_INDEX * kPointerSize;
++
++ LoadP(scratch, FieldMemOperand(scratch, token_offset));
++ LoadP(ip, FieldMemOperand(ip, token_offset));
++ cmp(scratch, ip);
++ bne(miss);
++
++ bind(&same_contexts);
++}
++
++
++void MacroAssembler::GetNumberHash(Register t0, Register scratch) {
++ // First of all we assign the hash seed to scratch.
++ LoadRoot(scratch, Heap::kHashSeedRootIndex);
++ SmiUntag(scratch);
++
++ // Xor original key with a seed.
++ xor_(t0, t0, scratch);
++
++ // Compute the hash code from the untagged key. This must be kept in sync
++ // with ComputeIntegerHash in utils.h.
++ //
++ // hash = ~hash + (hash << 15);
++ notx(scratch, t0);
++ slwi(t0, t0, Operand(15));
++ add(t0, scratch, t0);
++ // hash = hash ^ (hash >> 12);
++ srwi(scratch, t0, Operand(12));
++ xor_(t0, t0, scratch);
++ // hash = hash + (hash << 2);
++ slwi(scratch, t0, Operand(2));
++ add(t0, t0, scratch);
++ // hash = hash ^ (hash >> 4);
++ srwi(scratch, t0, Operand(4));
++ xor_(t0, t0, scratch);
++ // hash = hash * 2057;
++ mr(r0, t0);
++ slwi(scratch, t0, Operand(3));
++ add(t0, t0, scratch);
++ slwi(scratch, r0, Operand(11));
++ add(t0, t0, scratch);
++ // hash = hash ^ (hash >> 16);
++ srwi(scratch, t0, Operand(16));
++ xor_(t0, t0, scratch);
++}
++
++
++void MacroAssembler::LoadFromNumberDictionary(Label* miss,
++ Register elements,
++ Register key,
++ Register result,
++ Register t0,
++ Register t1,
++ Register t2) {
++ // Register use:
++ //
++ // elements - holds the slow-case elements of the receiver on entry.
++ // Unchanged unless 'result' is the same register.
++ //
++ // key - holds the smi key on entry.
++ // Unchanged unless 'result' is the same register.
++ //
++ // result - holds the result on exit if the load succeeded.
++ // Allowed to be the same as 'key' or 'result'.
++ // Unchanged on bailout so 'key' or 'result' can be used
++ // in further computation.
++ //
++ // Scratch registers:
++ //
++ // t0 - holds the untagged key on entry and holds the hash once computed.
++ //
++ // t1 - used to hold the capacity mask of the dictionary
++ //
++ // t2 - used for the index into the dictionary.
++ Label done;
++
++ GetNumberHash(t0, t1);
++
++ // Compute the capacity mask.
++ LoadP(t1, FieldMemOperand(elements, SeededNumberDictionary::kCapacityOffset));
++ SmiUntag(t1);
++ subi(t1, t1, Operand(1));
++
++ // Generate an unrolled loop that performs a few probes before giving up.
++ static const int kProbes = 4;
++ for (int i = 0; i < kProbes; i++) {
++ // Use t2 for index calculations and keep the hash intact in t0.
++ mr(t2, t0);
++ // Compute the masked index: (hash + i + i * i) & mask.
++ if (i > 0) {
++ addi(t2, t2, Operand(SeededNumberDictionary::GetProbeOffset(i)));
++ }
++ and_(t2, t2, t1);
++
++ // Scale the index by multiplying by the element size.
++ ASSERT(SeededNumberDictionary::kEntrySize == 3);
++ slwi(ip, t2, Operand(1));
++ add(t2, t2, ip); // t2 = t2 * 3
++
++ // Check if the key is identical to the name.
++ slwi(t2, t2, Operand(kPointerSizeLog2));
++ add(t2, elements, t2);
++ LoadP(ip,
++ FieldMemOperand(t2, SeededNumberDictionary::kElementsStartOffset));
++ cmp(key, ip);
++ if (i != kProbes - 1) {
++ beq(&done);
++ } else {
++ bne(miss);
++ }
++ }
++
++ bind(&done);
++ // Check that the value is a normal property.
++ // t2: elements + (index * kPointerSize)
++ const int kDetailsOffset =
++ SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize;
++ LoadP(t1, FieldMemOperand(t2, kDetailsOffset));
++ LoadSmiLiteral(ip, Smi::FromInt(PropertyDetails::TypeField::kMask));
++ and_(r0, t1, ip, SetRC);
++ bne(miss, cr0);
++
++ // Get the value at the masked, scaled index and return.
++ const int kValueOffset =
++ SeededNumberDictionary::kElementsStartOffset + kPointerSize;
++ LoadP(result, FieldMemOperand(t2, kValueOffset));
++}
++
++
++void MacroAssembler::AllocateInNewSpace(int object_size,
++ Register result,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required,
++ AllocationFlags flags) {
++ if (!FLAG_inline_new) {
++ if (emit_debug_code()) {
++ // Trash the registers to simulate an allocation failure.
++ li(result, Operand(0x7091));
++ li(scratch1, Operand(0x7191));
++ li(scratch2, Operand(0x7291));
++ }
++ b(gc_required);
++ return;
++ }
++
++ ASSERT(!result.is(scratch1));
++ ASSERT(!result.is(scratch2));
++ ASSERT(!scratch1.is(scratch2));
++ ASSERT(!scratch1.is(ip));
++ ASSERT(!scratch2.is(ip));
++
++ // Make object size into bytes.
++ if ((flags & SIZE_IN_WORDS) != 0) {
++ object_size *= kPointerSize;
++ }
++ ASSERT_EQ(0, static_cast<int>(object_size & kObjectAlignmentMask));
++
++ // Check relative positions of allocation top and limit addresses.
++ // The values must be adjacent in memory to allow the use of LDM.
++ // Also, assert that the registers are numbered such that the values
++ // are loaded in the correct order.
++ ExternalReference new_space_allocation_top =
++ ExternalReference::new_space_allocation_top_address(isolate());
++ ExternalReference new_space_allocation_limit =
++ ExternalReference::new_space_allocation_limit_address(isolate());
++ intptr_t top =
++ reinterpret_cast<intptr_t>(new_space_allocation_top.address());
++ intptr_t limit =
++ reinterpret_cast<intptr_t>(new_space_allocation_limit.address());
++ ASSERT((limit - top) == kPointerSize);
++ ASSERT(result.code() < ip.code());
++
++ // Set up allocation top address and object size registers.
++ Register topaddr = scratch1;
++ Register obj_size_reg = scratch2;
++ mov(topaddr, Operand(new_space_allocation_top));
++ // this won't work for very large object on PowerPC
++ li(obj_size_reg, Operand(object_size));
++
++ // This code stores a temporary value in ip. This is OK, as the code below
++ // does not need ip for implicit literal generation.
++ if ((flags & RESULT_CONTAINS_TOP) == 0) {
++ // Load allocation top into result and allocation limit into ip.
++ LoadP(result, MemOperand(topaddr));
++ LoadP(ip, MemOperand(topaddr, kPointerSize));
++ } else {
++ if (emit_debug_code()) {
++ // Assert that result actually contains top on entry. ip is used
++ // immediately below so this use of ip does not cause difference with
++ // respect to register content between debug and release mode.
++ LoadP(ip, MemOperand(topaddr));
++ cmp(result, ip);
++ Check(eq, "Unexpected allocation top");
++ }
++ // Load allocation limit into ip. Result already contains allocation top.
++ LoadP(ip, MemOperand(topaddr, limit - top), r0);
++ }
++
++ // Calculate new top and bail out if new space is exhausted. Use result
++ // to calculate the new top.
++ li(r0, Operand(-1));
++ addc(scratch2, result, obj_size_reg);
++ addze(r0, r0, LeaveOE, SetRC);
++ beq(gc_required, cr0);
++ cmpl(scratch2, ip);
++ bgt(gc_required);
++ StoreP(scratch2, MemOperand(topaddr));
++
++ // Tag object if requested.
++ if ((flags & TAG_OBJECT) != 0) {
++ addi(result, result, Operand(kHeapObjectTag));
++ }
++}
++
++
++void MacroAssembler::AllocateInNewSpace(Register object_size,
++ Register result,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required,
++ AllocationFlags flags) {
++ if (!FLAG_inline_new) {
++ if (emit_debug_code()) {
++ // Trash the registers to simulate an allocation failure.
++ li(result, Operand(0x7091));
++ li(scratch1, Operand(0x7191));
++ li(scratch2, Operand(0x7291));
++ }
++ b(gc_required);
++ return;
++ }
++
++ // Assert that the register arguments are different and that none of
++ // them are ip. ip is used explicitly in the code generated below.
++ ASSERT(!result.is(scratch1));
++ ASSERT(!result.is(scratch2));
++ ASSERT(!scratch1.is(scratch2));
++ ASSERT(!object_size.is(ip));
++ ASSERT(!result.is(ip));
++ ASSERT(!scratch1.is(ip));
++ ASSERT(!scratch2.is(ip));
++
++ // Check relative positions of allocation top and limit addresses.
++ // The values must be adjacent in memory to allow the use of LDM.
++ // Also, assert that the registers are numbered such that the values
++ // are loaded in the correct order.
++ ExternalReference new_space_allocation_top =
++ ExternalReference::new_space_allocation_top_address(isolate());
++ ExternalReference new_space_allocation_limit =
++ ExternalReference::new_space_allocation_limit_address(isolate());
++ intptr_t top =
++ reinterpret_cast<intptr_t>(new_space_allocation_top.address());
++ intptr_t limit =
++ reinterpret_cast<intptr_t>(new_space_allocation_limit.address());
++ ASSERT((limit - top) == kPointerSize);
++ ASSERT(result.code() < ip.code());
++
++ // Set up allocation top address.
++ Register topaddr = scratch1;
++ mov(topaddr, Operand(new_space_allocation_top));
++
++ // This code stores a temporary value in ip. This is OK, as the code below
++ // does not need ip for implicit literal generation.
++ if ((flags & RESULT_CONTAINS_TOP) == 0) {
++ // Load allocation top into result and allocation limit into ip.
++ LoadP(result, MemOperand(topaddr));
++ LoadP(ip, MemOperand(topaddr, kPointerSize));
++ } else {
++ if (emit_debug_code()) {
++ // Assert that result actually contains top on entry. ip is used
++ // immediately below so this use of ip does not cause difference with
++ // respect to register content between debug and release mode.
++ LoadP(ip, MemOperand(topaddr));
++ cmp(result, ip);
++ Check(eq, "Unexpected allocation top");
++ }
++ // Load allocation limit into ip. Result already contains allocation top.
++ LoadP(ip, MemOperand(topaddr, limit - top));
++ }
++
++ // Calculate new top and bail out if new space is exhausted. Use result
++ // to calculate the new top. Object size may be in words so a shift is
++ // required to get the number of bytes.
++ li(r0, Operand(-1));
++ if ((flags & SIZE_IN_WORDS) != 0) {
++ ShiftLeftImm(scratch2, object_size, Operand(kPointerSizeLog2));
++ addc(scratch2, result, scratch2);
++ } else {
++ addc(scratch2, result, object_size);
++ }
++ addze(r0, r0, LeaveOE, SetRC);
++ beq(gc_required, cr0);
++ cmpl(scratch2, ip);
++ bgt(gc_required);
++
++ // Update allocation top. result temporarily holds the new top.
++ if (emit_debug_code()) {
++ andi(r0, scratch2, Operand(kObjectAlignmentMask));
++ Check(eq, "Unaligned allocation in new space", cr0);
++ }
++ StoreP(scratch2, MemOperand(topaddr));
++
++ // Tag object if requested.
++ if ((flags & TAG_OBJECT) != 0) {
++ addi(result, result, Operand(kHeapObjectTag));
++ }
++}
++
++
++void MacroAssembler::UndoAllocationInNewSpace(Register object,
++ Register scratch) {
++ ExternalReference new_space_allocation_top =
++ ExternalReference::new_space_allocation_top_address(isolate());
++
++ // Make sure the object has no tag before resetting top.
++ mov(r0, Operand(~kHeapObjectTagMask));
++ and_(object, object, r0);
++ // was.. and_(object, object, Operand(~kHeapObjectTagMask));
++#ifdef DEBUG
++ // Check that the object un-allocated is below the current top.
++ mov(scratch, Operand(new_space_allocation_top));
++ LoadP(scratch, MemOperand(scratch));
++ cmp(object, scratch);
++ Check(lt, "Undo allocation of non allocated memory");
++#endif
++ // Write the address of the object to un-allocate as the current top.
++ mov(scratch, Operand(new_space_allocation_top));
++ StoreP(object, MemOperand(scratch));
++}
++
++
++void MacroAssembler::AllocateTwoByteString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* gc_required) {
++ // Calculate the number of bytes needed for the characters in the string while
++ // observing object alignment.
++ ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
++ slwi(scratch1, length, Operand(1)); // Length in bytes, not chars.
++ addi(scratch1, scratch1,
++ Operand(kObjectAlignmentMask + SeqTwoByteString::kHeaderSize));
++ mov(r0, Operand(~kObjectAlignmentMask));
++ and_(scratch1, scratch1, r0);
++
++ // Allocate two-byte string in new space.
++ AllocateInNewSpace(scratch1,
++ result,
++ scratch2,
++ scratch3,
++ gc_required,
++ TAG_OBJECT);
++
++ // Set the map, length and hash field.
++ InitializeNewString(result,
++ length,
++ Heap::kStringMapRootIndex,
++ scratch1,
++ scratch2);
++}
++
++
++void MacroAssembler::AllocateAsciiString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* gc_required) {
++ // Calculate the number of bytes needed for the characters in the string while
++ // observing object alignment.
++ ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0);
++ ASSERT(kCharSize == 1);
++ addi(scratch1, length,
++ Operand(kObjectAlignmentMask + SeqAsciiString::kHeaderSize));
++ li(r0, Operand(~kObjectAlignmentMask));
++ and_(scratch1, scratch1, r0);
++
++ // Allocate ASCII string in new space.
++ AllocateInNewSpace(scratch1,
++ result,
++ scratch2,
++ scratch3,
++ gc_required,
++ TAG_OBJECT);
++
++ // Set the map, length and hash field.
++ InitializeNewString(result,
++ length,
++ Heap::kAsciiStringMapRootIndex,
++ scratch1,
++ scratch2);
++}
++
++
++void MacroAssembler::AllocateTwoByteConsString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required) {
++ AllocateInNewSpace(ConsString::kSize,
++ result,
++ scratch1,
++ scratch2,
++ gc_required,
++ TAG_OBJECT);
++
++ InitializeNewString(result,
++ length,
++ Heap::kConsStringMapRootIndex,
++ scratch1,
++ scratch2);
++}
++
++
++void MacroAssembler::AllocateAsciiConsString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required) {
++ AllocateInNewSpace(ConsString::kSize,
++ result,
++ scratch1,
++ scratch2,
++ gc_required,
++ TAG_OBJECT);
++
++ InitializeNewString(result,
++ length,
++ Heap::kConsAsciiStringMapRootIndex,
++ scratch1,
++ scratch2);
++}
++
++
++void MacroAssembler::AllocateTwoByteSlicedString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required) {
++ AllocateInNewSpace(SlicedString::kSize,
++ result,
++ scratch1,
++ scratch2,
++ gc_required,
++ TAG_OBJECT);
++
++ InitializeNewString(result,
++ length,
++ Heap::kSlicedStringMapRootIndex,
++ scratch1,
++ scratch2);
++}
++
++
++void MacroAssembler::AllocateAsciiSlicedString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required) {
++ AllocateInNewSpace(SlicedString::kSize,
++ result,
++ scratch1,
++ scratch2,
++ gc_required,
++ TAG_OBJECT);
++
++ InitializeNewString(result,
++ length,
++ Heap::kSlicedAsciiStringMapRootIndex,
++ scratch1,
++ scratch2);
++}
++
++
++void MacroAssembler::CompareObjectType(Register object,
++ Register map,
++ Register type_reg,
++ InstanceType type) {
++ LoadP(map, FieldMemOperand(object, HeapObject::kMapOffset));
++ CompareInstanceType(map, type_reg, type);
++}
++
++
++void MacroAssembler::CompareInstanceType(Register map,
++ Register type_reg,
++ InstanceType type) {
++ lbz(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
++ cmpi(type_reg, Operand(type));
++}
++
++
++void MacroAssembler::CompareRoot(Register obj,
++ Heap::RootListIndex index) {
++ ASSERT(!obj.is(ip));
++ LoadRoot(ip, index);
++ cmp(obj, ip);
++}
++
++
++void MacroAssembler::CheckFastElements(Register map,
++ Register scratch,
++ Label* fail) {
++ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
++ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
++ STATIC_ASSERT(FAST_ELEMENTS == 2);
++ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
++ lbz(scratch, FieldMemOperand(map, Map::kBitField2Offset));
++ STATIC_ASSERT(Map::kMaximumBitField2FastHoleyElementValue < 0x8000);
++ cmpli(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue));
++ bgt(fail);
++}
++
++
++void MacroAssembler::CheckFastObjectElements(Register map,
++ Register scratch,
++ Label* fail) {
++ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
++ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
++ STATIC_ASSERT(FAST_ELEMENTS == 2);
++ STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3);
++ lbz(scratch, FieldMemOperand(map, Map::kBitField2Offset));
++ cmpli(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue));
++ ble(fail);
++ cmpli(scratch, Operand(Map::kMaximumBitField2FastHoleyElementValue));
++ bgt(fail);
++}
++
++
++void MacroAssembler::CheckFastSmiElements(Register map,
++ Register scratch,
++ Label* fail) {
++ STATIC_ASSERT(FAST_SMI_ELEMENTS == 0);
++ STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1);
++ lbz(scratch, FieldMemOperand(map, Map::kBitField2Offset));
++ cmpli(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue));
++ bgt(fail);
++}
++
++
++void MacroAssembler::StoreNumberToDoubleElements(Register value_reg,
++ Register key_reg,
++ Register receiver_reg,
++ Register elements_reg,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Label* fail) {
++ Label smi_value, maybe_nan, have_double_value, is_nan, done;
++#if V8_TARGET_ARCH_PPC64
++ Register double_reg = scratch2;
++#else
++ Register mantissa_reg = scratch2;
++ Register exponent_reg = scratch3;
++#endif
++
++ // Handle smi values specially.
++ JumpIfSmi(value_reg, &smi_value);
++
++ // Ensure that the object is a heap number
++ CheckMap(value_reg,
++ scratch1,
++ isolate()->factory()->heap_number_map(),
++ fail,
++ DONT_DO_SMI_CHECK);
++
++ // Check for nan: all NaN values have a value greater (signed) than 0x7ff00000
++ // in the exponent.
++#if V8_TARGET_ARCH_PPC64
++ mov(scratch1, Operand(kLastNonNaNInt64));
++ addi(scratch3, value_reg, Operand(-kHeapObjectTag));
++ ld(double_reg, MemOperand(scratch3, HeapNumber::kValueOffset));
++ cmp(double_reg, scratch1);
++#else
++ mov(scratch1, Operand(kNaNOrInfinityLowerBoundUpper32));
++ lwz(exponent_reg, FieldMemOperand(value_reg, HeapNumber::kExponentOffset));
++ cmp(exponent_reg, scratch1);
++#endif
++ bge(&maybe_nan);
++
++#if !V8_TARGET_ARCH_PPC64
++ lwz(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset));
++#endif
++
++ bind(&have_double_value);
++ SmiToDoubleArrayOffset(scratch1, key_reg);
++ add(scratch1, elements_reg, scratch1);
++#if V8_TARGET_ARCH_PPC64
++ addi(scratch1, scratch1, Operand(-kHeapObjectTag));
++ std(double_reg, MemOperand(scratch1, FixedDoubleArray::kHeaderSize));
++#else
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ stw(mantissa_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize));
++ uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
++ stw(exponent_reg, FieldMemOperand(scratch1, offset));
++#elif __BYTE_ORDER == __BIG_ENDIAN
++ stw(exponent_reg, FieldMemOperand(scratch1, FixedDoubleArray::kHeaderSize));
++ uint32_t offset = FixedDoubleArray::kHeaderSize + sizeof(kHoleNanLower32);
++ stw(mantissa_reg, FieldMemOperand(scratch1, offset));
++#endif
++#endif
++ b(&done);
++
++ bind(&maybe_nan);
++ // Could be NaN or Infinity. If fraction is not zero, it's NaN, otherwise
++ // it's an Infinity, and the non-NaN code path applies.
++ bgt(&is_nan);
++#if V8_TARGET_ARCH_PPC64
++ clrldi(r0, double_reg, Operand(32), SetRC);
++ beq(&have_double_value, cr0);
++#else
++ lwz(mantissa_reg, FieldMemOperand(value_reg, HeapNumber::kMantissaOffset));
++ cmpi(mantissa_reg, Operand::Zero());
++ beq(&have_double_value);
++#endif
++ bind(&is_nan);
++ // Load canonical NaN for storing into the double array.
++ uint64_t nan_int64 = BitCast<uint64_t>(
++ FixedDoubleArray::canonical_not_the_hole_nan_as_double());
++#if V8_TARGET_ARCH_PPC64
++ mov(double_reg, Operand(nan_int64));
++#else
++ mov(mantissa_reg, Operand(static_cast<intptr_t>(nan_int64)));
++ mov(exponent_reg, Operand(static_cast<intptr_t>(nan_int64 >> 32)));
++#endif
++ b(&have_double_value);
++
++ bind(&smi_value);
++ addi(scratch1, elements_reg,
++ Operand(FixedDoubleArray::kHeaderSize - kHeapObjectTag));
++ SmiToDoubleArrayOffset(scratch4, key_reg);
++ add(scratch1, scratch1, scratch4);
++ // scratch1 is now effective address of the double element
++
++ Register untagged_value = elements_reg;
++ SmiUntag(untagged_value, value_reg);
++ FloatingPointHelper::ConvertIntToDouble(this,
++ untagged_value,
++ d0);
++ stfd(d0, MemOperand(scratch1, 0));
++
++ bind(&done);
++}
++
++
++void MacroAssembler::AddAndCheckForOverflow(Register dst,
++ Register left,
++ Register right,
++ Register overflow_dst,
++ Register scratch) {
++ ASSERT(!dst.is(overflow_dst));
++ ASSERT(!dst.is(scratch));
++ ASSERT(!overflow_dst.is(scratch));
++ ASSERT(!overflow_dst.is(left));
++ ASSERT(!overflow_dst.is(right));
++
++ // C = A+B; C overflows if A/B have same sign and C has diff sign than A
++ if (dst.is(left)) {
++ mr(scratch, left); // Preserve left.
++ add(dst, left, right); // Left is overwritten.
++ xor_(scratch, dst, scratch); // Original left.
++ xor_(overflow_dst, dst, right);
++ and_(overflow_dst, overflow_dst, scratch, SetRC);
++ } else if (dst.is(right)) {
++ mr(scratch, right); // Preserve right.
++ add(dst, left, right); // Right is overwritten.
++ xor_(scratch, dst, scratch); // Original right.
++ xor_(overflow_dst, dst, left);
++ and_(overflow_dst, overflow_dst, scratch, SetRC);
++ } else {
++ add(dst, left, right);
++ xor_(overflow_dst, dst, left);
++ xor_(scratch, dst, right);
++ and_(overflow_dst, scratch, overflow_dst, SetRC);
++ }
++}
++
++void MacroAssembler::SubAndCheckForOverflow(Register dst,
++ Register left,
++ Register right,
++ Register overflow_dst,
++ Register scratch) {
++ ASSERT(!dst.is(overflow_dst));
++ ASSERT(!dst.is(scratch));
++ ASSERT(!overflow_dst.is(scratch));
++ ASSERT(!overflow_dst.is(left));
++ ASSERT(!overflow_dst.is(right));
++
++ // C = A-B; C overflows if A/B have diff signs and C has diff sign than A
++ if (dst.is(left)) {
++ mr(scratch, left); // Preserve left.
++ sub(dst, left, right); // Left is overwritten.
++ xor_(overflow_dst, dst, scratch);
++ xor_(scratch, scratch, right);
++ and_(overflow_dst, overflow_dst, scratch, SetRC);
++ } else if (dst.is(right)) {
++ mr(scratch, right); // Preserve right.
++ sub(dst, left, right); // Right is overwritten.
++ xor_(overflow_dst, dst, left);
++ xor_(scratch, left, scratch);
++ and_(overflow_dst, overflow_dst, scratch, SetRC);
++ } else {
++ sub(dst, left, right);
++ xor_(overflow_dst, dst, left);
++ xor_(scratch, left, right);
++ and_(overflow_dst, scratch, overflow_dst, SetRC);
++ }
++}
++
++
++void MacroAssembler::CompareMap(Register obj,
++ Register scratch,
++ Handle<Map> map,
++ Label* early_success,
++ CompareMapMode mode) {
++ LoadP(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
++ CompareMap(scratch, map, early_success, mode);
++}
++
++
++void MacroAssembler::CompareMap(Register obj_map,
++ Handle<Map> map,
++ Label* early_success,
++ CompareMapMode mode) {
++ mov(r0, Operand(map));
++ cmp(obj_map, r0);
++ if (mode == ALLOW_ELEMENT_TRANSITION_MAPS) {
++ ElementsKind kind = map->elements_kind();
++ if (IsFastElementsKind(kind)) {
++ bool packed = IsFastPackedElementsKind(kind);
++ Map* current_map = *map;
++ while (CanTransitionToMoreGeneralFastElementsKind(kind, packed)) {
++ kind = GetNextMoreGeneralFastElementsKind(kind, packed);
++ current_map = current_map->LookupElementsTransitionMap(kind);
++ if (!current_map) break;
++ beq(early_success);
++ mov(r0, Operand(Handle<Map>(current_map)));
++ cmp(obj_map, r0);
++ }
++ }
++ }
++}
++
++
++void MacroAssembler::CheckMap(Register obj,
++ Register scratch,
++ Handle<Map> map,
++ Label* fail,
++ SmiCheckType smi_check_type,
++ CompareMapMode mode) {
++ if (smi_check_type == DO_SMI_CHECK) {
++ JumpIfSmi(obj, fail);
++ }
++
++ Label success;
++ CompareMap(obj, scratch, map, &success, mode);
++ bne(fail);
++ bind(&success);
++}
++
++
++void MacroAssembler::CheckMap(Register obj,
++ Register scratch,
++ Heap::RootListIndex index,
++ Label* fail,
++ SmiCheckType smi_check_type) {
++ if (smi_check_type == DO_SMI_CHECK) {
++ JumpIfSmi(obj, fail);
++ }
++ LoadP(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
++ LoadRoot(ip, index);
++ cmp(scratch, ip);
++ bne(fail);
++}
++
++
++void MacroAssembler::DispatchMap(Register obj,
++ Register scratch,
++ Handle<Map> map,
++ Handle<Code> success,
++ SmiCheckType smi_check_type) {
++ Label fail;
++ if (smi_check_type == DO_SMI_CHECK) {
++ JumpIfSmi(obj, &fail);
++ }
++ LoadP(scratch, FieldMemOperand(obj, HeapObject::kMapOffset));
++ mov(ip, Operand(map));
++ cmp(scratch, ip);
++ bne(&fail);
++ Jump(success, RelocInfo::CODE_TARGET, al);
++ bind(&fail);
++}
++
++
++void MacroAssembler::TryGetFunctionPrototype(Register function,
++ Register result,
++ Register scratch,
++ Label* miss,
++ bool miss_on_bound_function) {
++ // Check that the receiver isn't a smi.
++ JumpIfSmi(function, miss);
++
++ // Check that the function really is a function. Load map into result reg.
++ CompareObjectType(function, result, scratch, JS_FUNCTION_TYPE);
++ bne(miss);
++
++ if (miss_on_bound_function) {
++ LoadP(scratch,
++ FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
++ lwz(scratch,
++ FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset));
++ TestBit(scratch,
++#if V8_TARGET_ARCH_PPC64
++ SharedFunctionInfo::kBoundFunction,
++#else
++ SharedFunctionInfo::kBoundFunction + kSmiTagSize,
++#endif
++ r0);
++ bne(miss, cr0);
++ }
++
++ // Make sure that the function has an instance prototype.
++ Label non_instance;
++ lbz(scratch, FieldMemOperand(result, Map::kBitFieldOffset));
++ andi(r0, scratch, Operand(1 << Map::kHasNonInstancePrototype));
++ bne(&non_instance, cr0);
++
++ // Get the prototype or initial map from the function.
++ LoadP(result,
++ FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
++
++ // If the prototype or initial map is the hole, don't return it and
++ // simply miss the cache instead. This will allow us to allocate a
++ // prototype object on-demand in the runtime system.
++ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ cmp(result, ip);
++ beq(miss);
++
++ // If the function does not have an initial map, we're done.
++ Label done;
++ CompareObjectType(result, scratch, scratch, MAP_TYPE);
++ bne(&done);
++
++ // Get the prototype from the initial map.
++ LoadP(result, FieldMemOperand(result, Map::kPrototypeOffset));
++ b(&done);
++
++ // Non-instance prototype: Fetch prototype from constructor field
++ // in initial map.
++ bind(&non_instance);
++ LoadP(result, FieldMemOperand(result, Map::kConstructorOffset));
++
++ // All done.
++ bind(&done);
++}
++
++
++void MacroAssembler::CallStub(CodeStub* stub, Condition cond) {
++ ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs.
++ Call(stub->GetCode(), RelocInfo::CODE_TARGET, TypeFeedbackId::None(), cond);
++}
++
++
++void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) {
++ ASSERT(allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe());
++ Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond);
++}
++
++
++static int AddressOffset(ExternalReference ref0, ExternalReference ref1) {
++ return ref0.address() - ref1.address();
++}
++
++
++void MacroAssembler::CallApiFunctionAndReturn(ExternalReference function,
++ int stack_space) {
++ ExternalReference next_address =
++ ExternalReference::handle_scope_next_address();
++ const int kNextOffset = 0;
++ const int kLimitOffset = AddressOffset(
++ ExternalReference::handle_scope_limit_address(),
++ next_address);
++ const int kLevelOffset = AddressOffset(
++ ExternalReference::handle_scope_level_address(),
++ next_address);
++
++ // Allocate HandleScope in callee-save registers.
++ // r26 - next_address
++ // r27 - next_address->kNextOffset
++ // r28 - next_address->kLimitOffset
++ // r29 - next_address->kLevelOffset
++ mov(r26, Operand(next_address));
++ LoadP(r27, MemOperand(r26, kNextOffset));
++ LoadP(r28, MemOperand(r26, kLimitOffset));
++ lwz(r29, MemOperand(r26, kLevelOffset));
++ addi(r29, r29, Operand(1));
++ stw(r29, MemOperand(r26, kLevelOffset));
++
++#if !ABI_RETURNS_HANDLES_IN_REGS
++ // PPC LINUX ABI
++ // The return value is pointer-sized non-scalar value.
++ // Space has already been allocated on the stack which will pass as an
++ // implicity first argument.
++ addi(r3, sp, Operand((kStackFrameExtraParamSlot + 1) * kPointerSize));
++#endif
++
++ // Native call returns to the DirectCEntry stub which redirects to the
++ // return address pushed on stack (could have moved after GC).
++ // DirectCEntry stub itself is generated early and never moves.
++ DirectCEntryStub stub;
++ stub.GenerateCall(this, function);
++
++#if !ABI_RETURNS_HANDLES_IN_REGS
++ // Retrieve return value from stack buffer
++ LoadP(r3, MemOperand(r3));
++#endif
++
++ Label promote_scheduled_exception;
++ Label delete_allocated_handles;
++ Label leave_exit_frame;
++ Label skip1, skip2;
++
++ // If result is non-zero, dereference to get the result value
++ // otherwise set it to undefined.
++ cmpi(r3, Operand::Zero());
++ bne(&skip1);
++ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ b(&skip2);
++ bind(&skip1);
++ LoadP(r3, MemOperand(r3));
++ bind(&skip2);
++
++ // No more valid handles (the result handle was the last one). Restore
++ // previous handle scope.
++ StoreP(r27, MemOperand(r26, kNextOffset));
++ if (emit_debug_code()) {
++ lwz(r4, MemOperand(r26, kLevelOffset));
++ cmp(r4, r29);
++ Check(eq, "Unexpected level after return from api call");
++ }
++ subi(r29, r29, Operand(1));
++ stw(r29, MemOperand(r26, kLevelOffset));
++ LoadP(ip, MemOperand(r26, kLimitOffset));
++ cmp(r28, ip);
++ bne(&delete_allocated_handles);
++
++ // Check if the function scheduled an exception.
++ bind(&leave_exit_frame);
++ LoadRoot(r27, Heap::kTheHoleValueRootIndex);
++ mov(ip, Operand(ExternalReference::scheduled_exception_address(isolate())));
++ LoadP(r28, MemOperand(ip));
++ cmp(r27, r28);
++ bne(&promote_scheduled_exception);
++
++ // LeaveExitFrame expects unwind space to be in a register.
++ mov(r27, Operand(stack_space));
++ LeaveExitFrame(false, r27);
++ blr();
++
++ bind(&promote_scheduled_exception);
++ TailCallExternalReference(
++ ExternalReference(Runtime::kPromoteScheduledException, isolate()),
++ 0,
++ 1);
++
++ // HandleScope limit has changed. Delete allocated extensions.
++ bind(&delete_allocated_handles);
++ StoreP(r28, MemOperand(r26, kLimitOffset));
++ mr(r27, r3);
++ PrepareCallCFunction(1, r28);
++ mov(r3, Operand(ExternalReference::isolate_address()));
++ CallCFunction(
++ ExternalReference::delete_handle_scope_extensions(isolate()), 1);
++ mr(r3, r27);
++ b(&leave_exit_frame);
++}
++
++
++bool MacroAssembler::AllowThisStubCall(CodeStub* stub) {
++ if (!has_frame_ && stub->SometimesSetsUpAFrame()) return false;
++ return allow_stub_calls_ || stub->CompilingCallsToThisStubIsGCSafe();
++}
++
++
++void MacroAssembler::IllegalOperation(int num_arguments) {
++ if (num_arguments > 0) {
++ Add(sp, sp, num_arguments * kPointerSize, r0);
++ }
++ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
++}
++
++
++void MacroAssembler::IndexFromHash(Register hash, Register index) {
++ // If the hash field contains an array index pick it out. The assert checks
++ // that the constants for the maximum number of digits for an array index
++ // cached in the hash field and the number of bits reserved for it does not
++ // conflict.
++ ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
++ (1 << String::kArrayIndexValueBits));
++ // We want the smi-tagged index in key. kArrayIndexValueMask has zeros in
++ // the low kHashShift bits.
++ STATIC_ASSERT(String::kHashShift == 2);
++ STATIC_ASSERT(String::kArrayIndexValueBits == 24);
++ // index = SmiTag((hash >> 2) & 0x00FFFFFF);
++#if V8_TARGET_ARCH_PPC64
++ ExtractBitRange(index, hash, 25, 2);
++ SmiTag(index);
++#else
++ STATIC_ASSERT(kSmiShift == 1);
++ // 32-bit can do this in one instruction:
++ // index = (hash & 0x03FFFFFC) >> 1;
++ rlwinm(index, hash, 31, 7, 30);
++#endif
++}
++
++void MacroAssembler::SmiToDoubleFPRegister(Register smi,
++ DwVfpRegister value,
++ Register scratch1) {
++ SmiUntag(scratch1, smi);
++ FloatingPointHelper::ConvertIntToDouble(this, scratch1, value);
++}
++
++
++// Tries to get a signed int32 out of a double precision floating point heap
++// number. Rounds towards 0. Branch to 'not_int32' if the double is out of the
++// 32bits signed integer range.
++void MacroAssembler::ConvertToInt32(Register source,
++ Register dest,
++ Register scratch,
++ Register scratch2,
++ DwVfpRegister double_scratch,
++ Label *not_int32) {
++ // Retrieve double from heap
++ lfd(double_scratch, FieldMemOperand(source, HeapNumber::kValueOffset));
++
++ // Convert
++ fctidz(double_scratch, double_scratch);
++
++ addi(sp, sp, Operand(-kDoubleSize));
++ stfd(double_scratch, MemOperand(sp, 0));
++#if V8_TARGET_ARCH_PPC64
++ ld(dest, MemOperand(sp, 0));
++#else
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ lwz(scratch, MemOperand(sp, 4));
++ lwz(dest, MemOperand(sp, 0));
++#else
++ lwz(scratch, MemOperand(sp, 0));
++ lwz(dest, MemOperand(sp, 4));
++#endif
++#endif
++ addi(sp, sp, Operand(kDoubleSize));
++
++ // The result is not a 32-bit integer when the high 33 bits of the
++ // result are not identical.
++#if V8_TARGET_ARCH_PPC64
++ TestIfInt32(dest, scratch, scratch2);
++#else
++ TestIfInt32(scratch, dest, scratch2);
++#endif
++ bne(not_int32);
++}
++
++
++void MacroAssembler::EmitVFPTruncate(VFPRoundingMode rounding_mode,
++ Register result,
++ DwVfpRegister double_input,
++ Register scratch,
++ DwVfpRegister double_scratch,
++ CheckForInexactConversion check_inexact) {
++ // Convert
++ if (rounding_mode == kRoundToZero) {
++ fctidz(double_scratch, double_input);
++ } else {
++ SetRoundingMode(rounding_mode);
++ fctid(double_scratch, double_input);
++ ResetRoundingMode();
++ }
++
++ addi(sp, sp, Operand(-kDoubleSize));
++ stfd(double_scratch, MemOperand(sp, 0));
++#if V8_TARGET_ARCH_PPC64
++ ld(result, MemOperand(sp, 0));
++#else
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ lwz(scratch, MemOperand(sp, 4));
++ lwz(result, MemOperand(sp, 0));
++#else
++ lwz(scratch, MemOperand(sp, 0));
++ lwz(result, MemOperand(sp, 4));
++#endif
++#endif
++ addi(sp, sp, Operand(kDoubleSize));
++
++ // The result is a 32-bit integer when the high 33 bits of the
++ // result are identical.
++#if V8_TARGET_ARCH_PPC64
++ TestIfInt32(result, scratch, r0);
++#else
++ TestIfInt32(scratch, result, r0);
++#endif
++
++ if (check_inexact == kCheckForInexactConversion) {
++ Label done;
++ bne(&done);
++
++ // convert back and compare
++ fcfid(double_scratch, double_scratch);
++ fcmpu(double_scratch, double_input);
++ bind(&done);
++ }
++}
++
++
++void MacroAssembler::EmitOutOfInt32RangeTruncate(Register result,
++ Register input_high,
++ Register input_low,
++ Register scratch) {
++ Label done, high_shift_needed, pos_shift, neg_shift, shift_done;
++
++ li(result, Operand::Zero());
++
++ // check for NaN or +/-Infinity
++ // by extracting exponent (mask: 0x7ff00000)
++ STATIC_ASSERT(HeapNumber::kExponentMask == 0x7ff00000u);
++ ExtractBitMask(scratch, input_high, HeapNumber::kExponentMask);
++ cmpli(scratch, Operand(0x7ff));
++ beq(&done);
++
++ // Express exponent as delta to (number of mantissa bits + 31).
++ addi(scratch,
++ scratch,
++ Operand(-(HeapNumber::kExponentBias + HeapNumber::kMantissaBits + 31)));
++
++ // If the delta is strictly positive, all bits would be shifted away,
++ // which means that we can return 0.
++ cmpi(scratch, Operand::Zero());
++ bgt(&done);
++
++ const int kShiftBase = HeapNumber::kNonMantissaBitsInTopWord - 1;
++ // Calculate shift.
++ addi(scratch, scratch, Operand(kShiftBase + HeapNumber::kMantissaBits));
++
++ // Save the sign.
++ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
++ Register sign = result;
++ result = no_reg;
++ ExtractSignBit32(sign, input_high);
++
++ // Shifts >= 32 bits should result in zero.
++ // slw extracts only the 6 most significant bits of the shift value.
++ cmpi(scratch, Operand(32));
++ blt(&high_shift_needed);
++ li(input_high, Operand::Zero());
++ subfic(scratch, scratch, Operand(32));
++ b(&neg_shift);
++
++ // Set the implicit 1 before the mantissa part in input_high.
++ bind(&high_shift_needed);
++ STATIC_ASSERT(HeapNumber::kMantissaBitsInTopWord >= 16);
++ oris(input_high,
++ input_high,
++ Operand(1 << ((HeapNumber::kMantissaBitsInTopWord) - 16)));
++ // Shift the mantissa bits to the correct position.
++ // We don't need to clear non-mantissa bits as they will be shifted away.
++ // If they weren't, it would mean that the answer is in the 32bit range.
++ slw(input_high, input_high, scratch);
++ subfic(scratch, scratch, Operand(32));
++ b(&pos_shift);
++
++ // Replace the shifted bits with bits from the lower mantissa word.
++
++ bind(&neg_shift);
++ neg(scratch, scratch);
++ slw(input_low, input_low, scratch);
++ b(&shift_done);
++
++ bind(&pos_shift);
++ srw(input_low, input_low, scratch);
++
++ bind(&shift_done);
++ orx(input_high, input_high, input_low);
++
++ // Restore sign if necessary.
++ cmpi(sign, Operand::Zero());
++ result = sign;
++ sign = no_reg;
++ mr(result, input_high);
++ beq(&done);
++ neg(result, result);
++
++ bind(&done);
++}
++
++
++void MacroAssembler::EmitECMATruncate(Register result,
++ DwVfpRegister double_input,
++ DwVfpRegister double_scratch,
++ Register scratch,
++ Register input_high,
++ Register input_low) {
++ ASSERT(!input_high.is(result));
++ ASSERT(!input_low.is(result));
++ ASSERT(!input_low.is(input_high));
++ ASSERT(!scratch.is(result) &&
++ !scratch.is(input_high) &&
++ !scratch.is(input_low));
++ ASSERT(!double_scratch.is(double_input));
++
++ Label done;
++
++ fctidz(double_scratch, double_input);
++
++ // reserve a slot on the stack
++ stfdu(double_scratch, MemOperand(sp, -8));
++#if V8_TARGET_ARCH_PPC64
++ ld(result, MemOperand(sp, 0));
++#else
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ lwz(scratch, MemOperand(sp, 4));
++ lwz(result, MemOperand(sp));
++#else
++ lwz(scratch, MemOperand(sp, 0));
++ lwz(result, MemOperand(sp, 4));
++#endif
++#endif
++
++ // The result is a 32-bit integer when the high 33 bits of the
++ // result are identical.
++#if V8_TARGET_ARCH_PPC64
++ TestIfInt32(result, scratch, r0);
++#else
++ TestIfInt32(scratch, result, r0);
++#endif
++ beq(&done);
++
++ // Load the double value and perform a manual truncation.
++ stfd(double_input, MemOperand(sp));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ lwz(input_low, MemOperand(sp));
++ lwz(input_high, MemOperand(sp, 4));
++#else
++ lwz(input_high, MemOperand(sp));
++ lwz(input_low, MemOperand(sp, 4));
++#endif
++ EmitOutOfInt32RangeTruncate(result,
++ input_high,
++ input_low,
++ scratch);
++
++ bind(&done);
++
++ // restore the stack
++ addi(sp, sp, Operand(8));
++}
++
++
++void MacroAssembler::GetLeastBitsFromSmi(Register dst,
++ Register src,
++ int num_least_bits) {
++#if V8_TARGET_ARCH_PPC64
++ rldicl(dst, src, kBitsPerPointer - kSmiShift,
++ kBitsPerPointer - num_least_bits);
++#else
++ rlwinm(dst, src, kBitsPerPointer - kSmiShift,
++ kBitsPerPointer - num_least_bits, 31);
++#endif
++}
++
++
++void MacroAssembler::GetLeastBitsFromInt32(Register dst,
++ Register src,
++ int num_least_bits) {
++ rlwinm(dst, src, 0, 32 - num_least_bits, 31);
++}
++
++
++void MacroAssembler::CallRuntime(const Runtime::Function* f,
++ int num_arguments) {
++ // All parameters are on the stack. r3 has the return value after call.
++
++ // If the expected number of arguments of the runtime function is
++ // constant, we check that the actual number of arguments match the
++ // expectation.
++ if (f->nargs >= 0 && f->nargs != num_arguments) {
++ IllegalOperation(num_arguments);
++ return;
++ }
++
++ // TODO(1236192): Most runtime routines don't need the number of
++ // arguments passed in because it is constant. At some point we
++ // should remove this need and make the runtime routine entry code
++ // smarter.
++ mov(r3, Operand(num_arguments));
++ mov(r4, Operand(ExternalReference(f, isolate())));
++#if V8_TARGET_ARCH_PPC64
++ CEntryStub stub(f->result_size);
++#else
++ CEntryStub stub(1);
++#endif
++ CallStub(&stub);
++}
++
++
++void MacroAssembler::CallRuntime(Runtime::FunctionId fid, int num_arguments) {
++ CallRuntime(Runtime::FunctionForId(fid), num_arguments);
++}
++
++
++void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) {
++ const Runtime::Function* function = Runtime::FunctionForId(id);
++ li(r3, Operand(function->nargs));
++ mov(r4, Operand(ExternalReference(function, isolate())));
++ CEntryStub stub(1, kSaveFPRegs);
++ CallStub(&stub);
++}
++
++
++void MacroAssembler::CallExternalReference(const ExternalReference& ext,
++ int num_arguments) {
++ mov(r3, Operand(num_arguments));
++ mov(r4, Operand(ext));
++
++ CEntryStub stub(1);
++ CallStub(&stub);
++}
++
++
++void MacroAssembler::TailCallExternalReference(const ExternalReference& ext,
++ int num_arguments,
++ int result_size) {
++ // TODO(1236192): Most runtime routines don't need the number of
++ // arguments passed in because it is constant. At some point we
++ // should remove this need and make the runtime routine entry code
++ // smarter.
++ mov(r3, Operand(num_arguments));
++ JumpToExternalReference(ext);
++}
++
++
++void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid,
++ int num_arguments,
++ int result_size) {
++ TailCallExternalReference(ExternalReference(fid, isolate()),
++ num_arguments,
++ result_size);
++}
++
++
++void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) {
++ mov(r4, Operand(builtin));
++ CEntryStub stub(1);
++ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
++}
++
++
++void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper) {
++ // You can't call a builtin without a valid frame.
++ ASSERT(flag == JUMP_FUNCTION || has_frame());
++
++ GetBuiltinEntry(r5, id);
++ if (flag == CALL_FUNCTION) {
++ call_wrapper.BeforeCall(CallSize(r2));
++ SetCallKind(r8, CALL_AS_METHOD);
++ Call(r5);
++ call_wrapper.AfterCall();
++ } else {
++ ASSERT(flag == JUMP_FUNCTION);
++ SetCallKind(r8, CALL_AS_METHOD);
++ Jump(r5);
++ }
++}
++
++
++void MacroAssembler::GetBuiltinFunction(Register target,
++ Builtins::JavaScript id) {
++ // Load the builtins object into target register.
++ LoadP(target,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ LoadP(target, FieldMemOperand(target, GlobalObject::kBuiltinsOffset));
++ // Load the JavaScript builtin function from the builtins object.
++ LoadP(target,
++ FieldMemOperand(target,
++ JSBuiltinsObject::OffsetOfFunctionWithId(id)), r0);
++}
++
++
++void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
++ ASSERT(!target.is(r4));
++ GetBuiltinFunction(r4, id);
++ // Load the code entry point from the builtins object.
++ LoadP(target, FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
++}
++
++
++void MacroAssembler::SetCounter(StatsCounter* counter, int value,
++ Register scratch1, Register scratch2) {
++ if (FLAG_native_code_counters && counter->Enabled()) {
++ mov(scratch1, Operand(value));
++ mov(scratch2, Operand(ExternalReference(counter)));
++ stw(scratch1, MemOperand(scratch2));
++ }
++}
++
++
++void MacroAssembler::IncrementCounter(StatsCounter* counter, int value,
++ Register scratch1, Register scratch2) {
++ ASSERT(value > 0);
++ if (FLAG_native_code_counters && counter->Enabled()) {
++ mov(scratch2, Operand(ExternalReference(counter)));
++ lwz(scratch1, MemOperand(scratch2));
++ addi(scratch1, scratch1, Operand(value));
++ stw(scratch1, MemOperand(scratch2));
++ }
++}
++
++
++void MacroAssembler::DecrementCounter(StatsCounter* counter, int value,
++ Register scratch1, Register scratch2) {
++ ASSERT(value > 0);
++ if (FLAG_native_code_counters && counter->Enabled()) {
++ mov(scratch2, Operand(ExternalReference(counter)));
++ lwz(scratch1, MemOperand(scratch2));
++ subi(scratch1, scratch1, Operand(value));
++ stw(scratch1, MemOperand(scratch2));
++ }
++}
++
++
++void MacroAssembler::Assert(Condition cond, const char* msg, CRegister cr) {
++ if (emit_debug_code())
++ Check(cond, msg, cr);
++}
++
++
++void MacroAssembler::AssertRegisterIsRoot(Register reg,
++ Heap::RootListIndex index) {
++ if (emit_debug_code()) {
++ LoadRoot(ip, index);
++ cmp(reg, ip);
++ Check(eq, "Register did not match expected root");
++ }
++}
++
++
++void MacroAssembler::AssertFastElements(Register elements) {
++ if (emit_debug_code()) {
++ ASSERT(!elements.is(ip));
++ Label ok;
++ push(elements);
++ LoadP(elements, FieldMemOperand(elements, HeapObject::kMapOffset));
++ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
++ cmp(elements, ip);
++ beq(&ok);
++ LoadRoot(ip, Heap::kFixedDoubleArrayMapRootIndex);
++ cmp(elements, ip);
++ beq(&ok);
++ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
++ cmp(elements, ip);
++ beq(&ok);
++ Abort("JSObject with fast elements map has slow elements");
++ bind(&ok);
++ pop(elements);
++ }
++}
++
++
++void MacroAssembler::Check(Condition cond, const char* msg, CRegister cr) {
++ Label L;
++ b(cond, &L, cr);
++ Abort(msg);
++ // will not return here
++ bind(&L);
++}
++
++
++void MacroAssembler::Abort(const char* msg) {
++ Label abort_start;
++ bind(&abort_start);
++ // We want to pass the msg string like a smi to avoid GC
++ // problems, however msg is not guaranteed to be aligned
++ // properly. Instead, we pass an aligned pointer that is
++ // a proper v8 smi, but also pass the alignment difference
++ // from the real pointer as a smi.
++ intptr_t p1 = reinterpret_cast<intptr_t>(msg);
++ intptr_t p0 = (p1 & ~kSmiTagMask) + kSmiTag;
++ ASSERT(reinterpret_cast<Object*>(p0)->IsSmi());
++#ifdef DEBUG
++ if (msg != NULL) {
++ RecordComment("Abort message: ");
++ RecordComment(msg);
++ }
++#endif
++
++ mov(r0, Operand(p0));
++ push(r0);
++ LoadSmiLiteral(r0, Smi::FromInt(p1 - p0));
++ push(r0);
++ // Disable stub call restrictions to always allow calls to abort.
++ if (!has_frame_) {
++ // We don't actually want to generate a pile of code for this, so just
++ // claim there is a stack frame, without generating one.
++ FrameScope scope(this, StackFrame::NONE);
++ CallRuntime(Runtime::kAbort, 2);
++ } else {
++ CallRuntime(Runtime::kAbort, 2);
++ }
++ // will not return here
++}
++
++
++void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
++ if (context_chain_length > 0) {
++ // Move up the chain of contexts to the context containing the slot.
++ LoadP(dst, MemOperand(cp, Context::SlotOffset(Context::PREVIOUS_INDEX)));
++ for (int i = 1; i < context_chain_length; i++) {
++ LoadP(dst, MemOperand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX)));
++ }
++ } else {
++ // Slot is in the current function context. Move it into the
++ // destination register in case we store into it (the write barrier
++ // cannot be allowed to destroy the context in esi).
++ mr(dst, cp);
++ }
++}
++
++
++void MacroAssembler::LoadTransitionedArrayMapConditional(
++ ElementsKind expected_kind,
++ ElementsKind transitioned_kind,
++ Register map_in_out,
++ Register scratch,
++ Label* no_map_match) {
++ // Load the global or builtins object from the current context.
++ LoadP(scratch,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ LoadP(scratch, FieldMemOperand(scratch, GlobalObject::kNativeContextOffset));
++
++ // Check that the function's map is the same as the expected cached map.
++ LoadP(scratch,
++ MemOperand(scratch,
++ Context::SlotOffset(Context::JS_ARRAY_MAPS_INDEX)));
++ size_t offset = expected_kind * kPointerSize +
++ FixedArrayBase::kHeaderSize;
++ LoadP(ip, FieldMemOperand(scratch, offset));
++ cmp(map_in_out, ip);
++ bne(no_map_match);
++
++ // Use the transitioned cached map.
++ offset = transitioned_kind * kPointerSize +
++ FixedArrayBase::kHeaderSize;
++ LoadP(map_in_out, FieldMemOperand(scratch, offset));
++}
++
++
++void MacroAssembler::LoadInitialArrayMap(
++ Register function_in, Register scratch,
++ Register map_out, bool can_have_holes) {
++ ASSERT(!function_in.is(map_out));
++ Label done;
++ LoadP(map_out, FieldMemOperand(function_in,
++ JSFunction::kPrototypeOrInitialMapOffset));
++ if (!FLAG_smi_only_arrays) {
++ ElementsKind kind = can_have_holes ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
++ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
++ kind,
++ map_out,
++ scratch,
++ &done);
++ } else if (can_have_holes) {
++ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
++ FAST_HOLEY_SMI_ELEMENTS,
++ map_out,
++ scratch,
++ &done);
++ }
++ bind(&done);
++}
++
++
++void MacroAssembler::LoadGlobalFunction(int index, Register function) {
++ // Load the global or builtins object from the current context.
++ LoadP(function,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ // Load the native context from the global or builtins object.
++ LoadP(function, FieldMemOperand(function,
++ GlobalObject::kNativeContextOffset));
++ // Load the function from the native context.
++ LoadP(function, MemOperand(function, Context::SlotOffset(index)), r0);
++}
++
++
++void MacroAssembler::LoadGlobalFunctionInitialMap(Register function,
++ Register map,
++ Register scratch) {
++ // Load the initial map. The global functions all have initial maps.
++ LoadP(map,
++ FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset));
++ if (emit_debug_code()) {
++ Label ok, fail;
++ CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, DO_SMI_CHECK);
++ b(&ok);
++ bind(&fail);
++ Abort("Global functions must have initial map");
++ bind(&ok);
++ }
++}
++
++
++void MacroAssembler::JumpIfNotPowerOfTwoOrZero(
++ Register reg,
++ Register scratch,
++ Label* not_power_of_two_or_zero) {
++ subi(scratch, reg, Operand(1));
++ cmpi(scratch, Operand::Zero());
++ blt(not_power_of_two_or_zero);
++ and_(r0, scratch, reg, SetRC);
++ bne(not_power_of_two_or_zero, cr0);
++}
++
++
++void MacroAssembler::JumpIfNotPowerOfTwoOrZeroAndNeg(
++ Register reg,
++ Register scratch,
++ Label* zero_and_neg,
++ Label* not_power_of_two) {
++ subi(scratch, reg, Operand(1));
++ cmpi(scratch, Operand::Zero());
++ blt(zero_and_neg);
++ and_(r0, scratch, reg, SetRC);
++ bne(not_power_of_two, cr0);
++}
++
++#if !V8_TARGET_ARCH_PPC64
++void MacroAssembler::SmiTagCheckOverflow(Register reg, Register overflow) {
++ ASSERT(!reg.is(overflow));
++ mr(overflow, reg); // Save original value.
++ SmiTag(reg);
++ xor_(overflow, overflow, reg, SetRC); // Overflow if (value ^ 2 * value) < 0.
++}
++
++
++void MacroAssembler::SmiTagCheckOverflow(Register dst,
++ Register src,
++ Register overflow) {
++ if (dst.is(src)) {
++ // Fall back to slower case.
++ SmiTagCheckOverflow(dst, overflow);
++ } else {
++ ASSERT(!dst.is(src));
++ ASSERT(!dst.is(overflow));
++ ASSERT(!src.is(overflow));
++ SmiTag(dst, src);
++ xor_(overflow, dst, src, SetRC); // Overflow if (value ^ 2 * value) < 0.
++ }
++}
++#endif
++
++void MacroAssembler::JumpIfNotBothSmi(Register reg1,
++ Register reg2,
++ Label* on_not_both_smi) {
++ STATIC_ASSERT(kSmiTag == 0);
++ ASSERT_EQ(1, static_cast<int>(kSmiTagMask));
++ orx(r0, reg1, reg2, LeaveRC);
++ JumpIfNotSmi(r0, on_not_both_smi);
++}
++
++
++void MacroAssembler::UntagAndJumpIfSmi(
++ Register dst, Register src, Label* smi_case) {
++ STATIC_ASSERT(kSmiTag == 0);
++ STATIC_ASSERT(kSmiTagSize == 1);
++ TestBit(src, 0, r0);
++ SmiUntag(dst, src);
++ beq(smi_case, cr0);
++}
++
++
++void MacroAssembler::UntagAndJumpIfNotSmi(
++ Register dst, Register src, Label* non_smi_case) {
++ STATIC_ASSERT(kSmiTag == 0);
++ STATIC_ASSERT(kSmiTagSize == 1);
++ TestBit(src, 0, r0);
++ SmiUntag(dst, src);
++ bne(non_smi_case, cr0);
++}
++
++
++void MacroAssembler::JumpIfEitherSmi(Register reg1,
++ Register reg2,
++ Label* on_either_smi) {
++ STATIC_ASSERT(kSmiTag == 0);
++ JumpIfSmi(reg1, on_either_smi);
++ JumpIfSmi(reg2, on_either_smi);
++}
++
++
++void MacroAssembler::AssertNotSmi(Register object) {
++ if (emit_debug_code()) {
++ STATIC_ASSERT(kSmiTag == 0);
++ andi(r0, object, Operand(kSmiTagMask));
++ Check(ne, "Operand is a smi", cr0);
++ }
++}
++
++
++void MacroAssembler::AssertSmi(Register object) {
++ if (emit_debug_code()) {
++ STATIC_ASSERT(kSmiTag == 0);
++ andi(r0, object, Operand(kSmiTagMask));
++ Check(eq, "Operand is not smi", cr0);
++ }
++}
++
++
++void MacroAssembler::AssertString(Register object) {
++ if (emit_debug_code()) {
++ STATIC_ASSERT(kSmiTag == 0);
++ andi(r0, object, Operand(kSmiTagMask));
++ Check(ne, "Operand is not a string", cr0);
++ push(object);
++ LoadP(object, FieldMemOperand(object, HeapObject::kMapOffset));
++ CompareInstanceType(object, object, FIRST_NONSTRING_TYPE);
++ pop(object);
++ Check(lt, "Operand is not a string");
++ }
++}
++
++
++
++void MacroAssembler::AssertRootValue(Register src,
++ Heap::RootListIndex root_value_index,
++ const char* message) {
++ if (emit_debug_code()) {
++ CompareRoot(src, root_value_index);
++ Check(eq, message);
++ }
++}
++
++
++void MacroAssembler::JumpIfNotHeapNumber(Register object,
++ Register heap_number_map,
++ Register scratch,
++ Label* on_not_heap_number) {
++ LoadP(scratch, FieldMemOperand(object, HeapObject::kMapOffset));
++ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
++ cmp(scratch, heap_number_map);
++ bne(on_not_heap_number);
++}
++
++
++void MacroAssembler::JumpIfNonSmisNotBothSequentialAsciiStrings(
++ Register first,
++ Register second,
++ Register scratch1,
++ Register scratch2,
++ Label* failure) {
++ // Test that both first and second are sequential ASCII strings.
++ // Assume that they are non-smis.
++ LoadP(scratch1, FieldMemOperand(first, HeapObject::kMapOffset));
++ LoadP(scratch2, FieldMemOperand(second, HeapObject::kMapOffset));
++ lbz(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
++ lbz(scratch2, FieldMemOperand(scratch2, Map::kInstanceTypeOffset));
++
++ JumpIfBothInstanceTypesAreNotSequentialAscii(scratch1,
++ scratch2,
++ scratch1,
++ scratch2,
++ failure);
++}
++
++void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register first,
++ Register second,
++ Register scratch1,
++ Register scratch2,
++ Label* failure) {
++ // Check that neither is a smi.
++ STATIC_ASSERT(kSmiTag == 0);
++ and_(scratch1, first, second);
++ JumpIfSmi(scratch1, failure);
++ JumpIfNonSmisNotBothSequentialAsciiStrings(first,
++ second,
++ scratch1,
++ scratch2,
++ failure);
++}
++
++
++// Allocates a heap number or jumps to the need_gc label if the young space
++// is full and a scavenge is needed.
++void MacroAssembler::AllocateHeapNumber(Register result,
++ Register scratch1,
++ Register scratch2,
++ Register heap_number_map,
++ Label* gc_required,
++ TaggingMode tagging_mode) {
++ // Allocate an object in the heap for the heap number and tag it as a heap
++ // object.
++ AllocateInNewSpace(HeapNumber::kSize,
++ result,
++ scratch1,
++ scratch2,
++ gc_required,
++ tagging_mode == TAG_RESULT ? TAG_OBJECT :
++ NO_ALLOCATION_FLAGS);
++
++ // Store heap number map in the allocated object.
++ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
++ if (tagging_mode == TAG_RESULT) {
++ StoreP(heap_number_map, FieldMemOperand(result, HeapObject::kMapOffset),
++ r0);
++ } else {
++ StoreP(heap_number_map, MemOperand(result, HeapObject::kMapOffset));
++ }
++}
++
++
++void MacroAssembler::AllocateHeapNumberWithValue(Register result,
++ DwVfpRegister value,
++ Register scratch1,
++ Register scratch2,
++ Register heap_number_map,
++ Label* gc_required) {
++ AllocateHeapNumber(result, scratch1, scratch2, heap_number_map, gc_required);
++ stfd(value, FieldMemOperand(result, HeapNumber::kValueOffset));
++}
++
++
++// Copies a fixed number of fields of heap objects from src to dst.
++void MacroAssembler::CopyFields(Register dst,
++ Register src,
++ RegList temps,
++ int field_count) {
++ // At least one bit set in the first 15 registers.
++ ASSERT((temps & ((1 << 15) - 1)) != 0);
++ ASSERT((temps & dst.bit()) == 0);
++ ASSERT((temps & src.bit()) == 0);
++ // Primitive implementation using only one temporary register.
++
++ Register tmp = no_reg;
++ // Find a temp register in temps list.
++ for (int i = 0; i < 15; i++) {
++ if ((temps & (1 << i)) != 0) {
++ tmp.set_code(i);
++ break;
++ }
++ }
++ ASSERT(!tmp.is(no_reg));
++
++ for (int i = 0; i < field_count; i++) {
++ LoadP(tmp, FieldMemOperand(src, i * kPointerSize), r0);
++ StoreP(tmp, FieldMemOperand(dst, i * kPointerSize), r0);
++ }
++}
++
++
++void MacroAssembler::CopyBytes(Register src,
++ Register dst,
++ Register length,
++ Register scratch) {
++ Label align_loop, aligned, word_loop, byte_loop, byte_loop_1, done;
++
++ ASSERT(!scratch.is(r0));
++
++ cmpi(length, Operand::Zero());
++ beq(&done);
++
++ // Check src alignment and length to see whether word_loop is possible
++ andi(scratch, src, Operand(kPointerSize - 1));
++ beq(&aligned, cr0);
++ subfic(scratch, scratch, Operand(kPointerSize * 2));
++ cmp(length, scratch);
++ blt(&byte_loop);
++
++ // Align src before copying in word size chunks.
++ subi(scratch, scratch, Operand(kPointerSize));
++ mtctr(scratch);
++ bind(&align_loop);
++ lbz(scratch, MemOperand(src));
++ addi(src, src, Operand(1));
++ subi(length, length, Operand(1));
++ stb(scratch, MemOperand(dst));
++ addi(dst, dst, Operand(1));
++ bdnz(&align_loop);
++
++ bind(&aligned);
++
++ // Copy bytes in word size chunks.
++ if (emit_debug_code()) {
++ andi(r0, src, Operand(kPointerSize - 1));
++ Assert(eq, "Expecting alignment for CopyBytes", cr0);
++ }
++
++ ShiftRightImm(scratch, length, Operand(kPointerSizeLog2));
++ cmpi(scratch, Operand::Zero());
++ beq(&byte_loop);
++
++ mtctr(scratch);
++ bind(&word_loop);
++ LoadP(scratch, MemOperand(src));
++ addi(src, src, Operand(kPointerSize));
++ subi(length, length, Operand(kPointerSize));
++ if (CpuFeatures::IsSupported(UNALIGNED_ACCESSES)) {
++ // currently false for PPC - but possible future opt
++ StoreP(scratch, MemOperand(dst));
++ addi(dst, dst, Operand(kPointerSize));
++ } else {
++#if __BYTE_ORDER == __LITTLE_ENDIAN
++ stb(scratch, MemOperand(dst, 0));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 1));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 2));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 3));
++#if V8_TARGET_ARCH_PPC64
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 4));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 5));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 6));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 7));
++#endif
++#else
++#if V8_TARGET_ARCH_PPC64
++ stb(scratch, MemOperand(dst, 7));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 6));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 5));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 4));
++ ShiftRightImm(scratch, scratch, Operand(8));
++#endif
++ stb(scratch, MemOperand(dst, 3));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 2));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 1));
++ ShiftRightImm(scratch, scratch, Operand(8));
++ stb(scratch, MemOperand(dst, 0));
++#endif
++ addi(dst, dst, Operand(kPointerSize));
++ }
++ bdnz(&word_loop);
++
++ // Copy the last bytes if any left.
++ cmpi(length, Operand::Zero());
++ beq(&done);
++
++ bind(&byte_loop);
++ mtctr(length);
++ bind(&byte_loop_1);
++ lbz(scratch, MemOperand(src));
++ addi(src, src, Operand(1));
++ stb(scratch, MemOperand(dst));
++ addi(dst, dst, Operand(1));
++ bdnz(&byte_loop_1);
++
++ bind(&done);
++}
++
++
++void MacroAssembler::InitializeFieldsWithFiller(Register start_offset,
++ Register end_offset,
++ Register filler) {
++ Label loop, entry;
++ b(&entry);
++ bind(&loop);
++ StoreP(filler, MemOperand(start_offset), r0);
++ addi(start_offset, start_offset, Operand(kPointerSize));
++ bind(&entry);
++ cmp(start_offset, end_offset);
++ blt(&loop);
++}
++
++
++void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii(
++ Register first,
++ Register second,
++ Register scratch1,
++ Register scratch2,
++ Label* failure) {
++ int kFlatAsciiStringMask =
++ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
++ int kFlatAsciiStringTag = ASCII_STRING_TYPE;
++ andi(scratch1, first, Operand(kFlatAsciiStringMask));
++ andi(scratch2, second, Operand(kFlatAsciiStringMask));
++ cmpi(scratch1, Operand(kFlatAsciiStringTag));
++ bne(failure);
++ cmpi(scratch2, Operand(kFlatAsciiStringTag));
++ bne(failure);
++}
++
++
++void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type,
++ Register scratch,
++ Label* failure) {
++ int kFlatAsciiStringMask =
++ kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask;
++ int kFlatAsciiStringTag = ASCII_STRING_TYPE;
++ andi(scratch, type, Operand(kFlatAsciiStringMask));
++ cmpi(scratch, Operand(kFlatAsciiStringTag));
++ bne(failure);
++}
++
++static const int kRegisterPassedArguments = 8;
++
++
++int MacroAssembler::CalculateStackPassedWords(int num_reg_arguments,
++ int num_double_arguments) {
++ int stack_passed_words = 0;
++ if (num_double_arguments > DoubleRegister::kNumRegisters) {
++ stack_passed_words +=
++ 2 * (num_double_arguments - DoubleRegister::kNumRegisters);
++ }
++ // Up to four simple arguments are passed in registers r0..r3.
++ if (num_reg_arguments > kRegisterPassedArguments) {
++ stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
++ }
++ return stack_passed_words;
++}
++
++
++void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
++ int num_double_arguments,
++ Register scratch) {
++ int frame_alignment = ActivationFrameAlignment();
++ int stack_passed_arguments = CalculateStackPassedWords(
++ num_reg_arguments, num_double_arguments);
++ if (frame_alignment > kPointerSize) {
++ // Make stack end at alignment and make room for stack arguments,
++ // the original value of sp and, on native, the required slots to
++ // make ABI work.
++ mr(scratch, sp);
++#if !defined(USE_SIMULATOR)
++ subi(sp, sp, Operand((stack_passed_arguments +
++ kNumRequiredStackFrameSlots) * kPointerSize));
++#else
++ subi(sp, sp, Operand((stack_passed_arguments + 1) * kPointerSize));
++#endif
++ ASSERT(IsPowerOf2(frame_alignment));
++ li(r0, Operand(-frame_alignment));
++ and_(sp, sp, r0);
++#if !defined(USE_SIMULATOR)
++ // On the simulator we pass args on the stack
++ StoreP(scratch, MemOperand(sp));
++#else
++ // On the simulator we pass args on the stack
++ StoreP(scratch,
++ MemOperand(sp, stack_passed_arguments * kPointerSize), r0);
++#endif
++ } else {
++ subi(sp, sp, Operand((stack_passed_arguments +
++ kNumRequiredStackFrameSlots) * kPointerSize));
++ }
++}
++
++
++void MacroAssembler::PrepareCallCFunction(int num_reg_arguments,
++ Register scratch) {
++ PrepareCallCFunction(num_reg_arguments, 0, scratch);
++}
++
++
++void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg) {
++ Move(d1, dreg);
++}
++
++
++void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg1,
++ DoubleRegister dreg2) {
++ if (dreg2.is(d1)) {
++ ASSERT(!dreg1.is(d2));
++ Move(d2, dreg2);
++ Move(d1, dreg1);
++ } else {
++ Move(d1, dreg1);
++ Move(d2, dreg2);
++ }
++}
++
++
++void MacroAssembler::SetCallCDoubleArguments(DoubleRegister dreg,
++ Register reg) {
++ Move(d1, dreg);
++ Move(r3, reg);
++}
++
++
++void MacroAssembler::CallCFunction(ExternalReference function,
++ int num_reg_arguments,
++ int num_double_arguments) {
++ mov(ip, Operand(function));
++ CallCFunctionHelper(ip, num_reg_arguments, num_double_arguments);
++}
++
++
++void MacroAssembler::CallCFunction(Register function,
++ int num_reg_arguments,
++ int num_double_arguments) {
++ CallCFunctionHelper(function, num_reg_arguments, num_double_arguments);
++}
++
++
++void MacroAssembler::CallCFunction(ExternalReference function,
++ int num_arguments) {
++ CallCFunction(function, num_arguments, 0);
++}
++
++
++void MacroAssembler::CallCFunction(Register function,
++ int num_arguments) {
++ CallCFunction(function, num_arguments, 0);
++}
++
++
++void MacroAssembler::CallCFunctionHelper(Register function,
++ int num_reg_arguments,
++ int num_double_arguments) {
++ ASSERT(has_frame());
++ // Make sure that the stack is aligned before calling a C function unless
++ // running in the simulator. The simulator has its own alignment check which
++ // provides more information.
++
++ // Just call directly. The function called cannot cause a GC, or
++ // allow preemption, so the return address in the link register
++ // stays correct.
++#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++ // AIX uses a function descriptor. When calling C code be aware
++ // of this descriptor and pick up values from it
++ Register dest = ip;
++ LoadP(ToRegister(2), MemOperand(function, kPointerSize));
++ LoadP(dest, MemOperand(function, 0));
++#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++ Register dest = ip;
++ Move(ip, function);
++#else
++ Register dest = function;
++#endif
++
++ Call(dest);
++
++ int stack_passed_arguments = CalculateStackPassedWords(
++ num_reg_arguments, num_double_arguments);
++ if (ActivationFrameAlignment() > kPointerSize) {
++#if !defined(USE_SIMULATOR)
++ // On real hardware we follow the ABI
++ LoadP(sp, MemOperand(sp));
++#else
++ // On the simulator we pass args on the stack
++ LoadP(sp, MemOperand(sp, stack_passed_arguments * kPointerSize), r0);
++#endif
++ } else {
++ addi(sp, sp, Operand((stack_passed_arguments +
++ kNumRequiredStackFrameSlots) * kPointerSize));
++ }
++}
++
++
++void MacroAssembler::FlushICache(Register address, size_t size,
++ Register scratch) {
++ Label done;
++
++ dcbf(r0, address);
++ sync();
++ icbi(r0, address);
++ isync();
++
++ // This code handles ranges which cross a single cacheline boundary.
++ // scratch is last cacheline which intersects range.
++ ASSERT(size > 0 && size <= kCacheLineSize);
++ addi(scratch, address, Operand(size - 1));
++ ClearRightImm(scratch, scratch, Operand(kCacheLineSizeLog2));
++ cmpl(scratch, address);
++ ble(&done);
++
++ dcbf(r0, scratch);
++ sync();
++ icbi(r0, scratch);
++ isync();
++
++ bind(&done);
++}
++
++// This code assumes a FIXED_SEQUENCE for lis/ori
++void MacroAssembler::PatchRelocatedValue(Register lis_location,
++ Register scratch,
++ Register new_value) {
++ lwz(scratch, MemOperand(lis_location));
++ // At this point scratch is a lis instruction.
++ if (emit_debug_code()) {
++ And(scratch, scratch, Operand(kOpcodeMask | (0x1f * B16)));
++ Cmpi(scratch, Operand(ADDIS), r0);
++ Check(eq, "The instruction to patch should be a lis.");
++ lwz(scratch, MemOperand(lis_location));
++ }
++
++ // insert new high word into lis instruction
++#if V8_TARGET_ARCH_PPC64
++ srdi(ip, new_value, Operand(32));
++ rlwimi(scratch, ip, 16, 16, 31);
++#else
++ rlwimi(scratch, new_value, 16, 16, 31);
++#endif
++
++ stw(scratch, MemOperand(lis_location));
++
++ lwz(scratch, MemOperand(lis_location, kInstrSize));
++ // scratch is now ori.
++ if (emit_debug_code()) {
++ And(scratch, scratch, Operand(kOpcodeMask));
++ Cmpi(scratch, Operand(ORI), r0);
++ Check(eq, "The instruction should be an ori");
++ lwz(scratch, MemOperand(lis_location, kInstrSize));
++ }
++
++ // insert new low word into ori instruction
++#if V8_TARGET_ARCH_PPC64
++ rlwimi(scratch, ip, 0, 16, 31);
++#else
++ rlwimi(scratch, new_value, 0, 16, 31);
++#endif
++ stw(scratch, MemOperand(lis_location, kInstrSize));
++
++#if V8_TARGET_ARCH_PPC64
++ if (emit_debug_code()) {
++ lwz(scratch, MemOperand(lis_location, 2*kInstrSize));
++ // scratch is now sldi.
++ And(scratch, scratch, Operand(kOpcodeMask|kExt5OpcodeMask));
++ Cmpi(scratch, Operand(EXT5|RLDICR), r0);
++ Check(eq, "The instruction should be an sldi");
++ }
++
++ lwz(scratch, MemOperand(lis_location, 3*kInstrSize));
++ // scratch is now ori.
++ if (emit_debug_code()) {
++ And(scratch, scratch, Operand(kOpcodeMask));
++ Cmpi(scratch, Operand(ORIS), r0);
++ Check(eq, "The instruction should be an oris");
++ lwz(scratch, MemOperand(lis_location, 3*kInstrSize));
++ }
++
++ rlwimi(scratch, new_value, 16, 16, 31);
++ stw(scratch, MemOperand(lis_location, 3*kInstrSize));
++
++ lwz(scratch, MemOperand(lis_location, 4*kInstrSize));
++ // scratch is now ori.
++ if (emit_debug_code()) {
++ And(scratch, scratch, Operand(kOpcodeMask));
++ Cmpi(scratch, Operand(ORI), r0);
++ Check(eq, "The instruction should be an ori");
++ lwz(scratch, MemOperand(lis_location, 4*kInstrSize));
++ }
++ rlwimi(scratch, new_value, 0, 16, 31);
++ stw(scratch, MemOperand(lis_location, 4*kInstrSize));
++#endif
++
++ // Update the I-cache so the new lis and addic can be executed.
++#if V8_TARGET_ARCH_PPC64
++ FlushICache(lis_location, 5 * kInstrSize, scratch);
++#else
++ FlushICache(lis_location, 2 * kInstrSize, scratch);
++#endif
++}
++
++// This code assumes a FIXED_SEQUENCE for lis/ori
++void MacroAssembler::GetRelocatedValueLocation(Register lis_location,
++ Register result,
++ Register scratch) {
++ lwz(result, MemOperand(lis_location));
++ if (emit_debug_code()) {
++ And(result, result, Operand(kOpcodeMask | (0x1f * B16)));
++ Cmpi(result, Operand(ADDIS), r0);
++ Check(eq, "The instruction should be a lis.");
++ lwz(result, MemOperand(lis_location));
++ }
++
++ // result now holds a lis instruction. Extract the immediate.
++ slwi(result, result, Operand(16));
++
++ lwz(scratch, MemOperand(lis_location, kInstrSize));
++ if (emit_debug_code()) {
++ And(scratch, scratch, Operand(kOpcodeMask));
++ Cmpi(scratch, Operand(ORI), r0);
++ Check(eq, "The instruction should be an ori");
++ lwz(scratch, MemOperand(lis_location, kInstrSize));
++ }
++ // Copy the low 16bits from ori instruction into result
++ rlwimi(result, scratch, 0, 16, 31);
++
++#if V8_TARGET_ARCH_PPC64
++ if (emit_debug_code()) {
++ lwz(scratch, MemOperand(lis_location, 2*kInstrSize));
++ // scratch is now sldi.
++ And(scratch, scratch, Operand(kOpcodeMask|kExt5OpcodeMask));
++ Cmpi(scratch, Operand(EXT5|RLDICR), r0);
++ Check(eq, "The instruction should be an sldi");
++ }
++
++ lwz(scratch, MemOperand(lis_location, 3*kInstrSize));
++ // scratch is now ori.
++ if (emit_debug_code()) {
++ And(scratch, scratch, Operand(kOpcodeMask));
++ Cmpi(scratch, Operand(ORIS), r0);
++ Check(eq, "The instruction should be an oris");
++ lwz(scratch, MemOperand(lis_location, 3*kInstrSize));
++ }
++ sldi(result, result, Operand(16));
++ rldimi(result, scratch, 0, 48);
++
++ lwz(scratch, MemOperand(lis_location, 4*kInstrSize));
++ // scratch is now ori.
++ if (emit_debug_code()) {
++ And(scratch, scratch, Operand(kOpcodeMask));
++ Cmpi(scratch, Operand(ORI), r0);
++ Check(eq, "The instruction should be an ori");
++ lwz(scratch, MemOperand(lis_location, 4*kInstrSize));
++ }
++ sldi(result, result, Operand(16));
++ rldimi(result, scratch, 0, 48);
++#endif
++}
++
++
++void MacroAssembler::CheckPageFlag(
++ Register object,
++ Register scratch, // scratch may be same register as object
++ int mask,
++ Condition cc,
++ Label* condition_met) {
++ ASSERT(cc == ne || cc == eq);
++ ClearRightImm(scratch, object, Operand(kPageSizeBits));
++ LoadP(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset));
++
++ And(r0, scratch, Operand(mask), SetRC);
++
++ if (cc == ne) {
++ bne(condition_met, cr0);
++ }
++ if (cc == eq) {
++ beq(condition_met, cr0);
++ }
++}
++
++
++void MacroAssembler::JumpIfBlack(Register object,
++ Register scratch0,
++ Register scratch1,
++ Label* on_black) {
++ HasColor(object, scratch0, scratch1, on_black, 1, 0); // kBlackBitPattern.
++ ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0);
++}
++
++
++void MacroAssembler::HasColor(Register object,
++ Register bitmap_scratch,
++ Register mask_scratch,
++ Label* has_color,
++ int first_bit,
++ int second_bit) {
++ ASSERT(!AreAliased(object, bitmap_scratch, mask_scratch, no_reg));
++
++ GetMarkBits(object, bitmap_scratch, mask_scratch);
++
++ Label other_color, word_boundary;
++ lwz(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize));
++ // Test the first bit
++ and_(r0, ip, mask_scratch, SetRC);
++ b(first_bit == 1 ? eq : ne, &other_color, cr0);
++ // Shift left 1
++ // May need to load the next cell
++ slwi(mask_scratch, mask_scratch, Operand(1), SetRC);
++ beq(&word_boundary, cr0);
++ // Test the second bit
++ and_(r0, ip, mask_scratch, SetRC);
++ b(second_bit == 1 ? ne : eq, has_color, cr0);
++ b(&other_color);
++
++ bind(&word_boundary);
++ lwz(ip, MemOperand(bitmap_scratch,
++ MemoryChunk::kHeaderSize + kIntSize));
++ andi(r0, ip, Operand(1));
++ b(second_bit == 1 ? ne : eq, has_color, cr0);
++ bind(&other_color);
++}
++
++
++// Detect some, but not all, common pointer-free objects. This is used by the
++// incremental write barrier which doesn't care about oddballs (they are always
++// marked black immediately so this code is not hit).
++void MacroAssembler::JumpIfDataObject(Register value,
++ Register scratch,
++ Label* not_data_object) {
++ Label is_data_object;
++ LoadP(scratch, FieldMemOperand(value, HeapObject::kMapOffset));
++ CompareRoot(scratch, Heap::kHeapNumberMapRootIndex);
++ beq(&is_data_object);
++ ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1);
++ ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80);
++ // If it's a string and it's not a cons string then it's an object
containing
++ // no GC pointers.
++ lbz(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset));
++ STATIC_ASSERT((kIsIndirectStringMask | kIsNotStringMask) == 0x81);
++ andi(scratch, scratch, Operand(kIsIndirectStringMask | kIsNotStringMask));
++ bne(not_data_object, cr0);
++ bind(&is_data_object);
++}
++
++
++void MacroAssembler::GetMarkBits(Register addr_reg,
++ Register bitmap_reg,
++ Register mask_reg) {
++ ASSERT(!AreAliased(addr_reg, bitmap_reg, mask_reg, no_reg));
++ ASSERT((~Page::kPageAlignmentMask & 0xffff) == 0);
++ lis(r0, Operand((~Page::kPageAlignmentMask >> 16)));
++ and_(bitmap_reg, addr_reg, r0);
++ const int kLowBits = kPointerSizeLog2 + Bitmap::kBitsPerCellLog2;
++ ExtractBitRange(mask_reg, addr_reg,
++ kLowBits - 1,
++ kPointerSizeLog2);
++ ExtractBitRange(ip, addr_reg,
++ kPageSizeBits - 1,
++ kLowBits);
++ ShiftLeftImm(ip, ip, Operand(Bitmap::kBytesPerCellLog2));
++ add(bitmap_reg, bitmap_reg, ip);
++ li(ip, Operand(1));
++ slw(mask_reg, ip, mask_reg);
++}
++
++
++void MacroAssembler::EnsureNotWhite(
++ Register value,
++ Register bitmap_scratch,
++ Register mask_scratch,
++ Register load_scratch,
++ Label* value_is_white_and_not_data) {
++ ASSERT(!AreAliased(value, bitmap_scratch, mask_scratch, ip));
++ GetMarkBits(value, bitmap_scratch, mask_scratch);
++
++ // If the value is black or grey we don't need to do anything.
++ ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0);
++ ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0);
++ ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0);
++ ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0);
++
++ Label done;
++
++ // Since both black and grey have a 1 in the first position and white does
++ // not have a 1 there we only need to check one bit.
++ lwz(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize));
++ and_(r0, mask_scratch, load_scratch, SetRC);
++ bne(&done, cr0);
++
++ if (emit_debug_code()) {
++ // Check for impossible bit pattern.
++ Label ok;
++ // LSL may overflow, making the check conservative.
++ slwi(r0, mask_scratch, Operand(1));
++ and_(r0, load_scratch, r0, SetRC);
++ beq(&ok, cr0);
++ stop("Impossible marking bit pattern");
++ bind(&ok);
++ }
++
++ // Value is white. We check whether it is data that doesn't need scanning.
++ // Currently only checks for HeapNumber and non-cons strings.
++ Register map = load_scratch; // Holds map while checking type.
++ Register length = load_scratch; // Holds length of object after testing type.
++ Label is_data_object, maybe_string_object, is_string_object, is_encoded;
++#if V8_TARGET_ARCH_PPC64
++ Label length_computed;
++#endif
++
++
++ // Check for heap-number
++ LoadP(map, FieldMemOperand(value, HeapObject::kMapOffset));
++ CompareRoot(map, Heap::kHeapNumberMapRootIndex);
++ bne(&maybe_string_object);
++ li(length, Operand(HeapNumber::kSize));
++ b(&is_data_object);
++ bind(&maybe_string_object);
++
++ // Check for strings.
++ ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1);
++ ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80);
++ // If it's a string and it's not a cons string then it's an object
containing
++ // no GC pointers.
++ Register instance_type = load_scratch;
++ lbz(instance_type, FieldMemOperand(map, Map::kInstanceTypeOffset));
++ andi(r0, instance_type, Operand(kIsIndirectStringMask | kIsNotStringMask));
++ bne(value_is_white_and_not_data, cr0);
++ // It's a non-indirect (non-cons and non-slice) string.
++ // If it's external, the length is just ExternalString::kSize.
++ // Otherwise it's String::kHeaderSize + string->length() * (1 or 2).
++ // External strings are the only ones with the kExternalStringTag bit
++ // set.
++ ASSERT_EQ(0, kSeqStringTag & kExternalStringTag);
++ ASSERT_EQ(0, kConsStringTag & kExternalStringTag);
++ andi(r0, instance_type, Operand(kExternalStringTag));
++ beq(&is_string_object, cr0);
++ li(length, Operand(ExternalString::kSize));
++ b(&is_data_object);
++ bind(&is_string_object);
++
++ // Sequential string, either ASCII or UC16.
++ // For ASCII (char-size of 1) we untag the smi to get the length.
++ // For UC16 (char-size of 2):
++ // - (32-bit) we just leave the smi tag in place, thereby getting
++ // the length multiplied by 2.
++ // - (64-bit) we compute the offset in the 2-byte array
++ ASSERT(kAsciiStringTag == 4 && kStringEncodingMask == 4);
++ LoadP(ip, FieldMemOperand(value, String::kLengthOffset));
++ andi(r0, instance_type, Operand(kStringEncodingMask));
++ beq(&is_encoded, cr0);
++ SmiUntag(ip);
++#if V8_TARGET_ARCH_PPC64
++ b(&length_computed);
++#endif
++ bind(&is_encoded);
++#if V8_TARGET_ARCH_PPC64
++ SmiToShortArrayOffset(ip, ip);
++ bind(&length_computed);
++#else
++ ASSERT(kSmiShift == 1);
++#endif
++ addi(length, ip, Operand(SeqString::kHeaderSize + kObjectAlignmentMask));
++ li(r0, Operand(~kObjectAlignmentMask));
++ and_(length, length, r0);
++
++ bind(&is_data_object);
++ // Value is a data object, and it is white. Mark it black. Since we know
++ // that the object is white we can make it black by flipping one bit.
++ lwz(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize));
++ orx(ip, ip, mask_scratch);
++ stw(ip, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize));
++
++ mov(ip, Operand(~Page::kPageAlignmentMask));
++ and_(bitmap_scratch, bitmap_scratch, ip);
++ lwz(ip, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset));
++ add(ip, ip, length);
++ stw(ip, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset));
++
++ bind(&done);
++}
++
++// Saturate a value into 8-bit unsigned integer
++// if input_value < 0, output_value is 0
++// if input_value > 255, output_value is 255
++// otherwise output_value is the input_value
++void MacroAssembler::ClampUint8(Register output_reg, Register input_reg) {
++ Label done, negative_label, overflow_label;
++ int satval = (1 << 8) - 1;
++
++ cmpi(input_reg, Operand::Zero());
++ blt(&negative_label);
++
++ cmpi(input_reg, Operand(satval));
++ bgt(&overflow_label);
++ if (!output_reg.is(input_reg)) {
++ mr(output_reg, input_reg);
++ }
++ b(&done);
++
++ bind(&negative_label);
++ li(output_reg, Operand::Zero()); // set to 0 if negative
++ b(&done);
++
++
++ bind(&overflow_label); // set to satval if > satval
++ li(output_reg, Operand(satval));
++
++ bind(&done);
++}
++
++void MacroAssembler::SetRoundingMode(VFPRoundingMode RN) {
++ mtfsfi(7, RN);
++}
++
++void MacroAssembler::ResetRoundingMode() {
++ mtfsfi(7, kRoundToNearest); // reset (default is kRoundToNearest)
++}
++
++void MacroAssembler::ClampDoubleToUint8(Register result_reg,
++ DoubleRegister input_reg,
++ DoubleRegister temp_double_reg,
++ DoubleRegister temp_double_reg2) {
++ Label above_zero;
++ Label done;
++ Label in_bounds;
++
++ LoadDoubleLiteral(temp_double_reg, 0.0, result_reg);
++ fcmpu(input_reg, temp_double_reg);
++ bgt(&above_zero);
++
++ // Double value is less than zero, NaN or Inf, return 0.
++ LoadIntLiteral(result_reg, 0);
++ b(&done);
++
++ // Double value is >= 255, return 255.
++ bind(&above_zero);
++ LoadDoubleLiteral(temp_double_reg, 255.0, result_reg);
++ fcmpu(input_reg, temp_double_reg);
++ ble(&in_bounds);
++ LoadIntLiteral(result_reg, 255);
++ b(&done);
++
++ // In 0-255 range, round and truncate.
++ bind(&in_bounds);
++
++ // round to nearest (default rounding mode)
++ fctiw(temp_double_reg, input_reg);
++
++ // reserve a slot on the stack
++ stfdu(temp_double_reg, MemOperand(sp, -8));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ lwz(result_reg, MemOperand(sp));
++#else
++ lwz(result_reg, MemOperand(sp, 4));
++#endif
++ // restore the stack
++ addi(sp, sp, Operand(8));
++
++ bind(&done);
++}
++
++void MacroAssembler::LoadInstanceDescriptors(Register map,
++ Register descriptors) {
++ LoadP(descriptors, FieldMemOperand(map, Map::kDescriptorsOffset));
++}
++
++
++void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) {
++ LoadP(dst, FieldMemOperand(map, Map::kBitField3Offset));
++ DecodeField<Map::NumberOfOwnDescriptorsBits>(dst);
++}
++
++
++void MacroAssembler::EnumLength(Register dst, Register map) {
++ STATIC_ASSERT(Map::EnumLengthBits::kShift == 0);
++ LoadP(dst, FieldMemOperand(map, Map::kBitField3Offset));
++ LoadSmiLiteral(r0, Smi::FromInt(Map::EnumLengthBits::kMask));
++ and_(dst, dst, r0);
++}
++
++
++void MacroAssembler::CheckEnumCache(Register null_value, Label* call_runtime) {
++ Register empty_fixed_array_value = r9;
++ LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex);
++ Label next, start;
++ mr(r5, r3);
++
++ // Check if the enum length field is properly initialized, indicating that
++ // there is an enum cache.
++ LoadP(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
++
++ EnumLength(r6, r4);
++ CmpSmiLiteral(r6, Smi::FromInt(Map::kInvalidEnumCache), r0);
++ beq(call_runtime);
++
++ b(&start);
++
++ bind(&next);
++ LoadP(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
++
++ // For all objects but the receiver, check that the cache is empty.
++ EnumLength(r6, r4);
++ CmpSmiLiteral(r6, Smi::FromInt(0), r0);
++ bne(call_runtime);
++
++ bind(&start);
++
++ // Check that there are no elements. Register r5 contains the current JS
++ // object we've reached through the prototype chain.
++ LoadP(r5, FieldMemOperand(r5, JSObject::kElementsOffset));
++ cmp(r5, empty_fixed_array_value);
++ bne(call_runtime);
++
++ LoadP(r5, FieldMemOperand(r4, Map::kPrototypeOffset));
++ cmp(r5, null_value);
++ bne(&next);
++}
++
++
++////////////////////////////////////////////////////////////////////////////////
++//
++// New MacroAssembler Interfaces added for PPC
++//
++////////////////////////////////////////////////////////////////////////////////
++void MacroAssembler::LoadIntLiteral(Register dst, int value) {
++ if (is_int16(value)) {
++ li(dst, Operand(value));
++ } else {
++ int hi_word = static_cast<int>(value) >> 16;
++ if ((hi_word << 16) == value) {
++ lis(dst, Operand(hi_word));
++ } else {
++ mov(dst, Operand(value));
++ }
++ }
++}
++
++void MacroAssembler::LoadSmiLiteral(Register dst, Smi *smi) {
++ intptr_t value = reinterpret_cast<intptr_t>(smi);
++#if V8_TARGET_ARCH_PPC64
++ ASSERT((value & 0xffffffff) == 0);
++ LoadIntLiteral(dst, value >> 32);
++ ShiftLeftImm(dst, dst, Operand(32));
++#else
++ LoadIntLiteral(dst, value);
++#endif
++}
++
++void MacroAssembler::LoadDoubleLiteral(DwVfpRegister result,
++ double value,
++ Register scratch) {
++ addi(sp, sp, Operand(-8)); // reserve 1 temp double on the stack
++
++ // avoid gcc strict aliasing error using union cast
++ union {
++ double dval;
++#if V8_TARGET_ARCH_PPC64
++ intptr_t ival;
++#else
++ intptr_t ival[2];
++#endif
++ } litVal;
++
++ litVal.dval = value;
++#if V8_TARGET_ARCH_PPC64
++ mov(scratch, Operand(litVal.ival));
++ std(scratch, MemOperand(sp));
++#else
++ LoadIntLiteral(scratch, litVal.ival[0]);
++ stw(scratch, MemOperand(sp, 0));
++ LoadIntLiteral(scratch, litVal.ival[1]);
++ stw(scratch, MemOperand(sp, 4));
++#endif
++ lfd(result, MemOperand(sp, 0));
++
++ addi(sp, sp, Operand(8)); // restore the stack ptr
++}
++
++void MacroAssembler::Add(Register dst, Register src,
++ intptr_t value, Register scratch) {
++ if (is_int16(value)) {
++ addi(dst, src, Operand(value));
++ } else {
++ mov(scratch, Operand(value));
++ add(dst, src, scratch);
++ }
++}
++
++void MacroAssembler::Cmpi(Register src1, const Operand& src2, Register scratch,
++ CRegister cr) {
++ intptr_t value = src2.immediate();
++ if (is_int16(value)) {
++ cmpi(src1, src2, cr);
++ } else {
++ mov(scratch, src2);
++ cmp(src1, scratch, cr);
++ }
++}
++
++void MacroAssembler::Cmpli(Register src1, const Operand& src2, Register scratch,
++ CRegister cr) {
++ intptr_t value = src2.immediate();
++ if (is_uint16(value)) {
++ cmpli(src1, src2, cr);
++ } else {
++ mov(scratch, src2);
++ cmpl(src1, scratch, cr);
++ }
++}
++
++void MacroAssembler::And(Register ra, Register rs, const Operand& rb,
++ RCBit rc) {
++ if (rb.is_reg()) {
++ and_(ra, rs, rb.rm(), rc);
++ } else {
++ if (is_uint16(rb.imm_) && rb.rmode_ == RelocInfo::NONE
++ && rc == SetRC) {
++ andi(ra, rs, rb);
++ } else {
++ // mov handles the relocation.
++ ASSERT(!rs.is(r0));
++ mov(r0, rb);
++ and_(ra, rs, r0, rc);
++ }
++ }
++}
++
++void MacroAssembler::Or(Register ra, Register rs, const Operand& rb, RCBit rc) {
++ if (rb.is_reg()) {
++ orx(ra, rs, rb.rm(), rc);
++ } else {
++ if (is_uint16(rb.imm_) && rb.rmode_ == RelocInfo::NONE && rc ==
LeaveRC) {
++ ori(ra, rs, rb);
++ } else {
++ // mov handles the relocation.
++ ASSERT(!rs.is(r0));
++ mov(r0, rb);
++ orx(ra, rs, r0, rc);
++ }
++ }
++}
++
++void MacroAssembler::Xor(Register ra, Register rs, const Operand& rb,
++ RCBit rc) {
++ if (rb.is_reg()) {
++ xor_(ra, rs, rb.rm(), rc);
++ } else {
++ if (is_uint16(rb.imm_) && rb.rmode_ == RelocInfo::NONE && rc ==
LeaveRC) {
++ xori(ra, rs, rb);
++ } else {
++ // mov handles the relocation.
++ ASSERT(!rs.is(r0));
++ mov(r0, rb);
++ xor_(ra, rs, r0, rc);
++ }
++ }
++}
++
++void MacroAssembler::CmpSmiLiteral(Register src1, Smi *smi, Register scratch,
++ CRegister cr) {
++#if V8_TARGET_ARCH_PPC64
++ LoadSmiLiteral(scratch, smi);
++ cmp(src1, scratch, cr);
++#else
++ Cmpi(src1, Operand(smi), scratch, cr);
++#endif
++}
++
++void MacroAssembler::CmplSmiLiteral(Register src1, Smi *smi, Register scratch,
++ CRegister cr) {
++#if V8_TARGET_ARCH_PPC64
++ LoadSmiLiteral(scratch, smi);
++ cmpl(src1, scratch, cr);
++#else
++ Cmpli(src1, Operand(smi), scratch, cr);
++#endif
++}
++
++void MacroAssembler::AddSmiLiteral(Register dst, Register src, Smi *smi,
++ Register scratch) {
++#if V8_TARGET_ARCH_PPC64
++ LoadSmiLiteral(scratch, smi);
++ add(dst, src, scratch);
++#else
++ Add(dst, src, reinterpret_cast<intptr_t>(smi), scratch);
++#endif
++}
++
++void MacroAssembler::SubSmiLiteral(Register dst, Register src, Smi *smi,
++ Register scratch) {
++#if V8_TARGET_ARCH_PPC64
++ LoadSmiLiteral(scratch, smi);
++ sub(dst, src, scratch);
++#else
++ Add(dst, src, -(reinterpret_cast<intptr_t>(smi)), scratch);
++#endif
++}
++
++void MacroAssembler::AndSmiLiteral(Register dst, Register src, Smi *smi,
++ Register scratch, RCBit rc) {
++#if V8_TARGET_ARCH_PPC64
++ LoadSmiLiteral(scratch, smi);
++ and_(dst, src, scratch, rc);
++#else
++ And(dst, src, Operand(smi), rc);
++#endif
++}
++
++
++// Load a "pointer" sized value from the memory location
++void MacroAssembler::LoadP(Register dst, const MemOperand& mem,
++ Register scratch) {
++ int offset = mem.offset();
++
++ if (!scratch.is(no_reg) && !is_int16(offset)) {
++ /* cannot use d-form */
++ LoadIntLiteral(scratch, offset);
++#if V8_TARGET_ARCH_PPC64
++ ldx(dst, MemOperand(mem.ra(), scratch));
++#else
++ lwzx(dst, MemOperand(mem.ra(), scratch));
++#endif
++ } else {
++#if V8_TARGET_ARCH_PPC64
++ int misaligned = (offset & 3);
++ if (misaligned) {
++ // adjust base to conform to offset alignment requirements
++ // Todo: enhance to use scratch if dst is unsuitable
++ ASSERT(!dst.is(r0));
++ addi(dst, mem.ra(), Operand((offset & 3) - 4));
++ ld(dst, MemOperand(dst, (offset & ~3) + 4));
++ } else {
++ ld(dst, mem);
++ }
++#else
++ lwz(dst, mem);
++#endif
++ }
++}
++
++// Store a "pointer" sized value to the memory location
++void MacroAssembler::StoreP(Register src, const MemOperand& mem,
++ Register scratch) {
++ int offset = mem.offset();
++
++ if (!scratch.is(no_reg) && !is_int16(offset)) {
++ /* cannot use d-form */
++ LoadIntLiteral(scratch, offset);
++#if V8_TARGET_ARCH_PPC64
++ stdx(src, MemOperand(mem.ra(), scratch));
++#else
++ stwx(src, MemOperand(mem.ra(), scratch));
++#endif
++ } else {
++#if V8_TARGET_ARCH_PPC64
++ int misaligned = (offset & 3);
++ if (misaligned) {
++ // adjust base to conform to offset alignment requirements
++ // a suitable scratch is required here
++ ASSERT(!scratch.is(no_reg));
++ if (scratch.is(r0)) {
++ LoadIntLiteral(scratch, offset);
++ stdx(src, MemOperand(mem.ra(), scratch));
++ } else {
++ addi(scratch, mem.ra(), Operand((offset & 3) - 4));
++ std(src, MemOperand(scratch, (offset & ~3) + 4));
++ }
++ } else {
++ std(src, mem);
++ }
++#else
++ stw(src, mem);
++#endif
++ }
++}
++
++void MacroAssembler::LoadWordArith(Register dst, const MemOperand& mem,
++ Register scratch) {
++ int offset = mem.offset();
++
++ if (!scratch.is(no_reg) && !is_int16(offset)) {
++ /* cannot use d-form */
++ LoadIntLiteral(scratch, offset);
++#if V8_TARGET_ARCH_PPC64
++ // lwax(dst, MemOperand(mem.ra(), scratch));
++ ASSERT(0); // lwax not yet implemented
++#else
++ lwzx(dst, MemOperand(mem.ra(), scratch));
++#endif
++ } else {
++#if V8_TARGET_ARCH_PPC64
++ int misaligned = (offset & 3);
++ if (misaligned) {
++ // adjust base to conform to offset alignment requirements
++ // Todo: enhance to use scratch if dst is unsuitable
++ ASSERT(!dst.is(r0));
++ addi(dst, mem.ra(), Operand((offset & 3) - 4));
++ lwa(dst, MemOperand(dst, (offset & ~3) + 4));
++ } else {
++ lwa(dst, mem);
++ }
++#else
++ lwz(dst, mem);
++#endif
++ }
++}
++
++// Variable length depending on whether offset fits into immediate field
++// MemOperand currently only supports d-form
++void MacroAssembler::LoadWord(Register dst, const MemOperand& mem,
++ Register scratch, bool updateForm) {
++ Register base = mem.ra();
++ int offset = mem.offset();
++
++ bool use_dform = true;
++ if (!is_int16(offset)) {
++ use_dform = false;
++ LoadIntLiteral(scratch, offset);
++ }
++
++ if (!updateForm) {
++ if (use_dform) {
++ lwz(dst, mem);
++ } else {
++ lwzx(dst, MemOperand(base, scratch));
++ }
++ } else {
++ if (use_dform) {
++ lwzu(dst, mem);
++ } else {
++ lwzux(dst, MemOperand(base, scratch));
++ }
++ }
++}
++
++// Variable length depending on whether offset fits into immediate field
++// MemOperand current only supports d-form
++void MacroAssembler::StoreWord(Register src, const MemOperand& mem,
++ Register scratch, bool updateForm) {
++ Register base = mem.ra();
++ int offset = mem.offset();
++
++ bool use_dform = true;
++ if (!is_int16(offset)) {
++ use_dform = false;
++ LoadIntLiteral(scratch, offset);
++ }
++
++ if (!updateForm) {
++ if (use_dform) {
++ stw(src, mem);
++ } else {
++ stwx(src, MemOperand(base, scratch));
++ }
++ } else {
++ if (use_dform) {
++ stwu(src, mem);
++ } else {
++ stwux(src, MemOperand(base, scratch));
++ }
++ }
++}
++
++// Variable length depending on whether offset fits into immediate field
++// MemOperand currently only supports d-form
++void MacroAssembler::LoadHalfWord(Register dst, const MemOperand& mem,
++ Register scratch, bool updateForm) {
++ Register base = mem.ra();
++ int offset = mem.offset();
++
++ bool use_dform = true;
++ if (!is_int16(offset)) {
++ use_dform = false;
++ LoadIntLiteral(scratch, offset);
++ }
++
++ if (!updateForm) {
++ if (use_dform) {
++ lhz(dst, mem);
++ } else {
++ lhzx(dst, MemOperand(base, scratch));
++ }
++ } else {
++ // If updateForm is ever true, then lhzu will
++ // need to be implemented
++ assert(0);
++#if 0 // LoadHalfWord w\ update not yet needed
++ if (use_dform) {
++ lhzu(dst, mem);
++ } else {
++ lhzux(dst, MemOperand(base, scratch));
++ }
++#endif
++ }
++}
++
++// Variable length depending on whether offset fits into immediate field
++// MemOperand current only supports d-form
++void MacroAssembler::StoreHalfWord(Register src, const MemOperand& mem,
++ Register scratch, bool updateForm) {
++ Register base = mem.ra();
++ int offset = mem.offset();
++
++ bool use_dform = true;
++ if (!is_int16(offset)) {
++ use_dform = false;
++ LoadIntLiteral(scratch, offset);
++ }
++
++ if (!updateForm) {
++ if (use_dform) {
++ sth(src, mem);
++ } else {
++ sthx(src, MemOperand(base, scratch));
++ }
++ } else {
++ // If updateForm is ever true, then sthu will
++ // need to be implemented
++ assert(0);
++#if 0 // StoreHalfWord w\ update not yet needed
++ if (use_dform) {
++ sthu(src, mem);
++ } else {
++ sthux(src, MemOperand(base, scratch));
++ }
++#endif
++ }
++}
++
++// Variable length depending on whether offset fits into immediate field
++// MemOperand currently only supports d-form
++void MacroAssembler::LoadByte(Register dst, const MemOperand& mem,
++ Register scratch, bool updateForm) {
++ Register base = mem.ra();
++ int offset = mem.offset();
++
++ bool use_dform = true;
++ if (!is_int16(offset)) {
++ use_dform = false;
++ LoadIntLiteral(scratch, offset);
++ }
++
++ if (!updateForm) {
++ if (use_dform) {
++ lbz(dst, mem);
++ } else {
++ lbzx(dst, MemOperand(base, scratch));
++ }
++ } else {
++ // If updateForm is ever true, then lbzu will
++ // need to be implemented
++ assert(0);
++#if 0 // LoadByte w\ update not yet needed
++ if (use_dform) {
++ lbzu(dst, mem);
++ } else {
++ lbzux(dst, MemOperand(base, scratch));
++ }
++#endif
++ }
++}
++
++// Variable length depending on whether offset fits into immediate field
++// MemOperand current only supports d-form
++void MacroAssembler::StoreByte(Register src, const MemOperand& mem,
++ Register scratch, bool updateForm) {
++ Register base = mem.ra();
++ int offset = mem.offset();
++
++ bool use_dform = true;
++ if (!is_int16(offset)) {
++ use_dform = false;
++ LoadIntLiteral(scratch, offset);
++ }
++
++ if (!updateForm) {
++ if (use_dform) {
++ stb(src, mem);
++ } else {
++ stbx(src, MemOperand(base, scratch));
++ }
++ } else {
++ // If updateForm is ever true, then stbu will
++ // need to be implemented
++ assert(0);
++#if 0 // StoreByte w\ update not yet needed
++ if (use_dform) {
++ stbu(src, mem);
++ } else {
++ stbux(src, MemOperand(base, scratch));
++ }
++#endif
++ }
++}
++
++#ifdef DEBUG
++bool AreAliased(Register reg1,
++ Register reg2,
++ Register reg3,
++ Register reg4,
++ Register reg5,
++ Register reg6) {
++ int n_of_valid_regs = reg1.is_valid() + reg2.is_valid() +
++ reg3.is_valid() + reg4.is_valid() + reg5.is_valid() + reg6.is_valid();
++
++ RegList regs = 0;
++ if (reg1.is_valid()) regs |= reg1.bit();
++ if (reg2.is_valid()) regs |= reg2.bit();
++ if (reg3.is_valid()) regs |= reg3.bit();
++ if (reg4.is_valid()) regs |= reg4.bit();
++ if (reg5.is_valid()) regs |= reg5.bit();
++ if (reg6.is_valid()) regs |= reg6.bit();
++ int n_of_non_aliasing_regs = NumRegs(regs);
++
++ return n_of_valid_regs != n_of_non_aliasing_regs;
++}
++#endif
++
++
++CodePatcher::CodePatcher(byte* address, int instructions)
++ : address_(address),
++ instructions_(instructions),
++ size_(instructions * Assembler::kInstrSize),
++ masm_(NULL, address, size_ + Assembler::kGap) {
++ // Create a new macro assembler pointing to the address of the code to patch.
++ // The size is adjusted with kGap on order for the assembler to generate size
++ // bytes of instructions without failing with buffer size constraints.
++ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
++}
++
++
++CodePatcher::~CodePatcher() {
++ // Indicate that code has changed.
++ CPU::FlushICache(address_, size_);
++
++ // Check that the code was patched as expected.
++ ASSERT(masm_.pc_ == address_ + size_);
++ ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
++}
++
++
++void CodePatcher::Emit(Instr instr) {
++ masm()->emit(instr);
++}
++
++
++void CodePatcher::EmitCondition(Condition cond) {
++ Instr instr = Assembler::instr_at(masm_.pc_);
++ switch (cond) {
++ case eq:
++ instr = (instr & ~kCondMask) | BT;
++ break;
++ case ne:
++ instr = (instr & ~kCondMask) | BF;
++ break;
++ default:
++ UNIMPLEMENTED();
++ }
++ masm_.emit(instr);
++}
++
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/macro-assembler-ppc.h.ppc
v8-3.14.5.10/src/ppc/macro-assembler-ppc.h
+--- v8-3.14.5.10/src/ppc/macro-assembler-ppc.h.ppc 2016-06-07 14:15:46.002392937 -0400
++++ v8-3.14.5.10/src/ppc/macro-assembler-ppc.h 2016-06-07 14:15:46.002392937 -0400
+@@ -0,0 +1,1657 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_MACRO_ASSEMBLER_PPC_H_
++#define V8_PPC_MACRO_ASSEMBLER_PPC_H_
++
++#include "assembler.h"
++#include "frames.h"
++#include "v8globals.h"
++
++namespace v8 {
++namespace internal {
++
++// ----------------------------------------------------------------------------
++// Static helper functions
++
++// Generate a MemOperand for loading a field from an object.
++inline MemOperand FieldMemOperand(Register object, int offset) {
++ return MemOperand(object, offset - kHeapObjectTag);
++}
++
++
++
++// Give alias names to registers
++const Register cp = { 20 }; // JavaScript context pointer
++const Register kRootRegister = { 21 }; // Roots array pointer.
++
++// Flags used for the AllocateInNewSpace functions.
++enum AllocationFlags {
++ // No special flags.
++ NO_ALLOCATION_FLAGS = 0,
++ // Return the pointer to the allocated already tagged as a heap object.
++ TAG_OBJECT = 1 << 0,
++ // The content of the result register already contains the allocation top in
++ // new space.
++ RESULT_CONTAINS_TOP = 1 << 1,
++ // Specify that the requested size of the space to allocate is specified in
++ // words instead of bytes.
++ SIZE_IN_WORDS = 1 << 2
++};
++
++// Flags used for AllocateHeapNumber
++enum TaggingMode {
++ // Tag the result.
++ TAG_RESULT,
++ // Don't tag
++ DONT_TAG_RESULT
++};
++
++// Flags used for the ObjectToDoubleVFPRegister function.
++enum ObjectToDoubleFlags {
++ // No special flags.
++ NO_OBJECT_TO_DOUBLE_FLAGS = 0,
++ // Object is known to be a non smi.
++ OBJECT_NOT_SMI = 1 << 0,
++ // Don't load NaNs or infinities, branch to the non number case instead.
++ AVOID_NANS_AND_INFINITIES = 1 << 1
++};
++
++
++enum RememberedSetAction { EMIT_REMEMBERED_SET, OMIT_REMEMBERED_SET };
++enum SmiCheck { INLINE_SMI_CHECK, OMIT_SMI_CHECK };
++enum LinkRegisterStatus { kLRHasNotBeenSaved, kLRHasBeenSaved };
++
++
++#ifdef DEBUG
++bool AreAliased(Register reg1,
++ Register reg2,
++ Register reg3 = no_reg,
++ Register reg4 = no_reg,
++ Register reg5 = no_reg,
++ Register reg6 = no_reg);
++#endif
++
++// These exist to provide portability between 32 and 64bit
++#if V8_TARGET_ARCH_PPC64
++#define LoadPU ldu
++#define LoadPX ldx
++#define LoadPUX ldux
++#define StorePU stdu
++#define StorePX stdx
++#define StorePUX stdux
++#define ShiftLeftImm sldi
++#define ShiftRightImm srdi
++#define ClearLeftImm clrldi
++#define ClearRightImm clrrdi
++#define ShiftRightArithImm sradi
++#define ShiftLeft sld
++#define ShiftRight srd
++#define ShiftRightArith srad
++#define Mul mulld
++#define Div divd
++#else
++#define LoadPU lwzu
++#define LoadPX lwzx
++#define LoadPUX lwzux
++#define StorePU stwu
++#define StorePX stwx
++#define StorePUX stwux
++#define ShiftLeftImm slwi
++#define ShiftRightImm srwi
++#define ClearLeftImm clrlwi
++#define ClearRightImm clrrwi
++#define ShiftRightArithImm srawi
++#define ShiftLeft slw
++#define ShiftRight srw
++#define ShiftRightArith sraw
++#define Mul mullw
++#define Div divw
++#endif
++
++
++// MacroAssembler implements a collection of frequently used macros.
++class MacroAssembler: public Assembler {
++ public:
++ // The isolate parameter can be NULL if the macro assembler should
++ // not use isolate-dependent functionality. In this case, it's the
++ // responsibility of the caller to never invoke such function on the
++ // macro assembler.
++ MacroAssembler(Isolate* isolate, void* buffer, int size);
++
++ // Jump, Call, and Ret pseudo instructions implementing inter-working.
++ void Jump(Register target, Condition cond = al);
++ void Jump(Address target, RelocInfo::Mode rmode, Condition cond = al,
++ CRegister cr = cr7);
++ void Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
++ static int CallSize(Register target, Condition cond = al);
++ void Call(Register target, Condition cond = al);
++ int CallSize(Address target, RelocInfo::Mode rmode, Condition cond = al);
++ static int CallSizeNotPredictableCodeSize(Address target,
++ RelocInfo::Mode rmode,
++ Condition cond = al);
++ void Call(Address target, RelocInfo::Mode rmode, Condition cond = al);
++ int CallSize(Handle<Code> code,
++ RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
++ TypeFeedbackId ast_id = TypeFeedbackId::None(),
++ Condition cond = al);
++ void Call(Handle<Code> code,
++ RelocInfo::Mode rmode = RelocInfo::CODE_TARGET,
++ TypeFeedbackId ast_id = TypeFeedbackId::None(),
++ Condition cond = al);
++ void Ret(Condition cond = al);
++
++ // Emit code to discard a non-negative number of pointer-sized elements
++ // from the stack, clobbering only the sp register.
++ void Drop(int count, Condition cond = al);
++
++ void Ret(int drop, Condition cond = al);
++
++ void Call(Label* target);
++
++ // Emit call to the code we are currently generating.
++ void CallSelf() {
++ Handle<Code> self(reinterpret_cast<Code**>(CodeObject().location()));
++ Call(self, RelocInfo::CODE_TARGET);
++ }
++
++ // Register move. May do nothing if the registers are identical.
++ void Move(Register dst, Handle<Object> value);
++ void Move(Register dst, Register src, Condition cond = al);
++ void Move(DoubleRegister dst, DoubleRegister src);
++
++ void MultiPush(RegList regs);
++ void MultiPop(RegList regs);
++
++ // Load an object from the root table.
++ void LoadRoot(Register destination,
++ Heap::RootListIndex index,
++ Condition cond = al);
++ // Store an object to the root table.
++ void StoreRoot(Register source,
++ Heap::RootListIndex index,
++ Condition cond = al);
++
++ void LoadHeapObject(Register dst, Handle<HeapObject> object);
++
++ void LoadObject(Register result, Handle<Object> object) {
++ if (object->IsHeapObject()) {
++ LoadHeapObject(result, Handle<HeapObject>::cast(object));
++ } else {
++ Move(result, object);
++ }
++ }
++
++ // ---------------------------------------------------------------------------
++ // GC Support
++
++ void IncrementalMarkingRecordWriteHelper(Register object,
++ Register value,
++ Register address);
++
++ enum RememberedSetFinalAction {
++ kReturnAtEnd,
++ kFallThroughAtEnd
++ };
++
++ // Record in the remembered set the fact that we have a pointer to new space
++ // at the address pointed to by the addr register. Only works if addr is not
++ // in new space.
++ void RememberedSetHelper(Register object, // Used for debug code.
++ Register addr,
++ Register scratch,
++ SaveFPRegsMode save_fp,
++ RememberedSetFinalAction and_then);
++
++ void CheckPageFlag(Register object,
++ Register scratch,
++ int mask,
++ Condition cc,
++ Label* condition_met);
++
++ // Check if object is in new space. Jumps if the object is not in new space.
++ // The register scratch can be object itself, but scratch will be clobbered.
++ void JumpIfNotInNewSpace(Register object,
++ Register scratch,
++ Label* branch) {
++ InNewSpace(object, scratch, ne, branch);
++ }
++
++ // Check if object is in new space. Jumps if the object is in new space.
++ // The register scratch can be object itself, but it will be clobbered.
++ void JumpIfInNewSpace(Register object,
++ Register scratch,
++ Label* branch) {
++ InNewSpace(object, scratch, eq, branch);
++ }
++
++ // Check if an object has a given incremental marking color.
++ void HasColor(Register object,
++ Register scratch0,
++ Register scratch1,
++ Label* has_color,
++ int first_bit,
++ int second_bit);
++
++ void JumpIfBlack(Register object,
++ Register scratch0,
++ Register scratch1,
++ Label* on_black);
++
++ // Checks the color of an object. If the object is already grey or black
++ // then we just fall through, since it is already live. If it is white and
++ // we can determine that it doesn't need to be scanned, then we just mark it
++ // black and fall through. For the rest we jump to the label so the
++ // incremental marker can fix its assumptions.
++ void EnsureNotWhite(Register object,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* object_is_white_and_not_data);
++
++ // Detects conservatively whether an object is data-only, i.e. it does need to
++ // be scanned by the garbage collector.
++ void JumpIfDataObject(Register value,
++ Register scratch,
++ Label* not_data_object);
++
++ // Notify the garbage collector that we wrote a pointer into an object.
++ // |object| is the object being stored into, |value| is the object being
++ // stored. value and scratch registers are clobbered by the operation.
++ // The offset is the offset from the start of the object, not the offset from
++ // the tagged HeapObject pointer. For use with FieldOperand(reg, off).
++ void RecordWriteField(
++ Register object,
++ int offset,
++ Register value,
++ Register scratch,
++ LinkRegisterStatus lr_status,
++ SaveFPRegsMode save_fp,
++ RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
++ SmiCheck smi_check = INLINE_SMI_CHECK);
++
++ // As above, but the offset has the tag presubtracted. For use with
++ // MemOperand(reg, off).
++ inline void RecordWriteContextSlot(
++ Register context,
++ int offset,
++ Register value,
++ Register scratch,
++ LinkRegisterStatus lr_status,
++ SaveFPRegsMode save_fp,
++ RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
++ SmiCheck smi_check = INLINE_SMI_CHECK) {
++ RecordWriteField(context,
++ offset + kHeapObjectTag,
++ value,
++ scratch,
++ lr_status,
++ save_fp,
++ remembered_set_action,
++ smi_check);
++ }
++
++ // For a given |object| notify the garbage collector that the slot |address|
++ // has been written. |value| is the object being stored. The value and
++ // address registers are clobbered by the operation.
++ void RecordWrite(
++ Register object,
++ Register address,
++ Register value,
++ LinkRegisterStatus lr_status,
++ SaveFPRegsMode save_fp,
++ RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
++ SmiCheck smi_check = INLINE_SMI_CHECK);
++
++ // Push a handle.
++ void Push(Handle<Object> handle);
++
++ // Push two registers. Pushes leftmost register first (to highest address).
++ void Push(Register src1, Register src2, Condition cond = al) {
++ ASSERT(!src1.is(src2));
++ StorePU(src1, MemOperand(sp, -kPointerSize));
++ StorePU(src2, MemOperand(sp, -kPointerSize));
++ }
++
++ // Push three registers. Pushes leftmost register first (to highest address).
++ void Push(Register src1, Register src2, Register src3, Condition cond = al) {
++ ASSERT(!src1.is(src2));
++ ASSERT(!src2.is(src3));
++ ASSERT(!src1.is(src3));
++ StorePU(src1, MemOperand(sp, -kPointerSize));
++ Push(src2, src3, cond);
++ }
++
++ // Push four registers. Pushes leftmost register first (to highest address).
++ void Push(Register src1,
++ Register src2,
++ Register src3,
++ Register src4,
++ Condition cond = al) {
++ ASSERT(!src1.is(src2));
++ ASSERT(!src2.is(src3));
++ ASSERT(!src1.is(src3));
++ ASSERT(!src1.is(src4));
++ ASSERT(!src2.is(src4));
++ ASSERT(!src3.is(src4));
++
++ StorePU(src1, MemOperand(sp, -kPointerSize));
++ Push(src2, src3, src4, cond);
++ }
++
++ // Pop two registers. Pops rightmost register first (from lower address).
++ void Pop(Register src1, Register src2, Condition cond = al) {
++ ASSERT(!src1.is(src2));
++ ASSERT(cond == al);
++ LoadP(src2, MemOperand(sp, 0));
++ LoadP(src1, MemOperand(sp, kPointerSize));
++ addi(sp, sp, Operand(2 * kPointerSize));
++ }
++
++ // Pop three registers. Pops rightmost register first (from lower address).
++ void Pop(Register src1, Register src2, Register src3, Condition cond = al) {
++ ASSERT(!src1.is(src2));
++ ASSERT(!src2.is(src3));
++ ASSERT(!src1.is(src3));
++ ASSERT(cond == al);
++ LoadP(src3, MemOperand(sp, 0));
++ LoadP(src2, MemOperand(sp, kPointerSize));
++ LoadP(src1, MemOperand(sp, 2 * kPointerSize));
++ addi(sp, sp, Operand(3 * kPointerSize));
++ }
++
++ // Pop four registers. Pops rightmost register first (from lower address).
++ void Pop(Register src1,
++ Register src2,
++ Register src3,
++ Register src4,
++ Condition cond = al) {
++ ASSERT(!src1.is(src2));
++ ASSERT(!src2.is(src3));
++ ASSERT(!src1.is(src3));
++ ASSERT(!src1.is(src4));
++ ASSERT(!src2.is(src4));
++ ASSERT(!src3.is(src4));
++ ASSERT(cond == al);
++ LoadP(src4, MemOperand(sp, 0));
++ LoadP(src3, MemOperand(sp, kPointerSize));
++ LoadP(src2, MemOperand(sp, 2 * kPointerSize));
++ LoadP(src1, MemOperand(sp, 3 * kPointerSize));
++ addi(sp, sp, Operand(4 * kPointerSize));
++ }
++
++ // Push and pop the registers that can hold pointers, as defined by the
++ // RegList constant kSafepointSavedRegisters.
++ void PushSafepointRegisters();
++ void PopSafepointRegisters();
++
++ // Store value in register src in the safepoint stack slot for
++ // register dst.
++ void StoreToSafepointRegisterSlot(Register src, Register dst);
++ // Load the value of the src register from its safepoint stack slot
++ // into register dst.
++ void LoadFromSafepointRegisterSlot(Register dst, Register src);
++
++ // Flush the I-cache from asm code. You should use CPU::FlushICache from C.
++ // Does not handle errors.
++ void FlushICache(Register address, size_t size,
++ Register scratch);
++
++ // Enter exit frame.
++ // stack_space - extra stack space, used for alignment before call to C.
++ void EnterExitFrame(bool save_doubles, int stack_space = 0);
++
++ // Leave the current exit frame. Expects the return value in r0.
++ // Expect the number of values, pushed prior to the exit frame, to
++ // remove in a register (or no_reg, if there is nothing to remove).
++ void LeaveExitFrame(bool save_doubles, Register argument_count);
++
++ // Get the actual activation frame alignment for target environment.
++ static int ActivationFrameAlignment();
++
++ void LoadContext(Register dst, int context_chain_length);
++
++ // Conditionally load the cached Array transitioned map of type
++ // transitioned_kind from the native context if the map in register
++ // map_in_out is the cached Array map in the native context of
++ // expected_kind.
++ void LoadTransitionedArrayMapConditional(
++ ElementsKind expected_kind,
++ ElementsKind transitioned_kind,
++ Register map_in_out,
++ Register scratch,
++ Label* no_map_match);
++
++ // Load the initial map for new Arrays from a JSFunction.
++ void LoadInitialArrayMap(Register function_in,
++ Register scratch,
++ Register map_out,
++ bool can_have_holes);
++
++ void LoadGlobalFunction(int index, Register function);
++
++ // Load the initial map from the global function. The registers
++ // function and map can be the same, function is then overwritten.
++ void LoadGlobalFunctionInitialMap(Register function,
++ Register map,
++ Register scratch);
++
++ void InitializeRootRegister() {
++ ExternalReference roots_array_start =
++ ExternalReference::roots_array_start(isolate());
++ mov(kRootRegister, Operand(roots_array_start));
++ }
++
++ // ----------------------------------------------------------------
++ // new PPC macro-assembler interfaces that are slightly higher level
++ // than assembler-ppc and may generate variable length sequences
++
++ // load a literal signed int value <value> to GPR <dst>
++ void LoadIntLiteral(Register dst, int value);
++
++ // load an SMI value <value> to GPR <dst>
++ void LoadSmiLiteral(Register dst, Smi *smi);
++
++ // load a literal double value <value> to FPR <result>
++ void LoadDoubleLiteral(DwVfpRegister result,
++ double value,
++ Register scratch);
++
++ void LoadWord(Register dst,
++ const MemOperand& mem,
++ Register scratch,
++ bool updateForm = false);
++
++ void LoadWordArith(Register dst,
++ const MemOperand& mem,
++ Register scratch = no_reg);
++
++ void StoreWord(Register src,
++ const MemOperand& mem,
++ Register scratch,
++ bool updateForm = false);
++
++ void LoadHalfWord(Register dst,
++ const MemOperand& mem,
++ Register scratch,
++ bool updateForm = false);
++
++ void StoreHalfWord(Register src,
++ const MemOperand& mem,
++ Register scratch,
++ bool updateForm = false);
++
++ void LoadByte(Register dst,
++ const MemOperand& mem,
++ Register scratch,
++ bool updateForm = false);
++
++ void StoreByte(Register src,
++ const MemOperand& mem,
++ Register scratch,
++ bool updateForm = false);
++
++
++
++ void Add(Register dst, Register src, intptr_t value, Register scratch);
++ void Cmpi(Register src1, const Operand& src2, Register scratch,
++ CRegister cr = cr7);
++ void Cmpli(Register src1, const Operand& src2, Register scratch,
++ CRegister cr = cr7);
++ void And(Register ra, Register rs, const Operand& rb, RCBit rc = LeaveRC);
++ void Or(Register ra, Register rs, const Operand& rb, RCBit rc = LeaveRC);
++ void Xor(Register ra, Register rs, const Operand& rb, RCBit rc = LeaveRC);
++
++ void AddSmiLiteral(Register dst, Register src, Smi *smi, Register scratch);
++ void SubSmiLiteral(Register dst, Register src, Smi *smi, Register scratch);
++ void CmpSmiLiteral(Register src1, Smi *smi, Register scratch,
++ CRegister cr = cr7);
++ void CmplSmiLiteral(Register src1, Smi *smi, Register scratch,
++ CRegister cr = cr7);
++ void AndSmiLiteral(Register dst, Register src, Smi *smi, Register scratch,
++ RCBit rc = LeaveRC);
++
++ // Set new rounding mode RN to FPSCR
++ void SetRoundingMode(VFPRoundingMode RN);
++
++ // reset rounding mode to default (kRoundToNearest)
++ void ResetRoundingMode();
++
++ // These exist to provide portability between 32 and 64bit
++ void LoadP(Register dst, const MemOperand& mem, Register scratch = no_reg);
++ void StoreP(Register src, const MemOperand& mem, Register scratch = no_reg);
++
++ // ---------------------------------------------------------------------------
++ // JavaScript invokes
++
++ // Set up call kind marking in ecx. The method takes ecx as an
++ // explicit first parameter to make the code more readable at the
++ // call sites.
++ void SetCallKind(Register dst, CallKind kind);
++
++ // Invoke the JavaScript function code by either calling or jumping.
++ void InvokeCode(Register code,
++ const ParameterCount& expected,
++ const ParameterCount& actual,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind);
++
++ void InvokeCode(Handle<Code> code,
++ const ParameterCount& expected,
++ const ParameterCount& actual,
++ RelocInfo::Mode rmode,
++ InvokeFlag flag,
++ CallKind call_kind);
++
++ // Invoke the JavaScript function in the given register. Changes the
++ // current context to the context in the function before invoking.
++ void InvokeFunction(Register function,
++ const ParameterCount& actual,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind);
++
++ void InvokeFunction(Handle<JSFunction> function,
++ const ParameterCount& actual,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind);
++
++ void IsObjectJSObjectType(Register heap_object,
++ Register map,
++ Register scratch,
++ Label* fail);
++
++ void IsInstanceJSObjectType(Register map,
++ Register scratch,
++ Label* fail);
++
++ void IsObjectJSStringType(Register object,
++ Register scratch,
++ Label* fail);
++
++#ifdef ENABLE_DEBUGGER_SUPPORT
++ // ---------------------------------------------------------------------------
++ // Debugger Support
++
++ void DebugBreak();
++#endif
++
++ // ---------------------------------------------------------------------------
++ // Exception handling
++
++ // Push a new try handler and link into try handler chain.
++ void PushTryHandler(StackHandler::Kind kind, int handler_index);
++
++ // Unlink the stack handler on top of the stack from the try handler chain.
++ // Must preserve the result register.
++ void PopTryHandler();
++
++ // Passes thrown value to the handler of top of the try handler chain.
++ void Throw(Register value);
++
++ // Propagates an uncatchable exception to the top of the current JS stack's
++ // handler chain.
++ void ThrowUncatchable(Register value);
++
++ // ---------------------------------------------------------------------------
++ // Inline caching support
++
++ // Generate code for checking access rights - used for security checks
++ // on access to global objects across environments. The holder register
++ // is left untouched, whereas both scratch registers are clobbered.
++ void CheckAccessGlobalProxy(Register holder_reg,
++ Register scratch,
++ Label* miss);
++
++ void GetNumberHash(Register t0, Register scratch);
++
++ void LoadFromNumberDictionary(Label* miss,
++ Register elements,
++ Register key,
++ Register result,
++ Register t0,
++ Register t1,
++ Register t2);
++
++
++ inline void MarkCode(NopMarkerTypes type) {
++ nop(type);
++ }
++
++ // Check if the given instruction is a 'type' marker.
++ // i.e. check if is is a mov r<type>, r<type> (referenced as nop(type))
++ // These instructions are generated to mark special location in the code,
++ // like some special IC code.
++ static inline bool IsMarkedCode(Instr instr, int type) {
++ ASSERT((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER));
++ return IsNop(instr, type);
++ }
++
++
++ static inline int GetCodeMarker(Instr instr) {
++ int dst_reg_offset = 12;
++ int dst_mask = 0xf << dst_reg_offset;
++ int src_mask = 0xf;
++ int dst_reg = (instr & dst_mask) >> dst_reg_offset;
++ int src_reg = instr & src_mask;
++ uint32_t non_register_mask = ~(dst_mask | src_mask);
++ uint32_t mov_mask = al | 13 << 21;
++
++ // Return <n> if we have a mov rn rn, else return -1.
++ int type = ((instr & non_register_mask) == mov_mask) &&
++ (dst_reg == src_reg) &&
++ (FIRST_IC_MARKER <= dst_reg) && (dst_reg <
LAST_CODE_MARKER)
++ ? src_reg
++ : -1;
++ ASSERT((type == -1) ||
++ ((FIRST_IC_MARKER <= type) && (type < LAST_CODE_MARKER)));
++ return type;
++ }
++
++
++ // ---------------------------------------------------------------------------
++ // Allocation support
++
++ // Allocate an object in new space. The object_size is specified
++ // either in bytes or in words if the allocation flag SIZE_IN_WORDS
++ // is passed. If the new space is exhausted control continues at the
++ // gc_required label. The allocated object is returned in result. If
++ // the flag tag_allocated_object is true the result is tagged as as
++ // a heap object. All registers are clobbered also when control
++ // continues at the gc_required label.
++ void AllocateInNewSpace(int object_size,
++ Register result,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required,
++ AllocationFlags flags);
++ void AllocateInNewSpace(Register object_size,
++ Register result,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required,
++ AllocationFlags flags);
++
++ // Undo allocation in new space. The object passed and objects allocated after
++ // it will no longer be allocated. The caller must make sure that no pointers
++ // are left to the object(s) no longer allocated as they would be invalid when
++ // allocation is undone.
++ void UndoAllocationInNewSpace(Register object, Register scratch);
++
++
++ void AllocateTwoByteString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* gc_required);
++ void AllocateAsciiString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* gc_required);
++ void AllocateTwoByteConsString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required);
++ void AllocateAsciiConsString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required);
++ void AllocateTwoByteSlicedString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required);
++ void AllocateAsciiSlicedString(Register result,
++ Register length,
++ Register scratch1,
++ Register scratch2,
++ Label* gc_required);
++
++ // Allocates a heap number or jumps to the gc_required label if the young
++ // space is full and a scavenge is needed. All registers are clobbered also
++ // when control continues at the gc_required label.
++ void AllocateHeapNumber(Register result,
++ Register scratch1,
++ Register scratch2,
++ Register heap_number_map,
++ Label* gc_required,
++ TaggingMode tagging_mode = TAG_RESULT);
++ void AllocateHeapNumberWithValue(Register result,
++ DwVfpRegister value,
++ Register scratch1,
++ Register scratch2,
++ Register heap_number_map,
++ Label* gc_required);
++
++ // Copies a fixed number of fields of heap objects from src to dst.
++ void CopyFields(Register dst, Register src, RegList temps, int field_count);
++
++ // Copies a number of bytes from src to dst. All registers are clobbered. On
++ // exit src and dst will point to the place just after where the last byte was
++ // read or written and length will be zero.
++ void CopyBytes(Register src,
++ Register dst,
++ Register length,
++ Register scratch);
++
++ // Initialize fields with filler values. Fields starting at |start_offset|
++ // not including end_offset are overwritten with the value in |filler|. At
++ // the end the loop, |start_offset| takes the value of |end_offset|.
++ void InitializeFieldsWithFiller(Register start_offset,
++ Register end_offset,
++ Register filler);
++
++ // ---------------------------------------------------------------------------
++ // Support functions.
++
++ // Try to get function prototype of a function and puts the value in
++ // the result register. Checks that the function really is a
++ // function and jumps to the miss label if the fast checks fail. The
++ // function register will be untouched; the other registers may be
++ // clobbered.
++ void TryGetFunctionPrototype(Register function,
++ Register result,
++ Register scratch,
++ Label* miss,
++ bool miss_on_bound_function = false);
++
++ // Compare object type for heap object. heap_object contains a non-Smi
++ // whose object type should be compared with the given type. This both
++ // sets the flags and leaves the object type in the type_reg register.
++ // It leaves the map in the map register (unless the type_reg and map register
++ // are the same register). It leaves the heap object in the heap_object
++ // register unless the heap_object register is the same register as one of the
++ // other registers.
++ void CompareObjectType(Register heap_object,
++ Register map,
++ Register type_reg,
++ InstanceType type);
++
++ // Compare instance type in a map. map contains a valid map object whose
++ // object type should be compared with the given type. This both
++ // sets the flags and leaves the object type in the type_reg register.
++ void CompareInstanceType(Register map,
++ Register type_reg,
++ InstanceType type);
++
++
++ // Check if a map for a JSObject indicates that the object has fast elements.
++ // Jump to the specified label if it does not.
++ void CheckFastElements(Register map,
++ Register scratch,
++ Label* fail);
++
++ // Check if a map for a JSObject indicates that the object can have both smi
++ // and HeapObject elements. Jump to the specified label if it does not.
++ void CheckFastObjectElements(Register map,
++ Register scratch,
++ Label* fail);
++
++ // Check if a map for a JSObject indicates that the object has fast smi only
++ // elements. Jump to the specified label if it does not.
++ void CheckFastSmiElements(Register map,
++ Register scratch,
++ Label* fail);
++
++ // Check to see if maybe_number can be stored as a double in
++ // FastDoubleElements. If it can, store it at the index specified by key in
++ // the FastDoubleElements array elements. Otherwise jump to fail, in which
++ // case scratch2, scratch3 and scratch4 are unmodified.
++ void StoreNumberToDoubleElements(Register value_reg,
++ Register key_reg,
++ Register receiver_reg,
++ // All regs below here overwritten.
++ Register elements_reg,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Label* fail);
++
++ // Compare an object's map with the specified map and its transitioned
++ // elements maps if mode is ALLOW_ELEMENT_TRANSITION_MAPS. Condition flags are
++ // set with result of map compare. If multiple map compares are required, the
++ // compare sequences branches to early_success.
++ void CompareMap(Register obj,
++ Register scratch,
++ Handle<Map> map,
++ Label* early_success,
++ CompareMapMode mode = REQUIRE_EXACT_MAP);
++
++ // As above, but the map of the object is already loaded into the register
++ // which is preserved by the code generated.
++ void CompareMap(Register obj_map,
++ Handle<Map> map,
++ Label* early_success,
++ CompareMapMode mode = REQUIRE_EXACT_MAP);
++
++ // Check if the map of an object is equal to a specified map and branch to
++ // label if not. Skip the smi check if not required (object is known to be a
++ // heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match
++ // against maps that are ElementsKind transition maps of the specified map.
++ void CheckMap(Register obj,
++ Register scratch,
++ Handle<Map> map,
++ Label* fail,
++ SmiCheckType smi_check_type,
++ CompareMapMode mode = REQUIRE_EXACT_MAP);
++
++
++ void CheckMap(Register obj,
++ Register scratch,
++ Heap::RootListIndex index,
++ Label* fail,
++ SmiCheckType smi_check_type);
++
++
++ // Check if the map of an object is equal to a specified map and branch to a
++ // specified target if equal. Skip the smi check if not required (object is
++ // known to be a heap object)
++ void DispatchMap(Register obj,
++ Register scratch,
++ Handle<Map> map,
++ Handle<Code> success,
++ SmiCheckType smi_check_type);
++
++
++ // Compare the object in a register to a value from the root list.
++ // Uses the ip register as scratch.
++ void CompareRoot(Register obj, Heap::RootListIndex index);
++
++
++ // Load and check the instance type of an object for being a string.
++ // Loads the type into the second argument register.
++ // Returns a condition that will be enabled if the object was a string.
++ Condition IsObjectStringType(Register obj,
++ Register type) {
++ LoadP(type, FieldMemOperand(obj, HeapObject::kMapOffset));
++ lbz(type, FieldMemOperand(type, Map::kInstanceTypeOffset));
++ andi(r0, type, Operand(kIsNotStringMask));
++ ASSERT_EQ(0, kStringTag);
++ return eq;
++ }
++
++
++ // Generates code for reporting that an illegal operation has
++ // occurred.
++ void IllegalOperation(int num_arguments);
++
++ // Picks out an array index from the hash field.
++ // Register use:
++ // hash - holds the index's hash. Clobbered.
++ // index - holds the overwritten index on exit.
++ void IndexFromHash(Register hash, Register index);
++
++ // Get the number of least significant bits from a register
++ void GetLeastBitsFromSmi(Register dst, Register src, int num_least_bits);
++ void GetLeastBitsFromInt32(Register dst, Register src, int mun_least_bits);
++
++ // Load the value of a smi object into a FP double register. The register
++ // scratch1 can be the same register as smi in which case smi will hold the
++ // untagged value afterwards.
++ void SmiToDoubleFPRegister(Register smi,
++ DwVfpRegister value,
++ Register scratch1);
++
++ // Overflow handling functions.
++ // Usage: call the appropriate arithmetic function and then call one of the
++ // flow control functions with the corresponding label.
++
++ // Compute dst = left + right, setting condition codes. dst may be same as
++ // either left or right (or a unique register). left and right must not be
++ // the same register.
++ void AddAndCheckForOverflow(Register dst,
++ Register left,
++ Register right,
++ Register overflow_dst,
++ Register scratch = r0);
++
++ // Compute dst = left - right, setting condition codes. dst may be same as
++ // either left or right (or a unique register). left and right must not be
++ // the same register.
++ void SubAndCheckForOverflow(Register dst,
++ Register left,
++ Register right,
++ Register overflow_dst,
++ Register scratch = r0);
++
++ void BranchOnOverflow(Label* label) {
++ blt(label, cr0);
++ }
++
++ void BranchOnNoOverflow(Label* label) {
++ bge(label, cr0);
++ }
++
++ void RetOnOverflow(void) {
++ Label label;
++
++ blt(&label, cr0);
++ Ret();
++ bind(&label);
++ }
++
++ void RetOnNoOverflow(void) {
++ Label label;
++
++ bge(&label, cr0);
++ Ret();
++ bind(&label);
++ }
++
++ // Convert the HeapNumber pointed to by source to a 32bits signed integer
++ // dest. If the HeapNumber does not fit into a 32bits signed integer branch
++ // to not_int32 label. If VFP3 is available double_scratch is used but not
++ // scratch2
++ void ConvertToInt32(Register source,
++ Register dest,
++ Register scratch,
++ Register scratch2,
++ DwVfpRegister double_scratch,
++ Label *not_int32);
++
++ // Truncates a double using a specific rounding mode, and writes the value
++ // to the result register.
++ // Clears the z flag (ne condition) if an overflow occurs.
++ // If kCheckForInexactConversion is passed, the z flag is also cleared if the
++ // conversion was inexact, i.e. if the double value could not be converted
++ // exactly to a 32-bit integer.
++ void EmitVFPTruncate(VFPRoundingMode rounding_mode,
++ Register result,
++ DwVfpRegister double_input,
++ Register scratch,
++ DwVfpRegister double_scratch,
++ CheckForInexactConversion check
++ = kDontCheckForInexactConversion);
++
++ // Helper for EmitECMATruncate.
++ // This will truncate a floating-point value outside of the signed 32bit
++ // integer range to a 32bit signed integer.
++ // Expects the double value loaded in input_high and input_low.
++ // Exits with the answer in 'result'.
++ // Note that this code does not work for values in the 32bit range!
++ void EmitOutOfInt32RangeTruncate(Register result,
++ Register input_high,
++ Register input_low,
++ Register scratch);
++
++ // Performs a truncating conversion of a floating point number as used by
++ // the JS bitwise operations. See ECMA-262 9.5: ToInt32.
++ // Exits with 'result' holding the answer and all other registers clobbered.
++ void EmitECMATruncate(Register result,
++ DwVfpRegister double_input,
++ DwVfpRegister double_scratch,
++ Register scratch,
++ Register scratch2,
++ Register scratch3);
++
++ // ---------------------------------------------------------------------------
++ // Runtime calls
++
++ // Call a code stub.
++ void CallStub(CodeStub* stub, Condition cond = al);
++
++ // Call a code stub.
++ void TailCallStub(CodeStub* stub, Condition cond = al);
++
++ // Call a runtime routine.
++ void CallRuntime(const Runtime::Function* f, int num_arguments);
++ void CallRuntimeSaveDoubles(Runtime::FunctionId id);
++
++ // Convenience function: Same as above, but takes the fid instead.
++ void CallRuntime(Runtime::FunctionId fid, int num_arguments);
++
++ // Convenience function: call an external reference.
++ void CallExternalReference(const ExternalReference& ext,
++ int num_arguments);
++
++ // Tail call of a runtime routine (jump).
++ // Like JumpToExternalReference, but also takes care of passing the number
++ // of parameters.
++ void TailCallExternalReference(const ExternalReference& ext,
++ int num_arguments,
++ int result_size);
++
++ // Convenience function: tail call a runtime routine (jump).
++ void TailCallRuntime(Runtime::FunctionId fid,
++ int num_arguments,
++ int result_size);
++
++ int CalculateStackPassedWords(int num_reg_arguments,
++ int num_double_arguments);
++
++ // Before calling a C-function from generated code, align arguments on stack.
++ // After aligning the frame, non-register arguments must be stored in
++ // sp[0], sp[4], etc., not pushed. The argument count assumes all arguments
++ // are word sized. If double arguments are used, this function assumes that
++ // all double arguments are stored before core registers; otherwise the
++ // correct alignment of the double values is not guaranteed.
++ // Some compilers/platforms require the stack to be aligned when calling
++ // C++ code.
++ // Needs a scratch register to do some arithmetic. This register will be
++ // trashed.
++ void PrepareCallCFunction(int num_reg_arguments,
++ int num_double_registers,
++ Register scratch);
++ void PrepareCallCFunction(int num_reg_arguments,
++ Register scratch);
++
++ // There are two ways of passing double arguments on ARM, depending on
++ // whether soft or hard floating point ABI is used. These functions
++ // abstract parameter passing for the three different ways we call
++ // C functions from generated code.
++ void SetCallCDoubleArguments(DoubleRegister dreg);
++ void SetCallCDoubleArguments(DoubleRegister dreg1, DoubleRegister dreg2);
++ void SetCallCDoubleArguments(DoubleRegister dreg, Register reg);
++
++ // Calls a C function and cleans up the space for arguments allocated
++ // by PrepareCallCFunction. The called function is not allowed to trigger a
++ // garbage collection, since that might move the code and invalidate the
++ // return address (unless this is somehow accounted for by the called
++ // function).
++ void CallCFunction(ExternalReference function, int num_arguments);
++ void CallCFunction(Register function, int num_arguments);
++ void CallCFunction(ExternalReference function,
++ int num_reg_arguments,
++ int num_double_arguments);
++ void CallCFunction(Register function,
++ int num_reg_arguments,
++ int num_double_arguments);
++
++ void GetCFunctionDoubleResult(const DoubleRegister dst);
++
++ // Calls an API function. Allocates HandleScope, extracts returned value
++ // from handle and propagates exceptions. Restores context. stack_space
++ // - space to be unwound on exit (includes the call JS arguments space and
++ // the additional space allocated for the fast call).
++ void CallApiFunctionAndReturn(ExternalReference function, int stack_space);
++
++ // Jump to a runtime routine.
++ void JumpToExternalReference(const ExternalReference& builtin);
++
++ // Invoke specified builtin JavaScript function. Adds an entry to
++ // the unresolved list if the name does not resolve.
++ void InvokeBuiltin(Builtins::JavaScript id,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper = NullCallWrapper());
++
++ // Store the code object for the given builtin in the target register and
++ // setup the function in r1.
++ void GetBuiltinEntry(Register target, Builtins::JavaScript id);
++
++ // Store the function for the given builtin in the target register.
++ void GetBuiltinFunction(Register target, Builtins::JavaScript id);
++
++ Handle<Object> CodeObject() {
++ ASSERT(!code_object_.is_null());
++ return code_object_;
++ }
++
++
++ // ---------------------------------------------------------------------------
++ // StatsCounter support
++
++ void SetCounter(StatsCounter* counter, int value,
++ Register scratch1, Register scratch2);
++ void IncrementCounter(StatsCounter* counter, int value,
++ Register scratch1, Register scratch2);
++ void DecrementCounter(StatsCounter* counter, int value,
++ Register scratch1, Register scratch2);
++
++
++ // ---------------------------------------------------------------------------
++ // Debugging
++
++ // Calls Abort(msg) if the condition cond is not satisfied.
++ // Use --debug_code to enable.
++ void Assert(Condition cond, const char* msg, CRegister cr = cr7);
++ void AssertRegisterIsRoot(Register reg, Heap::RootListIndex index);
++ void AssertFastElements(Register elements);
++
++ // Like Assert(), but always enabled.
++ void Check(Condition cond, const char* msg, CRegister cr = cr7);
++
++ // Print a message to stdout and abort execution.
++ void Abort(const char* msg);
++
++ // Verify restrictions about code generated in stubs.
++ void set_generating_stub(bool value) { generating_stub_ = value; }
++ bool generating_stub() { return generating_stub_; }
++ void set_allow_stub_calls(bool value) { allow_stub_calls_ = value; }
++ bool allow_stub_calls() { return allow_stub_calls_; }
++ void set_has_frame(bool value) { has_frame_ = value; }
++ bool has_frame() { return has_frame_; }
++ inline bool AllowThisStubCall(CodeStub* stub);
++
++ // ---------------------------------------------------------------------------
++ // Number utilities
++
++ // Check whether the value of reg is a power of two and not zero. If not
++ // control continues at the label not_power_of_two. If reg is a power of two
++ // the register scratch contains the value of (reg - 1) when control falls
++ // through.
++ void JumpIfNotPowerOfTwoOrZero(Register reg,
++ Register scratch,
++ Label* not_power_of_two_or_zero);
++ // Check whether the value of reg is a power of two and not zero.
++ // Control falls through if it is, with scratch containing the mask
++ // value (reg - 1).
++ // Otherwise control jumps to the 'zero_and_neg' label if the value of reg is
++ // zero or negative, or jumps to the 'not_power_of_two' label if the value is
++ // strictly positive but not a power of two.
++ void JumpIfNotPowerOfTwoOrZeroAndNeg(Register reg,
++ Register scratch,
++ Label* zero_and_neg,
++ Label* not_power_of_two);
++
++ // ---------------------------------------------------------------------------
++ // Bit testing/extraction
++ //
++ // Bit numbering is such that the least significant bit is bit 0
++ // (for consistency between 32/64-bit).
++
++ // Extract consecutive bits (defined by rangeStart - rangeEnd) from src
++ // and place them into the least significant bits of dst.
++ inline void ExtractBitRange(Register dst, Register src,
++ int rangeStart, int rangeEnd,
++ RCBit rc = LeaveRC) {
++ ASSERT(rangeStart >= rangeEnd && rangeStart < kBitsPerPointer);
++ int rotate = (rangeEnd == 0) ? 0 : kBitsPerPointer - rangeEnd;
++ int width = rangeStart - rangeEnd + 1;
++#if V8_TARGET_ARCH_PPC64
++ rldicl(dst, src, rotate, kBitsPerPointer - width, rc);
++#else
++ rlwinm(dst, src, rotate, kBitsPerPointer - width, kBitsPerPointer - 1, rc);
++#endif
++ }
++
++ inline void ExtractBit(Register dst, Register src, uint32_t bitNumber,
++ RCBit rc = LeaveRC) {
++ ExtractBitRange(dst, src, bitNumber, bitNumber, rc);
++ }
++
++ // Extract consecutive bits (defined by mask) from src and place them
++ // into the least significant bits of dst.
++ inline void ExtractBitMask(Register dst, Register src, uintptr_t mask,
++ RCBit rc = LeaveRC) {
++ int start = kBitsPerPointer - 1;
++ int end;
++ uintptr_t bit = (1L << start);
++
++ while (bit && (mask & bit) == 0) {
++ start--;
++ bit >>= 1;
++ }
++ end = start;
++ bit >>= 1;
++
++ while (bit && (mask & bit)) {
++ end--;
++ bit >>= 1;
++ }
++
++ // 1-bits in mask must be contiguous
++ ASSERT(bit == 0 || (mask & ((bit << 1) - 1)) == 0);
++
++ ExtractBitRange(dst, src, start, end, rc);
++ }
++
++ // Test single bit in value.
++ inline void TestBit(Register value, int bitNumber,
++ Register scratch = r0) {
++ ExtractBitRange(scratch, value, bitNumber, bitNumber, SetRC);
++ }
++
++ // Test consecutive bit range in value. Range is defined by
++ // rangeStart - rangeEnd.
++ inline void TestBitRange(Register value,
++ int rangeStart, int rangeEnd,
++ Register scratch = r0) {
++ ExtractBitRange(scratch, value, rangeStart, rangeEnd, SetRC);
++ }
++
++ // Test consecutive bit range in value. Range is defined by mask.
++ inline void TestBitMask(Register value, uintptr_t mask,
++ Register scratch = r0) {
++ ExtractBitMask(scratch, value, mask, SetRC);
++ }
++
++ inline void ExtractSignBit(Register dst, Register src,
++ RCBit rc = LeaveRC) {
++ const int bitNumber = kBitsPerPointer - 1;
++ ExtractBitRange(dst, src, bitNumber, bitNumber, rc);
++ }
++
++ inline void ExtractSignBit32(Register dst, Register src,
++ RCBit rc = LeaveRC) {
++ const int bitNumber = 31;
++ ExtractBitRange(dst, src, bitNumber, bitNumber, rc);
++ }
++
++ inline void TestSignBit(Register value,
++ Register scratch = r0) {
++ const int bitNumber = kBitsPerPointer - 1;
++ ExtractBitRange(scratch, value, bitNumber, bitNumber, SetRC);
++ }
++
++ inline void TestSignBit32(Register value,
++ Register scratch = r0) {
++ const int bitNumber = 31;
++ ExtractBitRange(scratch, value, bitNumber, bitNumber, SetRC);
++ }
++
++
++ // ---------------------------------------------------------------------------
++ // Smi utilities
++
++ // Shift left by 1
++ void SmiTag(Register reg, RCBit rc = LeaveRC) {
++ SmiTag(reg, reg, rc);
++ }
++ void SmiTag(Register dst, Register src, RCBit rc = LeaveRC) {
++ ShiftLeftImm(dst, src, Operand(kSmiShift), rc);
++ }
++
++#if !V8_TARGET_ARCH_PPC64
++ // Test for overflow < 0: use BranchOnOverflow() or BranchOnNoOverflow().
++ void SmiTagCheckOverflow(Register reg, Register overflow);
++ void SmiTagCheckOverflow(Register dst, Register src, Register overflow);
++
++ inline void JumpIfNotSmiCandidate(Register value, Register scratch,
++ Label* not_smi_label) {
++ // High bits must be identical to fit into an Smi
++ addis(scratch, value, Operand(0x40000000u >> 16));
++ cmpi(scratch, Operand::Zero());
++ blt(not_smi_label);
++ }
++#endif
++ inline void JumpIfNotUnsignedSmiCandidate(Register value, Register scratch,
++ Label* not_smi_label) {
++ // The test is different for unsigned int values. Since we need
++ // the value to be in the range of a positive smi, we can't
++ // handle any of the high bits being set in the value.
++ TestBitRange(value,
++ kBitsPerPointer - 1,
++ kBitsPerPointer - 1 - kSmiShift,
++ scratch);
++ bne(not_smi_label, cr0);
++ }
++
++ void SmiUntag(Register reg, RCBit rc = LeaveRC) {
++ SmiUntag(reg, reg, rc);
++ }
++
++ void SmiUntag(Register dst, Register src, RCBit rc = LeaveRC) {
++ ShiftRightArithImm(dst, src, kSmiShift, rc);
++ }
++
++ void SmiToPtrArrayOffset(Register dst, Register src) {
++#if V8_TARGET_ARCH_PPC64
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift > kPointerSizeLog2);
++ ShiftRightArithImm(dst, src, kSmiShift - kPointerSizeLog2);
++#else
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift < kPointerSizeLog2);
++ ShiftLeftImm(dst, src, Operand(kPointerSizeLog2 - kSmiShift));
++#endif
++ }
++
++ void SmiToByteArrayOffset(Register dst, Register src) {
++ SmiUntag(dst, src);
++ }
++
++ void SmiToShortArrayOffset(Register dst, Register src) {
++#if V8_TARGET_ARCH_PPC64
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift > 1);
++ ShiftRightArithImm(dst, src, kSmiShift - 1);
++#else
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift == 1);
++ if (!dst.is(src)) {
++ mr(dst, src);
++ }
++#endif
++ }
++
++ void SmiToIntArrayOffset(Register dst, Register src) {
++#if V8_TARGET_ARCH_PPC64
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift > 2);
++ ShiftRightArithImm(dst, src, kSmiShift - 2);
++#else
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift < 2);
++ ShiftLeftImm(dst, src, Operand(2 - kSmiShift));
++#endif
++ }
++
++#define SmiToFloatArrayOffset SmiToIntArrayOffset
++
++ void SmiToDoubleArrayOffset(Register dst, Register src) {
++#if V8_TARGET_ARCH_PPC64
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift > kDoubleSizeLog2);
++ ShiftRightArithImm(dst, src, kSmiShift - kDoubleSizeLog2);
++#else
++ STATIC_ASSERT(kSmiTag == 0 && kSmiShift < kDoubleSizeLog2);
++ ShiftLeftImm(dst, src, Operand(kDoubleSizeLog2 - kSmiShift));
++#endif
++ }
++
++ void SmiToArrayOffset(Register dst, Register src, int elementSizeLog2) {
++ if (kSmiShift < elementSizeLog2) {
++ ShiftLeftImm(dst, src, Operand(elementSizeLog2 - kSmiShift));
++ } else if (kSmiShift > elementSizeLog2) {
++ ShiftRightArithImm(dst, src, kSmiShift - elementSizeLog2);
++ } else if (!dst.is(src)) {
++ mr(dst, src);
++ }
++ }
++
++ void IndexToArrayOffset(Register dst, Register src, int elementSizeLog2,
++ bool isSmi) {
++ if (isSmi) {
++ SmiToArrayOffset(dst, src, elementSizeLog2);
++ } else {
++ ShiftLeftImm(dst, src, Operand(elementSizeLog2));
++ }
++ }
++
++ // Untag the source value into destination and jump if source is a smi.
++ // Souce and destination can be the same register.
++ void UntagAndJumpIfSmi(Register dst, Register src, Label* smi_case);
++
++ // Untag the source value into destination and jump if source is not a smi.
++ // Souce and destination can be the same register.
++ void UntagAndJumpIfNotSmi(Register dst, Register src, Label* non_smi_case);
++
++ inline void TestIfSmi(Register value, Register scratch) {
++ TestBit(value, 0, scratch); // tst(value, Operand(kSmiTagMask));
++ }
++
++ inline void TestIfPositiveSmi(Register value, Register scratch) {
++ STATIC_ASSERT((kSmiTagMask | kSmiSignMask) ==
++ (intptr_t)(1UL << (kBitsPerPointer - 1) | 1));
++#if V8_TARGET_ARCH_PPC64
++ rldicl(scratch, value, 1, kBitsPerPointer - 2, SetRC);
++#else
++ rlwinm(scratch, value, 1, kBitsPerPointer - 2, kBitsPerPointer - 1, SetRC);
++#endif
++ }
++
++ // Jump the register contains a smi.
++ inline void JumpIfSmi(Register value, Label* smi_label) {
++ TestIfSmi(value, r0);
++ beq(smi_label, cr0); // branch if SMI
++ }
++ // Jump if either of the registers contain a non-smi.
++ inline void JumpIfNotSmi(Register value, Label* not_smi_label) {
++ TestIfSmi(value, r0);
++ bne(not_smi_label, cr0);
++ }
++ // Jump if either of the registers contain a non-smi.
++ void JumpIfNotBothSmi(Register reg1, Register reg2, Label* on_not_both_smi);
++ // Jump if either of the registers contain a smi.
++ void JumpIfEitherSmi(Register reg1, Register reg2, Label* on_either_smi);
++
++ // Abort execution if argument is a smi, enabled via --debug-code.
++ void AssertNotSmi(Register object);
++ void AssertSmi(Register object);
++
++
++#if V8_TARGET_ARCH_PPC64
++ inline void TestIfInt32(Register value,
++ Register scratch1, Register scratch2,
++ CRegister cr = cr7) {
++ // High bits must be identical to fit into an 32-bit integer
++ srawi(scratch1, value, 31);
++ sradi(scratch2, value, 32);
++ cmp(scratch1, scratch2, cr);
++ }
++#else
++ inline void TestIfInt32(Register hi_word, Register lo_word,
++ Register scratch, CRegister cr = cr7) {
++ // High bits must be identical to fit into an 32-bit integer
++ srawi(scratch, lo_word, 31);
++ cmp(scratch, hi_word, cr);
++ }
++#endif
++
++ // Abort execution if argument is not a string, enabled via --debug-code.
++ void AssertString(Register object);
++
++ // Abort execution if argument is not the root value with the given index.
++ void AssertRootValue(Register src,
++ Heap::RootListIndex root_value_index,
++ const char* message);
++
++ // ---------------------------------------------------------------------------
++ // HeapNumber utilities
++
++ void JumpIfNotHeapNumber(Register object,
++ Register heap_number_map,
++ Register scratch,
++ Label* on_not_heap_number);
++
++ // ---------------------------------------------------------------------------
++ // String utilities
++
++ // Checks if both objects are sequential ASCII strings and jumps to label
++ // if either is not. Assumes that neither object is a smi.
++ void JumpIfNonSmisNotBothSequentialAsciiStrings(Register object1,
++ Register object2,
++ Register scratch1,
++ Register scratch2,
++ Label* failure);
++
++ // Checks if both objects are sequential ASCII strings and jumps to label
++ // if either is not.
++ void JumpIfNotBothSequentialAsciiStrings(Register first,
++ Register second,
++ Register scratch1,
++ Register scratch2,
++ Label* not_flat_ascii_strings);
++
++ // Checks if both instance types are sequential ASCII strings and jumps to
++ // label if either is not.
++ void JumpIfBothInstanceTypesAreNotSequentialAscii(
++ Register first_object_instance_type,
++ Register second_object_instance_type,
++ Register scratch1,
++ Register scratch2,
++ Label* failure);
++
++ // Check if instance type is sequential ASCII string and jump to label if
++ // it is not.
++ void JumpIfInstanceTypeIsNotSequentialAscii(Register type,
++ Register scratch,
++ Label* failure);
++
++
++ // ---------------------------------------------------------------------------
++ // Patching helpers.
++
++ // Patch the relocated value (lis/ori pair).
++ void PatchRelocatedValue(Register lis_location,
++ Register scratch,
++ Register new_value);
++ // Get the relocatad value (loaded data) from the lis/ori pair.
++ void GetRelocatedValueLocation(Register lis_location,
++ Register result,
++ Register scratch);
++
++ void ClampUint8(Register output_reg, Register input_reg);
++
++ // Saturate a value into 8-bit unsigned integer
++ // if input_value < 0, output_value is 0
++ // if input_value > 255, output_value is 255
++ // otherwise output_value is the (int)input_value (round to nearest)
++ void ClampDoubleToUint8(Register result_reg,
++ DoubleRegister input_reg,
++ DoubleRegister temp_double_reg,
++ DoubleRegister temp_double_reg2);
++
++
++ void LoadInstanceDescriptors(Register map, Register descriptors);
++ void EnumLength(Register dst, Register map);
++ void NumberOfOwnDescriptors(Register dst, Register map);
++
++ template<typename Field>
++ void DecodeField(Register reg) {
++ uintptr_t mask = reinterpret_cast<intptr_t>(Smi::FromInt(Field::kMask));
++ ExtractBitMask(reg, reg, mask);
++ SmiTag(reg);
++ }
++
++ // Activation support.
++ void EnterFrame(StackFrame::Type type);
++ void LeaveFrame(StackFrame::Type type);
++
++ // Expects object in r0 and returns map with validated enum cache
++ // in r0. Assumes that any other register can be used as a scratch.
++ void CheckEnumCache(Register null_value, Label* call_runtime);
++
++ private:
++ static const int kSmiShift = kSmiTagSize + kSmiShiftSize;
++
++ void CallCFunctionHelper(Register function,
++ int num_reg_arguments,
++ int num_double_arguments);
++
++ void Jump(intptr_t target, RelocInfo::Mode rmode,
++ Condition cond = al, CRegister cr = cr7);
++
++ // Helper functions for generating invokes.
++ void InvokePrologue(const ParameterCount& expected,
++ const ParameterCount& actual,
++ Handle<Code> code_constant,
++ Register code_reg,
++ Label* done,
++ bool* definitely_mismatches,
++ InvokeFlag flag,
++ const CallWrapper& call_wrapper,
++ CallKind call_kind);
++
++ void InitializeNewString(Register string,
++ Register length,
++ Heap::RootListIndex map_index,
++ Register scratch1,
++ Register scratch2);
++
++ // Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace.
++ void InNewSpace(Register object,
++ Register scratch,
++ Condition cond, // eq for new space, ne otherwise.
++ Label* branch);
++
++ // Helper for finding the mark bits for an address. Afterwards, the
++ // bitmap register points at the word with the mark bits and the mask
++ // the position of the first bit. Leaves addr_reg unchanged.
++ inline void GetMarkBits(Register addr_reg,
++ Register bitmap_reg,
++ Register mask_reg);
++
++ // Helper for throwing exceptions. Compute a handler address and jump to
++ // it. See the implementation for register usage.
++ void JumpToHandlerEntry();
++
++ // Compute memory operands for safepoint stack slots.
++ static int SafepointRegisterStackIndex(int reg_code);
++ MemOperand SafepointRegisterSlot(Register reg);
++ MemOperand SafepointRegistersAndDoublesSlot(Register reg);
++
++ bool generating_stub_;
++ bool allow_stub_calls_;
++ bool has_frame_;
++ // This handle will be patched with the code object on installation.
++ Handle<Object> code_object_;
++
++ // Needs access to SafepointRegisterStackIndex for optimized frame
++ // traversal.
++ friend class OptimizedFrame;
++};
++
++
++// The code patcher is used to patch (typically) small parts of code e.g. for
++// debugging and other types of instrumentation. When using the code patcher
++// the exact number of bytes specified must be emitted. It is not legal to emit
++// relocation information. If any of these constraints are violated it causes
++// an assertion to fail.
++class CodePatcher {
++ public:
++ CodePatcher(byte* address, int instructions);
++ virtual ~CodePatcher();
++
++ // Macro assembler to emit code.
++ MacroAssembler* masm() { return &masm_; }
++
++ // Emit an instruction directly.
++ void Emit(Instr instr);
++
++ // Emit the condition part of an instruction leaving the rest of the current
++ // instruction unchanged.
++ void EmitCondition(Condition cond);
++
++ private:
++ byte* address_; // The address of the code being patched.
++ int instructions_; // Number of instructions of the expected patch size.
++ int size_; // Number of bytes of the expected patch size.
++ MacroAssembler masm_; // Macro assembler used to generate the code.
++};
++
++
++// -----------------------------------------------------------------------------
++// Static helper functions.
++
++inline MemOperand ContextOperand(Register context, int index) {
++ return MemOperand(context, Context::SlotOffset(index));
++}
++
++
++inline MemOperand GlobalObjectOperand() {
++ return ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX);
++}
++
++
++#ifdef GENERATED_CODE_COVERAGE
++#define CODE_COVERAGE_STRINGIFY(x) #x
++#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
++#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
++#define ACCESS_MASM(masm) masm->stop(__FILE_LINE__); masm->
++#else
++#define ACCESS_MASM(masm) masm->
++#endif
++
++
++} } // namespace v8::internal
++
++#endif // V8_PPC_MACRO_ASSEMBLER_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc.ppc
v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc
+--- v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc.ppc 2016-06-07 14:15:46.002392937
-0400
++++ v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.cc 2016-06-07 14:15:46.002392937
-0400
+@@ -0,0 +1,1464 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "unicode.h"
++#include "log.h"
++#include "code-stubs.h"
++#include "regexp-stack.h"
++#include "macro-assembler.h"
++#include "regexp-macro-assembler.h"
++#include "ppc/regexp-macro-assembler-ppc.h"
++
++namespace v8 {
++namespace internal {
++
++#ifndef V8_INTERPRETED_REGEXP
++/*
++ * This assembler uses the following register assignment convention
++ * - r25: Temporarily stores the index of capture start after a matching pass
++ * for a global regexp.
++ * - r26: Pointer to current code object (Code*) including heap object tag.
++ * - r27: Current position in input, as negative offset from end of string.
++ * Please notice that this is the byte offset, not the character offset!
++ * - r28: Currently loaded character. Must be loaded using
++ * LoadCurrentCharacter before using any of the dispatch methods.
++ * - r29: Points to tip of backtrack stack
++ * - r30: End of input (points to byte after last character in input).
++ * - r31: Frame pointer. Used to access arguments, local variables and
++ * RegExp registers.
++ * - r12: IP register, used by assembler. Very volatile.
++ * - r1/sp : Points to tip of C stack.
++ *
++ * The remaining registers are free for computations.
++ * Each call to a public method should retain this convention.
++ *
++ * The stack will have the following structure:
++ * - fp[44] Isolate* isolate (address of the current isolate)
++ * - fp[40] secondary link/return address used by native call.
++ * - fp[36] lr save area (currently unused)
++ * - fp[32] backchain (currently unused)
++ * --- sp when called ---
++ * - fp[28] return address (lr).
++ * - fp[24] old frame pointer (r31).
++ * - fp[0..20] backup of registers r25..r30
++ * --- frame pointer ----
++ * - fp[-4] direct_call (if 1, direct call from JavaScript code,
++ * if 0, call through the runtime system).
++ * - fp[-8] stack_area_base (high end of the memory area to use as
++ * backtracking stack).
++ * - fp[-12] capture array size (may fit multiple sets of matches)
++ * - fp[-16] int* capture_array (int[num_saved_registers_], for output).
++ * - fp[-20] end of input (address of end of string).
++ * - fp[-24] start of input (address of first character in string).
++ * - fp[-28] start index (character index of start).
++ * - fp[-32] void* input_string (location of a handle containing the string).
++ * - fp[-36] success counter (only for global regexps to count matches).
++ * - fp[-40] Offset of location before start of input (effectively character
++ * position -1). Used to initialize capture registers to a
++ * non-position.
++ * - fp[-44] At start (if 1, we are starting at the start of the
++ * string, otherwise 0)
++ * - fp[-48] register 0 (Only positions must be stored in the first
++ * - register 1 num_saved_registers_ registers)
++ * - ...
++ * - register num_registers-1
++ * --- sp ---
++ *
++ * The first num_saved_registers_ registers are initialized to point to
++ * "character -1" in the string (i.e., char_size() bytes before the first
++ * character of the string). The remaining registers start out as garbage.
++ *
++ * The data up to the return address must be placed there by the calling
++ * code and the remaining arguments are passed in registers, e.g. by calling the
++ * code entry as cast to a function with the signature:
++ * int (*match)(String* input_string,
++ * int start_index,
++ * Address start,
++ * Address end,
++ * int* capture_output_array,
++ * byte* stack_area_base,
++ * Address secondary_return_address, // Only used by native call.
++ * bool direct_call = false)
++ * The call is performed by NativeRegExpMacroAssembler::Execute()
++ * (in regexp-macro-assembler.cc) via the CALL_GENERATED_REGEXP_CODE macro
++ * in ppc/simulator-ppc.h.
++ * When calling as a non-direct call (i.e., from C++ code), the return address
++ * area is overwritten with the LR register by the RegExp code. When doing a
++ * direct call from generated code, the return address is placed there by
++ * the calling code, as in a normal exit frame.
++ */
++
++#define __ ACCESS_MASM(masm_)
++
++RegExpMacroAssemblerPPC::RegExpMacroAssemblerPPC(
++ Mode mode,
++ int registers_to_save,
++ Zone* zone)
++ : NativeRegExpMacroAssembler(zone),
++ masm_(new MacroAssembler(Isolate::Current(), NULL, kRegExpCodeSize)),
++ mode_(mode),
++ num_registers_(registers_to_save),
++ num_saved_registers_(registers_to_save),
++ entry_label_(),
++ start_label_(),
++ success_label_(),
++ backtrack_label_(),
++ exit_label_(),
++ internal_failure_label_() {
++ ASSERT_EQ(0, registers_to_save % 2);
++
++ // Called from C
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++
++ __ b(&entry_label_); // We'll write the entry code later.
++ // If the code gets too big or corrupted, an internal exception will be
++ // raised, and we will exit right away.
++ __ bind(&internal_failure_label_);
++ __ li(r3, Operand(FAILURE));
++ __ Ret();
++ __ bind(&start_label_); // And then continue from here.
++}
++
++
++RegExpMacroAssemblerPPC::~RegExpMacroAssemblerPPC() {
++ delete masm_;
++ // Unuse labels in case we throw away the assembler without calling GetCode.
++ entry_label_.Unuse();
++ start_label_.Unuse();
++ success_label_.Unuse();
++ backtrack_label_.Unuse();
++ exit_label_.Unuse();
++ check_preempt_label_.Unuse();
++ stack_overflow_label_.Unuse();
++ internal_failure_label_.Unuse();
++}
++
++
++int RegExpMacroAssemblerPPC::stack_limit_slack() {
++ return RegExpStack::kStackLimitSlack;
++}
++
++
++void RegExpMacroAssemblerPPC::AdvanceCurrentPosition(int by) {
++ if (by != 0) {
++ __ addi(current_input_offset(),
++ current_input_offset(), Operand(by * char_size()));
++ }
++}
++
++
++void RegExpMacroAssemblerPPC::AdvanceRegister(int reg, int by) {
++ ASSERT(reg >= 0);
++ ASSERT(reg < num_registers_);
++ if (by != 0) {
++ __ LoadP(r3, register_location(reg), r0);
++ __ mov(r0, Operand(by));
++ __ add(r3, r3, r0);
++ __ StoreP(r3, register_location(reg), r0);
++ }
++}
++
++
++void RegExpMacroAssemblerPPC::Backtrack() {
++ CheckPreemption();
++ // Pop Code* offset from backtrack stack, add Code* and jump to location.
++ Pop(r3);
++ __ add(r3, r3, code_pointer());
++ __ mtctr(r3);
++ __ bcr();
++}
++
++
++void RegExpMacroAssemblerPPC::Bind(Label* label) {
++ __ bind(label);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckCharacter(uint32_t c, Label* on_equal) {
++ __ Cmpli(current_character(), Operand(c), r0);
++ BranchOrBacktrack(eq, on_equal);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckCharacterGT(uc16 limit, Label* on_greater) {
++ __ Cmpli(current_character(), Operand(limit), r0);
++ BranchOrBacktrack(gt, on_greater);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckAtStart(Label* on_at_start) {
++ Label not_at_start;
++ // Did we start the match at the start of the string at all?
++ __ LoadP(r3, MemOperand(frame_pointer(), kStartIndex));
++ __ cmpi(r3, Operand::Zero());
++ BranchOrBacktrack(ne, ¬_at_start);
++
++ // If we did, are we still at the start of the input?
++ __ LoadP(r4, MemOperand(frame_pointer(), kInputStart));
++ __ mr(r0, current_input_offset());
++ __ add(r3, end_of_input_address(), r0);
++ __ cmp(r4, r3);
++ BranchOrBacktrack(eq, on_at_start);
++ __ bind(¬_at_start);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckNotAtStart(Label* on_not_at_start) {
++ // Did we start the match at the start of the string at all?
++ __ LoadP(r3, MemOperand(frame_pointer(), kStartIndex));
++ __ cmpi(r3, Operand::Zero());
++ BranchOrBacktrack(ne, on_not_at_start);
++ // If we did, are we still at the start of the input?
++ __ LoadP(r4, MemOperand(frame_pointer(), kInputStart));
++ __ add(r3, end_of_input_address(), current_input_offset());
++ __ cmp(r3, r4);
++ BranchOrBacktrack(ne, on_not_at_start);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckCharacterLT(uc16 limit, Label* on_less) {
++ __ Cmpli(current_character(), Operand(limit), r0);
++ BranchOrBacktrack(lt, on_less);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckCharacters(Vector<const uc16> str,
++ int cp_offset,
++ Label* on_failure,
++ bool check_end_of_string) {
++ if (on_failure == NULL) {
++ // Instead of inlining a backtrack for each test, (re)use the global
++ // backtrack target.
++ on_failure = &backtrack_label_;
++ }
++
++ if (check_end_of_string) {
++ // Is last character of required match inside string.
++ CheckPosition(cp_offset + str.length() - 1, on_failure);
++ }
++
++ __ add(r3, end_of_input_address(), current_input_offset());
++ if (cp_offset != 0) {
++ int byte_offset = cp_offset * char_size();
++ __ addi(r3, r3, Operand(byte_offset));
++ }
++
++ // r3 : Address of characters to match against str.
++ int stored_high_byte = 0;
++ for (int i = 0; i < str.length(); i++) {
++ if (mode_ == ASCII) {
++ __ lbz(r4, MemOperand(r3));
++ __ addi(r3, r3, Operand(char_size()));
++ ASSERT(str[i] <= String::kMaxAsciiCharCode);
++ __ cmpi(r4, Operand(str[i]));
++ } else {
++ __ lhz(r4, MemOperand(r3));
++ __ addi(r3, r3, Operand(char_size()));
++ uc16 match_char = str[i];
++ int match_high_byte = (match_char >> 8);
++ if (match_high_byte == 0) {
++ __ cmpi(r4, Operand(str[i]));
++ } else {
++ if (match_high_byte != stored_high_byte) {
++ __ li(r5, Operand(match_high_byte));
++ stored_high_byte = match_high_byte;
++ }
++ __ addi(r6, r5, Operand(match_char & 0xff));
++ __ cmp(r4, r6);
++ }
++ }
++ BranchOrBacktrack(ne, on_failure);
++ }
++}
++
++
++void RegExpMacroAssemblerPPC::CheckGreedyLoop(Label* on_equal) {
++ Label backtrack_non_equal;
++ __ LoadP(r3, MemOperand(backtrack_stackpointer(), 0));
++ __ cmp(current_input_offset(), r3);
++ __ bne(&backtrack_non_equal);
++ __ addi(backtrack_stackpointer(),
++ backtrack_stackpointer(), Operand(kPointerSize));
++
++ __ bind(&backtrack_non_equal);
++ BranchOrBacktrack(eq, on_equal);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckNotBackReferenceIgnoreCase(
++ int start_reg,
++ Label* on_no_match) {
++ Label fallthrough;
++ __ LoadP(r3, register_location(start_reg), r0); // Index of start of capture
++ __ LoadP(r4, register_location(start_reg + 1), r0); // Index of end
++ __ sub(r4, r4, r3, LeaveOE, SetRC); // Length of capture.
++
++ // If length is zero, either the capture is empty or it is not participating.
++ // In either case succeed immediately.
++ __ beq(&fallthrough, cr0);
++
++ // Check that there are enough characters left in the input.
++ __ add(r0, r4, current_input_offset(), LeaveOE, SetRC);
++// __ cmn(r1, Operand(current_input_offset()));
++ BranchOrBacktrack(gt, on_no_match, cr0);
++
++ if (mode_ == ASCII) {
++ Label success;
++ Label fail;
++ Label loop_check;
++
++ // r3 - offset of start of capture
++ // r4 - length of capture
++ __ add(r3, r3, end_of_input_address());
++ __ add(r5, end_of_input_address(), current_input_offset());
++ __ add(r4, r3, r4);
++
++ // r3 - Address of start of capture.
++ // r4 - Address of end of capture
++ // r5 - Address of current input position.
++
++ Label loop;
++ __ bind(&loop);
++ __ lbz(r6, MemOperand(r3));
++ __ addi(r3, r3, Operand(char_size()));
++ __ lbz(r25, MemOperand(r5));
++ __ addi(r5, r5, Operand(char_size()));
++ __ cmp(r25, r6);
++ __ beq(&loop_check);
++
++ // Mismatch, try case-insensitive match (converting letters to lower-case).
++ __ ori(r6, r6, Operand(0x20)); // Convert capture character to lower-case.
++ __ ori(r25, r25, Operand(0x20)); // Also convert input character.
++ __ cmp(r25, r6);
++ __ bne(&fail);
++ __ subi(r6, r6, Operand('a'));
++ __ cmpli(r6, Operand('z' - 'a')); // Is r6 a lowercase letter?
++ __ bgt(&fail);
++
++
++ __ bind(&loop_check);
++ __ cmp(r3, r4);
++ __ blt(&loop);
++ __ b(&success);
++
++ __ bind(&fail);
++ BranchOrBacktrack(al, on_no_match);
++
++ __ bind(&success);
++ // Compute new value of character position after the matched part.
++ __ sub(current_input_offset(), r5, end_of_input_address());
++ } else {
++ ASSERT(mode_ == UC16);
++ int argument_count = 4;
++ __ PrepareCallCFunction(argument_count, r5);
++
++ // r3 - offset of start of capture
++ // r4 - length of capture
++
++ // Put arguments into arguments registers.
++ // Parameters are
++ // r3: Address byte_offset1 - Address captured substring's start.
++ // r4: Address byte_offset2 - Address of current character position.
++ // r5: size_t byte_length - length of capture in bytes(!)
++ // r6: Isolate* isolate
++
++ // Address of start of capture.
++ __ add(r3, r3, end_of_input_address());
++ // Length of capture.
++ __ mr(r5, r4);
++ // Save length in callee-save register for use on return.
++ __ mr(r25, r4);
++ // Address of current input position.
++ __ add(r4, current_input_offset(), end_of_input_address());
++ // Isolate.
++ __ mov(r6, Operand(ExternalReference::isolate_address()));
++
++ {
++ AllowExternalCallThatCantCauseGC scope(masm_);
++ ExternalReference function =
++ ExternalReference::re_case_insensitive_compare_uc16(masm_->isolate());
++ __ CallCFunction(function, argument_count);
++ }
++
++ // Check if function returned non-zero for success or zero for failure.
++ __ cmpi(r3, Operand::Zero());
++ BranchOrBacktrack(eq, on_no_match);
++ // On success, increment position by length of capture.
++ __ add(current_input_offset(), current_input_offset(), r25);
++ }
++
++ __ bind(&fallthrough);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckNotBackReference(
++ int start_reg,
++ Label* on_no_match) {
++ Label fallthrough;
++ Label success;
++
++ // Find length of back-referenced capture.
++ __ LoadP(r3, register_location(start_reg), r0);
++ __ LoadP(r4, register_location(start_reg + 1), r0);
++ __ sub(r4, r4, r3, LeaveOE, SetRC); // Length to check.
++ // Succeed on empty capture (including no capture).
++ __ beq(&fallthrough, cr0);
++
++ // Check that there are enough characters left in the input.
++ __ add(r0, r4, current_input_offset(), LeaveOE, SetRC);
++ BranchOrBacktrack(gt, on_no_match, cr0);
++
++ // Compute pointers to match string and capture string
++ __ add(r3, r3, end_of_input_address());
++ __ add(r5, end_of_input_address(), current_input_offset());
++ __ add(r4, r4, r3);
++
++ Label loop;
++ __ bind(&loop);
++ if (mode_ == ASCII) {
++ __ lbz(r6, MemOperand(r3));
++ __ addi(r3, r3, Operand(char_size()));
++ __ lbz(r25, MemOperand(r5));
++ __ addi(r5, r5, Operand(char_size()));
++ } else {
++ ASSERT(mode_ == UC16);
++ __ lhz(r6, MemOperand(r3));
++ __ addi(r3, r3, Operand(char_size()));
++ __ lhz(r25, MemOperand(r5));
++ __ addi(r5, r5, Operand(char_size()));
++ }
++ __ cmp(r6, r25);
++ BranchOrBacktrack(ne, on_no_match);
++ __ cmp(r3, r4);
++ __ blt(&loop);
++
++ // Move current character position to position after match.
++ __ sub(current_input_offset(), r5, end_of_input_address());
++ __ bind(&fallthrough);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckNotCharacter(unsigned c,
++ Label* on_not_equal) {
++ __ Cmpli(current_character(), Operand(c), r0);
++ BranchOrBacktrack(ne, on_not_equal);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckCharacterAfterAnd(uint32_t c,
++ uint32_t mask,
++ Label* on_equal) {
++ __ mov(r0, Operand(mask));
++ if (c == 0) {
++ __ and_(r3, current_character(), r0, SetRC);
++ } else {
++ __ and_(r3, current_character(), r0);
++ __ Cmpli(r3, Operand(c), r0, cr0);
++ }
++ BranchOrBacktrack(eq, on_equal, cr0);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckNotCharacterAfterAnd(unsigned c,
++ unsigned mask,
++ Label* on_not_equal) {
++ __ mov(r0, Operand(mask));
++ if (c == 0) {
++ __ and_(r3, current_character(), r0, SetRC);
++ } else {
++ __ and_(r3, current_character(), r0);
++ __ Cmpli(r3, Operand(c), r0, cr0);
++ }
++ BranchOrBacktrack(ne, on_not_equal, cr0);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckNotCharacterAfterMinusAnd(
++ uc16 c,
++ uc16 minus,
++ uc16 mask,
++ Label* on_not_equal) {
++ ASSERT(minus < String::kMaxUtf16CodeUnit);
++ __ subi(r3, current_character(), Operand(minus));
++ __ mov(r0, Operand(mask));
++ __ and_(r3, r3, r0);
++ __ Cmpli(r3, Operand(c), r0);
++ BranchOrBacktrack(ne, on_not_equal);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckCharacterInRange(
++ uc16 from,
++ uc16 to,
++ Label* on_in_range) {
++ __ mov(r0, Operand(from));
++ __ sub(r3, current_character(), r0);
++ __ Cmpli(r3, Operand(to - from), r0);
++ BranchOrBacktrack(le, on_in_range); // Unsigned lower-or-same condition.
++}
++
++
++void RegExpMacroAssemblerPPC::CheckCharacterNotInRange(
++ uc16 from,
++ uc16 to,
++ Label* on_not_in_range) {
++ __ mov(r0, Operand(from));
++ __ sub(r3, current_character(), r0);
++ __ Cmpli(r3, Operand(to - from), r0);
++ BranchOrBacktrack(gt, on_not_in_range); // Unsigned higher condition.
++}
++
++
++void RegExpMacroAssemblerPPC::CheckBitInTable(
++ Handle<ByteArray> table,
++ Label* on_bit_set) {
++ __ mov(r3, Operand(table));
++ if (mode_ != ASCII || kTableMask != String::kMaxAsciiCharCode) {
++ __ andi(r4, current_character(), Operand(kTableSize - 1));
++ __ addi(r4, r4, Operand(ByteArray::kHeaderSize - kHeapObjectTag));
++ } else {
++ __ addi(r4,
++ current_character(),
++ Operand(ByteArray::kHeaderSize - kHeapObjectTag));
++ }
++ __ lbzx(r3, MemOperand(r3, r4));
++ __ cmpi(r3, Operand::Zero());
++ BranchOrBacktrack(ne, on_bit_set);
++}
++
++
++bool RegExpMacroAssemblerPPC::CheckSpecialCharacterClass(uc16 type,
++ Label* on_no_match) {
++ // Range checks (c in min..max) are generally implemented by an unsigned
++ // (c - min) <= (max - min) check
++ switch (type) {
++ case 's':
++ // Match space-characters
++ if (mode_ == ASCII) {
++ // ASCII space characters are '\t'..'\r' and ' '.
++ Label success;
++ __ cmpi(current_character(), Operand(' '));
++ __ beq(&success);
++ // Check range 0x09..0x0d
++ __ subi(r3, current_character(), Operand('\t'));
++ __ cmpli(r3, Operand('\r' - '\t'));
++ BranchOrBacktrack(gt, on_no_match);
++ __ bind(&success);
++ return true;
++ }
++ return false;
++ case 'S':
++ // Match non-space characters.
++ if (mode_ == ASCII) {
++ // ASCII space characters are '\t'..'\r' and ' '.
++ __ cmpi(current_character(), Operand(' '));
++ BranchOrBacktrack(eq, on_no_match);
++ __ subi(r3, current_character(), Operand('\t'));
++ __ cmpli(r3, Operand('\r' - '\t'));
++ BranchOrBacktrack(le, on_no_match);
++ return true;
++ }
++ return false;
++ case 'd':
++ // Match ASCII digits ('0'..'9')
++ __ subi(r3, current_character(), Operand('0'));
++ __ cmpli(current_character(), Operand('9' - '0'));
++ BranchOrBacktrack(gt, on_no_match);
++ return true;
++ case 'D':
++ // Match non ASCII-digits
++ __ subi(r3, current_character(), Operand('0'));
++ __ cmpli(r3, Operand('9' - '0'));
++ BranchOrBacktrack(le, on_no_match);
++ return true;
++ case '.': {
++ // Match non-newlines (not 0x0a('\n'), 0x0d('\r'), 0x2028 and
0x2029)
++ __ xori(r3, current_character(), Operand(0x01));
++ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
++ __ subi(r3, r3, Operand(0x0b));
++ __ cmpli(r3, Operand(0x0c - 0x0b));
++ BranchOrBacktrack(le, on_no_match);
++ if (mode_ == UC16) {
++ // Compare original value to 0x2028 and 0x2029, using the already
++ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
++ // 0x201d (0x2028 - 0x0b) or 0x201e.
++ __ subi(r3, r3, Operand(0x2028 - 0x0b));
++ __ cmpli(r3, Operand(1));
++ BranchOrBacktrack(le, on_no_match);
++ }
++ return true;
++ }
++ case 'n': {
++ // Match newlines (0x0a('\n'), 0x0d('\r'), 0x2028 and 0x2029)
++ __ xori(r3, current_character(), Operand(0x01));
++ // See if current character is '\n'^1 or '\r'^1, i.e., 0x0b or 0x0c
++ __ subi(r3, r3, Operand(0x0b));
++ __ cmpli(r3, Operand(0x0c - 0x0b));
++ if (mode_ == ASCII) {
++ BranchOrBacktrack(gt, on_no_match);
++ } else {
++ Label done;
++ __ ble(&done);
++ // Compare original value to 0x2028 and 0x2029, using the already
++ // computed (current_char ^ 0x01 - 0x0b). I.e., check for
++ // 0x201d (0x2028 - 0x0b) or 0x201e.
++ __ subi(r3, r3, Operand(0x2028 - 0x0b));
++ __ cmpli(r3, Operand(1));
++ BranchOrBacktrack(gt, on_no_match);
++ __ bind(&done);
++ }
++ return true;
++ }
++ case 'w': {
++ if (mode_ != ASCII) {
++ // Table is 128 entries, so all ASCII characters can be tested.
++ __ cmpi(current_character(), Operand('z'));
++ BranchOrBacktrack(gt, on_no_match);
++ }
++ ExternalReference map = ExternalReference::re_word_character_map();
++ __ mov(r3, Operand(map));
++ __ lbzx(r3, MemOperand(r3, current_character()));
++ __ cmpli(r3, Operand::Zero());
++ BranchOrBacktrack(eq, on_no_match);
++ return true;
++ }
++ case 'W': {
++ Label done;
++ if (mode_ != ASCII) {
++ // Table is 128 entries, so all ASCII characters can be tested.
++ __ cmpli(current_character(), Operand('z'));
++ __ bgt(&done);
++ }
++ ExternalReference map = ExternalReference::re_word_character_map();
++ __ mov(r3, Operand(map));
++ __ lbzx(r3, MemOperand(r3, current_character()));
++ __ cmpli(r3, Operand::Zero());
++ BranchOrBacktrack(ne, on_no_match);
++ if (mode_ != ASCII) {
++ __ bind(&done);
++ }
++ return true;
++ }
++ case '*':
++ // Match any character.
++ return true;
++ // No custom implementation (yet): s(UC16), S(UC16).
++ default:
++ return false;
++ }
++}
++
++
++void RegExpMacroAssemblerPPC::Fail() {
++ __ li(r3, Operand(FAILURE));
++ __ b(&exit_label_);
++}
++
++
++Handle<HeapObject> RegExpMacroAssemblerPPC::GetCode(Handle<String> source)
{
++ Label return_r3;
++
++ if (masm_->has_exception()) {
++ // If the code gets corrupted due to long regular expressions and lack of
++ // space on trampolines, an internal exception flag is set. If this case
++ // is detected, we will jump into exit sequence right away.
++ __ bind_to(&entry_label_, internal_failure_label_.pos());
++ } else {
++ // Finalize code - write the entry point code now we know how many
++ // registers we need.
++
++ // Entry code:
++ __ bind(&entry_label_);
++
++ // Tell the system that we have a stack frame. Because the type
++ // is MANUAL, no is generated.
++ FrameScope scope(masm_, StackFrame::MANUAL);
++
++ // Ensure register assigments are consistent with callee save mask
++ ASSERT(r25.bit() & kRegExpCalleeSaved);
++ ASSERT(code_pointer().bit() & kRegExpCalleeSaved);
++ ASSERT(current_input_offset().bit() & kRegExpCalleeSaved);
++ ASSERT(current_character().bit() & kRegExpCalleeSaved);
++ ASSERT(backtrack_stackpointer().bit() & kRegExpCalleeSaved);
++ ASSERT(end_of_input_address().bit() & kRegExpCalleeSaved);
++ ASSERT(frame_pointer().bit() & kRegExpCalleeSaved);
++
++ // Actually emit code to start a new stack frame.
++ // Push arguments
++ // Save callee-save registers.
++ // Start new stack frame.
++ // Store link register in existing stack-cell.
++ // Order here should correspond to order of offset constants in header file.
++ RegList registers_to_retain = kRegExpCalleeSaved;
++ RegList argument_registers = r3.bit() | r4.bit() | r5.bit() | r6.bit() |
++ r7.bit() | r8.bit() | r9.bit() | r10.bit();
++ __ mflr(r0);
++ __ push(r0);
++ __ MultiPush(argument_registers | registers_to_retain);
++ // Set frame pointer in space for it if this is not a direct call
++ // from generated code.
++ __ addi(frame_pointer(), sp, Operand(8 * kPointerSize));
++ __ li(r3, Operand::Zero());
++ __ push(r3); // Make room for success counter and initialize it to 0.
++ __ push(r3); // Make room for "position - 1" constant (value is
irrelevant)
++ // Check if we have space on the stack for registers.
++ Label stack_limit_hit;
++ Label stack_ok;
++
++ ExternalReference stack_limit =
++ ExternalReference::address_of_stack_limit(masm_->isolate());
++ __ mov(r3, Operand(stack_limit));
++ __ LoadP(r3, MemOperand(r3));
++ __ sub(r3, sp, r3, LeaveOE, SetRC);
++ // Handle it if the stack pointer is already below the stack limit.
++ __ ble(&stack_limit_hit, cr0);
++ // Check if there is room for the variable number of registers above
++ // the stack limit.
++ __ Cmpli(r3, Operand(num_registers_ * kPointerSize), r0);
++ __ bge(&stack_ok);
++ // Exit with OutOfMemory exception. There is not enough space on the stack
++ // for our working registers.
++ __ li(r3, Operand(EXCEPTION));
++ __ b(&return_r3);
++
++ __ bind(&stack_limit_hit);
++ CallCheckStackGuardState(r3);
++ __ cmpi(r3, Operand::Zero());
++ // If returned value is non-zero, we exit with the returned value as result.
++ __ bne(&return_r3);
++
++ __ bind(&stack_ok);
++
++ // Allocate space on stack for registers.
++ __ Add(sp, sp, -num_registers_ * kPointerSize, r0);
++ // Load string end.
++ __ LoadP(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
++ // Load input start.
++ __ LoadP(r3, MemOperand(frame_pointer(), kInputStart));
++ // Find negative length (offset of start relative to end).
++ __ sub(current_input_offset(), r3, end_of_input_address());
++ // Set r3 to address of char before start of the input string
++ // (effectively string position -1).
++ __ LoadP(r4, MemOperand(frame_pointer(), kStartIndex));
++ __ subi(r3, current_input_offset(), Operand(char_size()));
++ if (mode_ == UC16) {
++ __ ShiftLeftImm(r0, r4, Operand(1));
++ __ sub(r3, r3, r0);
++ } else {
++ __ sub(r3, r3, r4);
++ }
++ // Store this value in a local variable, for use when clearing
++ // position registers.
++ __ StoreP(r3, MemOperand(frame_pointer(), kInputStartMinusOne));
++
++ // Initialize code pointer register
++ __ mov(code_pointer(), Operand(masm_->CodeObject()));
++
++ Label load_char_start_regexp, start_regexp;
++ // Load newline if index is at start, previous character otherwise.
++ __ cmpi(r4, Operand::Zero());
++ __ bne(&load_char_start_regexp);
++ __ li(current_character(), Operand('\n'));
++ __ b(&start_regexp);
++
++ // Global regexp restarts matching here.
++ __ bind(&load_char_start_regexp);
++ // Load previous char as initial value of current character register.
++ LoadCurrentCharacterUnchecked(-1, 1);
++ __ bind(&start_regexp);
++
++ // Initialize on-stack registers.
++ if (num_saved_registers_ > 0) { // Always is, if generated from a regexp.
++ // Fill saved registers with initial value = start offset - 1
++ if (num_saved_registers_ > 8) {
++ // One slot beyond address of register 0.
++ __ addi(r4, frame_pointer(), Operand(kRegisterZero + kPointerSize));
++ __ li(r5, Operand(num_saved_registers_));
++ __ mtctr(r5);
++ Label init_loop;
++ __ bind(&init_loop);
++ __ StorePU(r3, MemOperand(r4, -kPointerSize));
++ __ bdnz(&init_loop);
++ } else {
++ for (int i = 0; i < num_saved_registers_; i++) {
++ __ StoreP(r3, register_location(i), r0);
++ }
++ }
++ }
++
++ // Initialize backtrack stack pointer.
++ __ LoadP(backtrack_stackpointer(),
++ MemOperand(frame_pointer(), kStackHighEnd));
++
++ __ b(&start_label_);
++
++ // Exit code:
++ if (success_label_.is_linked()) {
++ // Save captures when successful.
++ __ bind(&success_label_);
++ if (num_saved_registers_ > 0) {
++ // copy captures to output
++ __ LoadP(r4, MemOperand(frame_pointer(), kInputStart));
++ __ LoadP(r3, MemOperand(frame_pointer(), kRegisterOutput));
++ __ LoadP(r5, MemOperand(frame_pointer(), kStartIndex));
++ __ sub(r4, end_of_input_address(), r4);
++ // r4 is length of input in bytes.
++ if (mode_ == UC16) {
++ __ ShiftRightImm(r4, r4, Operand(1));
++ }
++ // r4 is length of input in characters.
++ __ add(r4, r4, r5);
++ // r4 is length of string in characters.
++
++ ASSERT_EQ(0, num_saved_registers_ % 2);
++ // Always an even number of capture registers. This allows us to
++ // unroll the loop once to add an operation between a load of a register
++ // and the following use of that register.
++ for (int i = 0; i < num_saved_registers_; i += 2) {
++ __ LoadP(r5, register_location(i), r0);
++ __ LoadP(r6, register_location(i + 1), r0);
++ if (i == 0 && global_with_zero_length_check()) {
++ // Keep capture start in r25 for the zero-length check later.
++ __ mr(r25, r5);
++ }
++ if (mode_ == UC16) {
++ __ ShiftRightArithImm(r5, r5, 1);
++ __ add(r5, r4, r5);
++ __ ShiftRightArithImm(r6, r6, 1);
++ __ add(r6, r4, r6);
++ } else {
++ __ add(r5, r4, r5);
++ __ add(r6, r4, r6);
++ }
++ __ stw(r5, MemOperand(r3));
++ __ addi(r3, r3, Operand(kIntSize));
++ __ stw(r6, MemOperand(r3));
++ __ addi(r3, r3, Operand(kIntSize));
++ }
++ }
++
++ if (global()) {
++ // Restart matching if the regular expression is flagged as global.
++ __ LoadP(r3, MemOperand(frame_pointer(), kSuccessfulCaptures));
++ __ LoadP(r4, MemOperand(frame_pointer(), kNumOutputRegisters));
++ __ LoadP(r5, MemOperand(frame_pointer(), kRegisterOutput));
++ // Increment success counter.
++ __ addi(r3, r3, Operand(1));
++ __ StoreP(r3, MemOperand(frame_pointer(), kSuccessfulCaptures));
++ // Capture results have been stored, so the number of remaining global
++ // output registers is reduced by the number of stored captures.
++ __ subi(r4, r4, Operand(num_saved_registers_));
++ // Check whether we have enough room for another set of capture results.
++ __ cmpi(r4, Operand(num_saved_registers_));
++ __ blt(&return_r3);
++
++ __ StoreP(r4, MemOperand(frame_pointer(), kNumOutputRegisters));
++ // Advance the location for output.
++ __ addi(r5, r5, Operand(num_saved_registers_ * kIntSize));
++ __ StoreP(r5, MemOperand(frame_pointer(), kRegisterOutput));
++
++ // Prepare r3 to initialize registers with its value in the next run.
++ __ LoadP(r3, MemOperand(frame_pointer(), kInputStartMinusOne));
++
++ if (global_with_zero_length_check()) {
++ // Special case for zero-length matches.
++ // r25: capture start index
++ __ cmp(current_input_offset(), r25);
++ // Not a zero-length match, restart.
++ __ bne(&load_char_start_regexp);
++ // Offset from the end is zero if we already reached the end.
++ __ cmpi(current_input_offset(), Operand::Zero());
++ __ beq(&exit_label_);
++ // Advance current position after a zero-length match.
++ __ addi(current_input_offset(),
++ current_input_offset(),
++ Operand((mode_ == UC16) ? 2 : 1));
++ }
++
++ __ b(&load_char_start_regexp);
++ } else {
++ __ li(r3, Operand(SUCCESS));
++ }
++ }
++
++ // Exit and return r3
++ __ bind(&exit_label_);
++ if (global()) {
++ __ LoadP(r3, MemOperand(frame_pointer(), kSuccessfulCaptures));
++ }
++
++ __ bind(&return_r3);
++ // Skip sp past regexp registers and local variables..
++ __ mr(sp, frame_pointer());
++ // Restore registers r25..r31 and return (restoring lr to pc).
++ __ MultiPop(registers_to_retain);
++ __ pop(r0);
++ __ mtctr(r0);
++ __ bcr();
++
++ // Backtrack code (branch target for conditional backtracks).
++ if (backtrack_label_.is_linked()) {
++ __ bind(&backtrack_label_);
++ Backtrack();
++ }
++
++ Label exit_with_exception;
++
++ // Preempt-code
++ if (check_preempt_label_.is_linked()) {
++ SafeCallTarget(&check_preempt_label_);
++
++ CallCheckStackGuardState(r3);
++ __ cmpi(r3, Operand::Zero());
++ // If returning non-zero, we should end execution with the given
++ // result as return value.
++ __ bne(&return_r3);
++
++ // String might have moved: Reload end of string from frame.
++ __ LoadP(end_of_input_address(), MemOperand(frame_pointer(), kInputEnd));
++ SafeReturn();
++ }
++
++ // Backtrack stack overflow code.
++ if (stack_overflow_label_.is_linked()) {
++ SafeCallTarget(&stack_overflow_label_);
++ // Reached if the backtrack-stack limit has been hit.
++ Label grow_failed;
++
++ // Call GrowStack(backtrack_stackpointer(), &stack_base)
++ static const int num_arguments = 3;
++ __ PrepareCallCFunction(num_arguments, r3);
++ __ mr(r3, backtrack_stackpointer());
++ __ addi(r4, frame_pointer(), Operand(kStackHighEnd));
++ __ mov(r5, Operand(ExternalReference::isolate_address()));
++ ExternalReference grow_stack =
++ ExternalReference::re_grow_stack(masm_->isolate());
++ __ CallCFunction(grow_stack, num_arguments);
++ // If return NULL, we have failed to grow the stack, and
++ // must exit with a stack-overflow exception.
++ __ cmpi(r3, Operand::Zero());
++ __ beq(&exit_with_exception);
++ // Otherwise use return value as new stack pointer.
++ __ mr(backtrack_stackpointer(), r3);
++ // Restore saved registers and continue.
++ SafeReturn();
++ }
++
++ if (exit_with_exception.is_linked()) {
++ // If any of the code above needed to exit with an exception.
++ __ bind(&exit_with_exception);
++ // Exit with Result EXCEPTION(-1) to signal thrown exception.
++ __ li(r3, Operand(EXCEPTION));
++ __ b(&return_r3);
++ }
++ }
++
++ CodeDesc code_desc;
++ masm_->GetCode(&code_desc);
++ Handle<Code> code = FACTORY->NewCode(code_desc,
++ Code::ComputeFlags(Code::REGEXP),
++ masm_->CodeObject());
++ PROFILE(Isolate::Current(), RegExpCodeCreateEvent(*code, *source));
++ return Handle<HeapObject>::cast(code);
++}
++
++
++void RegExpMacroAssemblerPPC::GoTo(Label* to) {
++ BranchOrBacktrack(al, to);
++}
++
++
++void RegExpMacroAssemblerPPC::IfRegisterGE(int reg,
++ int comparand,
++ Label* if_ge) {
++ __ LoadP(r3, register_location(reg), r0);
++ __ Cmpi(r3, Operand(comparand), r0);
++ BranchOrBacktrack(ge, if_ge);
++}
++
++
++void RegExpMacroAssemblerPPC::IfRegisterLT(int reg,
++ int comparand,
++ Label* if_lt) {
++ __ LoadP(r3, register_location(reg), r0);
++ __ Cmpi(r3, Operand(comparand), r0);
++ BranchOrBacktrack(lt, if_lt);
++}
++
++
++void RegExpMacroAssemblerPPC::IfRegisterEqPos(int reg,
++ Label* if_eq) {
++ __ LoadP(r3, register_location(reg), r0);
++ __ cmp(r3, current_input_offset());
++ BranchOrBacktrack(eq, if_eq);
++}
++
++
++RegExpMacroAssembler::IrregexpImplementation
++ RegExpMacroAssemblerPPC::Implementation() {
++ return kPPCImplementation;
++}
++
++
++void RegExpMacroAssemblerPPC::LoadCurrentCharacter(int cp_offset,
++ Label* on_end_of_input,
++ bool check_bounds,
++ int characters) {
++ ASSERT(cp_offset >= -1); // ^ and \b can look behind one character.
++ ASSERT(cp_offset < (1<<30)); // Be sane! (And ensure negation works)
++ if (check_bounds) {
++ CheckPosition(cp_offset + characters - 1, on_end_of_input);
++ }
++ LoadCurrentCharacterUnchecked(cp_offset, characters);
++}
++
++
++void RegExpMacroAssemblerPPC::PopCurrentPosition() {
++ Pop(current_input_offset());
++}
++
++
++void RegExpMacroAssemblerPPC::PopRegister(int register_index) {
++ Pop(r3);
++ __ StoreP(r3, register_location(register_index), r0);
++}
++
++
++void RegExpMacroAssemblerPPC::PushBacktrack(Label* label) {
++ if (label->is_bound()) {
++ int target = label->pos();
++ __ mov(r3, Operand(target + Code::kHeaderSize - kHeapObjectTag));
++ } else {
++ Label after_constant;
++ __ b(&after_constant);
++ int offset = masm_->pc_offset();
++ int cp_offset = offset + Code::kHeaderSize - kHeapObjectTag;
++ __ emit(0);
++ masm_->label_at_put(label, offset);
++ __ bind(&after_constant);
++ __ LoadWord(r3, MemOperand(code_pointer(), cp_offset), r0);
++ }
++ Push(r3);
++ CheckStackLimit();
++}
++
++
++void RegExpMacroAssemblerPPC::PushCurrentPosition() {
++ Push(current_input_offset());
++}
++
++
++void RegExpMacroAssemblerPPC::PushRegister(int register_index,
++ StackCheckFlag check_stack_limit) {
++ __ LoadP(r3, register_location(register_index), r0);
++ Push(r3);
++ if (check_stack_limit) CheckStackLimit();
++}
++
++
++void RegExpMacroAssemblerPPC::ReadCurrentPositionFromRegister(int reg) {
++ __ LoadP(current_input_offset(), register_location(reg), r0);
++}
++
++
++void RegExpMacroAssemblerPPC::ReadStackPointerFromRegister(int reg) {
++ __ LoadP(backtrack_stackpointer(), register_location(reg), r0);
++ __ LoadP(r3, MemOperand(frame_pointer(), kStackHighEnd));
++ __ add(backtrack_stackpointer(), backtrack_stackpointer(), r3);
++}
++
++
++void RegExpMacroAssemblerPPC::SetCurrentPositionFromEnd(int by) {
++ Label after_position;
++ __ Cmpi(current_input_offset(), Operand(-by * char_size()), r0);
++ __ bge(&after_position);
++ __ mov(current_input_offset(), Operand(-by * char_size()));
++ // On RegExp code entry (where this operation is used), the character before
++ // the current position is expected to be already loaded.
++ // We have advanced the position, so it's safe to read backwards.
++ LoadCurrentCharacterUnchecked(-1, 1);
++ __ bind(&after_position);
++}
++
++
++void RegExpMacroAssemblerPPC::SetRegister(int register_index, int to) {
++ ASSERT(register_index >= num_saved_registers_); // Reserved for positions!
++ __ mov(r3, Operand(to));
++ __ StoreP(r3, register_location(register_index), r0);
++}
++
++
++bool RegExpMacroAssemblerPPC::Succeed() {
++ __ b(&success_label_);
++ return global();
++}
++
++
++void RegExpMacroAssemblerPPC::WriteCurrentPositionToRegister(int reg,
++ int cp_offset) {
++ if (cp_offset == 0) {
++ __ StoreP(current_input_offset(), register_location(reg), r0);
++ } else {
++ __ mov(r0, Operand(cp_offset * char_size()));
++ __ add(r3, current_input_offset(), r0);
++ __ StoreP(r3, register_location(reg), r0);
++ }
++}
++
++
++void RegExpMacroAssemblerPPC::ClearRegisters(int reg_from, int reg_to) {
++ ASSERT(reg_from <= reg_to);
++ __ LoadP(r3, MemOperand(frame_pointer(), kInputStartMinusOne));
++ for (int reg = reg_from; reg <= reg_to; reg++) {
++ __ StoreP(r3, register_location(reg), r0);
++ }
++}
++
++
++void RegExpMacroAssemblerPPC::WriteStackPointerToRegister(int reg) {
++ __ LoadP(r4, MemOperand(frame_pointer(), kStackHighEnd));
++ __ sub(r3, backtrack_stackpointer(), r4);
++ __ StoreP(r3, register_location(reg), r0);
++}
++
++
++// Private methods:
++
++void RegExpMacroAssemblerPPC::CallCheckStackGuardState(Register scratch) {
++ static const int num_arguments = 3;
++ __ PrepareCallCFunction(num_arguments, scratch);
++ // RegExp code frame pointer.
++ __ mr(r5, frame_pointer());
++ // Code* of self.
++ __ mov(r4, Operand(masm_->CodeObject()));
++ // r3 becomes return address pointer.
++ ExternalReference stack_guard_check =
++ ExternalReference::re_check_stack_guard_state(masm_->isolate());
++ CallCFunctionUsingStub(stack_guard_check, num_arguments);
++}
++
++
++// Helper function for reading a value out of a stack frame.
++template <typename T>
++static T& frame_entry(Address re_frame, int frame_offset) {
++ return reinterpret_cast<T&>(Memory::int32_at(re_frame + frame_offset));
++}
++
++
++int RegExpMacroAssemblerPPC::CheckStackGuardState(Address* return_address,
++ Code* re_code,
++ Address re_frame) {
++ Isolate* isolate = frame_entry<Isolate*>(re_frame, kIsolate);
++ ASSERT(isolate == Isolate::Current());
++ if (isolate->stack_guard()->IsStackOverflow()) {
++ isolate->StackOverflow();
++ return EXCEPTION;
++ }
++
++ // If not real stack overflow the stack guard was used to interrupt
++ // execution for another purpose.
++
++ // If this is a direct call from JavaScript retry the RegExp forcing the call
++ // through the runtime system. Currently the direct call cannot handle a GC.
++ if (frame_entry<int>(re_frame, kDirectCall) == 1) {
++ return RETRY;
++ }
++
++ // Prepare for possible GC.
++ HandleScope handles(isolate);
++ Handle<Code> code_handle(re_code);
++
++ Handle<String> subject(frame_entry<String*>(re_frame, kInputString));
++
++ // Current string.
++ bool is_ascii = subject->IsAsciiRepresentationUnderneath();
++
++ ASSERT(re_code->instruction_start() <= *return_address);
++ ASSERT(*return_address <=
++ re_code->instruction_start() + re_code->instruction_size());
++
++ MaybeObject* result = Execution::HandleStackGuardInterrupt(isolate);
++
++ if (*code_handle != re_code) { // Return address no longer valid
++ int delta = code_handle->address() - re_code->address();
++ // Overwrite the return address on the stack.
++ *return_address += delta;
++ }
++
++ if (result->IsException()) {
++ return EXCEPTION;
++ }
++
++ Handle<String> subject_tmp = subject;
++ int slice_offset = 0;
++
++ // Extract the underlying string and the slice offset.
++ if (StringShape(*subject_tmp).IsCons()) {
++ subject_tmp = Handle<String>(ConsString::cast(*subject_tmp)->first());
++ } else if (StringShape(*subject_tmp).IsSliced()) {
++ SlicedString* slice = SlicedString::cast(*subject_tmp);
++ subject_tmp = Handle<String>(slice->parent());
++ slice_offset = slice->offset();
++ }
++
++ // String might have changed.
++ if (subject_tmp->IsAsciiRepresentation() != is_ascii) {
++ // If we changed between an ASCII and an UC16 string, the specialized
++ // code cannot be used, and we need to restart regexp matching from
++ // scratch (including, potentially, compiling a new version of the code).
++ return RETRY;
++ }
++
++ // Otherwise, the content of the string might have moved. It must still
++ // be a sequential or external string with the same content.
++ // Update the start and end pointers in the stack frame to the current
++ // location (whether it has actually moved or not).
++ ASSERT(StringShape(*subject_tmp).IsSequential() ||
++ StringShape(*subject_tmp).IsExternal());
++
++ // The original start address of the characters to match.
++ const byte* start_address = frame_entry<const byte*>(re_frame, kInputStart);
++
++ // Find the current start address of the same character at the current string
++ // position.
++ int start_index = frame_entry<intptr_t>(re_frame, kStartIndex);
++ const byte* new_address = StringCharacterPosition(*subject_tmp,
++ start_index + slice_offset);
++
++ if (start_address != new_address) {
++ // If there is a difference, update the object pointer and start and end
++ // addresses in the RegExp stack frame to match the new value.
++ const byte* end_address = frame_entry<const byte* >(re_frame, kInputEnd);
++ int byte_length = static_cast<int>(end_address - start_address);
++ frame_entry<const String*>(re_frame, kInputString) = *subject;
++ frame_entry<const byte*>(re_frame, kInputStart) = new_address;
++ frame_entry<const byte*>(re_frame, kInputEnd) = new_address + byte_length;
++ } else if (frame_entry<const String*>(re_frame, kInputString) != *subject) {
++ // Subject string might have been a ConsString that underwent
++ // short-circuiting during GC. That will not change start_address but
++ // will change pointer inside the subject handle.
++ frame_entry<const String*>(re_frame, kInputString) = *subject;
++ }
++
++ return 0;
++}
++
++
++MemOperand RegExpMacroAssemblerPPC::register_location(int register_index) {
++ ASSERT(register_index < (1<<30));
++ if (num_registers_ <= register_index) {
++ num_registers_ = register_index + 1;
++ }
++ return MemOperand(frame_pointer(),
++ kRegisterZero - register_index * kPointerSize);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckPosition(int cp_offset,
++ Label* on_outside_input) {
++ __ Cmpi(current_input_offset(), Operand(-cp_offset * char_size()), r0);
++ BranchOrBacktrack(ge, on_outside_input);
++}
++
++
++void RegExpMacroAssemblerPPC::BranchOrBacktrack(Condition condition,
++ Label* to,
++ CRegister cr) {
++ if (condition == al) { // Unconditional.
++ if (to == NULL) {
++ Backtrack();
++ return;
++ }
++ __ b(to);
++ return;
++ }
++ if (to == NULL) {
++ __ b(condition, &backtrack_label_, cr);
++ return;
++ }
++ __ b(condition, to, cr);
++}
++
++
++void RegExpMacroAssemblerPPC::SafeCall(Label* to, Condition cond,
++ CRegister cr) {
++ __ b(cond, to, cr, SetLK);
++}
++
++
++void RegExpMacroAssemblerPPC::SafeReturn() {
++ __ pop(r0);
++ __ mov(ip, Operand(masm_->CodeObject()));
++ __ add(r0, r0, ip);
++ __ mtlr(r0);
++ __ blr();
++}
++
++
++void RegExpMacroAssemblerPPC::SafeCallTarget(Label* name) {
++ __ bind(name);
++ __ mflr(r0);
++ __ mov(ip, Operand(masm_->CodeObject()));
++ __ sub(r0, r0, ip);
++ __ push(r0);
++}
++
++
++void RegExpMacroAssemblerPPC::Push(Register source) {
++ ASSERT(!source.is(backtrack_stackpointer()));
++ __ StorePU(source, MemOperand(backtrack_stackpointer(), -kPointerSize));
++}
++
++
++void RegExpMacroAssemblerPPC::Pop(Register target) {
++ ASSERT(!target.is(backtrack_stackpointer()));
++ __ LoadP(target, MemOperand(backtrack_stackpointer()));
++ __ addi(backtrack_stackpointer(), backtrack_stackpointer(),
++ Operand(kPointerSize));
++}
++
++
++void RegExpMacroAssemblerPPC::CheckPreemption() {
++ // Check for preemption.
++ ExternalReference stack_limit =
++ ExternalReference::address_of_stack_limit(masm_->isolate());
++ __ mov(r3, Operand(stack_limit));
++ __ LoadP(r3, MemOperand(r3));
++ __ cmpl(sp, r3);
++ SafeCall(&check_preempt_label_, le);
++}
++
++
++void RegExpMacroAssemblerPPC::CheckStackLimit() {
++ ExternalReference stack_limit =
++ ExternalReference::address_of_regexp_stack_limit(masm_->isolate());
++ __ mov(r3, Operand(stack_limit));
++ __ LoadP(r3, MemOperand(r3));
++ __ cmpl(backtrack_stackpointer(), r3);
++ SafeCall(&stack_overflow_label_, le);
++}
++
++
++void RegExpMacroAssemblerPPC::CallCFunctionUsingStub(
++ ExternalReference function,
++ int num_arguments) {
++ // Must pass all arguments in registers. The stub pushes on the stack.
++ ASSERT(num_arguments <= 8);
++ __ mov(code_pointer(), Operand(function));
++ RegExpCEntryStub stub;
++ __ CallStub(&stub);
++ if (OS::ActivationFrameAlignment() > kPointerSize) {
++ __ LoadP(sp, MemOperand(sp, 0));
++ } else {
++ __ addi(sp, sp, Operand(kNumRequiredStackFrameSlots * kPointerSize));
++ }
++ __ mov(code_pointer(), Operand(masm_->CodeObject()));
++}
++
++
++bool RegExpMacroAssemblerPPC::CanReadUnaligned() {
++ return CpuFeatures::IsSupported(UNALIGNED_ACCESSES) && !slow_safe();
++}
++
++
++void RegExpMacroAssemblerPPC::LoadCurrentCharacterUnchecked(int cp_offset,
++ int characters) {
++ Register offset = current_input_offset();
++ if (cp_offset != 0) {
++ // r25 is not being used to store the capture start index at this point.
++ __ addi(r25, current_input_offset(), Operand(cp_offset * char_size()));
++ offset = r25;
++ }
++ // The lwz, stw, lhz, sth instructions can do unaligned accesses, if the CPU
++ // and the operating system running on the target allow it.
++ // We assume we don't want to do unaligned loads on PPC, so this function
++ // must only be used to load a single character at a time.
++
++ ASSERT(characters == 1);
++ __ add(current_character(), end_of_input_address(), offset);
++ if (mode_ == ASCII) {
++ __ lbz(current_character(), MemOperand(current_character()));
++ } else {
++ ASSERT(mode_ == UC16);
++ __ lhz(current_character(), MemOperand(current_character()));
++ }
++}
++
++
++void RegExpCEntryStub::Generate(MacroAssembler* masm_) {
++ int stack_alignment = OS::ActivationFrameAlignment();
++ if (stack_alignment < kPointerSize) stack_alignment = kPointerSize;
++
++ // Stack is already aligned for call, so decrement by alignment
++ // to make room for storing the return address.
++ int extra_stack_slots = stack_alignment >> kPointerSizeLog2;
++
++ __ addi(r3, sp, Operand(-stack_alignment));
++ __ mflr(r0);
++ __ StoreP(r0, MemOperand(r3, 0));
++
++ // PPC LINUX ABI:
++ extra_stack_slots += kNumRequiredStackFrameSlots;
++ __ addi(sp, sp, Operand(-extra_stack_slots * kPointerSize));
++
++#if ABI_USES_FUNCTION_DESCRIPTORS && !defined(USE_SIMULATOR)
++ // Native AIX/PPC64 Linux use a function descriptor.
++ __ LoadP(ToRegister(2), MemOperand(r26, kPointerSize)); // TOC
++ __ LoadP(ip, MemOperand(r26, 0)); // Instruction address
++ Register target = ip;
++#elif ABI_TOC_ADDRESSABILITY_VIA_IP
++ Register target = ip;
++ __ Move(target, r26);
++#else
++ Register target = r26;
++#endif
++
++ __ Call(target);
++
++ __ addi(sp, sp, Operand(extra_stack_slots * kPointerSize));
++
++ __ LoadP(r0, MemOperand(sp, -stack_alignment));
++ __ mtlr(r0);
++ __ blr();
++}
++
++#undef __
++
++#endif // V8_INTERPRETED_REGEXP
++
++}} // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.h.ppc
v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.h
+--- v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.h.ppc 2016-06-07 14:15:46.002392937
-0400
++++ v8-3.14.5.10/src/ppc/regexp-macro-assembler-ppc.h 2016-06-07 14:15:46.002392937
-0400
+@@ -0,0 +1,270 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#ifndef V8_PPC_REGEXP_MACRO_ASSEMBLER_PPC_H_
++#define V8_PPC_REGEXP_MACRO_ASSEMBLER_PPC_H_
++
++#include "ppc/assembler-ppc.h"
++#include "ppc/assembler-ppc-inl.h"
++
++namespace v8 {
++namespace internal {
++
++
++#ifndef V8_INTERPRETED_REGEXP
++class RegExpMacroAssemblerPPC: public NativeRegExpMacroAssembler {
++ public:
++ RegExpMacroAssemblerPPC(Mode mode, int registers_to_save, Zone* zone);
++ virtual ~RegExpMacroAssemblerPPC();
++ virtual int stack_limit_slack();
++ virtual void AdvanceCurrentPosition(int by);
++ virtual void AdvanceRegister(int reg, int by);
++ virtual void Backtrack();
++ virtual void Bind(Label* label);
++ virtual void CheckAtStart(Label* on_at_start);
++ virtual void CheckCharacter(unsigned c, Label* on_equal);
++ virtual void CheckCharacterAfterAnd(unsigned c,
++ unsigned mask,
++ Label* on_equal);
++ virtual void CheckCharacterGT(uc16 limit, Label* on_greater);
++ virtual void CheckCharacterLT(uc16 limit, Label* on_less);
++ virtual void CheckCharacters(Vector<const uc16> str,
++ int cp_offset,
++ Label* on_failure,
++ bool check_end_of_string);
++ // A "greedy loop" is a loop that is both greedy and with a simple
++ // body. It has a particularly simple implementation.
++ virtual void CheckGreedyLoop(Label* on_tos_equals_current_position);
++ virtual void CheckNotAtStart(Label* on_not_at_start);
++ virtual void CheckNotBackReference(int start_reg, Label* on_no_match);
++ virtual void CheckNotBackReferenceIgnoreCase(int start_reg,
++ Label* on_no_match);
++ virtual void CheckNotCharacter(unsigned c, Label* on_not_equal);
++ virtual void CheckNotCharacterAfterAnd(unsigned c,
++ unsigned mask,
++ Label* on_not_equal);
++ virtual void CheckNotCharacterAfterMinusAnd(uc16 c,
++ uc16 minus,
++ uc16 mask,
++ Label* on_not_equal);
++ virtual void CheckCharacterInRange(uc16 from,
++ uc16 to,
++ Label* on_in_range);
++ virtual void CheckCharacterNotInRange(uc16 from,
++ uc16 to,
++ Label* on_not_in_range);
++ virtual void CheckBitInTable(Handle<ByteArray> table, Label* on_bit_set);
++
++ // Checks whether the given offset from the current position is before
++ // the end of the string.
++ virtual void CheckPosition(int cp_offset, Label* on_outside_input);
++ virtual bool CheckSpecialCharacterClass(uc16 type,
++ Label* on_no_match);
++ virtual void Fail();
++ virtual Handle<HeapObject> GetCode(Handle<String> source);
++ virtual void GoTo(Label* label);
++ virtual void IfRegisterGE(int reg, int comparand, Label* if_ge);
++ virtual void IfRegisterLT(int reg, int comparand, Label* if_lt);
++ virtual void IfRegisterEqPos(int reg, Label* if_eq);
++ virtual IrregexpImplementation Implementation();
++ virtual void LoadCurrentCharacter(int cp_offset,
++ Label* on_end_of_input,
++ bool check_bounds = true,
++ int characters = 1);
++ virtual void PopCurrentPosition();
++ virtual void PopRegister(int register_index);
++ virtual void PushBacktrack(Label* label);
++ virtual void PushCurrentPosition();
++ virtual void PushRegister(int register_index,
++ StackCheckFlag check_stack_limit);
++ virtual void ReadCurrentPositionFromRegister(int reg);
++ virtual void ReadStackPointerFromRegister(int reg);
++ virtual void SetCurrentPositionFromEnd(int by);
++ virtual void SetRegister(int register_index, int to);
++ virtual bool Succeed();
++ virtual void WriteCurrentPositionToRegister(int reg, int cp_offset);
++ virtual void ClearRegisters(int reg_from, int reg_to);
++ virtual void WriteStackPointerToRegister(int reg);
++ virtual bool CanReadUnaligned();
++
++ // Called from RegExp if the stack-guard is triggered.
++ // If the code object is relocated, the return address is fixed before
++ // returning.
++ static int CheckStackGuardState(Address* return_address,
++ Code* re_code,
++ Address re_frame);
++
++ private:
++ // Offsets from frame_pointer() of function parameters and stored registers.
++ static const int kFramePointer = 0;
++
++ // Above the frame pointer - Stored registers and stack passed parameters.
++ // Register 25..31.
++ static const int kStoredRegisters = kFramePointer;
++ // Return address (stored from link register, read into pc on return).
++ static const int kReturnAddress = kStoredRegisters + 7 * kPointerSize;
++ static const int kCallerFrame = kReturnAddress + kPointerSize;
++ // Stack parameters placed by caller.
++ static const int kSecondaryReturnAddress = kCallerFrame +
++ kStackFrameExtraParamSlot * kPointerSize;
++ static const int kIsolate = kSecondaryReturnAddress + kPointerSize;
++
++ // Below the frame pointer.
++ // Register parameters stored by setup code.
++ static const int kDirectCall = kFramePointer - kPointerSize;
++ static const int kStackHighEnd = kDirectCall - kPointerSize;
++ static const int kNumOutputRegisters = kStackHighEnd - kPointerSize;
++ static const int kRegisterOutput = kNumOutputRegisters - kPointerSize;
++ static const int kInputEnd = kRegisterOutput - kPointerSize;
++ static const int kInputStart = kInputEnd - kPointerSize;
++ static const int kStartIndex = kInputStart - kPointerSize;
++ static const int kInputString = kStartIndex - kPointerSize;
++ // When adding local variables remember to push space for them in
++ // the frame in GetCode.
++ static const int kSuccessfulCaptures = kInputString - kPointerSize;
++ static const int kInputStartMinusOne = kSuccessfulCaptures - kPointerSize;
++ // First register address. Following registers are below it on the stack.
++ static const int kRegisterZero = kInputStartMinusOne - kPointerSize;
++
++ // Initial size of code buffer.
++ static const size_t kRegExpCodeSize = 1024;
++
++ // Load a number of characters at the given offset from the
++ // current position, into the current-character register.
++ void LoadCurrentCharacterUnchecked(int cp_offset, int character_count);
++
++ // Check whether preemption has been requested.
++ void CheckPreemption();
++
++ // Check whether we are exceeding the stack limit on the backtrack stack.
++ void CheckStackLimit();
++
++
++ // Generate a call to CheckStackGuardState.
++ void CallCheckStackGuardState(Register scratch);
++
++ // The ebp-relative location of a regexp register.
++ MemOperand register_location(int register_index);
++
++ // Register holding the current input position as negative offset from
++ // the end of the string.
++ inline Register current_input_offset() { return r27; }
++
++ // The register containing the current character after LoadCurrentCharacter.
++ inline Register current_character() { return r28; }
++
++ // Register holding address of the end of the input string.
++ inline Register end_of_input_address() { return r30; }
++
++ // Register holding the frame address. Local variables, parameters and
++ // regexp registers are addressed relative to this.
++ inline Register frame_pointer() { return fp; }
++
++ // The register containing the backtrack stack top. Provides a meaningful
++ // name to the register.
++ inline Register backtrack_stackpointer() { return r29; }
++
++ // Register holding pointer to the current code object.
++ inline Register code_pointer() { return r26; }
++
++ // Byte size of chars in the string to match (decided by the Mode argument)
++ inline int char_size() { return static_cast<int>(mode_); }
++
++ // Equivalent to a conditional branch to the label, unless the label
++ // is NULL, in which case it is a conditional Backtrack.
++ void BranchOrBacktrack(Condition condition, Label* to, CRegister cr = cr7);
++
++ // Call and return internally in the generated code in a way that
++ // is GC-safe (i.e., doesn't leave absolute code addresses on the stack)
++ inline void SafeCall(Label* to, Condition cond = al, CRegister cr = cr7);
++ inline void SafeReturn();
++ inline void SafeCallTarget(Label* name);
++
++ // Pushes the value of a register on the backtrack stack. Decrements the
++ // stack pointer by a word size and stores the register's value there.
++ inline void Push(Register source);
++
++ // Pops a value from the backtrack stack. Reads the word at the stack pointer
++ // and increments it by a word size.
++ inline void Pop(Register target);
++
++ // Calls a C function and cleans up the frame alignment done by
++ // by FrameAlign. The called function *is* allowed to trigger a garbage
++ // collection, but may not take more than four arguments (no arguments
++ // passed on the stack), and the first argument will be a pointer to the
++ // return address.
++ inline void CallCFunctionUsingStub(ExternalReference function,
++ int num_arguments);
++
++
++ MacroAssembler* masm_;
++
++ // Which mode to generate code for (ASCII or UC16).
++ Mode mode_;
++
++ // One greater than maximal register index actually used.
++ int num_registers_;
++
++ // Number of registers to output at the end (the saved registers
++ // are always 0..num_saved_registers_-1)
++ int num_saved_registers_;
++
++ // Manage a small pre-allocated pool for writing label targets
++ // to for pushing backtrack addresses.
++ int backtrack_constant_pool_offset_;
++ int backtrack_constant_pool_capacity_;
++
++ // Labels used internally.
++ Label entry_label_;
++ Label start_label_;
++ Label success_label_;
++ Label backtrack_label_;
++ Label exit_label_;
++ Label check_preempt_label_;
++ Label stack_overflow_label_;
++ Label internal_failure_label_;
++};
++
++// Set of non-volatile registers saved/restored by generated regexp code.
++const RegList kRegExpCalleeSaved =
++ 1 << 25 |
++ 1 << 26 |
++ 1 << 27 |
++ 1 << 28 |
++ 1 << 29 |
++ 1 << 30 |
++ 1 << 31;
++
++#endif // V8_INTERPRETED_REGEXP
++
++
++}} // namespace v8::internal
++
++#endif // V8_PPC_REGEXP_MACRO_ASSEMBLER_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/simulator-ppc.cc.ppc v8-3.14.5.10/src/ppc/simulator-ppc.cc
+--- v8-3.14.5.10/src/ppc/simulator-ppc.cc.ppc 2016-06-07 14:15:46.004392925 -0400
++++ v8-3.14.5.10/src/ppc/simulator-ppc.cc 2016-06-07 14:15:46.004392925 -0400
+@@ -0,0 +1,3479 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include <stdlib.h>
++#include <math.h>
++#include <cstdarg>
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "disasm.h"
++#include "assembler.h"
++#include "codegen.h"
++#include "ppc/constants-ppc.h"
++#include "ppc/simulator-ppc.h"
++#include "ppc/frames-ppc.h"
++
++#if defined(USE_SIMULATOR)
++
++// Only build the simulator if not compiling for real PPC hardware.
++namespace v8 {
++namespace internal {
++
++// This macro provides a platform independent use of sscanf. The reason for
++// SScanF not being implemented in a platform independent way through
++// ::v8::internal::OS in the same way as SNPrintF is that the
++// Windows C Run-Time Library does not provide vsscanf.
++#define SScanF sscanf // NOLINT
++
++// The PPCDebugger class is used by the simulator while debugging simulated
++// PowerPC code.
++class PPCDebugger {
++ public:
++ explicit PPCDebugger(Simulator* sim) : sim_(sim) { }
++ ~PPCDebugger();
++
++ void Stop(Instruction* instr);
++ void Info(Instruction* instr);
++ void Debug();
++
++ private:
++ static const Instr kBreakpointInstr = (TWI | 0x1f * B21);
++ static const Instr kNopInstr = (ORI); // ori, 0,0,0
++
++ Simulator* sim_;
++
++ intptr_t GetRegisterValue(int regnum);
++ double GetRegisterPairDoubleValue(int regnum);
++ double GetFPDoubleRegisterValue(int regnum);
++ bool GetValue(const char* desc, intptr_t* value);
++ bool GetFPDoubleValue(const char* desc, double* value);
++
++ // Set or delete a breakpoint. Returns true if successful.
++ bool SetBreakpoint(Instruction* break_pc);
++ bool DeleteBreakpoint(Instruction* break_pc);
++
++ // Undo and redo all breakpoints. This is needed to bracket disassembly and
++ // execution to skip past breakpoints when run from the debugger.
++ void UndoBreakpoints();
++ void RedoBreakpoints();
++};
++
++
++PPCDebugger::~PPCDebugger() {
++}
++
++
++
++#ifdef GENERATED_CODE_COVERAGE
++static FILE* coverage_log = NULL;
++
++
++static void InitializeCoverage() {
++ char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
++ if (file_name != NULL) {
++ coverage_log = fopen(file_name, "aw+");
++ }
++}
++
++
++void PPCDebugger::Stop(Instruction* instr) { // roohack need to fix for PPC
++ // Get the stop code.
++ uint32_t code = instr->SvcValue() & kStopCodeMask;
++ // Retrieve the encoded address, which comes just after this stop.
++ char** msg_address =
++ reinterpret_cast<char**>(sim_->get_pc() + Instruction::kInstrSize);
++ char* msg = *msg_address;
++ ASSERT(msg != NULL);
++
++ // Update this stop description.
++ if (isWatchedStop(code) && !watched_stops[code].desc) {
++ watched_stops[code].desc = msg;
++ }
++
++ if (strlen(msg) > 0) {
++ if (coverage_log != NULL) {
++ fprintf(coverage_log, "%s\n", msg);
++ fflush(coverage_log);
++ }
++ // Overwrite the instruction and address with nops.
++ instr->SetInstructionBits(kNopInstr);
++
reinterpret_cast<Instruction*>(msg_address)->SetInstructionBits(kNopInstr);
++ }
++ sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize + kPointerSize);
++}
++
++#else // ndef GENERATED_CODE_COVERAGE
++
++static void InitializeCoverage() {
++}
++
++
++void PPCDebugger::Stop(Instruction* instr) {
++ // Get the stop code.
++ // use of kStopCodeMask not right on PowerPC
++ uint32_t code = instr->SvcValue() & kStopCodeMask;
++ // Retrieve the encoded address, which comes just after this stop.
++ char* msg = *reinterpret_cast<char**>(sim_->get_pc()
++ + Instruction::kInstrSize);
++ // Update this stop description.
++ if (sim_->isWatchedStop(code) && !sim_->watched_stops[code].desc) {
++ sim_->watched_stops[code].desc = msg;
++ }
++ // Print the stop message and code if it is not the default code.
++ if (code != kMaxStopCode) {
++ PrintF("Simulator hit stop %u: %s\n", code, msg);
++ } else {
++ PrintF("Simulator hit %s\n", msg);
++ }
++ sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize + kPointerSize);
++ Debug();
++}
++#endif
++
++
++void PPCDebugger::Info(Instruction* instr) {
++ // Retrieve the encoded address immediately following the Info breakpoint.
++ char* msg = *reinterpret_cast<char**>(sim_->get_pc()
++ + Instruction::kInstrSize);
++ PrintF("Simulator info %s\n", msg);
++ sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize + kPointerSize);
++}
++
++
++intptr_t PPCDebugger::GetRegisterValue(int regnum) {
++ return sim_->get_register(regnum);
++}
++
++
++double PPCDebugger::GetRegisterPairDoubleValue(int regnum) {
++ return sim_->get_double_from_register_pair(regnum);
++}
++
++
++double PPCDebugger::GetFPDoubleRegisterValue(int regnum) {
++ return sim_->get_double_from_d_register(regnum);
++}
++
++bool PPCDebugger::GetValue(const char* desc, intptr_t* value) {
++ int regnum = Registers::Number(desc);
++ if (regnum != kNoRegister) {
++ *value = GetRegisterValue(regnum);
++ return true;
++ } else {
++ if (strncmp(desc, "0x", 2) == 0) {
++ return SScanF(desc + 2, "%x", reinterpret_cast<uint32_t*>(value))
== 1;
++ } else {
++ return SScanF(desc, "%u", reinterpret_cast<uint32_t*>(value)) ==
1;
++ }
++ }
++ return false;
++}
++
++bool PPCDebugger::GetFPDoubleValue(const char* desc, double* value) {
++ int regnum = FPRegisters::Number(desc);
++ if (regnum != kNoRegister) {
++ *value = sim_->get_double_from_d_register(regnum);
++ return true;
++ }
++ return false;
++}
++
++
++bool PPCDebugger::SetBreakpoint(Instruction* break_pc) {
++ // Check if a breakpoint can be set. If not return without any side-effects.
++ if (sim_->break_pc_ != NULL) {
++ return false;
++ }
++
++ // Set the breakpoint.
++ sim_->break_pc_ = break_pc;
++ sim_->break_instr_ = break_pc->InstructionBits();
++ // Not setting the breakpoint instruction in the code itself. It will be set
++ // when the debugger shell continues.
++ return true;
++}
++
++
++bool PPCDebugger::DeleteBreakpoint(Instruction* break_pc) {
++ if (sim_->break_pc_ != NULL) {
++ sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
++ }
++
++ sim_->break_pc_ = NULL;
++ sim_->break_instr_ = 0;
++ return true;
++}
++
++
++void PPCDebugger::UndoBreakpoints() {
++ if (sim_->break_pc_ != NULL) {
++ sim_->break_pc_->SetInstructionBits(sim_->break_instr_);
++ }
++}
++
++
++void PPCDebugger::RedoBreakpoints() {
++ if (sim_->break_pc_ != NULL) {
++ sim_->break_pc_->SetInstructionBits(kBreakpointInstr);
++ }
++}
++
++
++void PPCDebugger::Debug() {
++ intptr_t last_pc = -1;
++ bool done = false;
++
++#define COMMAND_SIZE 63
++#define ARG_SIZE 255
++
++#define STR(a) #a
++#define XSTR(a) STR(a)
++
++ char cmd[COMMAND_SIZE + 1];
++ char arg1[ARG_SIZE + 1];
++ char arg2[ARG_SIZE + 1];
++ char* argv[3] = { cmd, arg1, arg2 };
++
++ // make sure to have a proper terminating character if reaching the limit
++ cmd[COMMAND_SIZE] = 0;
++ arg1[ARG_SIZE] = 0;
++ arg2[ARG_SIZE] = 0;
++
++ // Undo all set breakpoints while running in the debugger shell. This will
++ // make them invisible to all commands.
++ UndoBreakpoints();
++ // Disable tracing while simulating
++ bool trace=::v8::internal::FLAG_trace_sim;
++ ::v8::internal::FLAG_trace_sim = false;
++
++ while (!done && !sim_->has_bad_pc()) {
++ if (last_pc != sim_->get_pc()) {
++ disasm::NameConverter converter;
++ disasm::Disassembler dasm(converter);
++ // use a reasonably large buffer
++ v8::internal::EmbeddedVector<char, 256> buffer;
++ dasm.InstructionDecode(buffer,
++ reinterpret_cast<byte*>(sim_->get_pc()));
++ PrintF(" 0x%08" V8PRIxPTR " %s\n", sim_->get_pc(),
buffer.start());
++ last_pc = sim_->get_pc();
++ }
++ char* line = ReadLine("sim> ");
++ if (line == NULL) {
++ break;
++ } else {
++ char* last_input = sim_->last_debugger_input();
++ if (strcmp(line, "\n") == 0 && last_input != NULL) {
++ line = last_input;
++ } else {
++ // Ownership is transferred to sim_;
++ sim_->set_last_debugger_input(line);
++ }
++ // Use sscanf to parse the individual parts of the command line. At the
++ // moment no command expects more than two parameters.
++ int argc = SScanF(line,
++ "%" XSTR(COMMAND_SIZE) "s "
++ "%" XSTR(ARG_SIZE) "s "
++ "%" XSTR(ARG_SIZE) "s",
++ cmd, arg1, arg2);
++ if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0))
{
++ intptr_t value;
++
++ // If at a breakpoint, proceed past it.
++ if
((reinterpret_cast<Instruction*>(sim_->get_pc()))->InstructionBits()
++ == 0x7d821008) {
++ sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize);
++ } else {
++ sim_->InstructionDecode(
++ reinterpret_cast<Instruction*>(sim_->get_pc()));
++ }
++
++ if (argc == 2 && last_pc != sim_->get_pc() &&
++ GetValue(arg1, &value)) {
++ for (int i = 1; i < value; i++) {
++ disasm::NameConverter converter;
++ disasm::Disassembler dasm(converter);
++ // use a reasonably large buffer
++ v8::internal::EmbeddedVector<char, 256> buffer;
++ dasm.InstructionDecode(buffer,
++ reinterpret_cast<byte*>(sim_->get_pc()));
++ PrintF(" 0x%08" V8PRIxPTR " %s\n", sim_->get_pc(),
++ buffer.start());
++ sim_->InstructionDecode(
++ reinterpret_cast<Instruction*>(sim_->get_pc()));
++ }
++ }
++ } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") ==
0)) {
++ // If at a breakpoint, proceed past it.
++ if
((reinterpret_cast<Instruction*>(sim_->get_pc()))->InstructionBits()
++ == 0x7d821008) {
++ sim_->set_pc(sim_->get_pc() + Instruction::kInstrSize);
++ } else {
++ // Execute the one instruction we broke at with breakpoints disabled.
++ sim_->InstructionDecode(
++ reinterpret_cast<Instruction*>(sim_->get_pc()));
++ }
++ // Leave the debugger shell.
++ done = true;
++ } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") ==
0)) {
++ if (argc == 2 || (argc == 3 && strcmp(arg2, "fp") == 0)) {
++ intptr_t value;
++ double dvalue;
++ if (strcmp(arg1, "all") == 0) {
++ for (int i = 0; i < kNumRegisters; i++) {
++ value = GetRegisterValue(i);
++ PrintF(" %3s: %08" V8PRIxPTR, Registers::Name(i), value);
++ if ((argc == 3 && strcmp(arg2, "fp") == 0) &&
++ i < 8 &&
++ (i % 2) == 0) {
++ dvalue = GetRegisterPairDoubleValue(i);
++ PrintF(" (%f)\n", dvalue);
++ } else if (i != 0 && !((i+1) & 3)) {
++ PrintF("\n");
++ }
++ }
++ PrintF(" pc: %08" V8PRIxPTR " lr: %08" V8PRIxPTR
" "
++ "ctr: %08" V8PRIxPTR " xer: %08x cr: %08x\n",
++ sim_->special_reg_pc_, sim_->special_reg_lr_,
++ sim_->special_reg_ctr_, sim_->special_reg_xer_,
++ sim_->condition_reg_);
++ } else if (strcmp(arg1, "alld") == 0) {
++ for (int i = 0; i < kNumRegisters; i++) {
++ value = GetRegisterValue(i);
++ PrintF(" %3s: %08" V8PRIxPTR
++ " %11" V8PRIdPTR, Registers::Name(i), value, value);
++ if ((argc == 3 && strcmp(arg2, "fp") == 0) &&
++ i < 8 &&
++ (i % 2) == 0) {
++ dvalue = GetRegisterPairDoubleValue(i);
++ PrintF(" (%f)\n", dvalue);
++ } else if (!((i+1) % 2)) {
++ PrintF("\n");
++ }
++ }
++ PrintF(" pc: %08" V8PRIxPTR " lr: %08" V8PRIxPTR
" "
++ "ctr: %08" V8PRIxPTR " xer: %08x cr: %08x\n",
++ sim_->special_reg_pc_, sim_->special_reg_lr_,
++ sim_->special_reg_ctr_, sim_->special_reg_xer_,
++ sim_->condition_reg_);
++ } else if (strcmp(arg1, "allf") == 0) {
++ for (int i = 0; i < kNumFPDoubleRegisters; i++) {
++ dvalue = GetFPDoubleRegisterValue(i);
++ uint64_t as_words = BitCast<uint64_t>(dvalue);
++ PrintF("%3s: %f 0x%08x %08x\n",
++ FPRegisters::Name(i),
++ dvalue,
++ static_cast<uint32_t>(as_words >> 32),
++ static_cast<uint32_t>(as_words & 0xffffffff));
++ }
++ } else if (arg1[0] == 'r' &&
++ (arg1[1] >= '0' && arg1[1] <= '9'
&&
++ (arg1[2] == '\0' ||
++ (arg1[2] >= '0' && arg1[2] <=
'9'
++ && arg1[3] == '\0')))) {
++ int regnum = strtoul(&arg1[1], 0, 10);
++ if (regnum != kNoRegister) {
++ value = GetRegisterValue(regnum);
++ PrintF("%s: 0x%08" V8PRIxPTR " %" V8PRIdPTR
"\n",
++ arg1, value, value);
++ } else {
++ PrintF("%s unrecognized\n", arg1);
++ }
++ } else {
++ if (GetValue(arg1, &value)) {
++ PrintF("%s: 0x%08" V8PRIxPTR " %" V8PRIdPTR
"\n",
++ arg1, value, value);
++ } else if (GetFPDoubleValue(arg1, &dvalue)) {
++ uint64_t as_words = BitCast<uint64_t>(dvalue);
++ PrintF("%s: %f 0x%08x %08x\n",
++ arg1,
++ dvalue,
++ static_cast<uint32_t>(as_words >> 32),
++ static_cast<uint32_t>(as_words & 0xffffffff));
++ } else {
++ PrintF("%s unrecognized\n", arg1);
++ }
++ }
++ } else {
++ PrintF("print <register>\n");
++ }
++ } else if ((strcmp(cmd, "po") == 0)
++ || (strcmp(cmd, "printobject") == 0)) {
++ if (argc == 2) {
++ intptr_t value;
++ if (GetValue(arg1, &value)) {
++ Object* obj = reinterpret_cast<Object*>(value);
++ PrintF("%s: \n", arg1);
++#ifdef DEBUG
++ obj->PrintLn();
++#else
++ obj->ShortPrint();
++ PrintF("\n");
++#endif
++ } else {
++ PrintF("%s unrecognized\n", arg1);
++ }
++ } else {
++ PrintF("printobject <value>\n");
++ }
++ } else if (strcmp(cmd, "setpc") == 0) {
++ intptr_t value;
++
++ if (!GetValue(arg1, &value)) {
++ PrintF("%s unrecognized\n", arg1);
++ continue;
++ }
++ sim_->set_pc(value);
++ } else if (strcmp(cmd, "stack") == 0 || strcmp(cmd, "mem") ==
0) {
++ intptr_t* cur = NULL;
++ intptr_t* end = NULL;
++ int next_arg = 1;
++
++ if (strcmp(cmd, "stack") == 0) {
++ cur =
reinterpret_cast<intptr_t*>(sim_->get_register(Simulator::sp));
++ } else { // "mem"
++ intptr_t value;
++ if (!GetValue(arg1, &value)) {
++ PrintF("%s unrecognized\n", arg1);
++ continue;
++ }
++ cur = reinterpret_cast<intptr_t*>(value);
++ next_arg++;
++ }
++
++ intptr_t words; // likely inaccurate variable name for 64bit
++ if (argc == next_arg) {
++ words = 10;
++ } else if (argc == next_arg + 1) {
++ if (!GetValue(argv[next_arg], &words)) {
++ words = 10;
++ }
++ }
++ end = cur + words;
++
++ while (cur < end) {
++ PrintF(" 0x%08" V8PRIxPTR ": 0x%08" V8PRIxPTR "
%10" V8PRIdPTR,
++ reinterpret_cast<intptr_t>(cur), *cur, *cur);
++ HeapObject* obj = reinterpret_cast<HeapObject*>(*cur);
++ int value = *cur;
++ Heap* current_heap = v8::internal::Isolate::Current()->heap();
++ if (current_heap->Contains(obj) || ((value & 1) == 0)) {
++ PrintF(" (");
++ if ((value & 1) == 0) {
++ PrintF("smi %d", value / 2);
++ } else {
++ obj->ShortPrint();
++ }
++ PrintF(")");
++ }
++ PrintF("\n");
++ cur++;
++ }
++ } else if (strcmp(cmd, "disasm") == 0 || strcmp(cmd, "di") ==
0) {
++ disasm::NameConverter converter;
++ disasm::Disassembler dasm(converter);
++ // use a reasonably large buffer
++ v8::internal::EmbeddedVector<char, 256> buffer;
++
++ byte* prev = NULL;
++ byte* cur = NULL;
++ byte* end = NULL;
++
++ if (argc == 1) {
++ cur = reinterpret_cast<byte*>(sim_->get_pc());
++ end = cur + (10 * Instruction::kInstrSize);
++ } else if (argc == 2) {
++ int regnum = Registers::Number(arg1);
++ if (regnum != kNoRegister || strncmp(arg1, "0x", 2) == 0) {
++ // The argument is an address or a register name.
++ intptr_t value;
++ if (GetValue(arg1, &value)) {
++ cur = reinterpret_cast<byte*>(value);
++ // Disassemble 10 instructions at <arg1>.
++ end = cur + (10 * Instruction::kInstrSize);
++ }
++ } else {
++ // The argument is the number of instructions.
++ intptr_t value;
++ if (GetValue(arg1, &value)) {
++ cur = reinterpret_cast<byte*>(sim_->get_pc());
++ // Disassemble <arg1> instructions.
++ end = cur + (value * Instruction::kInstrSize);
++ }
++ }
++ } else {
++ intptr_t value1;
++ intptr_t value2;
++ if (GetValue(arg1, &value1) && GetValue(arg2, &value2)) {
++ cur = reinterpret_cast<byte*>(value1);
++ end = cur + (value2 * Instruction::kInstrSize);
++ }
++ }
++
++ while (cur < end) {
++ prev = cur;
++ cur += dasm.InstructionDecode(buffer, cur);
++ PrintF(" 0x%08" V8PRIxPTR " %s\n",
++ reinterpret_cast<intptr_t>(prev), buffer.start());
++ }
++ } else if (strcmp(cmd, "gdb") == 0) {
++ PrintF("relinquishing control to gdb\n");
++ v8::internal::OS::DebugBreak();
++ PrintF("regaining control from gdb\n");
++ } else if (strcmp(cmd, "break") == 0) {
++ if (argc == 2) {
++ intptr_t value;
++ if (GetValue(arg1, &value)) {
++ if (!SetBreakpoint(reinterpret_cast<Instruction*>(value))) {
++ PrintF("setting breakpoint failed\n");
++ }
++ } else {
++ PrintF("%s unrecognized\n", arg1);
++ }
++ } else {
++ PrintF("break <address>\n");
++ }
++ } else if (strcmp(cmd, "del") == 0) {
++ if (!DeleteBreakpoint(NULL)) {
++ PrintF("deleting breakpoint failed\n");
++ }
++ } else if (strcmp(cmd, "cr") == 0) {
++ PrintF("Condition reg: %08x\n", sim_->condition_reg_);
++ } else if (strcmp(cmd, "lr") == 0) {
++ PrintF("Link reg: %08" V8PRIxPTR "\n",
sim_->special_reg_lr_);
++ } else if (strcmp(cmd, "ctr") == 0) {
++ PrintF("Ctr reg: %08" V8PRIxPTR "\n",
sim_->special_reg_ctr_);
++ } else if (strcmp(cmd, "xer") == 0) {
++ PrintF("XER: %08x\n", sim_->special_reg_xer_);
++ } else if (strcmp(cmd, "fpscr") == 0) {
++ PrintF("FPSCR: %08x\n", sim_->fp_condition_reg_);
++ } else if (strcmp(cmd, "stop") == 0) {
++ intptr_t value;
++ intptr_t stop_pc = sim_->get_pc() -
++ (Instruction::kInstrSize + kPointerSize);
++ Instruction* stop_instr = reinterpret_cast<Instruction*>(stop_pc);
++ Instruction* msg_address =
++ reinterpret_cast<Instruction*>(stop_pc + Instruction::kInstrSize);
++ if ((argc == 2) && (strcmp(arg1, "unstop") == 0)) {
++ // Remove the current stop.
++ if (sim_->isStopInstruction(stop_instr)) {
++ stop_instr->SetInstructionBits(kNopInstr);
++ msg_address->SetInstructionBits(kNopInstr);
++ } else {
++ PrintF("Not at debugger stop.\n");
++ }
++ } else if (argc == 3) {
++ // Print information about all/the specified breakpoint(s).
++ if (strcmp(arg1, "info") == 0) {
++ if (strcmp(arg2, "all") == 0) {
++ PrintF("Stop information:\n");
++ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
++ sim_->PrintStopInfo(i);
++ }
++ } else if (GetValue(arg2, &value)) {
++ sim_->PrintStopInfo(value);
++ } else {
++ PrintF("Unrecognized argument.\n");
++ }
++ } else if (strcmp(arg1, "enable") == 0) {
++ // Enable all/the specified breakpoint(s).
++ if (strcmp(arg2, "all") == 0) {
++ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
++ sim_->EnableStop(i);
++ }
++ } else if (GetValue(arg2, &value)) {
++ sim_->EnableStop(value);
++ } else {
++ PrintF("Unrecognized argument.\n");
++ }
++ } else if (strcmp(arg1, "disable") == 0) {
++ // Disable all/the specified breakpoint(s).
++ if (strcmp(arg2, "all") == 0) {
++ for (uint32_t i = 0; i < sim_->kNumOfWatchedStops; i++) {
++ sim_->DisableStop(i);
++ }
++ } else if (GetValue(arg2, &value)) {
++ sim_->DisableStop(value);
++ } else {
++ PrintF("Unrecognized argument.\n");
++ }
++ }
++ } else {
++ PrintF("Wrong usage. Use help command for more information.\n");
++ }
++ } else if ((strcmp(cmd, "t") == 0) || strcmp(cmd, "trace") ==
0) {
++ ::v8::internal::FLAG_trace_sim = !::v8::internal::FLAG_trace_sim;
++ PrintF("Trace of executed instructions is %s\n",
++ ::v8::internal::FLAG_trace_sim ? "on" : "off");
++ } else if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") ==
0)) {
++ PrintF("cont\n");
++ PrintF(" continue execution (alias 'c')\n");
++ PrintF("stepi [num instructions]\n");
++ PrintF(" step one/num instruction(s) (alias 'si')\n");
++ PrintF("print <register>\n");
++ PrintF(" print register content (alias 'p')\n");
++ PrintF(" use register name 'all' to display all integer
registers\n");
++ PrintF(" use register name 'alld' to display integer registers
"\
++ "with decimal values\n");
++ PrintF(" use register name 'rN' to display register number
'N'\n");
++ PrintF(" add argument 'fp' to print register pair double
values\n");
++ PrintF(" use register name 'allf' to display floating-point
"\
++ "registers\n");
++ PrintF("printobject <register>\n");
++ PrintF(" print an object from a register (alias 'po')\n");
++ PrintF("cr\n");
++ PrintF(" print condition register\n");
++ PrintF("lr\n");
++ PrintF(" print link register\n");
++ PrintF("ctr\n");
++ PrintF(" print ctr register\n");
++ PrintF("xer\n");
++ PrintF(" print XER\n");
++ PrintF("fpscr\n");
++ PrintF(" print FPSCR\n");
++ PrintF("stack [<num words>]\n");
++ PrintF(" dump stack content, default dump 10 words)\n");
++ PrintF("mem <address> [<num words>]\n");
++ PrintF(" dump memory content, default dump 10 words)\n");
++ PrintF("disasm [<instructions>]\n");
++ PrintF("disasm [<address/register>]\n");
++ PrintF("disasm [[<address/register>] <instructions>]\n");
++ PrintF(" disassemble code, default is 10 instructions\n");
++ PrintF(" from pc (alias 'di')\n");
++ PrintF("gdb\n");
++ PrintF(" enter gdb\n");
++ PrintF("break <address>\n");
++ PrintF(" set a break point on the address\n");
++ PrintF("del\n");
++ PrintF(" delete the breakpoint\n");
++ PrintF("trace (alias 't')\n");
++ PrintF(" toogle the tracing of all executed statements\n");
++ PrintF("stop feature:\n");
++ PrintF(" Description:\n");
++ PrintF(" Stops are debug instructions inserted by\n");
++ PrintF(" the Assembler::stop() function.\n");
++ PrintF(" When hitting a stop, the Simulator will\n");
++ PrintF(" stop and and give control to the PPCDebugger.\n");
++ PrintF(" The first %d stop codes are watched:\n",
++ Simulator::kNumOfWatchedStops);
++ PrintF(" - They can be enabled / disabled: the Simulator\n");
++ PrintF(" will / won't stop when hitting them.\n");
++ PrintF(" - The Simulator keeps track of how many times they \n");
++ PrintF(" are met. (See the info command.) Going over a\n");
++ PrintF(" disabled stop still increases its counter. \n");
++ PrintF(" Commands:\n");
++ PrintF(" stop info all/<code> : print infos about number
<code>\n");
++ PrintF(" or all stop(s).\n");
++ PrintF(" stop enable/disable all/<code> : enables /
disables\n");
++ PrintF(" all or number <code> stop(s)\n");
++ PrintF(" stop unstop\n");
++ PrintF(" ignore the stop instruction at the current
location\n");
++ PrintF(" from now on\n");
++ } else {
++ PrintF("Unknown command: %s\n", cmd);
++ }
++ }
++ }
++
++ // Add all the breakpoints back to stop execution and enter the debugger
++ // shell when hit.
++ RedoBreakpoints();
++ // Restore tracing
++ ::v8::internal::FLAG_trace_sim = trace;
++
++#undef COMMAND_SIZE
++#undef ARG_SIZE
++
++#undef STR
++#undef XSTR
++}
++
++
++static bool ICacheMatch(void* one, void* two) {
++ ASSERT((reinterpret_cast<intptr_t>(one) & CachePage::kPageMask) == 0);
++ ASSERT((reinterpret_cast<intptr_t>(two) & CachePage::kPageMask) == 0);
++ return one == two;
++}
++
++
++static uint32_t ICacheHash(void* key) {
++ return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(key)) >>
2;
++}
++
++
++static bool AllOnOnePage(uintptr_t start, int size) {
++ intptr_t start_page = (start & ~CachePage::kPageMask);
++ intptr_t end_page = ((start + size) & ~CachePage::kPageMask);
++ return start_page == end_page;
++}
++
++
++void Simulator::set_last_debugger_input(char* input) {
++ DeleteArray(last_debugger_input_);
++ last_debugger_input_ = input;
++}
++
++
++void Simulator::FlushICache(v8::internal::HashMap* i_cache,
++ void* start_addr,
++ size_t size) {
++ intptr_t start = reinterpret_cast<intptr_t>(start_addr);
++ int intra_line = (start & CachePage::kLineMask);
++ start -= intra_line;
++ size += intra_line;
++ size = ((size - 1) | CachePage::kLineMask) + 1;
++ int offset = (start & CachePage::kPageMask);
++ while (!AllOnOnePage(start, size - 1)) {
++ int bytes_to_flush = CachePage::kPageSize - offset;
++ FlushOnePage(i_cache, start, bytes_to_flush);
++ start += bytes_to_flush;
++ size -= bytes_to_flush;
++ ASSERT_EQ(0, static_cast<int>(start & CachePage::kPageMask));
++ offset = 0;
++ }
++ if (size != 0) {
++ FlushOnePage(i_cache, start, size);
++ }
++}
++
++
++CachePage* Simulator::GetCachePage(v8::internal::HashMap* i_cache, void* page) {
++ v8::internal::HashMap::Entry* entry = i_cache->Lookup(page,
++ ICacheHash(page),
++ true);
++ if (entry->value == NULL) {
++ CachePage* new_page = new CachePage();
++ entry->value = new_page;
++ }
++ return reinterpret_cast<CachePage*>(entry->value);
++}
++
++
++// Flush from start up to and not including start + size.
++void Simulator::FlushOnePage(v8::internal::HashMap* i_cache,
++ intptr_t start,
++ int size) {
++ ASSERT(size <= CachePage::kPageSize);
++ ASSERT(AllOnOnePage(start, size - 1));
++ ASSERT((start & CachePage::kLineMask) == 0);
++ ASSERT((size & CachePage::kLineMask) == 0);
++ void* page = reinterpret_cast<void*>(start & (~CachePage::kPageMask));
++ int offset = (start & CachePage::kPageMask);
++ CachePage* cache_page = GetCachePage(i_cache, page);
++ char* valid_bytemap = cache_page->ValidityByte(offset);
++ memset(valid_bytemap, CachePage::LINE_INVALID, size >> CachePage::kLineShift);
++}
++
++
++void Simulator::CheckICache(v8::internal::HashMap* i_cache,
++ Instruction* instr) {
++ intptr_t address = reinterpret_cast<intptr_t>(instr);
++ void* page = reinterpret_cast<void*>(address & (~CachePage::kPageMask));
++ void* line = reinterpret_cast<void*>(address & (~CachePage::kLineMask));
++ int offset = (address & CachePage::kPageMask);
++ CachePage* cache_page = GetCachePage(i_cache, page);
++ char* cache_valid_byte = cache_page->ValidityByte(offset);
++ bool cache_hit = (*cache_valid_byte == CachePage::LINE_VALID);
++ char* cached_line = cache_page->CachedData(offset & ~CachePage::kLineMask);
++ if (cache_hit) {
++ // Check that the data in memory matches the contents of the I-cache.
++ CHECK(memcmp(reinterpret_cast<void*>(instr),
++ cache_page->CachedData(offset),
++ Instruction::kInstrSize) == 0);
++ } else {
++ // Cache miss. Load memory into the cache.
++ memcpy(cached_line, line, CachePage::kLineLength);
++ *cache_valid_byte = CachePage::LINE_VALID;
++ }
++}
++
++
++void Simulator::Initialize(Isolate* isolate) {
++ if (isolate->simulator_initialized()) return;
++ isolate->set_simulator_initialized(true);
++ ::v8::internal::ExternalReference::set_redirector(isolate,
++ &RedirectExternalReference);
++}
++
++
++Simulator::Simulator(Isolate* isolate) : isolate_(isolate) {
++ i_cache_ = isolate_->simulator_i_cache();
++ if (i_cache_ == NULL) {
++ i_cache_ = new v8::internal::HashMap(&ICacheMatch);
++ isolate_->set_simulator_i_cache(i_cache_);
++ }
++ Initialize(isolate);
++ // Set up simulator support first. Some of this information is needed to
++ // setup the architecture state.
++ size_t stack_size = 1 * 1024*1024; // allocate 1MB for stack
++ stack_ = reinterpret_cast<char*>(malloc(stack_size));
++ pc_modified_ = false;
++ icount_ = 0;
++ break_pc_ = NULL;
++ break_instr_ = 0;
++
++ // Set up architecture state.
++ // All registers are initialized to zero to start with.
++ for (int i = 0; i < kNumGPRs; i++) {
++ registers_[i] = 0;
++ }
++ condition_reg_ = 0; // PowerPC
++ fp_condition_reg_ = 0; // PowerPC
++ special_reg_pc_ = 0; // PowerPC
++ special_reg_lr_ = 0; // PowerPC
++ special_reg_ctr_ = 0; // PowerPC
++
++ // Initializing FP registers.
++ for (int i = 0; i < kNumFPRs; i++) {
++ fp_register[i] = 0.0;
++ }
++
++ // The sp is initialized to point to the bottom (high address) of the
++ // allocated stack area. To be safe in potential stack underflows we leave
++ // some buffer below.
++ registers_[sp] = reinterpret_cast<intptr_t>(stack_) + stack_size - 64;
++ InitializeCoverage();
++
++ last_debugger_input_ = NULL;
++}
++
++
++// When the generated code calls an external reference we need to catch that in
++// the simulator. The external reference will be a function compiled for the
++// host architecture. We need to call that function instead of trying to
++// execute it with the simulator. We do that by redirecting the external
++// reference to a svc (Supervisor Call) instruction that is handled by
++// the simulator. We write the original destination of the jump just at a known
++// offset from the svc instruction so the simulator knows what to call.
++class Redirection {
++ public:
++ Redirection(void* external_function, ExternalReference::Type type)
++ : external_function_(external_function),
++ swi_instruction_(rtCallRedirInstr | kCallRtRedirected),
++ type_(type),
++ next_(NULL) {
++ Isolate* isolate = Isolate::Current();
++ next_ = isolate->simulator_redirection();
++ Simulator::current(isolate)->
++ FlushICache(isolate->simulator_i_cache(),
++ reinterpret_cast<void*>(&swi_instruction_),
++ Instruction::kInstrSize);
++ isolate->set_simulator_redirection(this);
++ }
++
++ void* address_of_swi_instruction() {
++ return reinterpret_cast<void*>(&swi_instruction_);
++ }
++
++ void* external_function() { return external_function_; }
++ ExternalReference::Type type() { return type_; }
++
++ static Redirection* Get(void* external_function,
++ ExternalReference::Type type) {
++ Isolate* isolate = Isolate::Current();
++ Redirection* current = isolate->simulator_redirection();
++ for (; current != NULL; current = current->next_) {
++ if (current->external_function_ == external_function) return current;
++ }
++ return new Redirection(external_function, type);
++ }
++
++ static Redirection* FromSwiInstruction(Instruction* swi_instruction) {
++ char* addr_of_swi = reinterpret_cast<char*>(swi_instruction);
++ char* addr_of_redirection =
++ addr_of_swi - OFFSET_OF(Redirection, swi_instruction_);
++ return reinterpret_cast<Redirection*>(addr_of_redirection);
++ }
++
++ private:
++ void* external_function_;
++ uint32_t swi_instruction_;
++ ExternalReference::Type type_;
++ Redirection* next_;
++};
++
++
++void* Simulator::RedirectExternalReference(void* external_function,
++ ExternalReference::Type type) {
++ Redirection* redirection = Redirection::Get(external_function, type);
++ return redirection->address_of_swi_instruction();
++}
++
++
++// Get the active Simulator for the current thread.
++Simulator* Simulator::current(Isolate* isolate) {
++ v8::internal::Isolate::PerIsolateThreadData* isolate_data =
++ isolate->FindOrAllocatePerThreadDataForThisThread();
++ ASSERT(isolate_data != NULL);
++
++ Simulator* sim = isolate_data->simulator();
++ if (sim == NULL) {
++ // TODO(146): delete the simulator object when a thread/isolate goes away.
++ sim = new Simulator(isolate);
++ isolate_data->set_simulator(sim);
++ }
++ return sim;
++}
++
++
++// Sets the register in the architecture state.
++void Simulator::set_register(int reg, intptr_t value) {
++ ASSERT((reg >= 0) && (reg < kNumGPRs));
++ registers_[reg] = value;
++}
++
++
++// Get the register from the architecture state.
++intptr_t Simulator::get_register(int reg) const {
++ ASSERT((reg >= 0) && (reg < kNumGPRs));
++ // Stupid code added to avoid bug in GCC.
++ // See:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
++ if (reg >= kNumGPRs) return 0;
++ // End stupid code.
++ return registers_[reg];
++}
++
++
++double Simulator::get_double_from_register_pair(int reg) {
++ ASSERT((reg >= 0) && (reg < kNumGPRs) && ((reg % 2) == 0));
++
++ double dm_val = 0.0;
++#ifndef V8_TARGET_ARCH_PPC64 // doesn't make sense in 64bit mode
++ // Read the bits from the unsigned integer register_[] array
++ // into the double precision floating point value and return it.
++ char buffer[sizeof(fp_register[0])];
++ memcpy(buffer, ®isters_[reg], sizeof(registers_[0]));
++ memcpy(&dm_val, buffer, sizeof(registers_[0]));
++#endif
++ return(dm_val);
++}
++
++// Raw access to the PC register.
++void Simulator::set_pc(intptr_t value) {
++ pc_modified_ = true;
++ special_reg_pc_ = value;
++}
++
++
++bool Simulator::has_bad_pc() const {
++ return ((special_reg_pc_ == bad_lr) || (special_reg_pc_ == end_sim_pc));
++}
++
++
++// Raw access to the PC register without the special adjustment when reading.
++intptr_t Simulator::get_pc() const {
++ return special_reg_pc_;
++}
++
++// For use in calls that take two double values which are currently
++// in d1 and d2
++void Simulator::GetFpArgs(double* x, double* y) {
++ *x = get_double_from_d_register(1);
++ *y = get_double_from_d_register(2);
++}
++
++// For use in calls that take one double value (d1)
++void Simulator::GetFpArgs(double* x) {
++ *x = get_double_from_d_register(1);
++}
++
++
++// For use in calls that take one double value (d1) and one integer
++// value (r3).
++void Simulator::GetFpArgs(double* x, intptr_t* y) {
++ *x = get_double_from_d_register(1);
++ *y = registers_[3];
++}
++
++
++// The return value is in d1.
++void Simulator::SetFpResult(const double& result) {
++ set_d_register_from_double(1, result);
++}
++
++
++void Simulator::TrashCallerSaveRegisters() {
++ // We don't trash the registers with the return value.
++#if 0 // A good idea to trash volatile registers, needs to be done
++ registers_[2] = 0x50Bad4U;
++ registers_[3] = 0x50Bad4U;
++ registers_[12] = 0x50Bad4U;
++#endif
++}
++
++
++uint32_t Simulator::ReadWU(intptr_t addr, Instruction* instr) {
++ uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
++ return *ptr;
++}
++
++int32_t Simulator::ReadW(intptr_t addr, Instruction* instr) {
++ int32_t* ptr = reinterpret_cast<int32_t*>(addr);
++ return *ptr;
++}
++
++
++void Simulator::WriteW(intptr_t addr, uint32_t value, Instruction* instr) {
++ uint32_t* ptr = reinterpret_cast<uint32_t*>(addr);
++ *ptr = value;
++ return;
++}
++
++void Simulator::WriteW(intptr_t addr, int32_t value, Instruction* instr) {
++ int32_t* ptr = reinterpret_cast<int32_t*>(addr);
++ *ptr = value;
++ return;
++}
++
++
++uint16_t Simulator::ReadHU(intptr_t addr, Instruction* instr) {
++ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
++ return *ptr;
++}
++
++
++int16_t Simulator::ReadH(intptr_t addr, Instruction* instr) {
++ int16_t* ptr = reinterpret_cast<int16_t*>(addr);
++ return *ptr;
++}
++
++
++void Simulator::WriteH(intptr_t addr, uint16_t value, Instruction* instr) {
++ uint16_t* ptr = reinterpret_cast<uint16_t*>(addr);
++ *ptr = value;
++ return;
++}
++
++
++void Simulator::WriteH(intptr_t addr, int16_t value, Instruction* instr) {
++ int16_t* ptr = reinterpret_cast<int16_t*>(addr);
++ *ptr = value;
++ return;
++}
++
++
++uint8_t Simulator::ReadBU(intptr_t addr) {
++ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
++ return *ptr;
++}
++
++
++int8_t Simulator::ReadB(intptr_t addr) {
++ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
++ return *ptr;
++}
++
++
++void Simulator::WriteB(intptr_t addr, uint8_t value) {
++ uint8_t* ptr = reinterpret_cast<uint8_t*>(addr);
++ *ptr = value;
++}
++
++
++void Simulator::WriteB(intptr_t addr, int8_t value) {
++ int8_t* ptr = reinterpret_cast<int8_t*>(addr);
++ *ptr = value;
++}
++
++
++intptr_t* Simulator::ReadDW(intptr_t addr) {
++ intptr_t* ptr = reinterpret_cast<intptr_t*>(addr);
++ return ptr;
++}
++
++
++void Simulator::WriteDW(intptr_t addr, int64_t value) {
++ int64_t* ptr = reinterpret_cast<int64_t*>(addr);
++ *ptr = value;
++ return;
++}
++
++
++// Returns the limit of the stack area to enable checking for stack overflows.
++uintptr_t Simulator::StackLimit() const {
++ // Leave a safety margin of 1024 bytes to prevent overrunning the stack when
++ // pushing values.
++ return reinterpret_cast<uintptr_t>(stack_) + 1024;
++}
++
++
++// Unsupported instructions use Format to print an error and stop execution.
++void Simulator::Format(Instruction* instr, const char* format) {
++ PrintF("Simulator found unsupported instruction:\n 0x%08" V8PRIxPTR ":
%s\n",
++ reinterpret_cast<intptr_t>(instr), format);
++ UNIMPLEMENTED();
++}
++
++
++// Calculate C flag value for additions.
++bool Simulator::CarryFrom(int32_t left, int32_t right, int32_t carry) {
++ uint32_t uleft = static_cast<uint32_t>(left);
++ uint32_t uright = static_cast<uint32_t>(right);
++ uint32_t urest = 0xffffffffU - uleft;
++
++ return (uright > urest) ||
++ (carry && (((uright + 1) > urest) || (uright > (urest - 1))));
++}
++
++
++// Calculate C flag value for subtractions.
++bool Simulator::BorrowFrom(int32_t left, int32_t right) {
++ uint32_t uleft = static_cast<uint32_t>(left);
++ uint32_t uright = static_cast<uint32_t>(right);
++
++ return (uright > uleft);
++}
++
++
++// Calculate V flag value for additions and subtractions.
++bool Simulator::OverflowFrom(int32_t alu_out,
++ int32_t left, int32_t right, bool addition) {
++ bool overflow;
++ if (addition) {
++ // operands have the same sign
++ overflow = ((left >= 0 && right >= 0) || (left < 0 && right
< 0))
++ // and operands and result have different sign
++ && ((left < 0 && alu_out >= 0) || (left >= 0
&& alu_out < 0));
++ } else {
++ // operands have different signs
++ overflow = ((left < 0 && right >= 0) || (left >= 0 && right
< 0))
++ // and first operand and result have different signs
++ && ((left < 0 && alu_out >= 0) || (left >= 0
&& alu_out < 0));
++ }
++ return overflow;
++}
++
++
++// Calls into the V8 runtime are based on this very simple interface.
++// Note: To be able to return two values from some calls the code in runtime.cc
++// uses the ObjectPair which is essentially two 32-bit values stuffed into a
++// 64-bit value. With the code below we assume that all runtime calls return
++// 64 bits of result. If they don't, the r4 result register contains a bogus
++// value, which is fine because it is caller-saved.
++typedef int64_t (*SimulatorRuntimeCall)(intptr_t arg0,
++ intptr_t arg1,
++ intptr_t arg2,
++ intptr_t arg3,
++ intptr_t arg4,
++ intptr_t arg5);
++typedef double (*SimulatorRuntimeFPCall)(double arg0,
++ double arg1);
++typedef int64_t (*SimulatorRuntimeFPCallX)(double arg0,
++ double arg1);
++typedef double (*SimulatorRuntimeFPCallY)(double arg0,
++ intptr_t arg1);
++
++// This signature supports direct call in to API function native callback
++// (refer to InvocationCallback in v8.h).
++typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectApiCall)(intptr_t arg0);
++
++// This signature supports direct call to accessor getter callback.
++typedef v8::Handle<v8::Value> (*SimulatorRuntimeDirectGetterCall)(
++ intptr_t arg0,
++ intptr_t arg1);
++
++// Software interrupt instructions are used by the simulator to call into the
++// C-based V8 runtime.
++void Simulator::SoftwareInterrupt(Instruction* instr) {
++ int svc = instr->SvcValue();
++ switch (svc) {
++ case kCallRtRedirected: {
++ // Check if stack is aligned. Error if not aligned is reported below to
++ // include information on the function called.
++ bool stack_aligned =
++ (get_register(sp)
++ & (::v8::internal::FLAG_sim_stack_alignment - 1)) == 0;
++ Redirection* redirection = Redirection::FromSwiInstruction(instr);
++ intptr_t arg0 = get_register(r3);
++ intptr_t arg1 = get_register(r4);
++ intptr_t arg2 = get_register(r5);
++ intptr_t arg3 = get_register(r6);
++ intptr_t arg4 = get_register(r7);
++ intptr_t arg5 = get_register(r8);
++ bool fp_call =
++ (redirection->type() == ExternalReference::BUILTIN_FP_FP_CALL) ||
++ (redirection->type() == ExternalReference::BUILTIN_COMPARE_CALL) ||
++ (redirection->type() == ExternalReference::BUILTIN_FP_CALL) ||
++ (redirection->type() == ExternalReference::BUILTIN_FP_INT_CALL);
++ // This is dodgy but it works because the C entry stubs are never moved.
++ // See comment in codegen-arm.cc and bug 1242173.
++ intptr_t saved_lr = special_reg_lr_;
++ intptr_t external =
++ reinterpret_cast<intptr_t>(redirection->external_function());
++ if (fp_call) {
++ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
++ SimulatorRuntimeFPCall target =
++ reinterpret_cast<SimulatorRuntimeFPCall>(external);
++ double dval0, dval1;
++ intptr_t ival;
++ switch (redirection->type()) {
++ case ExternalReference::BUILTIN_FP_FP_CALL:
++ case ExternalReference::BUILTIN_COMPARE_CALL:
++ GetFpArgs(&dval0, &dval1);
++ PrintF("Call to host function at %p with args %f, %f",
++ FUNCTION_ADDR(target), dval0, dval1);
++ break;
++ case ExternalReference::BUILTIN_FP_CALL:
++ GetFpArgs(&dval0);
++ PrintF("Call to host function at %p with arg %f",
++ FUNCTION_ADDR(target), dval0);
++ break;
++ case ExternalReference::BUILTIN_FP_INT_CALL:
++ GetFpArgs(&dval0, &ival);
++ PrintF("Call to host function at %p with args %f, %" V8PRIdPTR,
++ FUNCTION_ADDR(target), dval0, ival);
++ break;
++ default:
++ UNREACHABLE();
++ break;
++ }
++ if (!stack_aligned) {
++ PrintF(" with unaligned stack %08" V8PRIxPTR
++ "\n", get_register(sp));
++ }
++ PrintF("\n");
++ }
++ CHECK(stack_aligned);
++ SimulatorRuntimeFPCall target =
++ reinterpret_cast<SimulatorRuntimeFPCall>(external);
++ SimulatorRuntimeFPCallX targetx =
++ reinterpret_cast<SimulatorRuntimeFPCallX>(external);
++ SimulatorRuntimeFPCallY targety =
++ reinterpret_cast<SimulatorRuntimeFPCallY>(external);
++ double dval0, dval1, result;
++ intptr_t ival;
++ int32_t lo_res, hi_res;
++ int64_t iresult;
++ switch (redirection->type()) {
++ case ExternalReference::BUILTIN_FP_FP_CALL:
++ GetFpArgs(&dval0, &dval1);
++ result = target(dval0, dval1);
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %f\n", result);
++ }
++ SetFpResult(result);
++ break;
++ case ExternalReference::BUILTIN_COMPARE_CALL:
++ GetFpArgs(&dval0, &dval1);
++ iresult = targetx(dval0, dval1);
++ lo_res = static_cast<int32_t>(iresult);
++ hi_res = static_cast<int32_t>(iresult >> 32);
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %08x\n", lo_res);
++ }
++#if __BYTE_ORDER == __BIG_ENDIAN
++ set_register(r3, hi_res);
++ set_register(r4, lo_res);
++#else
++ set_register(r3, lo_res);
++ set_register(r4, hi_res);
++#endif
++ break;
++ case ExternalReference::BUILTIN_FP_CALL:
++ GetFpArgs(&dval0, &dval1);
++ result = target(dval0, dval1); // 2nd parm ignored
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %f\n", result);
++ }
++ SetFpResult(result);
++ break;
++ case ExternalReference::BUILTIN_FP_INT_CALL:
++ GetFpArgs(&dval0, &ival);
++ result = targety(dval0, ival);
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %f\n", result);
++ }
++ SetFpResult(result);
++ break;
++ default:
++ UNREACHABLE();
++ break;
++ }
++ } else if (redirection->type() == ExternalReference::DIRECT_API_CALL) {
++ // See callers of MacroAssembler::CallApiFunctionAndReturn for
++ // explanation of register usage.
++ SimulatorRuntimeDirectApiCall target =
++ reinterpret_cast<SimulatorRuntimeDirectApiCall>(external);
++ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
++ PrintF("Call to host function at %p args %08" V8PRIxPTR,
++ FUNCTION_ADDR(target), arg0);
++ if (!stack_aligned) {
++ PrintF(" with unaligned stack %08" V8PRIxPTR
++ "\n", get_register(sp));
++ }
++ PrintF("\n");
++ }
++ CHECK(stack_aligned);
++#if ABI_RETURNS_HANDLES_IN_REGS
++ intptr_t p0 = arg0;
++#else
++ intptr_t p0 = arg1;
++#endif
++ v8::Handle<v8::Value> result = target(p0);
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
++ }
++#if ABI_RETURNS_HANDLES_IN_REGS
++ arg0 = (intptr_t)*result;
++#else
++ *(reinterpret_cast<intptr_t*>(arg0)) = (intptr_t) *result;
++#endif
++ set_register(r3, arg0);
++ } else if (redirection->type() == ExternalReference::DIRECT_GETTER_CALL) {
++ // See callers of MacroAssembler::CallApiFunctionAndReturn for
++ // explanation of register usage.
++ SimulatorRuntimeDirectGetterCall target =
++ reinterpret_cast<SimulatorRuntimeDirectGetterCall>(external);
++ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
++ PrintF("Call to host function at %p args %08" V8PRIxPTR "
%08"
++ V8PRIxPTR,
++ FUNCTION_ADDR(target), arg0, arg1);
++ if (!stack_aligned) {
++ PrintF(" with unaligned stack %08" V8PRIxPTR
++ "\n", get_register(sp));
++ }
++ PrintF("\n");
++ }
++ CHECK(stack_aligned);
++#if ABI_RETURNS_HANDLES_IN_REGS
++ intptr_t p0 = arg0;
++ intptr_t p1 = arg1;
++#else
++ intptr_t p0 = arg1;
++ intptr_t p1 = arg2;
++#endif
++#if !ABI_PASSES_HANDLES_IN_REGS
++ p0 = *(reinterpret_cast<intptr_t *>(p0));
++#endif
++ v8::Handle<v8::Value> result = target(p0, p1);
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %p\n", reinterpret_cast<void *>(*result));
++ }
++#if ABI_RETURNS_HANDLES_IN_REGS
++ arg0 = (intptr_t)*result;
++#else
++ *(reinterpret_cast<intptr_t*>(arg0)) = (intptr_t) *result;
++#endif
++ set_register(r3, arg0);
++ } else {
++ // builtin call.
++ ASSERT(redirection->type() == ExternalReference::BUILTIN_CALL);
++ SimulatorRuntimeCall target =
++ reinterpret_cast<SimulatorRuntimeCall>(external);
++ if (::v8::internal::FLAG_trace_sim || !stack_aligned) {
++ PrintF(
++ "Call to host function at %p,\n"
++ "\t\t\t\targs %08" V8PRIxPTR ", %08" V8PRIxPTR ",
%08" V8PRIxPTR
++ ", %08" V8PRIxPTR ", %08" V8PRIxPTR ", %08"
V8PRIxPTR,
++ FUNCTION_ADDR(target),
++ arg0,
++ arg1,
++ arg2,
++ arg3,
++ arg4,
++ arg5);
++ if (!stack_aligned) {
++ PrintF(" with unaligned stack %08" V8PRIxPTR
++ "\n", get_register(sp));
++ }
++ PrintF("\n");
++ }
++ CHECK(stack_aligned);
++ int64_t result = target(arg0, arg1, arg2, arg3, arg4, arg5);
++#if V8_TARGET_ARCH_PPC64
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %08" V8PRIxPTR "\n", result);
++ }
++ set_register(r3, result);
++#else
++ int32_t lo_res = static_cast<int32_t>(result);
++ int32_t hi_res = static_cast<int32_t>(result >> 32);
++ if (::v8::internal::FLAG_trace_sim) {
++ PrintF("Returned %08x\n", lo_res);
++ }
++#if __BYTE_ORDER == __BIG_ENDIAN
++ set_register(r3, hi_res);
++ set_register(r4, lo_res);
++#else
++ set_register(r3, lo_res);
++ set_register(r4, hi_res);
++#endif
++#endif
++ }
++ set_pc(saved_lr);
++ break;
++ }
++ case kBreakpoint: {
++ PPCDebugger dbg(this);
++ dbg.Debug();
++ break;
++ }
++ case kInfo: {
++ PPCDebugger dbg(this);
++ dbg.Info(instr);
++ break;
++ }
++ // stop uses all codes greater than 1 << 23.
++ default: {
++ if (svc >= (1 << 23)) {
++ uint32_t code = svc & kStopCodeMask;
++ if (isWatchedStop(code)) {
++ IncreaseStopCounter(code);
++ }
++ // Stop if it is enabled, otherwise go on jumping over the stop
++ // and the message address.
++ if (isEnabledStop(code)) {
++ PPCDebugger dbg(this);
++ dbg.Stop(instr);
++ } else {
++ set_pc(get_pc() + Instruction::kInstrSize + kPointerSize);
++ }
++ } else {
++ // This is not a valid svc code.
++ UNREACHABLE();
++ break;
++ }
++ }
++ }
++}
++
++
++// Stop helper functions.
++bool Simulator::isStopInstruction(Instruction* instr) {
++ return (instr->Bits(27, 24) == 0xF) && (instr->SvcValue() >=
kStopCode);
++}
++
++
++bool Simulator::isWatchedStop(uint32_t code) {
++ ASSERT(code <= kMaxStopCode);
++ return code < kNumOfWatchedStops;
++}
++
++
++bool Simulator::isEnabledStop(uint32_t code) {
++ ASSERT(code <= kMaxStopCode);
++ // Unwatched stops are always enabled.
++ return !isWatchedStop(code) ||
++ !(watched_stops[code].count & kStopDisabledBit);
++}
++
++
++void Simulator::EnableStop(uint32_t code) {
++ ASSERT(isWatchedStop(code));
++ if (!isEnabledStop(code)) {
++ watched_stops[code].count &= ~kStopDisabledBit;
++ }
++}
++
++
++void Simulator::DisableStop(uint32_t code) {
++ ASSERT(isWatchedStop(code));
++ if (isEnabledStop(code)) {
++ watched_stops[code].count |= kStopDisabledBit;
++ }
++}
++
++
++void Simulator::IncreaseStopCounter(uint32_t code) {
++ ASSERT(code <= kMaxStopCode);
++ ASSERT(isWatchedStop(code));
++ if ((watched_stops[code].count & ~(1 << 31)) == 0x7fffffff) {
++ PrintF("Stop counter for code %i has overflowed.\n"
++ "Enabling this code and reseting the counter to 0.\n", code);
++ watched_stops[code].count = 0;
++ EnableStop(code);
++ } else {
++ watched_stops[code].count++;
++ }
++}
++
++
++// Print a stop status.
++void Simulator::PrintStopInfo(uint32_t code) {
++ ASSERT(code <= kMaxStopCode);
++ if (!isWatchedStop(code)) {
++ PrintF("Stop not watched.");
++ } else {
++ const char* state = isEnabledStop(code) ? "Enabled" :
"Disabled";
++ int32_t count = watched_stops[code].count & ~kStopDisabledBit;
++ // Don't print the state of unused breakpoints.
++ if (count != 0) {
++ if (watched_stops[code].desc) {
++ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i, \t%s\n",
++ code, code, state, count, watched_stops[code].desc);
++ } else {
++ PrintF("stop %i - 0x%x: \t%s, \tcounter = %i\n",
++ code, code, state, count);
++ }
++ }
++ }
++}
++
++
++void Simulator::SetCR0(intptr_t result, bool setSO) {
++ int bf = 0;
++ if (result < 0) { bf |= 0x80000000; }
++ if (result > 0) { bf |= 0x40000000; }
++ if (result == 0) { bf |= 0x20000000; }
++ if (setSO) { bf |= 0x10000000; }
++ condition_reg_ = (condition_reg_ & ~0xF0000000) | bf;
++}
++
++void Simulator::DecodeBranchConditional(Instruction* instr) {
++ int bo = instr->Bits(25, 21) << 21;
++ int offset = (instr->Bits(15, 2) << 18) >> 16;
++ int condition_bit = instr->Bits(20, 16);
++ int condition_mask = 0x80000000 >> condition_bit;
++ switch (bo) {
++ case DCBNZF: // Decrement CTR; branch if CTR != 0 and condition false
++ case DCBEZF: // Decrement CTR; branch if CTR == 0 and condition false
++ UNIMPLEMENTED();
++ case BF: { // Branch if condition false
++ if (!(condition_reg_ & condition_mask)) {
++ if (instr->Bit(0) == 1) { // LK flag set
++ special_reg_lr_ = get_pc() + 4;
++ }
++ set_pc(get_pc() + offset);
++ }
++ break;
++ }
++ case DCBNZT: // Decrement CTR; branch if CTR != 0 and condition true
++ case DCBEZT: // Decrement CTR; branch if CTR == 0 and condition true
++ UNIMPLEMENTED();
++ case BT: { // Branch if condition true
++ if (condition_reg_ & condition_mask) {
++ if (instr->Bit(0) == 1) { // LK flag set
++ special_reg_lr_ = get_pc() + 4;
++ }
++ set_pc(get_pc() + offset);
++ }
++ break;
++ }
++ case DCBNZ: // Decrement CTR; branch if CTR != 0
++ case DCBEZ: // Decrement CTR; branch if CTR == 0
++ special_reg_ctr_ -= 1;
++ if ((special_reg_ctr_ == 0) == (bo == DCBEZ)) {
++ if (instr->Bit(0) == 1) { // LK flag set
++ special_reg_lr_ = get_pc() + 4;
++ }
++ set_pc(get_pc() + offset);
++ }
++ break;
++ case BA: { // Branch always
++ if (instr->Bit(0) == 1) { // LK flag set
++ special_reg_lr_ = get_pc() + 4;
++ }
++ set_pc(get_pc() + offset);
++ break;
++ }
++ default:
++ UNIMPLEMENTED(); // Invalid encoding
++ }
++}
++
++// Handle execution based on instruction types.
++void Simulator::DecodeExt1(Instruction* instr) {
++ switch (instr->Bits(10, 1) << 1) {
++ case MCRF:
++ UNIMPLEMENTED(); // Not used by V8.
++ case BCLRX: {
++ // need to check BO flag
++ int old_pc = get_pc();
++ set_pc(special_reg_lr_);
++ if (instr->Bit(0) == 1) { // LK flag set
++ special_reg_lr_ = old_pc + 4;
++ }
++ break;
++ }
++ case BCCTRX: {
++ // need to check BO flag
++ int old_pc = get_pc();
++ set_pc(special_reg_ctr_);
++ if (instr->Bit(0) == 1) { // LK flag set
++ special_reg_lr_ = old_pc + 4;
++ }
++ break;
++ }
++ case CRNOR:
++ case RFI:
++ case CRANDC:
++ UNIMPLEMENTED();
++ case ISYNC: {
++ // todo - simulate isync
++ break;
++ }
++ case CRXOR: {
++ int bt = instr->Bits(25, 21);
++ int ba = instr->Bits(20, 16);
++ int bb = instr->Bits(15, 11);
++ int ba_val = ((0x80000000 >> ba) & condition_reg_) == 0 ? 0 : 1;
++ int bb_val = ((0x80000000 >> bb) & condition_reg_) == 0 ? 0 : 1;
++ int bt_val = ba_val ^ bb_val;
++ bt_val = bt_val << (31-bt); // shift bit to correct destination
++ condition_reg_ &= ~(0x80000000 >> bt);
++ condition_reg_ |= bt_val;
++ break;
++ }
++ case CRNAND:
++ case CRAND:
++ case CREQV:
++ case CRORC:
++ case CROR:
++ default: {
++ UNIMPLEMENTED(); // Not used by V8.
++ }
++ }
++}
++
++bool Simulator::DecodeExt2_10bit(Instruction *instr) {
++ bool found = true;
++
++ int opcode = instr->Bits(10, 1) << 1;
++ switch (opcode) {
++ case SRWX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ uint32_t rs_val = get_register(rs);
++ uintptr_t rb_val = get_register(rb);
++ intptr_t result = rs_val >> (rb_val & 0x3f);
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case SRDX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ uintptr_t rs_val = get_register(rs);
++ uintptr_t rb_val = get_register(rb);
++ intptr_t result = rs_val >> (rb_val & 0x7f);
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++#endif
++ case SRAW: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ int32_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ intptr_t result = rs_val >> (rb_val & 0x3f);
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case SRAD: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ intptr_t result = rs_val >> (rb_val & 0x7f);
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++#endif
++ case SRAWIX: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ int sh = instr->Bits(15, 11);
++ int32_t rs_val = get_register(rs);
++ intptr_t result = rs_val >> sh;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case EXTSW: {
++ const int shift = kBitsPerPointer - 32;
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t ra_val = (rs_val << shift) >> shift;
++ set_register(ra, ra_val);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(ra_val);
++ }
++ break;
++ }
++#endif
++ case EXTSH: {
++ const int shift = kBitsPerPointer - 16;
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t ra_val = (rs_val << shift) >> shift;
++ set_register(ra, ra_val);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(ra_val);
++ }
++ break;
++ }
++ case EXTSB: {
++ const int shift = kBitsPerPointer - 8;
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t ra_val = (rs_val << shift) >> shift;
++ set_register(ra, ra_val);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(ra_val);
++ }
++ break;
++ }
++ case LFSUX:
++ case LFSX: {
++ int frt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ int32_t val = ReadW(ra_val + rb_val, instr);
++ float *fptr = reinterpret_cast<float*>(&val);
++ set_d_register_from_double(frt, static_cast<double>(*fptr));
++ if (opcode == LFSUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case LFDUX:
++ case LFDX: {
++ int frt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ double *dptr = reinterpret_cast<double*>(ReadDW(ra_val + rb_val));
++ set_d_register_from_double(frt, *dptr);
++ if (opcode == LFDUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case STFSUX: {
++ case STFSX:
++ int frs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ float frs_val = static_cast<float>(get_double_from_d_register(frs));
++ int32_t *p= reinterpret_cast<int32_t*>(&frs_val);
++ WriteW(ra_val + rb_val, *p, instr);
++ if (opcode == STFSUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case STFDUX: {
++ case STFDX:
++ int frs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ double frs_val = get_double_from_d_register(frs);
++ int64_t *p = reinterpret_cast<int64_t *>(&frs_val);
++ WriteDW(ra_val + rb_val, *p);
++ if (opcode == STFDUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case SYNC: {
++ // todo - simulate sync
++ break;
++ }
++ case ICBI: {
++ // todo - simulate icbi
++ break;
++ }
++ default: {
++ found = false;
++ break;
++ }
++ }
++
++ if (found)
++ return found;
++
++ found = true;
++ opcode = instr->Bits(10, 2) << 2;
++ switch (opcode) {
++ case SRADIX: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ int sh = (instr->Bits(15, 11) | (instr->Bit(1) << 5));
++ intptr_t rs_val = get_register(rs);
++ intptr_t result = rs_val >> sh;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++ default: {
++ found = false;
++ break;
++ }
++ }
++
++ return found;
++}
++
++void Simulator::DecodeExt2_9bit(Instruction* instr) {
++ int opcode = instr->Bits(9, 1) << 1;
++ switch (opcode) {
++ case TW: {
++ // used for call redirection in simulation mode
++ SoftwareInterrupt(instr);
++ break;
++ }
++ case CMP: {
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ int cr = instr->Bits(25, 23);
++ int bf = 0;
++ if (ra_val < rb_val) { bf |= 0x80000000; }
++ if (ra_val > rb_val) { bf |= 0x40000000; }
++ if (ra_val == rb_val) { bf |= 0x20000000; }
++ int condition_mask = 0xF0000000 >> (cr*4);
++ int condition = bf >> (cr*4);
++ condition_reg_ = (condition_reg_ & ~condition_mask) | condition;
++ break;
++ }
++ case SUBFCX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ // int oe = instr->Bit(10);
++ uintptr_t ra_val = get_register(ra);
++ uintptr_t rb_val = get_register(rb);
++ uintptr_t alu_out = ~ra_val + rb_val + 1;
++ set_register(rt, alu_out);
++ // If the sign of rb and alu_out don't match, carry = 0
++ if ((alu_out ^ rb_val) & 0x80000000) {
++ special_reg_xer_ &= ~0xF0000000;
++ } else {
++ special_reg_xer_ = (special_reg_xer_ & ~0xF0000000) | 0x20000000;
++ }
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ // todo - handle OE bit
++ break;
++ }
++ case ADDCX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ // int oe = instr->Bit(10);
++ uintptr_t ra_val = get_register(ra);
++ uintptr_t rb_val = get_register(rb);
++ uintptr_t alu_out = ra_val + rb_val;
++ // Check overflow
++ if (~ra_val < rb_val) {
++ special_reg_xer_ = (special_reg_xer_ & ~0xF0000000) | 0x20000000;
++ } else {
++ special_reg_xer_ &= ~0xF0000000;
++ }
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(static_cast<intptr_t>(alu_out));
++ }
++ // todo - handle OE bit
++ break;
++ }
++ case MULHWX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ int32_t ra_val = (get_register(ra) & 0xFFFFFFFF);
++ int32_t rb_val = (get_register(rb) & 0xFFFFFFFF);
++ int64_t alu_out = (int64_t)ra_val * (int64_t)rb_val;
++ alu_out >>= 32;
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(static_cast<intptr_t>(alu_out));
++ }
++ // todo - handle OE bit
++ break;
++ }
++ case NEGX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ intptr_t ra_val = get_register(ra);
++ intptr_t alu_out = 1 + ~ra_val;
++ set_register(rt, alu_out);
++ if (instr->Bit(10)) { // OE bit set
++ if (ra_val == kMinInt) {
++ special_reg_xer_ |= 0xC0000000; // set SO,OV
++ } else {
++ special_reg_xer_ &= ~0x40000000; // clear OV
++ }
++ }
++ if (instr->Bit(0)) { // RC bit set
++ bool setSO = (special_reg_xer_ & 0x80000000);
++ SetCR0(alu_out, setSO);
++ }
++ break;
++ }
++ case SLWX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ uint32_t rs_val = get_register(rs);
++ uintptr_t rb_val = get_register(rb);
++ uint32_t result = rs_val << (rb_val & 0x3f);
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case SLDX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ uintptr_t rs_val = get_register(rs);
++ uintptr_t rb_val = get_register(rb);
++ uintptr_t result = rs_val << (rb_val & 0x7f);
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++#endif
++ case CNTLZWX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ uintptr_t rs_val = get_register(rs);
++ uintptr_t count = 0;
++ int n = 0;
++ uintptr_t bit = 0x80000000;
++ for (; n < 32; n++) {
++ if (bit & rs_val)
++ break;
++ count++;
++ bit >>= 1;
++ }
++ set_register(ra, count);
++ if (instr->Bit(0)) { // RC Bit set
++ int bf = 0;
++ if (count > 0) { bf |= 0x40000000; }
++ if (count == 0) { bf |= 0x20000000; }
++ condition_reg_ = (condition_reg_ & ~0xF0000000) | bf;
++ }
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case CNTLZDX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ uintptr_t rs_val = get_register(rs);
++ uintptr_t count = 0;
++ int n = 0;
++ uintptr_t bit = 0x8000000000000000UL;
++ for (; n < 64; n++) {
++ if (bit & rs_val)
++ break;
++ count++;
++ bit >>= 1;
++ }
++ set_register(ra, count);
++ if (instr->Bit(0)) { // RC Bit set
++ int bf = 0;
++ if (count > 0) { bf |= 0x40000000; }
++ if (count == 0) { bf |= 0x20000000; }
++ condition_reg_ = (condition_reg_ & ~0xF0000000) | bf;
++ }
++ break;
++ }
++#endif
++ case ANDX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ intptr_t alu_out = rs_val & rb_val;
++ set_register(ra, alu_out);
++ if (instr->Bit(0)) { // RC Bit set
++ SetCR0(alu_out);
++ }
++ break;
++ }
++ case ANDCX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ intptr_t alu_out = rs_val & ~rb_val;
++ set_register(ra, alu_out);
++ if (instr->Bit(0)) { // RC Bit set
++ SetCR0(alu_out);
++ }
++ break;
++ }
++ case CMPL: {
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ uintptr_t ra_val = get_register(ra);
++ uintptr_t rb_val = get_register(rb);
++ int cr = instr->Bits(25, 23);
++ int bf = 0;
++ if (ra_val < rb_val) { bf |= 0x80000000; }
++ if (ra_val > rb_val) { bf |= 0x40000000; }
++ if (ra_val == rb_val) { bf |= 0x20000000; }
++ int condition_mask = 0xF0000000 >> (cr*4);
++ int condition = bf >> (cr*4);
++ condition_reg_ = (condition_reg_ & ~condition_mask) | condition;
++ break;
++ }
++ case SUBFX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ // int oe = instr->Bit(10);
++ intptr_t ra_val = get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ intptr_t alu_out = rb_val - ra_val;
++ // todo - figure out underflow
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC Bit set
++ SetCR0(alu_out);
++ }
++ // todo - handle OE bit
++ break;
++ }
++ case ADDZEX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ intptr_t ra_val = get_register(ra);
++ if (special_reg_xer_ & 0x20000000) {
++ ra_val += 1;
++ }
++ set_register(rt, ra_val);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(ra_val);
++ }
++ // todo - handle OE bit
++ break;
++ }
++ case NORX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ intptr_t alu_out = ~(rs_val | rb_val);
++ set_register(ra, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ break;
++ }
++ case MULLW: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ int32_t ra_val = (get_register(ra) & 0xFFFFFFFF);
++ int32_t rb_val = (get_register(rb) & 0xFFFFFFFF);
++ int32_t alu_out = ra_val * rb_val;
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ // todo - handle OE bit
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case MULLD: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ int64_t ra_val = get_register(ra);
++ int64_t rb_val = get_register(rb);
++ int64_t alu_out = ra_val * rb_val;
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ // todo - handle OE bit
++ break;
++ }
++#endif
++ case DIVW: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ int32_t ra_val = get_register(ra);
++ int32_t rb_val = get_register(rb);
++ // result is undefined if divisor is zero.
++ int32_t alu_out = rb_val ? ra_val / rb_val : -1;
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ // todo - handle OE bit
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case DIVD: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ int64_t ra_val = get_register(ra);
++ int64_t rb_val = get_register(rb);
++ // result is undefined if divisor is zero.
++ int64_t alu_out = rb_val ? ra_val / rb_val : -1;
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ // todo - handle OE bit
++ break;
++ }
++#endif
++ case ADDX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ // int oe = instr->Bit(10);
++ intptr_t ra_val = get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ intptr_t alu_out = ra_val + rb_val;
++ set_register(rt, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ // todo - handle OE bit
++ break;
++ }
++ case XORX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ intptr_t alu_out = rs_val ^ rb_val;
++ set_register(ra, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ break;
++ }
++ case ORX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ intptr_t alu_out = rs_val | rb_val;
++ set_register(ra, alu_out);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(alu_out);
++ }
++ break;
++ }
++ case MFSPR: {
++ int rt = instr->RTValue();
++ int spr = instr->Bits(20, 11);
++ if (spr != 256) {
++ UNIMPLEMENTED(); // Only LRLR supported
++ }
++ set_register(rt, special_reg_lr_);
++ break;
++ }
++ case MTSPR: {
++ int rt = instr->RTValue();
++ intptr_t rt_val = get_register(rt);
++ int spr = instr->Bits(20, 11);
++ if (spr == 256) {
++ special_reg_lr_ = rt_val;
++ } else if (spr == 288) {
++ special_reg_ctr_ = rt_val;
++ } else if (spr == 32) {
++ special_reg_xer_ = rt_val;
++ } else {
++ UNIMPLEMENTED(); // Only LR supported
++ }
++ break;
++ }
++ case MFCR: {
++ int rt = instr->RTValue();
++ set_register(rt, condition_reg_);
++ break;
++ }
++ case STWUX:
++ case STWX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int32_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ WriteW(ra_val+rb_val, rs_val, instr);
++ if (opcode == STWUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case STBUX:
++ case STBX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int8_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ WriteB(ra_val+rb_val, rs_val);
++ if (opcode == STBUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case STHUX:
++ case STHX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int16_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ WriteH(ra_val+rb_val, rs_val, instr);
++ if (opcode == STHUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case LWZX:
++ case LWZUX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ set_register(rt, ReadWU(ra_val+rb_val, instr));
++ if (opcode == LWZUX) {
++ ASSERT(ra != 0 && ra != rt);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++#if V8_TARGET_ARCH_PPC64
++ case LDX:
++ case LDUX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ intptr_t *result = ReadDW(ra_val+rb_val);
++ set_register(rt, *result);
++ if (opcode == LDUX) {
++ ASSERT(ra != 0 && ra != rt);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case STDX:
++ case STDUX: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rs_val = get_register(rs);
++ intptr_t rb_val = get_register(rb);
++ WriteDW(ra_val+rb_val, rs_val);
++ if (opcode == STDUX) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++#endif
++ case LBZX:
++ case LBZUX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ set_register(rt, ReadBU(ra_val+rb_val) & 0xFF);
++ if (opcode == LBZUX) {
++ ASSERT(ra != 0 && ra != rt);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case LHZX:
++ case LHZUX: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int rb = instr->RBValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ intptr_t rb_val = get_register(rb);
++ set_register(rt, ReadHU(ra_val+rb_val, instr) & 0xFFFF);
++ if (opcode == LHZUX) {
++ ASSERT(ra != 0 && ra != rt);
++ set_register(ra, ra_val+rb_val);
++ }
++ break;
++ }
++ case DCBF: {
++ // todo - simulate dcbf
++ break;
++ }
++ default: {
++ PrintF("Unimplemented: %08x\n", instr->InstructionBits());
++ UNIMPLEMENTED(); // Not used by V8.
++ }
++ }
++}
++
++void Simulator::DecodeExt2(Instruction* instr) {
++ // Check first the 10-1 bit versions
++ if (DecodeExt2_10bit(instr))
++ return;
++ // Now look at the lesser encodings
++ DecodeExt2_9bit(instr);
++}
++
++void Simulator::DecodeExt4(Instruction* instr) {
++ switch (instr->Bits(5, 1) << 1) {
++ case FDIV: {
++ int frt = instr->RTValue();
++ int fra = instr->RAValue();
++ int frb = instr->RBValue();
++ double fra_val = get_double_from_d_register(fra);
++ double frb_val = get_double_from_d_register(frb);
++ double frt_val = fra_val / frb_val;
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FSUB: {
++ int frt = instr->RTValue();
++ int fra = instr->RAValue();
++ int frb = instr->RBValue();
++ double fra_val = get_double_from_d_register(fra);
++ double frb_val = get_double_from_d_register(frb);
++ double frt_val = fra_val - frb_val;
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FADD: {
++ int frt = instr->RTValue();
++ int fra = instr->RAValue();
++ int frb = instr->RBValue();
++ double fra_val = get_double_from_d_register(fra);
++ double frb_val = get_double_from_d_register(frb);
++ double frt_val = fra_val + frb_val;
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FSQRT: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ double frt_val = sqrt(frb_val);
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FSEL: {
++ int frt = instr->RTValue();
++ int fra = instr->RAValue();
++ int frb = instr->RBValue();
++ int frc = instr->RCValue();
++ double fra_val = get_double_from_d_register(fra);
++ double frb_val = get_double_from_d_register(frb);
++ double frc_val = get_double_from_d_register(frc);
++ double frt_val = ((fra_val >= 0.0) ? frc_val : frb_val);
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FMUL: {
++ int frt = instr->RTValue();
++ int fra = instr->RAValue();
++ int frc = instr->RCValue();
++ double fra_val = get_double_from_d_register(fra);
++ double frc_val = get_double_from_d_register(frc);
++ double frt_val = fra_val * frc_val;
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ }
++ int opcode = instr->Bits(10, 1) << 1;
++ switch (opcode) {
++ case FCMPU: {
++ int fra = instr->RAValue();
++ int frb = instr->RBValue();
++ double fra_val = get_double_from_d_register(fra);
++ double frb_val = get_double_from_d_register(frb);
++ int cr = instr->Bits(25, 23);
++ int bf = 0;
++ if (fra_val < frb_val) { bf |= 0x80000000; }
++ if (fra_val > frb_val) { bf |= 0x40000000; }
++ if (fra_val == frb_val) { bf |= 0x20000000; }
++ if (isunordered(fra_val, frb_val)) { bf |= 0x10000000; }
++ int condition_mask = 0xF0000000 >> (cr*4);
++ int condition = bf >> (cr*4);
++ condition_reg_ = (condition_reg_ & ~condition_mask) | condition;
++ return;
++ }
++ case FRSP: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ // frsp round 8-byte double-precision value to 8-byte
++ // single-precision value, ignore the round here
++ set_d_register_from_double(frt, frb_val);
++ if (instr->Bit(0)) { // RC bit set
++ // UNIMPLEMENTED();
++ }
++ return;
++ }
++ case FCFID: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double t_val = get_double_from_d_register(frb);
++ int64_t* frb_val_p = reinterpret_cast<int64_t*>(&t_val);
++ double frt_val = static_cast<double>(*frb_val_p);
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FCTID: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ int64_t frt_val;
++ int64_t one = 1; // work-around gcc
++ int64_t kMinLongLong = (one << 63);
++ int64_t kMaxLongLong = kMinLongLong - 1;
++
++ if (frb_val > kMaxLongLong) {
++ frt_val = kMaxLongLong;
++ } else if (frb_val < kMinLongLong) {
++ frt_val = kMinLongLong;
++ } else {
++ switch (fp_condition_reg_ & kVFPRoundingModeMask) {
++ case kRoundToZero:
++ frt_val = (int64_t)frb_val;
++ break;
++ case kRoundToPlusInf:
++ frt_val = (int64_t)ceil(frb_val);
++ break;
++ case kRoundToMinusInf:
++ frt_val = (int64_t)floor(frb_val);
++ break;
++ default:
++ frt_val = (int64_t)frb_val;
++ UNIMPLEMENTED(); // Not used by V8.
++ break;
++ }
++ }
++ double *p = reinterpret_cast<double*>(&frt_val);
++ set_d_register_from_double(frt, *p);
++ return;
++ }
++ case FCTIDZ: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ int64_t frt_val;
++ int64_t one = 1; // work-around gcc
++ int64_t kMinLongLong = (one << 63);
++ int64_t kMaxLongLong = kMinLongLong - 1;
++
++ if (frb_val > kMaxLongLong) {
++ frt_val = kMaxLongLong;
++ } else if (frb_val < kMinLongLong) {
++ frt_val = kMinLongLong;
++ } else {
++ frt_val = (int64_t)frb_val;
++ }
++ double *p = reinterpret_cast<double*>(&frt_val);
++ set_d_register_from_double(frt, *p);
++ return;
++ }
++ case FCTIW:
++ case FCTIWZ: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ int64_t frt_val;
++ if (frb_val > kMaxInt) {
++ frt_val = kMaxInt;
++ } else if (frb_val < kMinInt) {
++ frt_val = kMinInt;
++ } else {
++ if (opcode == FCTIWZ) {
++ frt_val = (int64_t)frb_val;
++ } else {
++ switch (fp_condition_reg_ & kVFPRoundingModeMask) {
++ case kRoundToZero:
++ frt_val = (int64_t)frb_val;
++ break;
++ case kRoundToPlusInf:
++ frt_val = (int64_t)ceil(frb_val);
++ break;
++ case kRoundToMinusInf:
++ frt_val = (int64_t)floor(frb_val);
++ break;
++ case kRoundToNearest:
++ frt_val = (int64_t)lround(frb_val);
++
++ // Round to even if exactly halfway. (lround rounds up)
++ if (fabs(static_cast<double>(frt_val) - frb_val) == 0.5 &&
++ (frt_val % 2)) {
++ frt_val += ((frt_val > 0) ? -1 : 1);
++ }
++
++ break;
++ default:
++ ASSERT(false);
++ frt_val = (int64_t)frb_val;
++ break;
++ }
++ }
++ }
++ double *p = reinterpret_cast<double*>(&frt_val);
++ set_d_register_from_double(frt, *p);
++ return;
++ }
++ case FNEG: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ double frt_val = -frb_val;
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FMR: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ double frt_val = frb_val;
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case MTFSFI: {
++ int bf = instr->Bits(25, 23);
++ int imm = instr->Bits(15, 12);
++ int fp_condition_mask = 0xF0000000 >> (bf*4);
++ fp_condition_reg_ &= ~fp_condition_mask;
++ fp_condition_reg_ |= (imm << (28 - (bf*4)));
++ if (instr->Bit(0)) { // RC bit set
++ condition_reg_ &= 0xF0FFFFFF;
++ condition_reg_ |= (imm << 23);
++ }
++ return;
++ }
++ case MTFSF: {
++ int frb = instr->RBValue();
++ double frb_dval = get_double_from_d_register(frb);
++ int64_t *p = reinterpret_cast<int64_t*>(&frb_dval);
++ int32_t frb_ival = static_cast<int32_t>((*p) & 0xffffffff);
++ int l = instr->Bits(25, 25);
++ if (l == 1) {
++ fp_condition_reg_ = frb_ival;
++ } else {
++ UNIMPLEMENTED();
++ }
++ if (instr->Bit(0)) { // RC bit set
++ UNIMPLEMENTED();
++ // int w = instr->Bits(16, 16);
++ // int flm = instr->Bits(24, 17);
++ }
++ return;
++ }
++ case MFFS: {
++ int frt = instr->RTValue();
++ int64_t lval = static_cast<int64_t>(fp_condition_reg_);
++ double* p = reinterpret_cast<double*>(&lval);
++ set_d_register_from_double(frt, *p);
++ return;
++ }
++ case FABS: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ double frt_val = abs(frb_val);
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ case FRIM: {
++ int frt = instr->RTValue();
++ int frb = instr->RBValue();
++ double frb_val = get_double_from_d_register(frb);
++ int64_t floor_val = (int64_t)frb_val;
++ if (floor_val > frb_val)
++ floor_val--;
++ double frt_val = static_cast<double>(floor_val);
++ set_d_register_from_double(frt, frt_val);
++ return;
++ }
++ }
++ UNIMPLEMENTED(); // Not used by V8.
++}
++
++#if V8_TARGET_ARCH_PPC64
++void Simulator::DecodeExt5(Instruction* instr) {
++ switch (instr->Bits(4, 2) << 2) {
++ case RLDICL: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ uintptr_t rs_val = get_register(rs);
++ int sh = (instr->Bits(15, 11) | (instr->Bit(1) << 5));
++ int mb = (instr->Bits(10, 6) | (instr->Bit(5) << 5));
++ ASSERT(sh >=0 && sh <= 63);
++ ASSERT(mb >=0 && mb <= 63);
++ // rotate left
++ uintptr_t result = (rs_val << sh) | (rs_val >> (64-sh));
++ uintptr_t mask = 0xffffffffffffffff >> mb;
++ result &= mask;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ return;
++ }
++ case RLDICR: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ uintptr_t rs_val = get_register(rs);
++ int sh = (instr->Bits(15, 11) | (instr->Bit(1) << 5));
++ int me = (instr->Bits(10, 6) | (instr->Bit(5) << 5));
++ ASSERT(sh >=0 && sh <= 63);
++ ASSERT(me >=0 && me <= 63);
++ // rotate left
++ uintptr_t result = (rs_val << sh) | (rs_val >> (64-sh));
++ uintptr_t mask = 0xffffffffffffffff << (63-me);
++ result &= mask;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ return;
++ }
++ case RLDIC: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ uintptr_t rs_val = get_register(rs);
++ int sh = (instr->Bits(15, 11) | (instr->Bit(1) << 5));
++ int mb = (instr->Bits(10, 6) | (instr->Bit(5) << 5));
++ ASSERT(sh >=0 && sh <= 63);
++ ASSERT(mb >=0 && mb <= 63);
++ // rotate left
++ uintptr_t result = (rs_val << sh) | (rs_val >> (64-sh));
++ uintptr_t mask = (0xffffffffffffffff >> mb) & (0xffffffffffffffff
<< sh);
++ result &= mask;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ return;
++ }
++ case RLDIMI: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ uintptr_t rs_val = get_register(rs);
++ intptr_t ra_val = get_register(ra);
++ int sh = (instr->Bits(15, 11) | (instr->Bit(1) << 5));
++ int mb = (instr->Bits(10, 6) | (instr->Bit(5) << 5));
++ int me = 63 - sh;
++ // rotate left
++ uintptr_t result = (rs_val << sh) | (rs_val >> (64-sh));
++ uintptr_t mask = 0;
++ if (mb < me+1) {
++ uintptr_t bit = 0x8000000000000000 >> mb;
++ for (; mb <= me; mb++) {
++ mask |= bit;
++ bit >>= 1;
++ }
++ } else if (mb == me+1) {
++ mask = 0xffffffffffffffff;
++ } else { // mb > me+1
++ uintptr_t bit = 0x8000000000000000 >> (me+1); // needs to be tested
++ mask = 0xffffffffffffffff;
++ for (;me < mb;me++) {
++ mask ^= bit;
++ bit >>= 1;
++ }
++ }
++ result &= mask;
++ ra_val &= ~mask;
++ result |= ra_val;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ return;
++ }
++ }
++ UNIMPLEMENTED(); // Not used by V8.
++}
++#endif
++
++// Executes the current instruction.
++void Simulator::InstructionDecode(Instruction* instr) {
++ if (v8::internal::FLAG_check_icache) {
++ CheckICache(isolate_->simulator_i_cache(), instr);
++ }
++ pc_modified_ = false;
++ if (::v8::internal::FLAG_trace_sim) {
++ disasm::NameConverter converter;
++ disasm::Disassembler dasm(converter);
++ // use a reasonably large buffer
++ v8::internal::EmbeddedVector<char, 256> buffer;
++ dasm.InstructionDecode(buffer, reinterpret_cast<byte*>(instr));
++ PrintF("%05d %08" V8PRIxPTR " %s\n", icount_,
++ reinterpret_cast<intptr_t>(instr), buffer.start());
++ }
++ int opcode = instr->OpcodeValue() << 26;
++ switch (opcode) {
++ case TWI: {
++ // used for call redirection in simulation mode
++ SoftwareInterrupt(instr);
++ break;
++ }
++ case SUBFIC: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ intptr_t ra_val = get_register(ra);
++ int32_t im_val = instr->Bits(15, 0);
++ im_val = SIGN_EXT_IMM16(im_val);
++ intptr_t alu_out = im_val - ra_val;
++ set_register(rt, alu_out);
++ // todo - handle RC bit
++ break;
++ }
++ case CMPLI: {
++ int ra = instr->RAValue();
++ uintptr_t ra_val = get_register(ra);
++ uint32_t im_val = instr->Bits(15, 0);
++ int cr = instr->Bits(25, 23);
++ int bf = 0;
++ if (ra_val < im_val) { bf |= 0x80000000; }
++ if (ra_val > im_val) { bf |= 0x40000000; }
++ if (ra_val == im_val) { bf |= 0x20000000; }
++ int condition_mask = 0xF0000000 >> (cr*4);
++ int condition = bf >> (cr*4);
++ condition_reg_ = (condition_reg_ & ~condition_mask) | condition;
++ break;
++ }
++ case CMPI: {
++ int ra = instr->RAValue();
++ intptr_t ra_val = get_register(ra);
++ int32_t im_val = instr->Bits(15, 0);
++ im_val = SIGN_EXT_IMM16(im_val);
++ int cr = instr->Bits(25, 23);
++ int bf = 0;
++ if (ra_val < im_val) { bf |= 0x80000000; }
++ if (ra_val > im_val) { bf |= 0x40000000; }
++ if (ra_val == im_val) { bf |= 0x20000000; }
++ int condition_mask = 0xF0000000 >> (cr*4);
++ int condition = bf >> (cr*4);
++ condition_reg_ = (condition_reg_ & ~condition_mask) | condition;
++ break;
++ }
++ case ADDIC: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ uintptr_t ra_val = get_register(ra);
++ uintptr_t im_val = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ uintptr_t alu_out = ra_val + im_val;
++ // Check overflow
++ if (~ra_val < im_val) {
++ special_reg_xer_ = (special_reg_xer_ & ~0xF0000000) | 0x20000000;
++ } else {
++ special_reg_xer_ &= ~0xF0000000;
++ }
++ set_register(rt, alu_out);
++ break;
++ }
++ case ADDI: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int32_t im_val = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ intptr_t alu_out;
++ if (ra == 0) {
++ alu_out = im_val;
++ } else {
++ intptr_t ra_val = get_register(ra);
++ alu_out = ra_val + im_val;
++ }
++ set_register(rt, alu_out);
++ // todo - handle RC bit
++ break;
++ }
++ case ADDIS: {
++ int rt = instr->RTValue();
++ int ra = instr->RAValue();
++ int32_t im_val = (instr->Bits(15, 0) << 16);
++ intptr_t alu_out;
++ if (ra == 0) { // treat r0 as zero
++ alu_out = im_val;
++ } else {
++ intptr_t ra_val = get_register(ra);
++ alu_out = ra_val + im_val;
++ }
++ set_register(rt, alu_out);
++ break;
++ }
++ case BCX: {
++ DecodeBranchConditional(instr);
++ break;
++ }
++ case BX: {
++ int offset = (instr->Bits(25, 2) << 8) >> 6;
++ if (instr->Bit(0) == 1) { // LK flag set
++ special_reg_lr_ = get_pc() + 4;
++ }
++ set_pc(get_pc() + offset);
++ // todo - AA flag
++ break;
++ }
++ case EXT1: {
++ DecodeExt1(instr);
++ break;
++ }
++ case RLWIMIX: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ uint32_t rs_val = get_register(rs);
++ int32_t ra_val = get_register(ra);
++ int sh = instr->Bits(15, 11);
++ int mb = instr->Bits(10, 6);
++ int me = instr->Bits(5, 1);
++ // rotate left
++ uint32_t result = (rs_val << sh) | (rs_val >> (32-sh));
++ int mask = 0;
++ if (mb < me+1) {
++ int bit = 0x80000000 >> mb;
++ for (; mb <= me; mb++) {
++ mask |= bit;
++ bit >>= 1;
++ }
++ } else if (mb == me+1) {
++ mask = 0xffffffff;
++ } else { // mb > me+1
++ int bit = 0x80000000 >> (me+1); // needs to be tested
++ mask = 0xffffffff;
++ for (;me < mb;me++) {
++ mask ^= bit;
++ bit >>= 1;
++ }
++ }
++ result &= mask;
++ ra_val &= ~mask;
++ result |= ra_val;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++ case RLWINMX: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ uint32_t rs_val = get_register(rs);
++ int sh = instr->Bits(15, 11);
++ int mb = instr->Bits(10, 6);
++ int me = instr->Bits(5, 1);
++ // rotate left
++ uint32_t result = (rs_val << sh) | (rs_val >> (32-sh));
++ int mask = 0;
++ if (mb < me+1) {
++ int bit = 0x80000000 >> mb;
++ for (; mb <= me; mb++) {
++ mask |= bit;
++ bit >>= 1;
++ }
++ } else if (mb == me+1) {
++ mask = 0xffffffff;
++ } else { // mb > me+1
++ int bit = 0x80000000 >> (me+1); // needs to be tested
++ mask = 0xffffffff;
++ for (;me < mb;me++) {
++ mask ^= bit;
++ bit >>= 1;
++ }
++ }
++ result &= mask;
++ set_register(ra, result);
++ if (instr->Bit(0)) { // RC bit set
++ SetCR0(result);
++ }
++ break;
++ }
++ case ORI: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ intptr_t rs_val = get_register(rs);
++ uint32_t im_val = instr->Bits(15, 0);
++ intptr_t alu_out = rs_val | im_val;
++ set_register(ra, alu_out);
++ break;
++ }
++ case ORIS: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ intptr_t rs_val = get_register(rs);
++ uint32_t im_val = instr->Bits(15, 0);
++ intptr_t alu_out = rs_val | (im_val << 16);
++ set_register(ra, alu_out);
++ break;
++ }
++ case XORI: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ intptr_t rs_val = get_register(rs);
++ uint32_t im_val = instr->Bits(15, 0);
++ intptr_t alu_out = rs_val ^ im_val;
++ set_register(ra, alu_out);
++ // todo - set condition based SO bit
++ break;
++ }
++ case XORIS: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ intptr_t rs_val = get_register(rs);
++ uint32_t im_val = instr->Bits(15, 0);
++ intptr_t alu_out = rs_val ^ (im_val << 16);
++ set_register(ra, alu_out);
++ break;
++ }
++ case ANDIx: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ intptr_t rs_val = get_register(rs);
++ uint32_t im_val = instr->Bits(15, 0);
++ intptr_t alu_out = rs_val & im_val;
++ set_register(ra, alu_out);
++ SetCR0(alu_out);
++ break;
++ }
++ case ANDISx: {
++ int rs = instr->RSValue();
++ int ra = instr->RAValue();
++ intptr_t rs_val = get_register(rs);
++ uint32_t im_val = instr->Bits(15, 0);
++ intptr_t alu_out = rs_val & (im_val << 16);
++ set_register(ra, alu_out);
++ SetCR0(alu_out);
++ break;
++ }
++ case EXT2: {
++ DecodeExt2(instr);
++ break;
++ }
++
++ case LWZU:
++ case LWZ: {
++ int ra = instr->RAValue();
++ int rt = instr->RTValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ set_register(rt, ReadWU(ra_val+offset, instr));
++ if (opcode == LWZU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case LBZU:
++ case LBZ: {
++ int ra = instr->RAValue();
++ int rt = instr->RTValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ set_register(rt, ReadB(ra_val+offset) & 0xFF);
++ if (opcode == LBZU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case STWU:
++ case STW: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int32_t rs_val = get_register(rs);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ WriteW(ra_val+offset, rs_val, instr);
++ if (opcode == STWU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ // printf("r%d %08x -> %08x\n", rs, rs_val, offset); // 0xdead
++ break;
++ }
++
++ case STBU:
++ case STB: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int8_t rs_val = get_register(rs);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ WriteB(ra_val+offset, rs_val);
++ if (opcode == STBU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case LHZU:
++ case LHZ: {
++ int ra = instr->RAValue();
++ int rt = instr->RTValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ uintptr_t result = ReadHU(ra_val+offset, instr) & 0xffff;
++ set_register(rt, result);
++ if (opcode == LHZU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case LHA:
++ case LHAU: {
++ UNIMPLEMENTED();
++ break;
++ }
++
++ case STHU:
++ case STH: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int16_t rs_val = get_register(rs);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ WriteH(ra_val+offset, rs_val, instr);
++ if (opcode == STHU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case LMW:
++ case STMW: {
++ UNIMPLEMENTED();
++ break;
++ }
++
++ case LFSU:
++ case LFS: {
++ int frt = instr->RTValue();
++ int ra = instr->RAValue();
++ int32_t offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int32_t val = ReadW(ra_val + offset, instr);
++ float *fptr = reinterpret_cast<float*>(&val);
++ set_d_register_from_double(frt, static_cast<double>(*fptr));
++ if (opcode == LFSU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case LFDU:
++ case LFD: {
++ int frt = instr->RTValue();
++ int ra = instr->RAValue();
++ int32_t offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ double *dptr = reinterpret_cast<double*>(ReadDW(ra_val + offset));
++ set_d_register_from_double(frt, *dptr);
++ if (opcode == LFDU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case STFSU: {
++ case STFS:
++ int frs = instr->RSValue();
++ int ra = instr->RAValue();
++ int32_t offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ float frs_val = static_cast<float>(get_double_from_d_register(frs));
++ int32_t *p= reinterpret_cast<int32_t*>(&frs_val);
++ WriteW(ra_val + offset, *p, instr);
++ if (opcode == STFSU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case STFDU:
++ case STFD: {
++ int frs = instr->RSValue();
++ int ra = instr->RAValue();
++ int32_t offset = SIGN_EXT_IMM16(instr->Bits(15, 0));
++ intptr_t ra_val = ra == 0 ? 0 : get_register(ra);
++ double frs_val = get_double_from_d_register(frs);
++ int64_t *p = reinterpret_cast<int64_t *>(&frs_val);
++ WriteDW(ra_val + offset, *p);
++ if (opcode == STFDU) {
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++
++ case EXT3:
++ UNIMPLEMENTED();
++ case EXT4: {
++ DecodeExt4(instr);
++ break;
++ }
++
++#if V8_TARGET_ARCH_PPC64
++ case EXT5: {
++ DecodeExt5(instr);
++ break;
++ }
++ case LD: {
++ int ra = instr->RAValue();
++ int rt = instr->RTValue();
++ int64_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0) & ~3);
++ switch (instr->Bits(1, 0)) {
++ case 0: { // ld
++ intptr_t *result = ReadDW(ra_val+offset);
++ set_register(rt, *result);
++ break;
++ }
++ case 1: { // ldu
++ intptr_t *result = ReadDW(ra_val+offset);
++ set_register(rt, *result);
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ break;
++ }
++ case 2: { // lwa
++ intptr_t result = ReadW(ra_val+offset, instr);
++ set_register(rt, result);
++ break;
++ }
++ }
++ break;
++ }
++
++ case STD: {
++ int ra = instr->RAValue();
++ int rs = instr->RSValue();
++ int64_t ra_val = ra == 0 ? 0 : get_register(ra);
++ int64_t rs_val = get_register(rs);
++ int offset = SIGN_EXT_IMM16(instr->Bits(15, 0) & ~3);
++ WriteDW(ra_val+offset, rs_val);
++ if (instr->Bit(0) == 1) { // This is the STDU form
++ ASSERT(ra != 0);
++ set_register(ra, ra_val+offset);
++ }
++ break;
++ }
++#endif
++
++ case FAKE_OPCODE: {
++ if (instr->Bits(MARKER_SUBOPCODE_BIT, MARKER_SUBOPCODE_BIT) == 1) {
++ int marker_code = instr->Bits(STUB_MARKER_HIGH_BIT, 0);
++ ASSERT(marker_code < F_NEXT_AVAILABLE_STUB_MARKER);
++ PrintF("Hit stub-marker: %d (EMIT_STUB_MARKER)\n",
++ marker_code);
++ } else {
++ int fake_opcode = instr->Bits(FAKE_OPCODE_HIGH_BIT, 0);
++ if (fake_opcode == fBKPT) {
++ PPCDebugger dbg(this);
++ PrintF("Simulator hit BKPT.\n");
++ dbg.Debug();
++ } else {
++ ASSERT(fake_opcode < fLastFaker);
++ PrintF("Hit ARM opcode: %d(FAKE_OPCODE defined in
constant-ppc.h)\n",
++ fake_opcode);
++ UNIMPLEMENTED();
++ }
++ }
++ break;
++ }
++
++ default: {
++ UNIMPLEMENTED();
++ break;
++ }
++ }
++ if (!pc_modified_) {
++ set_pc(reinterpret_cast<intptr_t>(instr) + Instruction::kInstrSize);
++ }
++}
++
++
++void Simulator::Execute() {
++ // Get the PC to simulate. Cannot use the accessor here as we need the
++ // raw PC value and not the one used as input to arithmetic instructions.
++ intptr_t program_counter = get_pc();
++
++ if (::v8::internal::FLAG_stop_sim_at == 0) {
++ // Fast version of the dispatch loop without checking whether the simulator
++ // should be stopping at a particular executed instruction.
++ while (program_counter != end_sim_pc) {
++ Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
++ icount_++;
++ InstructionDecode(instr);
++ program_counter = get_pc();
++ }
++ } else {
++ // FLAG_stop_sim_at is at the non-default value. Stop in the debugger when
++ // we reach the particular instuction count.
++ while (program_counter != end_sim_pc) {
++ Instruction* instr = reinterpret_cast<Instruction*>(program_counter);
++ icount_++;
++ if (icount_ == ::v8::internal::FLAG_stop_sim_at) {
++ PPCDebugger dbg(this);
++ dbg.Debug();
++ } else {
++ InstructionDecode(instr);
++ }
++ program_counter = get_pc();
++ }
++ }
++}
++
++
++intptr_t Simulator::Call(byte* entry, int argument_count, ...) {
++ va_list parameters;
++ va_start(parameters, argument_count);
++ // Set up arguments
++
++ // First eight arguments passed in registers r3-r10.
++ int reg_arg_count = (argument_count > 8) ? 8 : argument_count;
++ int stack_arg_count = argument_count - reg_arg_count;
++ for (int i = 0; i < reg_arg_count; i++) {
++ set_register(i + 3, va_arg(parameters, intptr_t));
++ }
++
++ // Remaining arguments passed on stack.
++ intptr_t original_stack = get_register(sp);
++ // Compute position of stack on entry to generated code.
++ intptr_t entry_stack = (original_stack -
++ (kNumRequiredStackFrameSlots + stack_arg_count) *
++ sizeof(intptr_t));
++ if (OS::ActivationFrameAlignment() != 0) {
++ entry_stack &= -OS::ActivationFrameAlignment();
++ }
++ // Store remaining arguments on stack, from low to high memory.
++ // +2 is a hack for the LR slot + old SP on PPC
++ intptr_t* stack_argument = reinterpret_cast<intptr_t*>(entry_stack) +
++ kStackFrameExtraParamSlot;
++ for (int i = 0; i < stack_arg_count; i++) {
++ stack_argument[i] = va_arg(parameters, intptr_t);
++ }
++ va_end(parameters);
++ set_register(sp, entry_stack);
++
++ // Prepare to execute the code at entry
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ // entry is the function descriptor
++ set_pc(*(reinterpret_cast<intptr_t *>(entry)));
++#else
++ // entry is the instruction address
++ set_pc(reinterpret_cast<intptr_t>(entry));
++#endif
++
++
++
++ // Put down marker for end of simulation. The simulator will stop simulation
++ // when the PC reaches this value. By saving the "end simulation" value
into
++ // the LR the simulation stops when returning to this call point.
++ special_reg_lr_ = end_sim_pc;
++
++ // Remember the values of non-volatile registers.
++ intptr_t r2_val = get_register(r2);
++ intptr_t r13_val = get_register(r13);
++ intptr_t r14_val = get_register(r14);
++ intptr_t r15_val = get_register(r15);
++ intptr_t r16_val = get_register(r16);
++ intptr_t r17_val = get_register(r17);
++ intptr_t r18_val = get_register(r18);
++ intptr_t r19_val = get_register(r19);
++ intptr_t r20_val = get_register(r20);
++ intptr_t r21_val = get_register(r21);
++ intptr_t r22_val = get_register(r22);
++ intptr_t r23_val = get_register(r23);
++ intptr_t r24_val = get_register(r24);
++ intptr_t r25_val = get_register(r25);
++ intptr_t r26_val = get_register(r26);
++ intptr_t r27_val = get_register(r27);
++ intptr_t r28_val = get_register(r28);
++ intptr_t r29_val = get_register(r29);
++ intptr_t r30_val = get_register(r30);
++ intptr_t r31_val = get_register(fp);
++
++ // Set up the non-volatile registers with a known value. To be able to check
++ // that they are preserved properly across JS execution.
++ intptr_t callee_saved_value = icount_;
++ set_register(r2, callee_saved_value);
++ set_register(r13, callee_saved_value);
++ set_register(r14, callee_saved_value);
++ set_register(r15, callee_saved_value);
++ set_register(r16, callee_saved_value);
++ set_register(r17, callee_saved_value);
++ set_register(r18, callee_saved_value);
++ set_register(r19, callee_saved_value);
++ set_register(r20, callee_saved_value);
++ set_register(r21, callee_saved_value);
++ set_register(r22, callee_saved_value);
++ set_register(r23, callee_saved_value);
++ set_register(r24, callee_saved_value);
++ set_register(r25, callee_saved_value);
++ set_register(r26, callee_saved_value);
++ set_register(r27, callee_saved_value);
++ set_register(r28, callee_saved_value);
++ set_register(r29, callee_saved_value);
++ set_register(r30, callee_saved_value);
++ set_register(fp, callee_saved_value);
++
++ // Start the simulation
++ Execute();
++
++ // Check that the non-volatile registers have been preserved.
++ CHECK_EQ(callee_saved_value, get_register(r2));
++ CHECK_EQ(callee_saved_value, get_register(r13));
++ CHECK_EQ(callee_saved_value, get_register(r14));
++ CHECK_EQ(callee_saved_value, get_register(r15));
++ CHECK_EQ(callee_saved_value, get_register(r16));
++ CHECK_EQ(callee_saved_value, get_register(r17));
++ CHECK_EQ(callee_saved_value, get_register(r18));
++ CHECK_EQ(callee_saved_value, get_register(r19));
++ CHECK_EQ(callee_saved_value, get_register(r20));
++ CHECK_EQ(callee_saved_value, get_register(r21));
++ CHECK_EQ(callee_saved_value, get_register(r22));
++ CHECK_EQ(callee_saved_value, get_register(r23));
++ CHECK_EQ(callee_saved_value, get_register(r24));
++ CHECK_EQ(callee_saved_value, get_register(r25));
++ CHECK_EQ(callee_saved_value, get_register(r26));
++ CHECK_EQ(callee_saved_value, get_register(r27));
++ CHECK_EQ(callee_saved_value, get_register(r28));
++ CHECK_EQ(callee_saved_value, get_register(r29));
++ CHECK_EQ(callee_saved_value, get_register(r30));
++ CHECK_EQ(callee_saved_value, get_register(fp));
++
++ // Restore non-volatile registers with the original value.
++ set_register(r2, r2_val);
++ set_register(r13, r13_val);
++ set_register(r14, r14_val);
++ set_register(r15, r15_val);
++ set_register(r16, r16_val);
++ set_register(r17, r17_val);
++ set_register(r18, r18_val);
++ set_register(r19, r19_val);
++ set_register(r20, r20_val);
++ set_register(r21, r21_val);
++ set_register(r22, r22_val);
++ set_register(r23, r23_val);
++ set_register(r24, r24_val);
++ set_register(r25, r25_val);
++ set_register(r26, r26_val);
++ set_register(r27, r27_val);
++ set_register(r28, r28_val);
++ set_register(r29, r29_val);
++ set_register(r30, r30_val);
++ set_register(fp, r31_val);
++
++ // Pop stack passed arguments.
++ CHECK_EQ(entry_stack, get_register(sp));
++ set_register(sp, original_stack);
++
++ intptr_t result = get_register(r3); // PowerPC
++ return result;
++}
++
++
++uintptr_t Simulator::PushAddress(uintptr_t address) {
++ uintptr_t new_sp = get_register(sp) - sizeof(uintptr_t);
++ uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(new_sp);
++ *stack_slot = address;
++ set_register(sp, new_sp);
++ return new_sp;
++}
++
++
++uintptr_t Simulator::PopAddress() {
++ uintptr_t current_sp = get_register(sp);
++ uintptr_t* stack_slot = reinterpret_cast<uintptr_t*>(current_sp);
++ uintptr_t address = *stack_slot;
++ set_register(sp, current_sp + sizeof(uintptr_t));
++ return address;
++}
++
++} } // namespace v8::internal
++
++#endif // USE_SIMULATOR
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/ppc/simulator-ppc.h.ppc v8-3.14.5.10/src/ppc/simulator-ppc.h
+--- v8-3.14.5.10/src/ppc/simulator-ppc.h.ppc 2016-06-07 14:15:46.004392925 -0400
++++ v8-3.14.5.10/src/ppc/simulator-ppc.h 2016-06-07 14:15:46.004392925 -0400
+@@ -0,0 +1,410 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++
++// Declares a Simulator for PPC instructions if we are not generating a native
++// PPC binary. This Simulator allows us to run and debug PPC code generation on
++// regular desktop machines.
++// V8 calls into generated code by "calling" the CALL_GENERATED_CODE macro,
++// which will start execution in the Simulator or forwards to the real entry
++// on a PPC HW platform.
++
++#ifndef V8_PPC_SIMULATOR_PPC_H_
++#define V8_PPC_SIMULATOR_PPC_H_
++
++#include "allocation.h"
++
++#if !defined(USE_SIMULATOR)
++// Running without a simulator on a native ppc platform.
++
++namespace v8 {
++namespace internal {
++
++// When running without a simulator we call the entry directly.
++#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
++ (entry(p0, p1, p2, p3, p4))
++
++typedef int (*ppc_regexp_matcher)(String*, int, const byte*, const byte*,
++ int*, int, Address, int, void*, Isolate*);
++
++
++// Call the generated regexp code directly. The code at the entry address
++// should act as a function matching the type ppc_regexp_matcher.
++// The ninth argument is a dummy that reserves the space used for
++// the return address added by the ExitFrame in native calls.
++#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
++ (FUNCTION_CAST<ppc_regexp_matcher>(entry)( \
++ p0, p1, p2, p3, p4, p5, p6, p7, NULL, p8))
++
++#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
++ reinterpret_cast<TryCatch*>(try_catch_address)
++
++// The stack limit beyond which we will throw stack overflow errors in
++// generated code. Because generated code on ppc uses the C stack, we
++// just use the C stack limit.
++class SimulatorStack : public v8::internal::AllStatic {
++ public:
++ static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
++ uintptr_t c_limit) {
++ USE(isolate);
++ return c_limit;
++ }
++
++ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
++ return try_catch_address;
++ }
++
++ static inline void UnregisterCTryCatch() { }
++};
++
++} } // namespace v8::internal
++
++#else // !defined(USE_SIMULATOR)
++// Running with a simulator.
++
++#include "constants-ppc.h"
++#include "hashmap.h"
++#include "assembler.h"
++
++namespace v8 {
++namespace internal {
++
++class CachePage {
++ public:
++ static const int LINE_VALID = 0;
++ static const int LINE_INVALID = 1;
++
++ static const int kPageShift = 12;
++ static const int kPageSize = 1 << kPageShift;
++ static const int kPageMask = kPageSize - 1;
++ static const int kLineShift = 2; // The cache line is only 4 bytes right now.
++ static const int kLineLength = 1 << kLineShift;
++ static const int kLineMask = kLineLength - 1;
++
++ CachePage() {
++ memset(&validity_map_, LINE_INVALID, sizeof(validity_map_));
++ }
++
++ char* ValidityByte(int offset) {
++ return &validity_map_[offset >> kLineShift];
++ }
++
++ char* CachedData(int offset) {
++ return &data_[offset];
++ }
++
++ private:
++ char data_[kPageSize]; // The cached data.
++ static const int kValidityMapSize = kPageSize >> kLineShift;
++ char validity_map_[kValidityMapSize]; // One byte per line.
++};
++
++
++class Simulator {
++ public:
++ friend class PPCDebugger;
++ enum Register {
++ no_reg = -1,
++ r0 = 0, sp, r2, r3, r4, r5, r6, r7,
++ r8, r9, r10, r11, r12, r13, r14, r15,
++ r16, r17, r18, r19, r20, r21, r22, r23,
++ r24, r25, r26, r27, r28, r29, r30, fp,
++ kNumGPRs = 32,
++ d0 = 0, d1, d2, d3, d4, d5, d6, d7,
++ d8, d9, d10, d11, d12, d13, d14, d15,
++ d16, d17, d18, d19, d20, d21, d22, d23,
++ d24, d25, d26, d27, d28, d29, d30, d31,
++ kNumFPRs = 32
++ };
++
++ explicit Simulator(Isolate* isolate);
++ ~Simulator();
++
++ // The currently executing Simulator instance. Potentially there can be one
++ // for each native thread.
++ static Simulator* current(v8::internal::Isolate* isolate);
++
++ // Accessors for register state.
++ void set_register(int reg, intptr_t value);
++ intptr_t get_register(int reg) const;
++ double get_double_from_register_pair(int reg);
++ void set_d_register_from_double(int dreg, const double dbl) {
++ ASSERT(dreg >= 0 && dreg < kNumFPRs);
++ fp_register[dreg] = dbl;
++ }
++ double get_double_from_d_register(int dreg) {
++ return fp_register[dreg];
++ }
++
++ // Special case of set_register and get_register to access the raw PC value.
++ void set_pc(intptr_t value);
++ intptr_t get_pc() const;
++
++ // Accessor to the internal simulator stack area.
++ uintptr_t StackLimit() const;
++
++ // Executes PPC instructions until the PC reaches end_sim_pc.
++ void Execute();
++
++ // Call on program start.
++ static void Initialize(Isolate* isolate);
++
++ // V8 generally calls into generated JS code with 5 parameters and into
++ // generated RegExp code with 7 parameters. This is a convenience function,
++ // which sets up the simulator state and grabs the result on return.
++ intptr_t Call(byte* entry, int argument_count, ...);
++
++ // Push an address onto the JS stack.
++ uintptr_t PushAddress(uintptr_t address);
++
++ // Pop an address from the JS stack.
++ uintptr_t PopAddress();
++
++ // Debugger input.
++ void set_last_debugger_input(char* input);
++ char* last_debugger_input() { return last_debugger_input_; }
++
++ // ICache checking.
++ static void FlushICache(v8::internal::HashMap* i_cache, void* start,
++ size_t size);
++
++ // Returns true if pc register contains one of the 'special_values' defined
++ // below (bad_lr, end_sim_pc).
++ bool has_bad_pc() const;
++
++ private:
++ enum special_values {
++ // Known bad pc value to ensure that the simulator does not execute
++ // without being properly setup.
++ bad_lr = -1,
++ // A pc value used to signal the simulator to stop execution. Generally
++ // the lr is set to this value on transition from native C code to
++ // simulated execution, so that the simulator can "return" to the native
++ // C code.
++ end_sim_pc = -2
++ };
++
++ // Unsupported instructions use Format to print an error and stop execution.
++ void Format(Instruction* instr, const char* format);
++
++ // Helper functions to set the conditional flags in the architecture state.
++ bool CarryFrom(int32_t left, int32_t right, int32_t carry = 0);
++ bool BorrowFrom(int32_t left, int32_t right);
++ bool OverflowFrom(int32_t alu_out,
++ int32_t left,
++ int32_t right,
++ bool addition);
++
++ // Helper functions to decode common "addressing" modes
++ int32_t GetShiftRm(Instruction* instr, bool* carry_out);
++ int32_t GetImm(Instruction* instr, bool* carry_out);
++ void ProcessPUW(Instruction* instr,
++ int num_regs,
++ int operand_size,
++ intptr_t* start_address,
++ intptr_t* end_address);
++ void HandleRList(Instruction* instr, bool load);
++ void HandleVList(Instruction* inst);
++ void SoftwareInterrupt(Instruction* instr);
++
++ // Stop helper functions.
++ inline bool isStopInstruction(Instruction* instr);
++ inline bool isWatchedStop(uint32_t bkpt_code);
++ inline bool isEnabledStop(uint32_t bkpt_code);
++ inline void EnableStop(uint32_t bkpt_code);
++ inline void DisableStop(uint32_t bkpt_code);
++ inline void IncreaseStopCounter(uint32_t bkpt_code);
++ void PrintStopInfo(uint32_t code);
++
++ // Read and write memory.
++ inline uint8_t ReadBU(intptr_t addr);
++ inline int8_t ReadB(intptr_t addr);
++ inline void WriteB(intptr_t addr, uint8_t value);
++ inline void WriteB(intptr_t addr, int8_t value);
++
++ inline uint16_t ReadHU(intptr_t addr, Instruction* instr);
++ inline int16_t ReadH(intptr_t addr, Instruction* instr);
++ // Note: Overloaded on the sign of the value.
++ inline void WriteH(intptr_t addr, uint16_t value, Instruction* instr);
++ inline void WriteH(intptr_t addr, int16_t value, Instruction* instr);
++
++ inline uint32_t ReadWU(intptr_t addr, Instruction* instr);
++ inline int32_t ReadW(intptr_t addr, Instruction* instr);
++ inline void WriteW(intptr_t addr, uint32_t value, Instruction* instr);
++ inline void WriteW(intptr_t addr, int32_t value, Instruction* instr);
++
++ intptr_t* ReadDW(intptr_t addr);
++ void WriteDW(intptr_t addr, int64_t value);
++
++ // PowerPC
++ void SetCR0(intptr_t result, bool setSO = false);
++ void DecodeBranchConditional(Instruction* instr);
++ void DecodeExt1(Instruction* instr);
++ bool DecodeExt2_10bit(Instruction* instr);
++ void DecodeExt2_9bit(Instruction* instr);
++ void DecodeExt2(Instruction* instr);
++ void DecodeExt4(Instruction* instr);
++#if V8_TARGET_ARCH_PPC64
++ void DecodeExt5(Instruction* instr);
++#endif
++
++ // Executes one instruction.
++ void InstructionDecode(Instruction* instr);
++
++ // ICache.
++ static void CheckICache(v8::internal::HashMap* i_cache, Instruction* instr);
++ static void FlushOnePage(v8::internal::HashMap* i_cache, intptr_t start,
++ int size);
++ static CachePage* GetCachePage(v8::internal::HashMap* i_cache, void* page);
++
++ // Runtime call support.
++ static void* RedirectExternalReference(
++ void* external_function,
++ v8::internal::ExternalReference::Type type);
++
++ // For use in calls that take double value arguments.
++ void GetFpArgs(double* x, double* y);
++ void GetFpArgs(double* x);
++ void GetFpArgs(double* x, intptr_t* y);
++ void SetFpResult(const double& result);
++ void TrashCallerSaveRegisters();
++
++ template<class ReturnType, int register_size>
++ ReturnType GetFromFPRegister(int reg_index);
++
++ template<class InputType, int register_size>
++ void SetFPRegister(int reg_index, const InputType& value);
++
++ // Architecture state.
++ // Saturating instructions require a Q flag to indicate saturation.
++ // There is currently no way to read the CPSR directly, and thus read the Q
++ // flag, so this is left unimplemented.
++ intptr_t registers_[kNumGPRs]; // PowerPC
++ int32_t condition_reg_; // PowerPC
++ int32_t fp_condition_reg_; // PowerPC
++ intptr_t special_reg_lr_; // PowerPC
++ intptr_t special_reg_pc_; // PowerPC
++ intptr_t special_reg_ctr_; // PowerPC
++ int32_t special_reg_xer_; // PowerPC
++
++ double fp_register[kNumFPRs];
++
++ // Simulator support.
++ char* stack_;
++ bool pc_modified_;
++ int icount_;
++
++ // Debugger input.
++ char* last_debugger_input_;
++
++ // Icache simulation
++ v8::internal::HashMap* i_cache_;
++
++ // Registered breakpoints.
++ Instruction* break_pc_;
++ Instr break_instr_;
++
++ v8::internal::Isolate* isolate_;
++
++ // A stop is watched if its code is less than kNumOfWatchedStops.
++ // Only watched stops support enabling/disabling and the counter feature.
++ static const uint32_t kNumOfWatchedStops = 256;
++
++ // Breakpoint is disabled if bit 31 is set.
++ static const uint32_t kStopDisabledBit = 1 << 31;
++
++ // A stop is enabled, meaning the simulator will stop when meeting the
++ // instruction, if bit 31 of watched_stops[code].count is unset.
++ // The value watched_stops[code].count & ~(1 << 31) indicates how many
times
++ // the breakpoint was hit or gone through.
++ struct StopCountAndDesc {
++ uint32_t count;
++ char* desc;
++ };
++ StopCountAndDesc watched_stops[kNumOfWatchedStops];
++};
++
++
++// When running with the simulator transition into simulated execution at this
++// point.
++#define CALL_GENERATED_CODE(entry, p0, p1, p2, p3, p4) \
++ reinterpret_cast<Object*>(Simulator::current(Isolate::Current())->Call( \
++ FUNCTION_ADDR(entry), 5, \
++ (intptr_t)p0, \
++ (intptr_t)p1, \
++ (intptr_t)p2, \
++ (intptr_t)p3, \
++ (intptr_t)p4))
++
++#define CALL_GENERATED_REGEXP_CODE(entry, p0, p1, p2, p3, p4, p5, p6, p7, p8) \
++ Simulator::current(Isolate::Current())->Call( \
++ entry, 10, \
++ (intptr_t)p0, \
++ (intptr_t)p1, \
++ (intptr_t)p2, \
++ (intptr_t)p3, \
++ (intptr_t)p4, \
++ (intptr_t)p5, \
++ (intptr_t)p6, \
++ (intptr_t)p7, \
++ (intptr_t)NULL, \
++ (intptr_t)p8)
++
++#define TRY_CATCH_FROM_ADDRESS(try_catch_address) \
++ try_catch_address == NULL ? \
++ NULL : *(reinterpret_cast<TryCatch**>(try_catch_address))
++
++
++// The simulator has its own stack. Thus it has a different stack limit from
++// the C-based native code. Setting the c_limit to indicate a very small
++// stack cause stack overflow errors, since the simulator ignores the input.
++// This is unlikely to be an issue in practice, though it might cause testing
++// trouble down the line.
++class SimulatorStack : public v8::internal::AllStatic {
++ public:
++ static inline uintptr_t JsLimitFromCLimit(v8::internal::Isolate* isolate,
++ uintptr_t c_limit) {
++ return Simulator::current(isolate)->StackLimit();
++ }
++
++ static inline uintptr_t RegisterCTryCatch(uintptr_t try_catch_address) {
++ Simulator* sim = Simulator::current(Isolate::Current());
++ return sim->PushAddress(try_catch_address);
++ }
++
++ static inline void UnregisterCTryCatch() {
++ Simulator::current(Isolate::Current())->PopAddress();
++ }
++};
++
++} } // namespace v8::internal
++
++#endif // !defined(USE_SIMULATOR)
++#endif // V8_PPC_SIMULATOR_PPC_H_
+diff -up v8-3.14.5.10/src/ppc/stub-cache-ppc.cc.ppc
v8-3.14.5.10/src/ppc/stub-cache-ppc.cc
+--- v8-3.14.5.10/src/ppc/stub-cache-ppc.cc.ppc 2016-06-07 14:15:46.005392919 -0400
++++ v8-3.14.5.10/src/ppc/stub-cache-ppc.cc 2016-06-07 14:15:46.005392919 -0400
+@@ -0,0 +1,4466 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++//
++// Copyright IBM Corp. 2012, 2013. All rights reserved.
++//
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#if defined(V8_TARGET_ARCH_PPC)
++
++#include "ic-inl.h"
++#include "codegen.h"
++#include "stub-cache.h"
++
++namespace v8 {
++namespace internal {
++
++#define __ ACCESS_MASM(masm)
++
++static void ProbeTable(Isolate* isolate,
++ MacroAssembler* masm,
++ Code::Flags flags,
++ StubCache::Table table,
++ Register receiver,
++ Register name,
++ // Number of the cache entry, not scaled.
++ Register offset,
++ Register scratch,
++ Register scratch2,
++ Register offset_scratch) {
++ ExternalReference key_offset(isolate->stub_cache()->key_reference(table));
++ ExternalReference value_offset(isolate->stub_cache()->value_reference(table));
++ ExternalReference map_offset(isolate->stub_cache()->map_reference(table));
++
++ uintptr_t key_off_addr = reinterpret_cast<uintptr_t>(key_offset.address());
++ uintptr_t value_off_addr =
++ reinterpret_cast<uintptr_t>(value_offset.address());
++ uintptr_t map_off_addr = reinterpret_cast<uintptr_t>(map_offset.address());
++
++ // Check the relative positions of the address fields.
++ ASSERT(value_off_addr > key_off_addr);
++ ASSERT((value_off_addr - key_off_addr) % 4 == 0);
++ ASSERT((value_off_addr - key_off_addr) < (256 * 4));
++ ASSERT(map_off_addr > key_off_addr);
++ ASSERT((map_off_addr - key_off_addr) % 4 == 0);
++ ASSERT((map_off_addr - key_off_addr) < (256 * 4));
++
++ Label miss;
++ Register base_addr = scratch;
++ scratch = no_reg;
++
++ // Multiply by 3 because there are 3 fields per entry (name, code, map).
++ __ ShiftLeftImm(offset_scratch, offset, Operand(1));
++ __ add(offset_scratch, offset, offset_scratch);
++
++ // Calculate the base address of the entry.
++ __ mov(base_addr, Operand(key_offset));
++ __ ShiftLeftImm(scratch2, offset_scratch, Operand(kPointerSizeLog2));
++ __ add(base_addr, base_addr, scratch2);
++
++ // Check that the key in the entry matches the name.
++ __ LoadP(ip, MemOperand(base_addr, 0));
++ __ cmp(name, ip);
++ __ bne(&miss);
++
++ // Check the map matches.
++ __ LoadP(ip, MemOperand(base_addr, map_off_addr - key_off_addr));
++ __ LoadP(scratch2, FieldMemOperand(receiver, HeapObject::kMapOffset));
++ __ cmp(ip, scratch2);
++ __ bne(&miss);
++
++ // Get the code entry from the cache.
++ Register code = scratch2;
++ scratch2 = no_reg;
++ __ LoadP(code, MemOperand(base_addr, value_off_addr - key_off_addr));
++
++ // Check that the flags match what we're looking for.
++ Register flags_reg = base_addr;
++ base_addr = no_reg;
++ __ lwz(flags_reg, FieldMemOperand(code, Code::kFlagsOffset));
++
++ ASSERT(!r0.is(flags_reg));
++ __ li(r0, Operand(Code::kFlagsNotUsedInLookup));
++ __ andc(flags_reg, flags_reg, r0);
++ __ mov(r0, Operand(flags));
++ __ cmpl(flags_reg, r0);
++ __ bne(&miss);
++
++#ifdef DEBUG
++ if (FLAG_test_secondary_stub_cache && table == StubCache::kPrimary) {
++ __ b(&miss);
++ } else if (FLAG_test_primary_stub_cache && table == StubCache::kSecondary)
{
++ __ b(&miss);
++ }
++#endif
++
++ // Jump to the first instruction in the code stub.
++ __ addi(r0, code, Operand(Code::kHeaderSize - kHeapObjectTag));
++ __ mtctr(r0);
++ __ bcr();
++
++ // Miss: fall through.
++ __ bind(&miss);
++}
++
++
++// Helper function used to check that the dictionary doesn't contain
++// the property. This function may return false negatives, so miss_label
++// must always call a backup property check that is complete.
++// This function is safe to call if the receiver has fast properties.
++// Name must be a symbol and receiver must be a heap object.
++static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
++ Label* miss_label,
++ Register receiver,
++ Handle<String> name,
++ Register scratch0,
++ Register scratch1) {
++ ASSERT(name->IsSymbol());
++ Counters* counters = masm->isolate()->counters();
++ __ IncrementCounter(counters->negative_lookups(), 1, scratch0, scratch1);
++ __ IncrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
++
++ Label done;
++
++ const int kInterceptorOrAccessCheckNeededMask =
++ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
++
++ // Bail out if the receiver has a named interceptor or requires access checks.
++ Register map = scratch1;
++ __ LoadP(map, FieldMemOperand(receiver, HeapObject::kMapOffset));
++ __ lbz(scratch0, FieldMemOperand(map, Map::kBitFieldOffset));
++ __ andi(r0, scratch0, Operand(kInterceptorOrAccessCheckNeededMask));
++ __ bne(miss_label, cr0);
++
++ // Check that receiver is a JSObject.
++ __ lbz(scratch0, FieldMemOperand(map, Map::kInstanceTypeOffset));
++ __ cmpi(scratch0, Operand(FIRST_SPEC_OBJECT_TYPE));
++ __ blt(miss_label);
++
++ // Load properties array.
++ Register properties = scratch0;
++ __ LoadP(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
++ // Check that the properties array is a dictionary.
++ __ LoadP(map, FieldMemOperand(properties, HeapObject::kMapOffset));
++ Register tmp = properties;
++ __ LoadRoot(tmp, Heap::kHashTableMapRootIndex);
++ __ cmp(map, tmp);
++ __ bne(miss_label);
++
++ // Restore the temporarily used register.
++ __ LoadP(properties, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
++
++
++ StringDictionaryLookupStub::GenerateNegativeLookup(masm,
++ miss_label,
++ &done,
++ receiver,
++ properties,
++ name,
++ scratch1);
++ __ bind(&done);
++ __ DecrementCounter(counters->negative_lookups_miss(), 1, scratch0, scratch1);
++}
++
++
++void StubCache::GenerateProbe(MacroAssembler* masm,
++ Code::Flags flags,
++ Register receiver,
++ Register name,
++ Register scratch,
++ Register extra,
++ Register extra2,
++ Register extra3) {
++ Isolate* isolate = masm->isolate();
++ Label miss;
++
++#if V8_TARGET_ARCH_PPC64
++ // Make sure that code is valid. The multiplying code relies on the
++ // entry size being 24.
++ ASSERT(sizeof(Entry) == 24);
++#else
++ // Make sure that code is valid. The multiplying code relies on the
++ // entry size being 12.
++ ASSERT(sizeof(Entry) == 12);
++#endif
++
++ // Make sure the flags does not name a specific type.
++ ASSERT(Code::ExtractTypeFromFlags(flags) == 0);
++
++ // Make sure that there are no register conflicts.
++ ASSERT(!scratch.is(receiver));
++ ASSERT(!scratch.is(name));
++ ASSERT(!extra.is(receiver));
++ ASSERT(!extra.is(name));
++ ASSERT(!extra.is(scratch));
++ ASSERT(!extra2.is(receiver));
++ ASSERT(!extra2.is(name));
++ ASSERT(!extra2.is(scratch));
++ ASSERT(!extra2.is(extra));
++
++ // Check scratch, extra and extra2 registers are valid.
++ ASSERT(!scratch.is(no_reg));
++ ASSERT(!extra.is(no_reg));
++ ASSERT(!extra2.is(no_reg));
++ ASSERT(!extra3.is(no_reg));
++
++ Counters* counters = masm->isolate()->counters();
++ __ IncrementCounter(counters->megamorphic_stub_cache_probes(), 1,
++ extra2, extra3);
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, &miss);
++
++ // Get the map of the receiver and compute the hash.
++ __ lwz(scratch, FieldMemOperand(name, String::kHashFieldOffset));
++ __ LoadP(ip, FieldMemOperand(receiver, HeapObject::kMapOffset));
++ __ add(scratch, scratch, ip);
++#if V8_TARGET_ARCH_PPC64
++ // Use only the low 32 bits of the map pointer.
++ __ rldicl(scratch, scratch, 0, 32);
++#endif
++ uint32_t mask = kPrimaryTableSize - 1;
++ // We shift out the last two bits because they are not part of the hash and
++ // they are always 01 for maps.
++ __ ShiftRightImm(scratch, scratch, Operand(kHeapObjectTagSize));
++ // Mask down the eor argument to the minimum to keep the immediate
++ // encodable.
++ __ xori(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask));
++ // Prefer and_ to ubfx here because ubfx takes 2 cycles.
++ __ andi(scratch, scratch, Operand(mask));
++
++ // Probe the primary table.
++ ProbeTable(isolate,
++ masm,
++ flags,
++ kPrimary,
++ receiver,
++ name,
++ scratch,
++ extra,
++ extra2,
++ extra3);
++
++ // Primary miss: Compute hash for secondary probe.
++ __ ShiftRightImm(extra, name, Operand(kHeapObjectTagSize));
++ __ sub(scratch, scratch, extra);
++ uint32_t mask2 = kSecondaryTableSize - 1;
++ __ addi(scratch, scratch, Operand((flags >> kHeapObjectTagSize) & mask2));
++ __ andi(scratch, scratch, Operand(mask2));
++
++ // Probe the secondary table.
++ ProbeTable(isolate,
++ masm,
++ flags,
++ kSecondary,
++ receiver,
++ name,
++ scratch,
++ extra,
++ extra2,
++ extra3);
++
++ // Cache miss: Fall-through and let caller handle the miss by
++ // entering the runtime system.
++ __ bind(&miss);
++ __ IncrementCounter(counters->megamorphic_stub_cache_misses(), 1,
++ extra2, extra3);
++}
++
++
++void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
++ int index,
++ Register prototype) {
++ // Load the global or builtins object from the current context.
++ __ LoadP(prototype,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ // Load the native context from the global or builtins object.
++ __ LoadP(prototype,
++ FieldMemOperand(prototype, GlobalObject::kNativeContextOffset));
++ // Load the function from the native context.
++ __ LoadP(prototype, MemOperand(prototype, Context::SlotOffset(index)), r0);
++ // Load the initial map. The global functions all have initial maps.
++ __ LoadP(prototype,
++ FieldMemOperand(prototype,
++ JSFunction::kPrototypeOrInitialMapOffset));
++ // Load the prototype from the initial map.
++ __ LoadP(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
++}
++
++
++void StubCompiler::GenerateDirectLoadGlobalFunctionPrototype(
++ MacroAssembler* masm,
++ int index,
++ Register prototype,
++ Label* miss) {
++ Isolate* isolate = masm->isolate();
++ // Check we're still in the same context.
++ __ LoadP(prototype,
++ MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
++ __ Move(ip, isolate->global_object());
++ __ cmp(prototype, ip);
++ __ bne(miss);
++ // Get the global function with the given index.
++ Handle<JSFunction> function(
++ JSFunction::cast(isolate->native_context()->get(index)));
++ // Load its initial map. The global functions all have initial maps.
++ __ Move(prototype, Handle<Map>(function->initial_map()));
++ // Load the prototype from the initial map.
++ __ LoadP(prototype, FieldMemOperand(prototype, Map::kPrototypeOffset));
++}
++
++
++// Load a fast property out of a holder object (src). In-object properties
++// are loaded directly otherwise the property is loaded from the properties
++// fixed array.
++void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
++ Register dst,
++ Register src,
++ Handle<JSObject> holder,
++ int index) {
++ // Adjust for the number of properties stored in the holder.
++ index -= holder->map()->inobject_properties();
++ if (index < 0) {
++ // Get the property straight out of the holder.
++ int offset = holder->map()->instance_size() + (index * kPointerSize);
++ __ LoadP(dst, FieldMemOperand(src, offset), r0);
++ } else {
++ // Calculate the offset into the properties array.
++ int offset = index * kPointerSize + FixedArray::kHeaderSize;
++ __ LoadP(dst, FieldMemOperand(src, JSObject::kPropertiesOffset));
++ __ LoadP(dst, FieldMemOperand(dst, offset), r0);
++ }
++}
++
++
++void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
++ Register receiver,
++ Register scratch,
++ Label* miss_label) {
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, miss_label);
++
++ // Check that the object is a JS array.
++ __ CompareObjectType(receiver, scratch, scratch, JS_ARRAY_TYPE);
++ __ bne(miss_label);
++
++ // Load length directly from the JS array.
++ __ LoadP(r3, FieldMemOperand(receiver, JSArray::kLengthOffset));
++ __ Ret();
++}
++
++
++// Generate code to check if an object is a string. If the object is a
++// heap object, its map's instance type is left in the scratch1 register.
++// If this is not needed, scratch1 and scratch2 may be the same register.
++static void GenerateStringCheck(MacroAssembler* masm,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Label* smi,
++ Label* non_string_object) {
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, smi);
++
++ // Check that the object is a string.
++ __ LoadP(scratch1, FieldMemOperand(receiver, HeapObject::kMapOffset));
++ __ lbz(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset));
++ __ andi(scratch2, scratch1, Operand(kIsNotStringMask));
++ // The cast is to resolve the overload for the argument of 0x0.
++ __ cmpi(scratch2, Operand(static_cast<intptr_t>(kStringTag)));
++ __ bne(non_string_object);
++}
++
++
++// Generate code to load the length from a string object and return the length.
++// If the receiver object is not a string or a wrapped string object the
++// execution continues at the miss label. The register containing the
++// receiver is potentially clobbered.
++void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Label* miss,
++ bool support_wrappers) {
++ Label check_wrapper;
++
++ // Check if the object is a string leaving the instance type in the
++ // scratch1 register.
++ GenerateStringCheck(masm, receiver, scratch1, scratch2, miss,
++ support_wrappers ? &check_wrapper : miss);
++
++ // Load length directly from the string.
++ __ LoadP(r3, FieldMemOperand(receiver, String::kLengthOffset));
++ __ Ret();
++
++ if (support_wrappers) {
++ // Check if the object is a JSValue wrapper.
++ __ bind(&check_wrapper);
++ __ cmpi(scratch1, Operand(JS_VALUE_TYPE));
++ __ bne(miss);
++
++ // Unwrap the value and check if the wrapped value is a string.
++ __ LoadP(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset));
++ GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss);
++ __ LoadP(r3, FieldMemOperand(scratch1, String::kLengthOffset));
++ __ Ret();
++ }
++}
++
++
++void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Label* miss_label) {
++ __ TryGetFunctionPrototype(receiver, scratch1, scratch2, miss_label);
++ __ mr(r3, scratch1);
++ __ Ret();
++}
++
++
++// Generate StoreField code, value is passed in r3 register.
++// When leaving generated code after success, the receiver_reg and name_reg
++// may be clobbered. Upon branch to miss_label, the receiver and name
++// registers have their original values.
++void StubCompiler::GenerateStoreField(MacroAssembler* masm,
++ Handle<JSObject> object,
++ int index,
++ Handle<Map> transition,
++ Handle<String> name,
++ Register receiver_reg,
++ Register name_reg,
++ Register scratch1,
++ Register scratch2,
++ Label* miss_label) {
++ // r3 : value
++ Label exit;
++
++ LookupResult lookup(masm->isolate());
++ object->Lookup(*name, &lookup);
++ if (lookup.IsFound() && (lookup.IsReadOnly() || !lookup.IsCacheable())) {
++ // In sloppy mode, we could just return the value and be done. However, we
++ // might be in strict mode, where we have to throw. Since we cannot tell,
++ // go into slow case unconditionally.
++ __ b(miss_label);
++ return;
++ }
++
++ // Check that the map of the object hasn't changed.
++ CompareMapMode mode = transition.is_null() ? ALLOW_ELEMENT_TRANSITION_MAPS
++ : REQUIRE_EXACT_MAP;
++ __ CheckMap(receiver_reg, scratch1, Handle<Map>(object->map()), miss_label,
++ DO_SMI_CHECK, mode);
++
++ // Perform global security token check if needed.
++ if (object->IsJSGlobalProxy()) {
++ __ CheckAccessGlobalProxy(receiver_reg, scratch1, miss_label);
++ }
++
++ // Check that we are allowed to write this.
++ if (!transition.is_null() && object->GetPrototype()->IsJSObject()) {
++ JSObject* holder;
++ if (lookup.IsFound()) {
++ holder = lookup.holder();
++ } else {
++ // Find the top object.
++ holder = *object;
++ do {
++ holder = JSObject::cast(holder->GetPrototype());
++ } while (holder->GetPrototype()->IsJSObject());
++ }
++ // We need an extra register, push
++ __ push(name_reg);
++ Label miss_pop, done_check;
++ CheckPrototypes(object, receiver_reg, Handle<JSObject>(holder), name_reg,
++ scratch1, scratch2, name, &miss_pop);
++ __ b(&done_check);
++ __ bind(&miss_pop);
++ __ pop(name_reg);
++ __ b(miss_label);
++ __ bind(&done_check);
++ __ pop(name_reg);
++ }
++
++ // Stub never generated for non-global objects that require access
++ // checks.
++ ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
++
++ // Perform map transition for the receiver if necessary.
++ if (!transition.is_null() && (object->map()->unused_property_fields() ==
0)) {
++ // The properties must be extended before we can store the value.
++ // We jump to a runtime call that extends the properties array.
++ __ push(receiver_reg);
++ __ mov(r5, Operand(transition));
++ __ Push(r5, r3);
++ __ TailCallExternalReference(
++ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage),
++ masm->isolate()),
++ 3,
++ 1);
++ return;
++ }
++
++ if (!transition.is_null()) {
++ // Update the map of the object.
++ __ mov(scratch1, Operand(transition));
++ __ StoreP(scratch1, FieldMemOperand(receiver_reg, HeapObject::kMapOffset),
++ r0);
++
++ // Update the write barrier for the map field and pass the now unused
++ // name_reg as scratch register.
++ __ RecordWriteField(receiver_reg,
++ HeapObject::kMapOffset,
++ scratch1,
++ name_reg,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ OMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ }
++
++ // Adjust for the number of properties stored in the object. Even in the
++ // face of a transition we can use the old map here because the size of the
++ // object and the number of in-object properties is not going to change.
++ index -= object->map()->inobject_properties();
++
++ if (index < 0) {
++ // Set the property straight into the object.
++ int offset = object->map()->instance_size() + (index * kPointerSize);
++ __ StoreP(r3, FieldMemOperand(receiver_reg, offset), r0);
++
++ // Skip updating write barrier if storing a smi.
++ __ JumpIfSmi(r3, &exit);
++
++ // Update the write barrier for the array address.
++ // Pass the now unused name_reg as a scratch register.
++ __ mr(name_reg, r3);
++ __ RecordWriteField(receiver_reg,
++ offset,
++ name_reg,
++ scratch1,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs);
++ } else {
++ // Write to the properties array.
++ int offset = index * kPointerSize + FixedArray::kHeaderSize;
++ // Get the properties array
++ __ LoadP(scratch1,
++ FieldMemOperand(receiver_reg, JSObject::kPropertiesOffset));
++ __ StoreP(r3, FieldMemOperand(scratch1, offset), r0);
++
++ // Skip updating write barrier if storing a smi.
++ __ JumpIfSmi(r3, &exit);
++
++ // Update the write barrier for the array address.
++ // Ok to clobber receiver_reg and name_reg, since we return.
++ __ mr(name_reg, r3);
++ __ RecordWriteField(scratch1,
++ offset,
++ name_reg,
++ receiver_reg,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs);
++ }
++
++ // Return the value (register r3).
++ __ bind(&exit);
++ __ Ret();
++}
++
++
++void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
++ ASSERT(kind == Code::LOAD_IC || kind == Code::KEYED_LOAD_IC);
++ Handle<Code> code = (kind == Code::LOAD_IC)
++ ? masm->isolate()->builtins()->LoadIC_Miss()
++ : masm->isolate()->builtins()->KeyedLoadIC_Miss();
++ __ Jump(code, RelocInfo::CODE_TARGET);
++}
++
++
++static void GenerateCallFunction(MacroAssembler* masm,
++ Handle<Object> object,
++ const ParameterCount& arguments,
++ Label* miss,
++ Code::ExtraICState extra_ic_state) {
++ // ----------- S t a t e -------------
++ // -- r3: receiver
++ // -- r4: function to call
++ // -----------------------------------
++
++ // Check that the function really is a function.
++ __ JumpIfSmi(r4, miss);
++ __ CompareObjectType(r4, r6, r6, JS_FUNCTION_TYPE);
++ __ bne(miss);
++
++ // Patch the receiver on the stack with the global proxy if
++ // necessary.
++ if (object->IsGlobalObject()) {
++ __ LoadP(r6, FieldMemOperand(r3, GlobalObject::kGlobalReceiverOffset));
++ __ StoreP(r6, MemOperand(sp, arguments.immediate() * kPointerSize), r0);
++ }
++
++ // Invoke the function.
++ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state)
++ ? CALL_AS_FUNCTION
++ : CALL_AS_METHOD;
++ __ InvokeFunction(r4, arguments, JUMP_FUNCTION, NullCallWrapper(), call_kind);
++}
++
++
++static void PushInterceptorArguments(MacroAssembler* masm,
++ Register receiver,
++ Register holder,
++ Register name,
++ Handle<JSObject> holder_obj) {
++ __ push(name);
++ Handle<InterceptorInfo> interceptor(holder_obj->GetNamedInterceptor());
++ ASSERT(!masm->isolate()->heap()->InNewSpace(*interceptor));
++ Register scratch = name;
++ __ mov(scratch, Operand(interceptor));
++ __ push(scratch);
++ __ push(receiver);
++ __ push(holder);
++ __ LoadP(scratch, FieldMemOperand(scratch, InterceptorInfo::kDataOffset));
++ __ push(scratch);
++ __ mov(scratch, Operand(ExternalReference::isolate_address()));
++ __ push(scratch);
++}
++
++
++static void CompileCallLoadPropertyWithInterceptor(
++ MacroAssembler* masm,
++ Register receiver,
++ Register holder,
++ Register name,
++ Handle<JSObject> holder_obj) {
++ PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
++
++ ExternalReference ref =
++ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly),
++ masm->isolate());
++ __ li(r3, Operand(6));
++ __ mov(r4, Operand(ref));
++
++ CEntryStub stub(1);
++ __ CallStub(&stub);
++}
++
++
++static const int kFastApiCallArguments = 4;
++
++// Reserves space for the extra arguments to API function in the
++// caller's frame.
++//
++// These arguments are set by CheckPrototypes and GenerateFastApiDirectCall.
++static void ReserveSpaceForFastApiCall(MacroAssembler* masm,
++ Register scratch) {
++ __ LoadSmiLiteral(scratch, Smi::FromInt(0));
++ for (int i = 0; i < kFastApiCallArguments; i++) {
++ __ push(scratch);
++ }
++}
++
++
++// Undoes the effects of ReserveSpaceForFastApiCall.
++static void FreeSpaceForFastApiCall(MacroAssembler* masm) {
++ __ Drop(kFastApiCallArguments);
++}
++
++
++static void GenerateFastApiDirectCall(MacroAssembler* masm,
++ const CallOptimization& optimization,
++ int argc) {
++ // ----------- S t a t e -------------
++ // -- sp[0] : holder (set by CheckPrototypes)
++ // -- sp[4] : callee JS function
++ // -- sp[8] : call data
++ // -- sp[12] : isolate
++ // -- sp[16] : last JS argument
++ // -- ...
++ // -- sp[(argc + 3) * 4] : first JS argument
++ // -- sp[(argc + 4) * 4] : receiver
++ // -----------------------------------
++ // Get the function and setup the context.
++ Handle<JSFunction> function = optimization.constant_function();
++ __ LoadHeapObject(r8, function);
++ __ LoadP(cp, FieldMemOperand(r8, JSFunction::kContextOffset));
++
++ // Pass the additional arguments.
++ Handle<CallHandlerInfo> api_call_info = optimization.api_call_info();
++ Handle<Object> call_data(api_call_info->data());
++ if (masm->isolate()->heap()->InNewSpace(*call_data)) {
++ __ Move(r3, api_call_info);
++ __ LoadP(r9, FieldMemOperand(r3, CallHandlerInfo::kDataOffset));
++ } else {
++ __ Move(r9, call_data);
++ }
++ __ mov(r10, Operand(ExternalReference::isolate_address()));
++ // Store JS function, call data and isolate.
++ __ StoreP(r8, MemOperand(sp, 1 * kPointerSize));
++ __ StoreP(r9, MemOperand(sp, 2 * kPointerSize));
++ __ StoreP(r10, MemOperand(sp, 3 * kPointerSize));
++
++ // Prepare arguments.
++ __ addi(r5, sp, Operand(3 * kPointerSize));
++
++#if !ABI_RETURNS_HANDLES_IN_REGS
++ bool alloc_return_buf = true;
++#else
++ bool alloc_return_buf = false;
++#endif
++
++ // Allocate the v8::Arguments structure in the arguments' space since
++ // it's not controlled by GC.
++ // PPC LINUX ABI:
++ //
++ // Create 5 or 6 extra slots on stack (depending on alloc_return_buf):
++ // [0] space for DirectCEntryStub's LR save
++ // [1] space for pointer-sized non-scalar return value (r3)
++ // [2-5] v8:Arguments
++ //
++ // If alloc_return buf, we shift the arguments over a register
++ // (e.g. r3 -> r4) to allow for the return value buffer in implicit
++ // first arg. CallApiFunctionAndReturn will setup r3.
++ int kApiStackSpace = 5 + (alloc_return_buf ? 1 : 0);
++ Register arg0 = alloc_return_buf ? r4 : r3;
++
++ FrameScope frame_scope(masm, StackFrame::MANUAL);
++ __ EnterExitFrame(false, kApiStackSpace);
++
++ // scalar and return
++
++ // arg0 = v8::Arguments&
++ // Arguments is after the return address.
++ __ addi(arg0, sp, Operand((kStackFrameExtraParamSlot +
++ (alloc_return_buf ? 2 : 1)) * kPointerSize));
++ // v8::Arguments::implicit_args_
++ __ StoreP(r5, MemOperand(arg0, 0 * kPointerSize));
++ // v8::Arguments::values_
++ __ addi(ip, r5, Operand(argc * kPointerSize));
++ __ StoreP(ip, MemOperand(arg0, 1 * kPointerSize));
++ // v8::Arguments::length_ = argc
++ __ li(ip, Operand(argc));
++ __ stw(ip, MemOperand(arg0, 2 * kPointerSize));
++ // v8::Arguments::is_construct_call = 0
++ __ li(ip, Operand::Zero());
++ __ StoreP(ip, MemOperand(arg0, 3 * kPointerSize));
++
++ const int kStackUnwindSpace = argc + kFastApiCallArguments + 1;
++ Address function_address = v8::ToCData<Address>(api_call_info->callback());
++ ApiFunction fun(function_address);
++ ExternalReference ref = ExternalReference(&fun,
++ ExternalReference::DIRECT_API_CALL,
++ masm->isolate());
++ AllowExternalCallThatCantCauseGC scope(masm);
++
++ __ CallApiFunctionAndReturn(ref, kStackUnwindSpace);
++}
++
++
++class CallInterceptorCompiler BASE_EMBEDDED {
++ public:
++ CallInterceptorCompiler(StubCompiler* stub_compiler,
++ const ParameterCount& arguments,
++ Register name,
++ Code::ExtraICState extra_ic_state)
++ : stub_compiler_(stub_compiler),
++ arguments_(arguments),
++ name_(name),
++ extra_ic_state_(extra_ic_state) {}
++
++ void Compile(MacroAssembler* masm,
++ Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Handle<String> name,
++ LookupResult* lookup,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Label* miss) {
++ ASSERT(holder->HasNamedInterceptor());
++ ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, miss);
++ CallOptimization optimization(lookup);
++ if (optimization.is_constant_call()) {
++ CompileCacheable(masm, object, receiver, scratch1, scratch2, scratch3,
++ holder, lookup, name, optimization, miss);
++ } else {
++ CompileRegular(masm, object, receiver, scratch1, scratch2, scratch3,
++ name, holder, miss);
++ }
++ }
++
++ private:
++ void CompileCacheable(MacroAssembler* masm,
++ Handle<JSObject> object,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Handle<JSObject> interceptor_holder,
++ LookupResult* lookup,
++ Handle<String> name,
++ const CallOptimization& optimization,
++ Label* miss_label) {
++ ASSERT(optimization.is_constant_call());
++ ASSERT(!lookup->holder()->IsGlobalObject());
++ Counters* counters = masm->isolate()->counters();
++ int depth1 = kInvalidProtoDepth;
++ int depth2 = kInvalidProtoDepth;
++ bool can_do_fast_api_call = false;
++ if (optimization.is_simple_api_call() &&
++ !lookup->holder()->IsGlobalObject()) {
++ depth1 = optimization.GetPrototypeDepthOfExpectedType(
++ object, interceptor_holder);
++ if (depth1 == kInvalidProtoDepth) {
++ depth2 = optimization.GetPrototypeDepthOfExpectedType(
++ interceptor_holder, Handle<JSObject>(lookup->holder()));
++ }
++ can_do_fast_api_call =
++ depth1 != kInvalidProtoDepth || depth2 != kInvalidProtoDepth;
++ }
++
++ __ IncrementCounter(counters->call_const_interceptor(), 1,
++ scratch1, scratch2);
++
++ if (can_do_fast_api_call) {
++ __ IncrementCounter(counters->call_const_interceptor_fast_api(), 1,
++ scratch1, scratch2);
++ ReserveSpaceForFastApiCall(masm, scratch1);
++ }
++
++ // Check that the maps from receiver to interceptor's holder
++ // haven't changed and thus we can invoke interceptor.
++ Label miss_cleanup;
++ Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
++ Register holder =
++ stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
++ scratch1, scratch2, scratch3,
++ name, depth1, miss);
++
++ // Invoke an interceptor and if it provides a value,
++ // branch to |regular_invoke|.
++ Label regular_invoke;
++ LoadWithInterceptor(masm, receiver, holder, interceptor_holder, scratch2,
++ ®ular_invoke);
++
++ // Interceptor returned nothing for this property. Try to use cached
++ // constant function.
++
++ // Check that the maps from interceptor's holder to constant function's
++ // holder haven't changed and thus we can use cached constant function.
++ if (*interceptor_holder != lookup->holder()) {
++ stub_compiler_->CheckPrototypes(interceptor_holder, receiver,
++ Handle<JSObject>(lookup->holder()),
++ scratch1, scratch2, scratch3,
++ name, depth2, miss);
++ } else {
++ // CheckPrototypes has a side effect of fetching a 'holder'
++ // for API (object which is instanceof for the signature). It's
++ // safe to omit it here, as if present, it should be fetched
++ // by the previous CheckPrototypes.
++ ASSERT(depth2 == kInvalidProtoDepth);
++ }
++
++ // Invoke function.
++ if (can_do_fast_api_call) {
++ GenerateFastApiDirectCall(masm, optimization, arguments_.immediate());
++ } else {
++ CallKind call_kind = CallICBase::Contextual::decode(extra_ic_state_)
++ ? CALL_AS_FUNCTION
++ : CALL_AS_METHOD;
++ __ InvokeFunction(optimization.constant_function(), arguments_,
++ JUMP_FUNCTION, NullCallWrapper(), call_kind);
++ }
++
++ // Deferred code for fast API call case---clean preallocated space.
++ if (can_do_fast_api_call) {
++ __ bind(&miss_cleanup);
++ FreeSpaceForFastApiCall(masm);
++ __ b(miss_label);
++ }
++
++ // Invoke a regular function.
++ __ bind(®ular_invoke);
++ if (can_do_fast_api_call) {
++ FreeSpaceForFastApiCall(masm);
++ }
++ }
++
++ void CompileRegular(MacroAssembler* masm,
++ Handle<JSObject> object,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Handle<String> name,
++ Handle<JSObject> interceptor_holder,
++ Label* miss_label) {
++ Register holder =
++ stub_compiler_->CheckPrototypes(object, receiver, interceptor_holder,
++ scratch1, scratch2, scratch3,
++ name, miss_label);
++
++ // Call a runtime function to load the interceptor property.
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ // Save the name_ register across the call.
++ __ push(name_);
++ PushInterceptorArguments(masm, receiver, holder, name_, interceptor_holder);
++ __ CallExternalReference(
++ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall),
++ masm->isolate()),
++ 6);
++ // Restore the name_ register.
++ __ pop(name_);
++ // Leave the internal frame.
++ }
++
++ void LoadWithInterceptor(MacroAssembler* masm,
++ Register receiver,
++ Register holder,
++ Handle<JSObject> holder_obj,
++ Register scratch,
++ Label* interceptor_succeeded) {
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++ __ Push(holder, name_);
++ CompileCallLoadPropertyWithInterceptor(masm,
++ receiver,
++ holder,
++ name_,
++ holder_obj);
++ __ pop(name_); // Restore the name.
++ __ pop(receiver); // Restore the holder.
++ }
++ // If interceptor returns no-result sentinel, call the constant function.
++ __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex);
++ __ cmp(r3, scratch);
++ __ bne(interceptor_succeeded);
++ }
++
++ StubCompiler* stub_compiler_;
++ const ParameterCount& arguments_;
++ Register name_;
++ Code::ExtraICState extra_ic_state_;
++};
++
++
++// Generate code to check that a global property cell is empty. Create
++// the property cell at compilation time if no cell exists for the
++// property.
++static void GenerateCheckPropertyCell(MacroAssembler* masm,
++ Handle<GlobalObject> global,
++ Handle<String> name,
++ Register scratch,
++ Label* miss) {
++ Handle<JSGlobalPropertyCell> cell =
++ GlobalObject::EnsurePropertyCell(global, name);
++ ASSERT(cell->value()->IsTheHole());
++ __ mov(scratch, Operand(cell));
++ __ LoadP(scratch,
++ FieldMemOperand(scratch, JSGlobalPropertyCell::kValueOffset));
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(scratch, ip);
++ __ bne(miss);
++}
++
++
++// Calls GenerateCheckPropertyCell for each global object in the prototype chain
++// from object to (but not including) holder.
++static void GenerateCheckPropertyCells(MacroAssembler* masm,
++ Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Handle<String> name,
++ Register scratch,
++ Label* miss) {
++ Handle<JSObject> current = object;
++ while (!current.is_identical_to(holder)) {
++ if (current->IsGlobalObject()) {
++ GenerateCheckPropertyCell(masm,
++ Handle<GlobalObject>::cast(current),
++ name,
++ scratch,
++ miss);
++ }
++ current = Handle<JSObject>(JSObject::cast(current->GetPrototype()));
++ }
++}
++
++#undef __
++#define __ ACCESS_MASM(masm())
++
++
++Register StubCompiler::CheckPrototypes(Handle<JSObject> object,
++ Register object_reg,
++ Handle<JSObject> holder,
++ Register holder_reg,
++ Register scratch1,
++ Register scratch2,
++ Handle<String> name,
++ int save_at_depth,
++ Label* miss) {
++ // Make sure there's no overlap between holder and object registers.
++ ASSERT(!scratch1.is(object_reg) && !scratch1.is(holder_reg));
++ ASSERT(!scratch2.is(object_reg) && !scratch2.is(holder_reg)
++ && !scratch2.is(scratch1));
++
++ // Keep track of the current object in register reg.
++ Register reg = object_reg;
++ int depth = 0;
++
++ if (save_at_depth == depth) {
++ __ StoreP(reg, MemOperand(sp));
++ }
++
++ // Check the maps in the prototype chain.
++ // Traverse the prototype chain from the object and do map checks.
++ Handle<JSObject> current = object;
++ while (!current.is_identical_to(holder)) {
++ ++depth;
++
++ // Only global objects and objects that do not require access
++ // checks are allowed in stubs.
++ ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
++
++ Handle<JSObject> prototype(JSObject::cast(current->GetPrototype()));
++ if (!current->HasFastProperties() &&
++ !current->IsJSGlobalObject() &&
++ !current->IsJSGlobalProxy()) {
++ if (!name->IsSymbol()) {
++ name = factory()->LookupSymbol(name);
++ }
++ ASSERT(current->property_dictionary()->FindEntry(*name) ==
++ StringDictionary::kNotFound);
++
++ GenerateDictionaryNegativeLookup(masm(), miss, reg, name,
++ scratch1, scratch2);
++
++ __ LoadP(scratch1, FieldMemOperand(reg, HeapObject::kMapOffset));
++ reg = holder_reg; // From now on the object will be in holder_reg.
++ __ LoadP(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
++ } else {
++ Handle<Map> current_map(current->map());
++ __ CheckMap(reg, scratch1, current_map, miss, DONT_DO_SMI_CHECK,
++ ALLOW_ELEMENT_TRANSITION_MAPS);
++
++ // Check access rights to the global object. This has to happen after
++ // the map check so that we know that the object is actually a global
++ // object.
++ if (current->IsJSGlobalProxy()) {
++ __ CheckAccessGlobalProxy(reg, scratch2, miss);
++ }
++ reg = holder_reg; // From now on the object will be in holder_reg.
++
++ if (heap()->InNewSpace(*prototype)) {
++ // The prototype is in new space; we cannot store a reference to it
++ // in the code. Load it from the map.
++ __ LoadP(reg, FieldMemOperand(scratch1, Map::kPrototypeOffset));
++ } else {
++ // The prototype is in old space; load it directly.
++ __ mov(reg, Operand(prototype));
++ }
++ }
++
++ if (save_at_depth == depth) {
++ __ StoreP(reg, MemOperand(sp));
++ }
++
++ // Go to the next object in the prototype chain.
++ current = prototype;
++ }
++
++ // Log the check depth.
++ LOG(masm()->isolate(), IntEvent("check-maps-depth", depth + 1));
++
++ // Check the holder map.
++ __ CheckMap(reg, scratch1, Handle<Map>(current->map()), miss,
++ DONT_DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS);
++
++ // Perform security check for access to the global object.
++ ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
++ if (holder->IsJSGlobalProxy()) {
++ __ CheckAccessGlobalProxy(reg, scratch1, miss);
++ }
++
++ // If we've skipped any global objects, it's not enough to verify that
++ // their maps haven't changed. We also need to check that the property
++ // cell for the property is still empty.
++ GenerateCheckPropertyCells(masm(), object, holder, name, scratch1, miss);
++
++ // Return the register containing the holder.
++ return reg;
++}
++
++
++void StubCompiler::GenerateLoadField(Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ int index,
++ Handle<String> name,
++ Label* miss) {
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, miss);
++
++ // Check that the maps haven't changed.
++ Register reg = CheckPrototypes(
++ object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
++ GenerateFastPropertyLoad(masm(), r3, reg, holder, index);
++ __ Ret();
++}
++
++
++void StubCompiler::GenerateLoadConstant(Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Register receiver,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Handle<JSFunction> value,
++ Handle<String> name,
++ Label* miss) {
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, miss);
++
++ // Check that the maps haven't changed.
++ CheckPrototypes(
++ object, receiver, holder, scratch1, scratch2, scratch3, name, miss);
++
++ // Return the constant value.
++ __ LoadHeapObject(r3, value);
++ __ Ret();
++}
++
++
++void StubCompiler::GenerateDictionaryLoadCallback(Register receiver,
++ Register name_reg,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Handle<AccessorInfo> callback,
++ Handle<String> name,
++ Label* miss) {
++ ASSERT(!receiver.is(scratch1));
++ ASSERT(!receiver.is(scratch2));
++ ASSERT(!receiver.is(scratch3));
++
++ // Load the properties dictionary.
++ Register dictionary = scratch1;
++ __ LoadP(dictionary, FieldMemOperand(receiver, JSObject::kPropertiesOffset));
++
++ // Probe the dictionary.
++ Label probe_done;
++ StringDictionaryLookupStub::GeneratePositiveLookup(masm(),
++ miss,
++ &probe_done,
++ dictionary,
++ name_reg,
++ scratch2,
++ scratch3);
++ __ bind(&probe_done);
++
++ // If probing finds an entry in the dictionary, scratch3 contains the
++ // pointer into the dictionary. Check that the value is the callback.
++ Register pointer = scratch3;
++ const int kElementsStartOffset = StringDictionary::kHeaderSize +
++ StringDictionary::kElementsStartIndex * kPointerSize;
++ const int kValueOffset = kElementsStartOffset + kPointerSize;
++ __ LoadP(scratch2, FieldMemOperand(pointer, kValueOffset));
++ __ mov(scratch3, Operand(callback));
++ __ cmp(scratch2, scratch3);
++ __ bne(miss);
++}
++
++
++void StubCompiler::GenerateLoadCallback(Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Register receiver,
++ Register name_reg,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Register scratch4,
++ Handle<AccessorInfo> callback,
++ Handle<String> name,
++ Label* miss) {
++#if !ABI_RETURNS_HANDLES_IN_REGS
++ bool alloc_return_buf = true;
++#else
++ bool alloc_return_buf = false;
++#endif
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, miss);
++
++ // Check that the maps haven't changed.
++ Register reg = CheckPrototypes(object, receiver, holder, scratch1,
++ scratch2, scratch3, name, miss);
++
++ if (!holder->HasFastProperties() && !holder->IsJSGlobalObject()) {
++ GenerateDictionaryLoadCallback(
++ reg, name_reg, scratch2, scratch3, scratch4, callback, name, miss);
++ }
++
++ // Build AccessorInfo::args_ list on the stack and push property name below
++ // the exit frame to make GC aware of them and store pointers to them.
++ __ push(receiver);
++ __ mr(scratch2, sp); // ip = AccessorInfo::args_
++ if (heap()->InNewSpace(callback->data())) {
++ __ Move(scratch3, callback);
++ __ LoadP(scratch3, FieldMemOperand(scratch3, AccessorInfo::kDataOffset));
++ } else {
++ __ Move(scratch3, Handle<Object>(callback->data()));
++ }
++ __ Push(reg, scratch3);
++ __ mov(scratch3, Operand(ExternalReference::isolate_address()));
++ __ Push(scratch3, name_reg);
++
++ // If ABI passes Handles (pointer-sized struct) in a register:
++ //
++ // Create 2 or 3 extra slots on stack (depending on alloc_return_buf):
++ // [0] space for DirectCEntryStub's LR save
++ // [1] space for pointer-sized non-scalar return value (r3)
++ // [2] AccessorInfo
++ //
++ // Otherwise:
++ //
++ // Create 3 or 4 extra slots on stack (depending on alloc_return_buf):
++ // [0] space for DirectCEntryStub's LR save
++ // [1] (optional) space for pointer-sized non-scalar return value (r3)
++ // [2] copy of Handle (first arg)
++ // [3] AccessorInfo
++ //
++ // If alloc_return_buf, we shift the arguments over a register
++ // (e.g. r3 -> r4) to allow for the return value buffer in implicit
++ // first arg. CallApiFunctionAndReturn will setup r3.
++#if ABI_PASSES_HANDLES_IN_REGS
++ const int kAccessorInfoSlot = kStackFrameExtraParamSlot +
++ (alloc_return_buf ? 2 : 1);
++#else
++ const int kAccessorInfoSlot = kStackFrameExtraParamSlot +
++ (alloc_return_buf ? 3 : 2);
++ int kArg0Slot = kStackFrameExtraParamSlot + (alloc_return_buf ? 2 : 1);
++#endif
++ const int kApiStackSpace = (alloc_return_buf ? 4 : 3);
++ Register arg0 = (alloc_return_buf ? r4 : r3);
++ Register arg1 = (alloc_return_buf ? r5 : r4);
++
++ __ mr(arg1, scratch2); // Saved in case scratch2 == arg0.
++ __ mr(arg0, sp); // arg0 = Handle<String>
++
++ FrameScope frame_scope(masm(), StackFrame::MANUAL);
++ __ EnterExitFrame(false, kApiStackSpace);
++
++#if !ABI_PASSES_HANDLES_IN_REGS
++ // pass 1st arg by reference
++ __ StoreP(arg0, MemOperand(sp, kArg0Slot * kPointerSize));
++ __ addi(arg0, sp, Operand(kArg0Slot * kPointerSize));
++#endif
++
++ // Create AccessorInfo instance on the stack above the exit frame with
++ // ip (internal::Object** args_) as the data.
++ __ StoreP(arg1, MemOperand(sp, kAccessorInfoSlot * kPointerSize));
++ // arg1 = AccessorInfo&
++ __ addi(arg1, sp, Operand(kAccessorInfoSlot * kPointerSize));
++
++ const int kStackUnwindSpace = 5;
++ Address getter_address = v8::ToCData<Address>(callback->getter());
++ ApiFunction fun(getter_address);
++ ExternalReference ref =
++ ExternalReference(&fun,
++ ExternalReference::DIRECT_GETTER_CALL,
++ masm()->isolate());
++ __ CallApiFunctionAndReturn(ref, kStackUnwindSpace);
++}
++
++
++void StubCompiler::GenerateLoadInterceptor(Handle<JSObject> object,
++ Handle<JSObject> interceptor_holder,
++ LookupResult* lookup,
++ Register receiver,
++ Register name_reg,
++ Register scratch1,
++ Register scratch2,
++ Register scratch3,
++ Handle<String> name,
++ Label* miss) {
++ ASSERT(interceptor_holder->HasNamedInterceptor());
++ ASSERT(!interceptor_holder->GetNamedInterceptor()->getter()->IsUndefined());
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, miss);
++
++ // So far the most popular follow ups for interceptor loads are FIELD
++ // and CALLBACKS, so inline only them, other cases may be added
++ // later.
++ bool compile_followup_inline = false;
++ if (lookup->IsFound() && lookup->IsCacheable()) {
++ if (lookup->IsField()) {
++ compile_followup_inline = true;
++ } else if (lookup->type() == CALLBACKS &&
++ lookup->GetCallbackObject()->IsAccessorInfo()) {
++ AccessorInfo* callback = AccessorInfo::cast(lookup->GetCallbackObject());
++ compile_followup_inline = callback->getter() != NULL &&
++ callback->IsCompatibleReceiver(*object);
++ }
++ }
++
++ if (compile_followup_inline) {
++ // Compile the interceptor call, followed by inline code to load the
++ // property from further up the prototype chain if the call fails.
++ // Check that the maps haven't changed.
++ Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
++ scratch1, scratch2, scratch3,
++ name, miss);
++ ASSERT(holder_reg.is(receiver) || holder_reg.is(scratch1));
++
++ // Preserve the receiver register explicitly whenever it is different from
++ // the holder and it is needed should the interceptor return without any
++ // result. The CALLBACKS case needs the receiver to be passed into C++ code,
++ // the FIELD case might cause a miss during the prototype check.
++ bool must_perfrom_prototype_check = *interceptor_holder != lookup->holder();
++ bool must_preserve_receiver_reg = !receiver.is(holder_reg) &&
++ (lookup->type() == CALLBACKS || must_perfrom_prototype_check);
++
++ // Save necessary data before invoking an interceptor.
++ // Requires a frame to make GC aware of pushed pointers.
++ {
++ FrameScope frame_scope(masm(), StackFrame::INTERNAL);
++ if (must_preserve_receiver_reg) {
++ __ Push(receiver, holder_reg, name_reg);
++ } else {
++ __ Push(holder_reg, name_reg);
++ }
++ // Invoke an interceptor. Note: map checks from receiver to
++ // interceptor's holder has been compiled before (see a caller
++ // of this method.)
++ CompileCallLoadPropertyWithInterceptor(masm(),
++ receiver,
++ holder_reg,
++ name_reg,
++ interceptor_holder);
++ // Check if interceptor provided a value for property. If it's
++ // the case, return immediately.
++ Label interceptor_failed;
++ __ LoadRoot(scratch1, Heap::kNoInterceptorResultSentinelRootIndex);
++ __ cmp(r3, scratch1);
++ __ beq(&interceptor_failed);
++ frame_scope.GenerateLeaveFrame();
++ __ Ret();
++
++ __ bind(&interceptor_failed);
++ __ pop(name_reg);
++ __ pop(holder_reg);
++ if (must_preserve_receiver_reg) {
++ __ pop(receiver);
++ }
++ // Leave the internal frame.
++ }
++ // Check that the maps from interceptor's holder to lookup's holder
++ // haven't changed. And load lookup's holder into |holder| register.
++ if (must_perfrom_prototype_check) {
++ holder_reg = CheckPrototypes(interceptor_holder,
++ holder_reg,
++ Handle<JSObject>(lookup->holder()),
++ scratch1,
++ scratch2,
++ scratch3,
++ name,
++ miss);
++ }
++
++ if (lookup->IsField()) {
++ // We found FIELD property in prototype chain of interceptor's holder.
++ // Retrieve a field from field's holder.
++ GenerateFastPropertyLoad(masm(), r3, holder_reg,
++ Handle<JSObject>(lookup->holder()),
++ lookup->GetFieldIndex());
++ __ Ret();
++ } else {
++ // We found CALLBACKS property in prototype chain of interceptor's
++ // holder.
++ ASSERT(lookup->type() == CALLBACKS);
++ Handle<AccessorInfo> callback(
++ AccessorInfo::cast(lookup->GetCallbackObject()));
++ ASSERT(callback->getter() != NULL);
++
++ // Tail call to runtime.
++ // Important invariant in CALLBACKS case: the code above must be
++ // structured to never clobber |receiver| register.
++ __ Move(scratch2, callback);
++ // holder_reg is either receiver or scratch1.
++ if (!receiver.is(holder_reg)) {
++ ASSERT(scratch1.is(holder_reg));
++ __ Push(receiver, holder_reg);
++ } else {
++ __ push(receiver);
++ __ push(holder_reg);
++ }
++ __ LoadP(scratch3,
++ FieldMemOperand(scratch2, AccessorInfo::kDataOffset));
++ __ mov(scratch1, Operand(ExternalReference::isolate_address()));
++ __ Push(scratch3, scratch1, scratch2, name_reg);
++
++ ExternalReference ref =
++ ExternalReference(IC_Utility(IC::kLoadCallbackProperty),
++ masm()->isolate());
++ __ TailCallExternalReference(ref, 6, 1);
++ }
++ } else { // !compile_followup_inline
++ // Call the runtime system to load the interceptor.
++ // Check that the maps haven't changed.
++ Register holder_reg = CheckPrototypes(object, receiver, interceptor_holder,
++ scratch1, scratch2, scratch3,
++ name, miss);
++ PushInterceptorArguments(masm(), receiver, holder_reg,
++ name_reg, interceptor_holder);
++
++ ExternalReference ref =
++ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad),
++ masm()->isolate());
++ __ TailCallExternalReference(ref, 6, 1);
++ }
++}
++
++
++void CallStubCompiler::GenerateNameCheck(Handle<String> name, Label* miss) {
++ if (kind_ == Code::KEYED_CALL_IC) {
++ __ Cmpi(r5, Operand(name), r0);
++ __ bne(miss);
++ }
++}
++
++
++void CallStubCompiler::GenerateGlobalReceiverCheck(Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Handle<String> name,
++ Label* miss) {
++ ASSERT(holder->IsGlobalObject());
++
++ // Get the number of arguments.
++ const int argc = arguments().immediate();
++
++ // Get the receiver from the stack.
++ __ LoadP(r3, MemOperand(sp, argc * kPointerSize), r0);
++
++ // Check that the maps haven't changed.
++ __ JumpIfSmi(r3, miss);
++ CheckPrototypes(object, r3, holder, r6, r4, r7, name, miss);
++}
++
++
++void CallStubCompiler::GenerateLoadFunctionFromCell(
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Label* miss) {
++ // Get the value from the cell.
++ __ mov(r6, Operand(cell));
++ __ LoadP(r4, FieldMemOperand(r6, JSGlobalPropertyCell::kValueOffset));
++
++ // Check that the cell contains the same function.
++ if (heap()->InNewSpace(*function)) {
++ // We can't embed a pointer to a function in new space so we have
++ // to verify that the shared function info is unchanged. This has
++ // the nice side effect that multiple closures based on the same
++ // function can all use this call IC. Before we load through the
++ // function, we have to verify that it still is a function.
++ __ JumpIfSmi(r4, miss);
++ __ CompareObjectType(r4, r6, r6, JS_FUNCTION_TYPE);
++ __ bne(miss);
++
++ // Check the shared function info. Make sure it hasn't changed.
++ __ Move(r6, Handle<SharedFunctionInfo>(function->shared()));
++ __ LoadP(r7, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++ __ cmp(r7, r6);
++ } else {
++ __ mov(r6, Operand(function));
++ __ cmp(r4, r6);
++ }
++ __ bne(miss);
++}
++
++
++void CallStubCompiler::GenerateMissBranch() {
++ Handle<Code> code =
++ isolate()->stub_cache()->ComputeCallMiss(arguments().immediate(),
++ kind_,
++ extra_state_);
++ __ Jump(code, RelocInfo::CODE_TARGET);
++}
++
++
++Handle<Code> CallStubCompiler::CompileCallField(Handle<JSObject> object,
++ Handle<JSObject> holder,
++ int index,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ GenerateNameCheck(name, &miss);
++
++ const int argc = arguments().immediate();
++
++ // Get the receiver of the function from the stack into r3.
++ __ LoadP(r3, MemOperand(sp, argc * kPointerSize), r0);
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(r3, &miss);
++
++ // Do the right check and compute the holder register.
++ Register reg = CheckPrototypes(object, r3, holder, r4, r6, r7, name, &miss);
++ GenerateFastPropertyLoad(masm(), r4, reg, holder, index);
++
++ GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_);
++
++ // Handle call cache miss.
++ __ bind(&miss);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(Code::FIELD, name);
++}
++
++
++Handle<Code> CallStubCompiler::CompileArrayPushCall(
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
++ // -- ...
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++
++ // If object is not an array, bail out to regular call.
++ if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null();
++
++ Label miss;
++ GenerateNameCheck(name, &miss);
++
++ Register receiver = r4;
++ // Get the receiver from the stack
++ const int argc = arguments().immediate();
++ __ LoadP(receiver, MemOperand(sp, argc * kPointerSize), r0);
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, &miss);
++
++ // Check that the maps haven't changed.
++ CheckPrototypes(Handle<JSObject>::cast(object), receiver, holder, r6, r3, r7,
++ name, &miss);
++
++ if (argc == 0) {
++ // Nothing to do, just return the length.
++ __ LoadP(r3, FieldMemOperand(receiver, JSArray::kLengthOffset));
++ __ Drop(argc + 1);
++ __ Ret();
++ } else {
++ Label call_builtin;
++
++ if (argc == 1) { // Otherwise fall through to call the builtin.
++ Label attempt_to_grow_elements;
++
++ Register elements = r9;
++ Register end_elements = r8;
++ // Get the elements array of the object.
++ __ LoadP(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
++
++ // Check that the elements are in fast mode and writable.
++ __ CheckMap(elements,
++ r3,
++ Heap::kFixedArrayMapRootIndex,
++ &call_builtin,
++ DONT_DO_SMI_CHECK);
++
++
++ // Get the array's length into r3 and calculate new length.
++ __ LoadP(r3, FieldMemOperand(receiver, JSArray::kLengthOffset));
++ __ AddSmiLiteral(r3, r3, Smi::FromInt(argc), r0);
++
++ // Get the elements' length.
++ __ LoadP(r7, FieldMemOperand(elements, FixedArray::kLengthOffset));
++
++ // Check if we could survive without allocation.
++ __ cmp(r3, r7);
++ __ bgt(&attempt_to_grow_elements);
++
++ // Check if value is a smi.
++ Label with_write_barrier;
++ __ LoadP(r7, MemOperand(sp, (argc - 1) * kPointerSize), r0);
++ __ JumpIfNotSmi(r7, &with_write_barrier);
++
++ // Save new length.
++ __ StoreP(r3, FieldMemOperand(receiver, JSArray::kLengthOffset), r0);
++
++ // Store the value.
++ // We may need a register containing the address end_elements below,
++ // so write back the value in end_elements.
++ __ SmiToPtrArrayOffset(end_elements, r3);
++ __ add(end_elements, elements, end_elements);
++ const int kEndElementsOffset =
++ FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize;
++ __ Add(end_elements, end_elements, kEndElementsOffset, r0);
++ __ StoreP(r7, MemOperand(end_elements));
++
++ // Check for a smi.
++ __ Drop(argc + 1);
++ __ Ret();
++
++ __ bind(&with_write_barrier);
++
++ __ LoadP(r6, FieldMemOperand(receiver, HeapObject::kMapOffset));
++
++ if (FLAG_smi_only_arrays && !FLAG_trace_elements_transitions) {
++ Label fast_object, not_fast_object;
++ __ CheckFastObjectElements(r6, r10, ¬_fast_object);
++ __ b(&fast_object);
++ // In case of fast smi-only, convert to fast object, otherwise bail out.
++ __ bind(¬_fast_object);
++ __ CheckFastSmiElements(r6, r10, &call_builtin);
++ // r4: receiver
++ // r6: map
++ Label try_holey_map;
++ __ LoadTransitionedArrayMapConditional(FAST_SMI_ELEMENTS,
++ FAST_ELEMENTS,
++ r6,
++ r10,
++ &try_holey_map);
++ __ mr(r5, receiver);
++ ElementsTransitionGenerator::
++ GenerateMapChangeElementsTransition(masm());
++ __ b(&fast_object);
++
++ __ bind(&try_holey_map);
++ __ LoadTransitionedArrayMapConditional(FAST_HOLEY_SMI_ELEMENTS,
++ FAST_HOLEY_ELEMENTS,
++ r6,
++ r10,
++ &call_builtin);
++ __ mr(r5, receiver);
++ ElementsTransitionGenerator::
++ GenerateMapChangeElementsTransition(masm());
++ __ bind(&fast_object);
++ } else {
++ __ CheckFastObjectElements(r6, r6, &call_builtin);
++ }
++
++ // Save new length.
++ __ StoreP(r3, FieldMemOperand(receiver, JSArray::kLengthOffset), r0);
++
++ // Store the value.
++ // We may need a register containing the address end_elements below,
++ // so write back the value in end_elements.
++ __ SmiToPtrArrayOffset(end_elements, r3);
++ __ add(end_elements, elements, end_elements);
++ __ Add(end_elements, end_elements, kEndElementsOffset, r0);
++ __ StoreP(r7, MemOperand(end_elements));
++
++ __ RecordWrite(elements,
++ end_elements,
++ r7,
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET,
++ OMIT_SMI_CHECK);
++ __ Drop(argc + 1);
++ __ Ret();
++
++ __ bind(&attempt_to_grow_elements);
++ // r3: array's length + 1.
++ // r7: elements' length.
++
++ if (!FLAG_inline_new) {
++ __ b(&call_builtin);
++ }
++
++ __ LoadP(r5, MemOperand(sp, (argc - 1) * kPointerSize), r0);
++ // Growing elements that are SMI-only requires special handling in case
++ // the new element is non-Smi. For now, delegate to the builtin.
++ Label no_fast_elements_check;
++ __ JumpIfSmi(r5, &no_fast_elements_check);
++ __ LoadP(r10, FieldMemOperand(receiver, HeapObject::kMapOffset));
++ __ CheckFastObjectElements(r10, r10, &call_builtin);
++ __ bind(&no_fast_elements_check);
++
++ Isolate* isolate = masm()->isolate();
++ ExternalReference new_space_allocation_top =
++ ExternalReference::new_space_allocation_top_address(isolate);
++ ExternalReference new_space_allocation_limit =
++ ExternalReference::new_space_allocation_limit_address(isolate);
++
++ const int kAllocationDelta = 4;
++ // Load top and check if it is the end of elements.
++ __ SmiToPtrArrayOffset(end_elements, r3);
++ __ add(end_elements, elements, end_elements);
++ __ Add(end_elements, end_elements, kEndElementsOffset, r0);
++ __ mov(r10, Operand(new_space_allocation_top));
++ __ LoadP(r6, MemOperand(r10));
++ __ cmp(end_elements, r6);
++ __ bne(&call_builtin);
++
++ __ mov(r22, Operand(new_space_allocation_limit));
++ __ LoadP(r22, MemOperand(r22));
++ __ addi(r6, r6, Operand(kAllocationDelta * kPointerSize));
++ __ cmpl(r6, r22);
++ __ bgt(&call_builtin);
++
++ // We fit and could grow elements.
++ // Update new_space_allocation_top.
++ __ StoreP(r6, MemOperand(r10));
++ // Push the argument.
++ __ StoreP(r5, MemOperand(end_elements));
++ // Fill the rest with holes.
++ __ LoadRoot(r6, Heap::kTheHoleValueRootIndex);
++ for (int i = 1; i < kAllocationDelta; i++) {
++ __ StoreP(r6, MemOperand(end_elements, i * kPointerSize), r0);
++ }
++
++ // Update elements' and array's sizes.
++ __ StoreP(r3, FieldMemOperand(receiver, JSArray::kLengthOffset), r0);
++ __ AddSmiLiteral(r7, r7, Smi::FromInt(kAllocationDelta), r0);
++ __ StoreP(r7, FieldMemOperand(elements, FixedArray::kLengthOffset), r0);
++
++ // Elements are in new space, so write barrier is not required.
++ __ Drop(argc + 1);
++ __ Ret();
++ }
++ __ bind(&call_builtin);
++ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPush,
++ masm()->isolate()),
++ argc + 1,
++ 1);
++ }
++
++ // Handle call cache miss.
++ __ bind(&miss);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(function);
++}
++
++
++Handle<Code> CallStubCompiler::CompileArrayPopCall(
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
++ // -- ...
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++
++ // If object is not an array, bail out to regular call.
++ if (!object->IsJSArray() || !cell.is_null()) return Handle<Code>::null();
++
++ Label miss, return_undefined, call_builtin;
++ Register receiver = r4;
++ Register elements = r6;
++ GenerateNameCheck(name, &miss);
++
++ // Get the receiver from the stack
++ const int argc = arguments().immediate();
++ __ LoadP(receiver, MemOperand(sp, argc * kPointerSize), r0);
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(receiver, &miss);
++
++ // Check that the maps haven't changed.
++ CheckPrototypes(Handle<JSObject>::cast(object), receiver, holder, elements,
++ r7, r3, name, &miss);
++
++ // Get the elements array of the object.
++ __ LoadP(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
++
++ // Check that the elements are in fast mode and writable.
++ __ CheckMap(elements,
++ r3,
++ Heap::kFixedArrayMapRootIndex,
++ &call_builtin,
++ DONT_DO_SMI_CHECK);
++
++ // Get the array's length into r7 and calculate new length.
++ __ LoadP(r7, FieldMemOperand(receiver, JSArray::kLengthOffset));
++ __ SubSmiLiteral(r7, r7, Smi::FromInt(1), r0);
++ __ cmpi(r7, Operand::Zero());
++ __ blt(&return_undefined);
++
++ // Get the last element.
++ __ LoadRoot(r9, Heap::kTheHoleValueRootIndex);
++ // We can't address the last element in one operation. Compute the more
++ // expensive shift first, and use an offset later on.
++ __ SmiToPtrArrayOffset(r3, r7);
++ __ add(elements, elements, r3);
++ __ LoadP(r3, FieldMemOperand(elements, FixedArray::kHeaderSize));
++ __ cmp(r3, r9);
++ __ beq(&call_builtin);
++
++ // Set the array's length.
++ __ StoreP(r7, FieldMemOperand(receiver, JSArray::kLengthOffset), r0);
++
++ // Fill with the hole.
++ __ StoreP(r9, FieldMemOperand(elements, FixedArray::kHeaderSize), r0);
++ __ Drop(argc + 1);
++ __ Ret();
++
++ __ bind(&return_undefined);
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ __ Drop(argc + 1);
++ __ Ret();
++
++ __ bind(&call_builtin);
++ __ TailCallExternalReference(ExternalReference(Builtins::c_ArrayPop,
++ masm()->isolate()),
++ argc + 1,
++ 1);
++
++ // Handle call cache miss.
++ __ bind(&miss);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(function);
++}
++
++
++Handle<Code> CallStubCompiler::CompileStringCharCodeAtCall(
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : function name
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
++ // -- ...
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++
++ // If object is not a string, bail out to regular call.
++ if (!object->IsString() || !cell.is_null()) return Handle<Code>::null();
++
++ const int argc = arguments().immediate();
++ Label miss;
++ Label name_miss;
++ Label index_out_of_range;
++ Label* index_out_of_range_label = &index_out_of_range;
++
++ if (kind_ == Code::CALL_IC &&
++ (CallICBase::StringStubState::decode(extra_state_) ==
++ DEFAULT_STRING_STUB)) {
++ index_out_of_range_label = &miss;
++ }
++ GenerateNameCheck(name, &name_miss);
++
++ // Check that the maps starting from the prototype haven't changed.
++ GenerateDirectLoadGlobalFunctionPrototype(masm(),
++ Context::STRING_FUNCTION_INDEX,
++ r3,
++ &miss);
++ ASSERT(!object.is_identical_to(holder));
++ CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())),
++ r3, holder, r4, r6, r7, name, &miss);
++
++ Register receiver = r4;
++ Register index = r7;
++ Register result = r3;
++ __ LoadP(receiver, MemOperand(sp, argc * kPointerSize), r0);
++ if (argc > 0) {
++ __ LoadP(index, MemOperand(sp, (argc - 1) * kPointerSize), r0);
++ } else {
++ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
++ }
++
++ StringCharCodeAtGenerator generator(receiver,
++ index,
++ result,
++ &miss, // When not a string.
++ &miss, // When not a number.
++ index_out_of_range_label,
++ STRING_INDEX_IS_NUMBER);
++ generator.GenerateFast(masm());
++ __ Drop(argc + 1);
++ __ Ret();
++
++ StubRuntimeCallHelper call_helper;
++ generator.GenerateSlow(masm(), call_helper);
++
++ if (index_out_of_range.is_linked()) {
++ __ bind(&index_out_of_range);
++ __ LoadRoot(r3, Heap::kNanValueRootIndex);
++ __ Drop(argc + 1);
++ __ Ret();
++ }
++
++ __ bind(&miss);
++ // Restore function name in r5.
++ __ Move(r5, name);
++ __ bind(&name_miss);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(function);
++}
++
++
++Handle<Code> CallStubCompiler::CompileStringCharAtCall(
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : function name
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
++ // -- ...
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++
++ // If object is not a string, bail out to regular call.
++ if (!object->IsString() || !cell.is_null()) return Handle<Code>::null();
++
++ const int argc = arguments().immediate();
++ Label miss;
++ Label name_miss;
++ Label index_out_of_range;
++ Label* index_out_of_range_label = &index_out_of_range;
++ if (kind_ == Code::CALL_IC &&
++ (CallICBase::StringStubState::decode(extra_state_) ==
++ DEFAULT_STRING_STUB)) {
++ index_out_of_range_label = &miss;
++ }
++ GenerateNameCheck(name, &name_miss);
++
++ // Check that the maps starting from the prototype haven't changed.
++ GenerateDirectLoadGlobalFunctionPrototype(masm(),
++ Context::STRING_FUNCTION_INDEX,
++ r3,
++ &miss);
++ ASSERT(!object.is_identical_to(holder));
++ CheckPrototypes(Handle<JSObject>(JSObject::cast(object->GetPrototype())),
++ r3, holder, r4, r6, r7, name, &miss);
++
++ Register receiver = r3;
++ Register index = r7;
++ Register scratch = r6;
++ Register result = r3;
++ __ LoadP(receiver, MemOperand(sp, argc * kPointerSize), r0);
++ if (argc > 0) {
++ __ LoadP(index, MemOperand(sp, (argc - 1) * kPointerSize), r0);
++ } else {
++ __ LoadRoot(index, Heap::kUndefinedValueRootIndex);
++ }
++
++ StringCharAtGenerator generator(receiver,
++ index,
++ scratch,
++ result,
++ &miss, // When not a string.
++ &miss, // When not a number.
++ index_out_of_range_label,
++ STRING_INDEX_IS_NUMBER);
++ generator.GenerateFast(masm());
++ __ Drop(argc + 1);
++ __ Ret();
++
++ StubRuntimeCallHelper call_helper;
++ generator.GenerateSlow(masm(), call_helper);
++
++ if (index_out_of_range.is_linked()) {
++ __ bind(&index_out_of_range);
++ __ LoadRoot(r3, Heap::kEmptyStringRootIndex);
++ __ Drop(argc + 1);
++ __ Ret();
++ }
++
++ __ bind(&miss);
++ // Restore function name in r5.
++ __ Move(r5, name);
++ __ bind(&name_miss);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(function);
++}
++
++
++Handle<Code> CallStubCompiler::CompileStringFromCharCodeCall(
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : function name
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
++ // -- ...
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++
++ const int argc = arguments().immediate();
++
++ // If the object is not a JSObject or we got an unexpected number of
++ // arguments, bail out to the regular call.
++ if (!object->IsJSObject() || argc != 1) return Handle<Code>::null();
++
++ Label miss;
++ GenerateNameCheck(name, &miss);
++
++ if (cell.is_null()) {
++ __ LoadP(r4, MemOperand(sp, 1 * kPointerSize));
++
++ STATIC_ASSERT(kSmiTag == 0);
++ __ JumpIfSmi(r4, &miss);
++
++ CheckPrototypes(Handle<JSObject>::cast(object), r4, holder, r3, r6, r7,
++ name, &miss);
++ } else {
++ ASSERT(cell->value() == *function);
++ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
++ &miss);
++ GenerateLoadFunctionFromCell(cell, function, &miss);
++ }
++
++ // Load the char code argument.
++ Register code = r4;
++ __ LoadP(code, MemOperand(sp, 0 * kPointerSize));
++
++ // Check the code is a smi.
++ Label slow;
++ STATIC_ASSERT(kSmiTag == 0);
++ __ JumpIfNotSmi(code, &slow);
++
++ // Convert the smi code to uint16.
++ __ LoadSmiLiteral(r0, Smi::FromInt(0xffff));
++ __ and_(code, code, r0);
++
++ StringCharFromCodeGenerator generator(code, r3);
++ generator.GenerateFast(masm());
++ __ Drop(argc + 1);
++ __ Ret();
++
++ StubRuntimeCallHelper call_helper;
++ generator.GenerateSlow(masm(), call_helper);
++
++ // Tail call the full function. We do not have to patch the receiver
++ // because the function makes no use of it.
++ __ bind(&slow);
++ __ InvokeFunction(
++ function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
++
++ __ bind(&miss);
++ // r5: function name.
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return cell.is_null() ? GetCode(function) : GetCode(Code::NORMAL, name);
++}
++
++
++Handle<Code> CallStubCompiler::CompileMathFloorCall(
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : function name
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
++ // -- ...
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++
++ const int argc = arguments().immediate();
++ // If the object is not a JSObject or we got an unexpected number of
++ // arguments, bail out to the regular call.
++ if (!object->IsJSObject() || argc != 1) return Handle<Code>::null();
++
++ Label miss, slow, not_smi, positive, drop_arg_return;
++ GenerateNameCheck(name, &miss);
++
++ if (cell.is_null()) {
++ __ LoadP(r4, MemOperand(sp, 1 * kPointerSize));
++ STATIC_ASSERT(kSmiTag == 0);
++ __ JumpIfSmi(r4, &miss);
++ CheckPrototypes(Handle<JSObject>::cast(object), r4, holder, r3, r6, r7,
++ name, &miss);
++ } else {
++ ASSERT(cell->value() == *function);
++ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
++ &miss);
++ GenerateLoadFunctionFromCell(cell, function, &miss);
++ }
++
++ // Load the (only) argument into r3.
++ __ LoadP(r3, MemOperand(sp, 0 * kPointerSize));
++
++ // If the argument is a smi, just return.
++ STATIC_ASSERT(kSmiTag == 0);
++ __ andi(r0, r3, Operand(kSmiTagMask));
++ __ bne(¬_smi, cr0);
++ __ Drop(argc + 1);
++ __ Ret();
++ __ bind(¬_smi);
++
++ __ CheckMap(r3, r4, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
++
++ // Load the HeapNumber value.
++ __ lfd(d1, FieldMemOperand(r3, HeapNumber::kValueOffset));
++
++ // Round to integer minus
++ if (CpuFeatures::IsSupported(FPU)) {
++ // The frim instruction is only supported on POWER5
++ // and higher
++ __ frim(d1, d1);
++#if V8_TARGET_ARCH_PPC64
++ __ fctidz(d1, d1);
++#else
++ __ fctiwz(d1, d1);
++#endif
++ } else {
++ // This sequence is more portable (avoids frim)
++ // This should be evaluated to determine if frim provides any
++ // perf benefit or if we can simply use the compatible sequence
++ // always
++ __ SetRoundingMode(kRoundToMinusInf);
++#if V8_TARGET_ARCH_PPC64
++ __ fctid(d1, d1);
++#else
++ __ fctiw(d1, d1);
++#endif
++ __ ResetRoundingMode();
++ }
++ // Convert the argument to an integer.
++ __ stfdu(d1, MemOperand(sp, -8));
++#if V8_TARGET_ARCH_PPC64
++ __ ld(r3, MemOperand(sp, 0));
++#else
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(r3, MemOperand(sp, 0));
++#else
++ __ lwz(r3, MemOperand(sp, 4));
++#endif
++#endif
++ __ addi(sp, sp, Operand(8));
++
++ // if resulting conversion is negative, invert for bit tests
++ __ TestSignBit(r3, r0);
++ __ mr(r0, r3);
++ __ beq(&positive, cr0);
++ __ neg(r0, r3);
++ __ bind(&positive);
++
++ // if any of the high bits are set, fail to generic
++ __ JumpIfNotUnsignedSmiCandidate(r0, r0, &slow);
++
++ // Tag the result.
++ STATIC_ASSERT(kSmiTag == 0);
++ __ SmiTag(r3);
++
++ // Check for -0
++ __ cmpi(r3, Operand::Zero());
++ __ bne(&drop_arg_return);
++
++ __ LoadP(r4, MemOperand(sp, 0 * kPointerSize));
++ __ lwz(r4, FieldMemOperand(r4, HeapNumber::kExponentOffset));
++ __ TestSignBit32(r4, r0);
++ __ beq(&drop_arg_return, cr0);
++ // If our HeapNumber is negative it was -0, so load its address and return.
++ __ LoadP(r3, MemOperand(sp));
++
++ __ bind(&drop_arg_return);
++ __ Drop(argc + 1);
++ __ Ret();
++
++ __ bind(&slow);
++ // Tail call the full function. We do not have to patch the receiver
++ // because the function makes no use of it.
++ __ InvokeFunction(
++ function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
++
++ __ bind(&miss);
++ // r5: function name.
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return cell.is_null() ? GetCode(function) : GetCode(Code::NORMAL, name);
++}
++
++
++Handle<Code> CallStubCompiler::CompileMathAbsCall(
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : function name
++ // -- lr : return address
++ // -- sp[(argc - n - 1) * 4] : arg[n] (zero-based)
++ // -- ...
++ // -- sp[argc * 4] : receiver
++ // -----------------------------------
++
++ const int argc = arguments().immediate();
++ // If the object is not a JSObject or we got an unexpected number of
++ // arguments, bail out to the regular call.
++ if (!object->IsJSObject() || argc != 1) return Handle<Code>::null();
++
++ Label miss;
++ GenerateNameCheck(name, &miss);
++ if (cell.is_null()) {
++ __ LoadP(r4, MemOperand(sp, 1 * kPointerSize));
++ STATIC_ASSERT(kSmiTag == 0);
++ __ JumpIfSmi(r4, &miss);
++ CheckPrototypes(Handle<JSObject>::cast(object), r4, holder, r3, r6, r7,
++ name, &miss);
++ } else {
++ ASSERT(cell->value() == *function);
++ GenerateGlobalReceiverCheck(Handle<JSObject>::cast(object), holder, name,
++ &miss);
++ GenerateLoadFunctionFromCell(cell, function, &miss);
++ }
++
++ // Load the (only) argument into r3.
++ __ LoadP(r3, MemOperand(sp, 0 * kPointerSize));
++
++ // Check if the argument is a smi.
++ Label not_smi;
++ STATIC_ASSERT(kSmiTag == 0);
++ __ JumpIfNotSmi(r3, ¬_smi);
++
++ // Do bitwise not or do nothing depending on the sign of the
++ // argument.
++ __ ShiftRightArithImm(r0, r3, kBitsPerPointer - 1);
++ __ xor_(r4, r3, r0);
++
++ // Add 1 or do nothing depending on the sign of the argument.
++ __ sub(r3, r4, r0, LeaveOE, SetRC);
++
++ // If the result is still negative, go to the slow case.
++ // This only happens for the most negative smi.
++ Label slow;
++ __ blt(&slow, cr0);
++
++ // Smi case done.
++ __ Drop(argc + 1);
++ __ Ret();
++
++ // Check if the argument is a heap number and load its exponent and
++ // sign.
++ __ bind(¬_smi);
++ __ CheckMap(r3, r4, Heap::kHeapNumberMapRootIndex, &slow, DONT_DO_SMI_CHECK);
++ __ lwz(r4, FieldMemOperand(r3, HeapNumber::kExponentOffset));
++
++ // Check the sign of the argument. If the argument is positive,
++ // just return it.
++ Label negative_sign;
++ __ andis(r0, r4, Operand(HeapNumber::kSignMask >> 16));
++ __ bne(&negative_sign, cr0);
++ __ Drop(argc + 1);
++ __ Ret();
++
++ // If the argument is negative, clear the sign, and return a new
++ // number.
++ __ bind(&negative_sign);
++ STATIC_ASSERT(HeapNumber::kSignMask == 0x80000000u);
++ __ xoris(r4, r4, Operand(HeapNumber::kSignMask >> 16));
++ __ lwz(r6, FieldMemOperand(r3, HeapNumber::kMantissaOffset));
++ __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r3, r7, r8, r9, &slow);
++ __ stw(r4, FieldMemOperand(r3, HeapNumber::kExponentOffset));
++ __ stw(r6, FieldMemOperand(r3, HeapNumber::kMantissaOffset));
++ __ Drop(argc + 1);
++ __ Ret();
++
++ // Tail call the full function. We do not have to patch the receiver
++ // because the function makes no use of it.
++ __ bind(&slow);
++ __ InvokeFunction(
++ function, arguments(), JUMP_FUNCTION, NullCallWrapper(), CALL_AS_METHOD);
++
++ __ bind(&miss);
++ // r5: function name.
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return cell.is_null() ? GetCode(function) : GetCode(Code::NORMAL, name);
++}
++
++
++Handle<Code> CallStubCompiler::CompileFastApiCall(
++ const CallOptimization& optimization,
++ Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ Counters* counters = isolate()->counters();
++
++ ASSERT(optimization.is_simple_api_call());
++ // Bail out if object is a global object as we don't want to
++ // repatch it to global receiver.
++ if (object->IsGlobalObject()) return Handle<Code>::null();
++ if (!cell.is_null()) return Handle<Code>::null();
++ if (!object->IsJSObject()) return Handle<Code>::null();
++ int depth = optimization.GetPrototypeDepthOfExpectedType(
++ Handle<JSObject>::cast(object), holder);
++ if (depth == kInvalidProtoDepth) return Handle<Code>::null();
++
++ Label miss, miss_before_stack_reserved;
++ GenerateNameCheck(name, &miss_before_stack_reserved);
++
++ // Get the receiver from the stack.
++ const int argc = arguments().immediate();
++ __ LoadP(r4, MemOperand(sp, argc * kPointerSize), r0);
++
++ // Check that the receiver isn't a smi.
++ __ JumpIfSmi(r4, &miss_before_stack_reserved);
++
++ __ IncrementCounter(counters->call_const(), 1, r3, r6);
++ __ IncrementCounter(counters->call_const_fast_api(), 1, r3, r6);
++
++ ReserveSpaceForFastApiCall(masm(), r3);
++
++ // Check that the maps haven't changed and find a Holder as a side effect.
++ CheckPrototypes(Handle<JSObject>::cast(object), r4, holder, r3, r6, r7, name,
++ depth, &miss);
++
++ GenerateFastApiDirectCall(masm(), optimization, argc);
++
++ __ bind(&miss);
++ FreeSpaceForFastApiCall(masm());
++
++ __ bind(&miss_before_stack_reserved);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(function);
++}
++
++
++Handle<Code> CallStubCompiler::CompileCallConstant(Handle<Object> object,
++ Handle<JSObject> holder,
++ Handle<JSFunction> function,
++ Handle<String> name,
++ CheckType check) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ if (HasCustomCallGenerator(function)) {
++ Handle<Code> code = CompileCustomCall(object, holder,
++ Handle<JSGlobalPropertyCell>::null(),
++ function, name);
++ // A null handle means bail out to the regular compiler code below.
++ if (!code.is_null()) return code;
++ }
++
++ Label miss;
++ GenerateNameCheck(name, &miss);
++
++ // Get the receiver from the stack
++ const int argc = arguments().immediate();
++ __ LoadP(r4, MemOperand(sp, argc * kPointerSize), r0);
++
++ // Check that the receiver isn't a smi.
++ if (check != NUMBER_CHECK) {
++ __ JumpIfSmi(r4, &miss);
++ }
++
++ // Make sure that it's okay not to patch the on stack receiver
++ // unless we're doing a receiver map check.
++ ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
++ switch (check) {
++ case RECEIVER_MAP_CHECK:
++ __ IncrementCounter(masm()->isolate()->counters()->call_const(),
++ 1, r3, r6);
++
++ // Check that the maps haven't changed.
++ CheckPrototypes(Handle<JSObject>::cast(object), r4, holder, r3, r6, r7,
++ name, &miss);
++
++ // Patch the receiver on the stack with the global proxy if
++ // necessary.
++ if (object->IsGlobalObject()) {
++ __ LoadP(r6, FieldMemOperand(r4, GlobalObject::kGlobalReceiverOffset));
++ __ StoreP(r6, MemOperand(sp, argc * kPointerSize));
++ }
++ break;
++
++ case STRING_CHECK:
++ if (function->IsBuiltin() || !function->shared()->is_classic_mode()) {
++ // Check that the object is a two-byte string or a symbol.
++ __ CompareObjectType(r4, r6, r6, FIRST_NONSTRING_TYPE);
++ __ bge(&miss);
++ // Check that the maps starting from the prototype haven't changed.
++ GenerateDirectLoadGlobalFunctionPrototype(
++ masm(), Context::STRING_FUNCTION_INDEX, r3, &miss);
++ CheckPrototypes(
++ Handle<JSObject>(JSObject::cast(object->GetPrototype())),
++ r3, holder, r6, r4, r7, name, &miss);
++ } else {
++ // Calling non-strict non-builtins with a value as the receiver
++ // requires boxing.
++ __ b(&miss);
++ }
++ break;
++
++ case NUMBER_CHECK:
++ if (function->IsBuiltin() || !function->shared()->is_classic_mode()) {
++ Label fast;
++ // Check that the object is a smi or a heap number.
++ __ JumpIfSmi(r4, &fast);
++ __ CompareObjectType(r4, r3, r3, HEAP_NUMBER_TYPE);
++ __ bne(&miss);
++ __ bind(&fast);
++ // Check that the maps starting from the prototype haven't changed.
++ GenerateDirectLoadGlobalFunctionPrototype(
++ masm(), Context::NUMBER_FUNCTION_INDEX, r3, &miss);
++ CheckPrototypes(
++ Handle<JSObject>(JSObject::cast(object->GetPrototype())),
++ r3, holder, r6, r4, r7, name, &miss);
++ } else {
++ // Calling non-strict non-builtins with a value as the receiver
++ // requires boxing.
++ __ b(&miss);
++ }
++ break;
++
++ case BOOLEAN_CHECK:
++ if (function->IsBuiltin() || !function->shared()->is_classic_mode()) {
++ Label fast;
++ // Check that the object is a boolean.
++ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
++ __ cmp(r4, ip);
++ __ beq(&fast);
++ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
++ __ cmp(r4, ip);
++ __ bne(&miss);
++ __ bind(&fast);
++ // Check that the maps starting from the prototype haven't changed.
++ GenerateDirectLoadGlobalFunctionPrototype(
++ masm(), Context::BOOLEAN_FUNCTION_INDEX, r3, &miss);
++ CheckPrototypes(
++ Handle<JSObject>(JSObject::cast(object->GetPrototype())),
++ r3, holder, r6, r4, r7, name, &miss);
++ } else {
++ // Calling non-strict non-builtins with a value as the receiver
++ // requires boxing.
++ __ b(&miss);
++ }
++ break;
++ }
++
++ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
++ ? CALL_AS_FUNCTION
++ : CALL_AS_METHOD;
++ __ InvokeFunction(
++ function, arguments(), JUMP_FUNCTION, NullCallWrapper(), call_kind);
++
++ // Handle call cache miss.
++ __ bind(&miss);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(function);
++}
++
++
++Handle<Code> CallStubCompiler::CompileCallInterceptor(Handle<JSObject>
object,
++ Handle<JSObject> holder,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++ GenerateNameCheck(name, &miss);
++
++ // Get the number of arguments.
++ const int argc = arguments().immediate();
++ LookupResult lookup(isolate());
++ LookupPostInterceptor(holder, name, &lookup);
++
++ // Get the receiver from the stack.
++ __ LoadP(r4, MemOperand(sp, argc * kPointerSize), r0);
++
++ CallInterceptorCompiler compiler(this, arguments(), r5, extra_state_);
++ compiler.Compile(masm(), object, holder, name, &lookup, r4, r6, r7, r3,
++ &miss);
++
++ // Move returned value, the function to call, to r4.
++ __ mr(r4, r3);
++ // Restore receiver.
++ __ LoadP(r3, MemOperand(sp, argc * kPointerSize), r0);
++
++ GenerateCallFunction(masm(), object, arguments(), &miss, extra_state_);
++
++ // Handle call cache miss.
++ __ bind(&miss);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(Code::INTERCEPTOR, name);
++}
++
++
++Handle<Code> CallStubCompiler::CompileCallGlobal(
++ Handle<JSObject> object,
++ Handle<GlobalObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<JSFunction> function,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ if (HasCustomCallGenerator(function)) {
++ Handle<Code> code = CompileCustomCall(object, holder, cell, function, name);
++ // A null handle means bail out to the regular compiler code below.
++ if (!code.is_null()) return code;
++ }
++
++ Label miss;
++ GenerateNameCheck(name, &miss);
++
++ // Get the number of arguments.
++ const int argc = arguments().immediate();
++ GenerateGlobalReceiverCheck(object, holder, name, &miss);
++ GenerateLoadFunctionFromCell(cell, function, &miss);
++
++ // Patch the receiver on the stack with the global proxy if
++ // necessary.
++ if (object->IsGlobalObject()) {
++ __ LoadP(r6, FieldMemOperand(r3, GlobalObject::kGlobalReceiverOffset));
++ __ StoreP(r6, MemOperand(sp, argc * kPointerSize), r0);
++ }
++
++ // Set up the context (function already in r4).
++ __ LoadP(cp, FieldMemOperand(r4, JSFunction::kContextOffset));
++
++ // Jump to the cached code (tail call).
++ Counters* counters = masm()->isolate()->counters();
++ __ IncrementCounter(counters->call_global_inline(), 1, r6, r7);
++ ParameterCount expected(function->shared()->formal_parameter_count());
++ CallKind call_kind = CallICBase::Contextual::decode(extra_state_)
++ ? CALL_AS_FUNCTION
++ : CALL_AS_METHOD;
++ // We call indirectly through the code field in the function to
++ // allow recompilation to take effect without changing any of the
++ // call sites.
++ __ LoadP(r6, FieldMemOperand(r4, JSFunction::kCodeEntryOffset));
++ __ InvokeCode(r6, expected, arguments(), JUMP_FUNCTION,
++ NullCallWrapper(), call_kind);
++
++ // Handle call cache miss.
++ __ bind(&miss);
++ __ IncrementCounter(counters->call_global_inline_miss(), 1, r4, r6);
++ GenerateMissBranch();
++
++ // Return the generated code.
++ return GetCode(Code::NORMAL, name);
++}
++
++
++Handle<Code> StoreStubCompiler::CompileStoreField(Handle<JSObject> object,
++ int index,
++ Handle<Map> transition,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ GenerateStoreField(masm(),
++ object,
++ index,
++ transition,
++ name,
++ r4, r5, r6, r7,
++ &miss);
++ __ bind(&miss);
++ Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(transition.is_null()
++ ? Code::FIELD
++ : Code::MAP_TRANSITION, name);
++}
++
++
++Handle<Code> StoreStubCompiler::CompileStoreCallback(
++ Handle<String> name,
++ Handle<JSObject> receiver,
++ Handle<JSObject> holder,
++ Handle<AccessorInfo> callback) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++ // Check that the maps haven't changed.
++ __ JumpIfSmi(r4, &miss);
++ CheckPrototypes(receiver, r4, holder, r6, r7, r8, name, &miss);
++
++ // Stub never generated for non-global objects that require access checks.
++ ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
++
++ __ push(r4); // receiver
++ __ mov(ip, Operand(callback)); // callback info
++ __ Push(ip, r5, r3);
++
++ // Do tail-call to the runtime system.
++ ExternalReference store_callback_property =
++ ExternalReference(IC_Utility(IC::kStoreCallbackProperty),
++ masm()->isolate());
++ __ TailCallExternalReference(store_callback_property, 4, 1);
++
++ // Handle store cache miss.
++ __ bind(&miss);
++ Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++#undef __
++#define __ ACCESS_MASM(masm)
++
++
++void StoreStubCompiler::GenerateStoreViaSetter(
++ MacroAssembler* masm,
++ Handle<JSFunction> setter) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ // Save value register, so we can restore it later.
++ __ push(r3);
++
++ if (!setter.is_null()) {
++ // Call the JavaScript setter with receiver and value on the stack.
++ __ Push(r4, r3);
++ ParameterCount actual(1);
++ __ InvokeFunction(setter, actual, CALL_FUNCTION, NullCallWrapper(),
++ CALL_AS_METHOD);
++ } else {
++ // If we generate a global code snippet for deoptimization only, remember
++ // the place to continue after deoptimization.
++
masm->isolate()->heap()->SetSetterStubDeoptPCOffset(masm->pc_offset());
++ }
++
++ // We have to return the passed value, not the return value of the setter.
++ __ pop(r3);
++
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ }
++ __ Ret();
++}
++
++
++#undef __
++#define __ ACCESS_MASM(masm())
++
++
++Handle<Code> StoreStubCompiler::CompileStoreViaSetter(
++ Handle<String> name,
++ Handle<JSObject> receiver,
++ Handle<JSObject> holder,
++ Handle<JSFunction> setter) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ // Check that the maps haven't changed.
++ __ JumpIfSmi(r4, &miss);
++ CheckPrototypes(receiver, r4, holder, r6, r7, r8, name, &miss);
++
++ GenerateStoreViaSetter(masm(), setter);
++
++ __ bind(&miss);
++ Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++Handle<Code> StoreStubCompiler::CompileStoreInterceptor(
++ Handle<JSObject> receiver,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ // Check that the map of the object hasn't changed.
++ __ CheckMap(r4, r6, Handle<Map>(receiver->map()), &miss,
++ DO_SMI_CHECK, ALLOW_ELEMENT_TRANSITION_MAPS);
++
++ // Perform global security token check if needed.
++ if (receiver->IsJSGlobalProxy()) {
++ __ CheckAccessGlobalProxy(r4, r6, &miss);
++ }
++
++ // Stub is never generated for non-global objects that require access
++ // checks.
++ ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
++
++ __ Push(r4, r5, r3); // Receiver, name, value.
++
++ __ LoadSmiLiteral(r3, Smi::FromInt(strict_mode_));
++ __ push(r3); // strict mode
++
++ // Do tail-call to the runtime system.
++ ExternalReference store_ic_property =
++ ExternalReference(IC_Utility(IC::kStoreInterceptorProperty),
++ masm()->isolate());
++ __ TailCallExternalReference(store_ic_property, 4, 1);
++
++ // Handle store cache miss.
++ __ bind(&miss);
++ Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(Code::INTERCEPTOR, name);
++}
++
++
++Handle<Code> StoreStubCompiler::CompileStoreGlobal(
++ Handle<GlobalObject> object,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ // Check that the map of the global has not changed.
++ __ LoadP(r6, FieldMemOperand(r4, HeapObject::kMapOffset));
++ __ mov(r7, Operand(Handle<Map>(object->map())));
++ __ cmp(r6, r7);
++ __ bne(&miss);
++
++ // Check that the value in the cell is not the hole. If it is, this
++ // cell could have been deleted and reintroducing the global needs
++ // to update the property details in the property dictionary of the
++ // global object. We bail out to the runtime system to do that.
++ __ mov(r7, Operand(cell));
++ __ LoadRoot(r8, Heap::kTheHoleValueRootIndex);
++ __ LoadP(r9, FieldMemOperand(r7, JSGlobalPropertyCell::kValueOffset));
++ __ cmp(r8, r9);
++ __ beq(&miss);
++
++ // Store the value in the cell.
++ __ StoreP(r3, FieldMemOperand(r7, JSGlobalPropertyCell::kValueOffset), r0);
++ // Cells are always rescanned, so no write barrier here.
++
++ Counters* counters = masm()->isolate()->counters();
++ __ IncrementCounter(counters->named_store_global_inline(), 1, r7, r6);
++ __ Ret();
++
++ // Handle store cache miss.
++ __ bind(&miss);
++ __ IncrementCounter(counters->named_store_global_inline_miss(), 1, r7, r6);
++ Handle<Code> ic = masm()->isolate()->builtins()->StoreIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(Code::NORMAL, name);
++}
++
++
++Handle<Code> LoadStubCompiler::CompileLoadNonexistent(Handle<String> name,
++ Handle<JSObject> object,
++ Handle<JSObject> last) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ // Check that receiver is not a smi.
++ __ JumpIfSmi(r3, &miss);
++
++ // Check the maps of the full prototype chain.
++ CheckPrototypes(object, r3, last, r6, r4, r7, name, &miss);
++
++ // If the last object in the prototype chain is a global object,
++ // check that the global property cell is empty.
++ if (last->IsGlobalObject()) {
++ GenerateCheckPropertyCell(
++ masm(), Handle<GlobalObject>::cast(last), name, r4, &miss);
++ }
++
++ // Return undefined if maps of the full prototype chain are still the
++ // same and no global property with this name contains a value.
++ __ LoadRoot(r3, Heap::kUndefinedValueRootIndex);
++ __ Ret();
++
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::NONEXISTENT, factory()->empty_string());
++}
++
++
++Handle<Code> LoadStubCompiler::CompileLoadField(Handle<JSObject> object,
++ Handle<JSObject> holder,
++ int index,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ GenerateLoadField(object, holder, r3, r6, r4, r7, index, name, &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::FIELD, name);
++}
++
++
++Handle<Code> LoadStubCompiler::CompileLoadCallback(
++ Handle<String> name,
++ Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Handle<AccessorInfo> callback) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++ GenerateLoadCallback(object, holder, r3, r5, r6, r4, r7, r8, callback, name,
++ &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++#undef __
++#define __ ACCESS_MASM(masm)
++
++
++void LoadStubCompiler::GenerateLoadViaGetter(MacroAssembler* masm,
++ Handle<JSFunction> getter) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ {
++ FrameScope scope(masm, StackFrame::INTERNAL);
++
++ if (!getter.is_null()) {
++ // Call the JavaScript getter with the receiver on the stack.
++ __ push(r3);
++ ParameterCount actual(0);
++ __ InvokeFunction(getter, actual, CALL_FUNCTION, NullCallWrapper(),
++ CALL_AS_METHOD);
++ } else {
++ // If we generate a global code snippet for deoptimization only, remember
++ // the place to continue after deoptimization.
++
masm->isolate()->heap()->SetGetterStubDeoptPCOffset(masm->pc_offset());
++ }
++
++ // Restore context register.
++ __ LoadP(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
++ }
++ __ Ret();
++}
++
++
++#undef __
++#define __ ACCESS_MASM(masm())
++
++
++Handle<Code> LoadStubCompiler::CompileLoadViaGetter(
++ Handle<String> name,
++ Handle<JSObject> receiver,
++ Handle<JSObject> holder,
++ Handle<JSFunction> getter) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ // Check that the maps haven't changed.
++ __ JumpIfSmi(r3, &miss);
++ CheckPrototypes(receiver, r3, holder, r6, r7, r4, name, &miss);
++
++ GenerateLoadViaGetter(masm(), getter);
++
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++Handle<Code> LoadStubCompiler::CompileLoadConstant(Handle<JSObject> object,
++ Handle<JSObject> holder,
++ Handle<JSFunction> value,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ GenerateLoadConstant(object, holder, r3, r6, r4, r7, value, name, &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::CONSTANT_FUNCTION, name);
++}
++
++
++Handle<Code> LoadStubCompiler::CompileLoadInterceptor(Handle<JSObject>
object,
++ Handle<JSObject> holder,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ LookupResult lookup(isolate());
++ LookupPostInterceptor(holder, name, &lookup);
++ GenerateLoadInterceptor(object, holder, &lookup, r3, r5, r6, r4, r7, name,
++ &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::INTERCEPTOR, name);
++}
++
++
++Handle<Code> LoadStubCompiler::CompileLoadGlobal(
++ Handle<JSObject> object,
++ Handle<GlobalObject> holder,
++ Handle<JSGlobalPropertyCell> cell,
++ Handle<String> name,
++ bool is_dont_delete) {
++ // ----------- S t a t e -------------
++ // -- r3 : receiver
++ // -- r5 : name
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ // Check that the map of the global has not changed.
++ __ JumpIfSmi(r3, &miss);
++ CheckPrototypes(object, r3, holder, r6, r7, r4, name, &miss);
++
++ // Get the value from the cell.
++ __ mov(r6, Operand(cell));
++ __ LoadP(r7, FieldMemOperand(r6, JSGlobalPropertyCell::kValueOffset));
++
++ // Check for deleted property if property can actually be deleted.
++ if (!is_dont_delete) {
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(r7, ip);
++ __ beq(&miss);
++ }
++
++ __ mr(r3, r7);
++ Counters* counters = masm()->isolate()->counters();
++ __ IncrementCounter(counters->named_load_global_stub(), 1, r4, r6);
++ __ Ret();
++
++ __ bind(&miss);
++ __ IncrementCounter(counters->named_load_global_stub_miss(), 1, r4, r6);
++ GenerateLoadMiss(masm(), Code::LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::NORMAL, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadField(Handle<String> name,
++ Handle<JSObject> receiver,
++ Handle<JSObject> holder,
++ int index) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ // Check the key is the cached one.
++ __ Cmpi(r3, Operand(name), r0);
++ __ bne(&miss);
++
++ GenerateLoadField(receiver, holder, r4, r5, r6, r7, index, name, &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
++
++ return GetCode(Code::FIELD, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadCallback(
++ Handle<String> name,
++ Handle<JSObject> receiver,
++ Handle<JSObject> holder,
++ Handle<AccessorInfo> callback) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ // Check the key is the cached one.
++ __ Cmpi(r3, Operand(name), r0);
++ __ bne(&miss);
++
++ GenerateLoadCallback(receiver, holder, r4, r3, r5, r6, r7, r8, callback, name,
++ &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
++
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadConstant(
++ Handle<String> name,
++ Handle<JSObject> receiver,
++ Handle<JSObject> holder,
++ Handle<JSFunction> value) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ // Check the key is the cached one.
++ __ mov(r5, Operand(name));
++ __ cmp(r3, r5);
++ __ bne(&miss);
++
++ GenerateLoadConstant(receiver, holder, r4, r5, r6, r7, value, name, &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
++
++ // Return the generated code.
++ return GetCode(Code::CONSTANT_FUNCTION, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadInterceptor(
++ Handle<JSObject> receiver,
++ Handle<JSObject> holder,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ // Check the key is the cached one.
++ __ Cmpi(r3, Operand(name), r0);
++ __ bne(&miss);
++
++ LookupResult lookup(isolate());
++ LookupPostInterceptor(holder, name, &lookup);
++ GenerateLoadInterceptor(receiver, holder, &lookup, r4, r3, r5, r6, r7, name,
++ &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
++
++ return GetCode(Code::INTERCEPTOR, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadArrayLength(
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ // Check the key is the cached one.
++ __ Cmpi(r3, Operand(name), r0);
++ __ bne(&miss);
++
++ GenerateLoadArrayLength(masm(), r4, r5, &miss);
++ __ bind(&miss);
++ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
++
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadStringLength(
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ Counters* counters = masm()->isolate()->counters();
++ __ IncrementCounter(counters->keyed_load_string_length(), 1, r5, r6);
++
++ // Check the key is the cached one.
++ __ Cmpi(r3, Operand(name), r0);
++ __ bne(&miss);
++
++ GenerateLoadStringLength(masm(), r4, r5, r6, &miss, true);
++ __ bind(&miss);
++ __ DecrementCounter(counters->keyed_load_string_length(), 1, r5, r6);
++
++ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
++
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadFunctionPrototype(
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++
++ Counters* counters = masm()->isolate()->counters();
++ __ IncrementCounter(counters->keyed_load_function_prototype(), 1, r5, r6);
++
++ // Check the name hasn't changed.
++ __ Cmpi(r3, Operand(name), r0);
++ __ bne(&miss);
++
++ GenerateLoadFunctionPrototype(masm(), r4, r5, r6, &miss);
++ __ bind(&miss);
++ __ DecrementCounter(counters->keyed_load_function_prototype(), 1, r5, r6);
++ GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
++
++ return GetCode(Code::CALLBACKS, name);
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadElement(
++ Handle<Map> receiver_map) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ ElementsKind elements_kind = receiver_map->elements_kind();
++ Handle<Code> stub = KeyedLoadElementStub(elements_kind).GetCode();
++
++ __ DispatchMap(r4, r5, receiver_map, stub, DO_SMI_CHECK);
++
++ Handle<Code> ic = isolate()->builtins()->KeyedLoadIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(Code::NORMAL, factory()->empty_string());
++}
++
++
++Handle<Code> KeyedLoadStubCompiler::CompileLoadPolymorphic(
++ MapHandleList* receiver_maps,
++ CodeHandleList* handler_ics) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss;
++ __ JumpIfSmi(r4, &miss);
++
++ int receiver_count = receiver_maps->length();
++ __ LoadP(r5, FieldMemOperand(r4, HeapObject::kMapOffset));
++ for (int current = 0; current < receiver_count; ++current) {
++ Label no_match;
++ __ mov(ip, Operand(receiver_maps->at(current)));
++ __ cmp(r5, ip);
++ __ bne(&no_match);
++ __ Jump(handler_ics->at(current), RelocInfo::CODE_TARGET, al);
++ __ bind(&no_match);
++ }
++
++ __ bind(&miss);
++ Handle<Code> miss_ic = isolate()->builtins()->KeyedLoadIC_Miss();
++ __ Jump(miss_ic, RelocInfo::CODE_TARGET, al);
++
++ // Return the generated code.
++ return GetCode(Code::NORMAL, factory()->empty_string(), MEGAMORPHIC);
++}
++
++
++Handle<Code> KeyedStoreStubCompiler::CompileStoreField(Handle<JSObject>
object,
++ int index,
++ Handle<Map> transition,
++ Handle<String> name) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : name
++ // -- r5 : receiver
++ // -- lr : return address
++ // -----------------------------------
++ Label miss;
++
++ Counters* counters = masm()->isolate()->counters();
++ __ IncrementCounter(counters->keyed_store_field(), 1, r6, r7);
++
++ // Check that the name has not changed.
++ __ Cmpi(r4, Operand(name), r0);
++ __ bne(&miss);
++
++ // r6 is used as scratch register. r4 and r5 keep their values if a jump to
++ // the miss label is generated.
++ GenerateStoreField(masm(),
++ object,
++ index,
++ transition,
++ name,
++ r5, r4, r6, r7,
++ &miss);
++ __ bind(&miss);
++
++ __ DecrementCounter(counters->keyed_store_field(), 1, r6, r7);
++ Handle<Code> ic = masm()->isolate()->builtins()->KeyedStoreIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(transition.is_null()
++ ? Code::FIELD
++ : Code::MAP_TRANSITION, name);
++}
++
++
++Handle<Code> KeyedStoreStubCompiler::CompileStoreElement(
++ Handle<Map> receiver_map) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -- r6 : scratch
++ // -----------------------------------
++ ElementsKind elements_kind = receiver_map->elements_kind();
++ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
++ Handle<Code> stub =
++ KeyedStoreElementStub(is_js_array, elements_kind, grow_mode_).GetCode();
++
++ __ DispatchMap(r5, r6, receiver_map, stub, DO_SMI_CHECK);
++
++ Handle<Code> ic = isolate()->builtins()->KeyedStoreIC_Miss();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode(Code::NORMAL, factory()->empty_string());
++}
++
++
++Handle<Code> KeyedStoreStubCompiler::CompileStorePolymorphic(
++ MapHandleList* receiver_maps,
++ CodeHandleList* handler_stubs,
++ MapHandleList* transitioned_maps) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -- r6 : scratch
++ // -----------------------------------
++ Label miss;
++ __ JumpIfSmi(r5, &miss);
++
++ int receiver_count = receiver_maps->length();
++ __ LoadP(r6, FieldMemOperand(r5, HeapObject::kMapOffset));
++ for (int i = 0; i < receiver_count; ++i) {
++ __ mov(ip, Operand(receiver_maps->at(i)));
++ __ cmp(r6, ip);
++ if (transitioned_maps->at(i).is_null()) {
++ Label skip;
++ __ bne(&skip);
++ __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET);
++ __ bind(&skip);
++ } else {
++ Label next_map;
++ __ bne(&next_map);
++ __ mov(r6, Operand(transitioned_maps->at(i)));
++ __ Jump(handler_stubs->at(i), RelocInfo::CODE_TARGET, al);
++ __ bind(&next_map);
++ }
++ }
++
++ __ bind(&miss);
++ Handle<Code> miss_ic = isolate()->builtins()->KeyedStoreIC_Miss();
++ __ Jump(miss_ic, RelocInfo::CODE_TARGET, al);
++
++ // Return the generated code.
++ return GetCode(Code::NORMAL, factory()->empty_string(), MEGAMORPHIC);
++}
++
++
++Handle<Code> ConstructStubCompiler::CompileConstructStub(
++ Handle<JSFunction> function) {
++ // ----------- S t a t e -------------
++ // -- r3 : argc
++ // -- r4 : constructor
++ // -- lr : return address
++ // -- [sp] : last argument
++ // -----------------------------------
++ Label generic_stub_call;
++
++ // Use r10 for holding undefined which is used in several places below.
++ __ LoadRoot(r10, Heap::kUndefinedValueRootIndex);
++
++#ifdef ENABLE_DEBUGGER_SUPPORT
++ // Check to see whether there are any break points in the function code. If
++ // there are jump to the generic constructor stub which calls the actual
++ // code for the function thereby hitting the break points.
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kSharedFunctionInfoOffset));
++ __ LoadP(r5, FieldMemOperand(r5, SharedFunctionInfo::kDebugInfoOffset));
++ __ cmp(r5, r10);
++ __ bne(&generic_stub_call);
++#endif
++
++ // Load the initial map and verify that it is in fact a map.
++ // r4: constructor function
++ // r10: undefined
++ __ LoadP(r5, FieldMemOperand(r4, JSFunction::kPrototypeOrInitialMapOffset));
++ __ JumpIfSmi(r5, &generic_stub_call);
++ __ CompareObjectType(r5, r6, r7, MAP_TYPE);
++ __ bne(&generic_stub_call);
++
++#ifdef DEBUG
++ // Cannot construct functions this way.
++ // r3: argc
++ // r4: constructor function
++ // r5: initial map
++ // r10: undefined
++ __ CompareInstanceType(r5, r6, JS_FUNCTION_TYPE);
++ __ Check(ne, "Function constructed by construct stub.");
++#endif
++
++ // Now allocate the JSObject in new space.
++ // r3: argc
++ // r4: constructor function
++ // r5: initial map
++ // r10: undefined
++ ASSERT(function->has_initial_map());
++ __ lbz(r6, FieldMemOperand(r5, Map::kInstanceSizeOffset));
++#ifdef DEBUG
++ int instance_size = function->initial_map()->instance_size();
++ __ cmpi(r6, Operand(instance_size >> kPointerSizeLog2));
++ __ Check(eq, "Instance size of initial map changed.");
++#endif
++ __ AllocateInNewSpace(r6, r7, r8, r9, &generic_stub_call, SIZE_IN_WORDS);
++
++ // Allocated the JSObject, now initialize the fields. Map is set to initial
++ // map and properties and elements are set to empty fixed array.
++ // r3: argc
++ // r4: constructor function
++ // r5: initial map
++ // r6: object size (in words)
++ // r7: JSObject (not tagged)
++ // r10: undefined
++ __ LoadRoot(r9, Heap::kEmptyFixedArrayRootIndex);
++ __ mr(r8, r7);
++ ASSERT_EQ(0 * kPointerSize, JSObject::kMapOffset);
++ __ StoreP(r5, MemOperand(r8));
++ __ addi(r8, r8, Operand(kPointerSize));
++ ASSERT_EQ(1 * kPointerSize, JSObject::kPropertiesOffset);
++ __ StoreP(r9, MemOperand(r8));
++ __ addi(r8, r8, Operand(kPointerSize));
++ ASSERT_EQ(2 * kPointerSize, JSObject::kElementsOffset);
++ __ StoreP(r9, MemOperand(r8));
++ __ addi(r8, r8, Operand(kPointerSize));
++
++ // Calculate the location of the first argument. The stack contains only the
++ // argc arguments.
++ __ ShiftLeftImm(r4, r3, Operand(kPointerSizeLog2));
++ __ add(r4, sp, r4);
++
++ // Fill all the in-object properties with undefined.
++ // r3: argc
++ // r4: first argument
++ // r6: object size (in words)
++ // r7: JSObject (not tagged)
++ // r8: First in-object property of JSObject (not tagged)
++ // r10: undefined
++ // Fill the initialized properties with a constant value or a passed argument
++ // depending on the this.x = ...; assignment in the function.
++ Handle<SharedFunctionInfo> shared(function->shared());
++ for (int i = 0; i < shared->this_property_assignments_count(); i++) {
++ if (shared->IsThisPropertyAssignmentArgument(i)) {
++ Label not_passed, next;
++ // Check if the argument assigned to the property is actually passed.
++ int arg_number = shared->GetThisPropertyAssignmentArgument(i);
++ __ cmpi(r3, Operand(arg_number));
++ __ ble(¬_passed);
++ // Argument passed - find it on the stack.
++ __ LoadP(r5, MemOperand(r4, (arg_number + 1) * -kPointerSize), r0);
++ __ StoreP(r5, MemOperand(r8));
++ __ addi(r8, r8, Operand(kPointerSize));
++ __ b(&next);
++ __ bind(¬_passed);
++ // Set the property to undefined.
++ __ StoreP(r10, MemOperand(r8));
++ __ addi(r8, r8, Operand(kPointerSize));
++ __ bind(&next);
++ } else {
++ // Set the property to the constant value.
++ Handle<Object> constant(shared->GetThisPropertyAssignmentConstant(i));
++ __ mov(r5, Operand(constant));
++ __ StoreP(r5, MemOperand(r8));
++ __ addi(r8, r8, Operand(kPointerSize));
++ }
++ }
++
++ // Fill the unused in-object property fields with undefined.
++ ASSERT(function->has_initial_map());
++ for (int i = shared->this_property_assignments_count();
++ i < function->initial_map()->inobject_properties();
++ i++) {
++ __ StoreP(r10, MemOperand(r8));
++ __ addi(r8, r8, Operand(kPointerSize));
++ }
++
++ // r3: argc
++ // r7: JSObject (not tagged)
++ // Move argc to r4 and the JSObject to return to r3 and tag it.
++ __ mr(r4, r3);
++ __ mr(r3, r7);
++ __ ori(r3, r3, Operand(kHeapObjectTag));
++
++ // r3: JSObject
++ // r4: argc
++ // Remove caller arguments and receiver from the stack and return.
++ __ ShiftLeftImm(r4, r4, Operand(kPointerSizeLog2));
++ __ add(sp, sp, r4);
++ __ addi(sp, sp, Operand(kPointerSize));
++ Counters* counters = masm()->isolate()->counters();
++ __ IncrementCounter(counters->constructed_objects(), 1, r4, r5);
++ __ IncrementCounter(counters->constructed_objects_stub(), 1, r4, r5);
++ __ blr();
++
++ // Jump to the generic stub in case the specialized code cannot handle the
++ // construction.
++ __ bind(&generic_stub_call);
++ Handle<Code> code =
masm()->isolate()->builtins()->JSConstructStubGeneric();
++ __ Jump(code, RelocInfo::CODE_TARGET);
++
++ // Return the generated code.
++ return GetCode();
++}
++
++
++#undef __
++#define __ ACCESS_MASM(masm)
++
++
++void KeyedLoadStubCompiler::GenerateLoadDictionaryElement(
++ MacroAssembler* masm) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label slow, miss_force_generic;
++
++ Register key = r3;
++ Register receiver = r4;
++
++ __ JumpIfNotSmi(key, &miss_force_generic);
++ __ SmiUntag(r5, key);
++ __ LoadP(r7, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ __ LoadFromNumberDictionary(&slow, r7, key, r3, r5, r6, r8);
++ __ Ret();
++
++ __ bind(&slow);
++ __ IncrementCounter(
++ masm->isolate()->counters()->keyed_load_external_array_slow(),
++ 1, r5, r6);
++
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Handle<Code> slow_ic =
++ masm->isolate()->builtins()->KeyedLoadIC_Slow();
++ __ Jump(slow_ic, RelocInfo::CODE_TARGET);
++
++ // Miss case, call the runtime.
++ __ bind(&miss_force_generic);
++
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++
++ Handle<Code> miss_ic =
++ masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
++ __ Jump(miss_ic, RelocInfo::CODE_TARGET);
++}
++
++
++static void GenerateSmiKeyCheck(MacroAssembler* masm,
++ Register key,
++ Register scratch0,
++ Register scratch1,
++ DwVfpRegister double_scratch0,
++ DwVfpRegister double_scratch1,
++ Label* fail) {
++ Label key_ok;
++ // Check for smi or a smi inside a heap number. We convert the heap
++ // number and check if the conversion is exact and fits into the smi
++ // range.
++ __ JumpIfSmi(key, &key_ok);
++ __ CheckMap(key,
++ scratch0,
++ Heap::kHeapNumberMapRootIndex,
++ fail,
++ DONT_DO_SMI_CHECK);
++ __ lfd(double_scratch0, FieldMemOperand(key, HeapNumber::kValueOffset));
++ __ EmitVFPTruncate(kRoundToZero,
++ scratch0,
++ double_scratch0,
++ scratch1,
++ double_scratch1,
++ kCheckForInexactConversion);
++ __ bne(fail);
++#if V8_TARGET_ARCH_PPC64
++ __ SmiTag(key, scratch0);
++#else
++ __ SmiTagCheckOverflow(scratch1, scratch0, r0);
++ __ BranchOnOverflow(fail);
++ __ mr(key, scratch1);
++#endif
++ __ bind(&key_ok);
++}
++
++
++void KeyedLoadStubCompiler::GenerateLoadExternalArray(
++ MacroAssembler* masm,
++ ElementsKind elements_kind) {
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss_force_generic, slow, failed_allocation;
++
++ Register key = r3;
++ Register receiver = r4;
++
++ // This stub is meant to be tail-jumped to, the receiver must already
++ // have been verified by the caller to not be a smi.
++
++ // Check that the key is a smi or a heap number convertible to a smi.
++ GenerateSmiKeyCheck(masm, key, r7, r8, d1, d2, &miss_force_generic);
++
++ __ LoadP(r6, FieldMemOperand(receiver, JSObject::kElementsOffset));
++ // r6: elements array
++
++ // Check that the index is in range.
++ __ LoadP(ip, FieldMemOperand(r6, ExternalArray::kLengthOffset));
++ __ cmpl(key, ip);
++ // Unsigned comparison catches both negative and too-large values.
++ __ bge(&miss_force_generic);
++
++ __ LoadP(r6, FieldMemOperand(r6, ExternalArray::kExternalPointerOffset));
++ // r6: base pointer of external storage
++
++ // We are not untagging smi key since an additional shift operation
++ // may be required to compute the array element's offset.
++
++ Register value = r5;
++ switch (elements_kind) {
++ case EXTERNAL_BYTE_ELEMENTS:
++ __ SmiToByteArrayOffset(value, key);
++ __ lbzx(value, MemOperand(r6, value));
++ __ extsb(value, value);
++ break;
++ case EXTERNAL_PIXEL_ELEMENTS:
++ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
++ __ SmiToByteArrayOffset(value, key);
++ __ lbzx(value, MemOperand(r6, value));
++ break;
++ case EXTERNAL_SHORT_ELEMENTS:
++ __ SmiToShortArrayOffset(value, key);
++ __ lhzx(value, MemOperand(r6, value));
++ __ extsh(value, value);
++ break;
++ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
++ __ SmiToShortArrayOffset(value, key);
++ __ lhzx(value, MemOperand(r6, value));
++ break;
++ case EXTERNAL_INT_ELEMENTS:
++ __ SmiToIntArrayOffset(value, key);
++ __ lwzx(value, MemOperand(r6, value));
++#if V8_TARGET_ARCH_PPC64
++ __ extsw(value, value);
++#endif
++ break;
++ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
++ __ SmiToIntArrayOffset(value, key);
++ __ lwzx(value, MemOperand(r6, value));
++ break;
++ case EXTERNAL_FLOAT_ELEMENTS:
++ __ SmiToFloatArrayOffset(value, key);
++ __ lfsx(d0, MemOperand(r6, value));
++ break;
++ case EXTERNAL_DOUBLE_ELEMENTS:
++ __ SmiToDoubleArrayOffset(value, key);
++ __ lfdx(d0, MemOperand(r6, value));
++ break;
++ case FAST_ELEMENTS:
++ case FAST_SMI_ELEMENTS:
++ case FAST_DOUBLE_ELEMENTS:
++ case FAST_HOLEY_ELEMENTS:
++ case FAST_HOLEY_SMI_ELEMENTS:
++ case FAST_HOLEY_DOUBLE_ELEMENTS:
++ case DICTIONARY_ELEMENTS:
++ case NON_STRICT_ARGUMENTS_ELEMENTS:
++ UNREACHABLE();
++ break;
++ }
++
++ // For integer array types:
++ // r5: value
++ // For float array type:
++ // d0: single value
++ // For double array type:
++ // d0: double value
++
++ if (elements_kind == EXTERNAL_INT_ELEMENTS) {
++ // For the Int and UnsignedInt array types, we need to see whether
++ // the value can be represented in a Smi. If not, we need to convert
++ // it to a HeapNumber.
++#if !V8_TARGET_ARCH_PPC64
++ Label box_int;
++ // Check that the value fits in a smi.
++ __ JumpIfNotSmiCandidate(value, r0, &box_int);
++#endif
++ // Tag integer as smi and return it.
++ __ SmiTag(r3, value);
++ __ Ret();
++
++#if !V8_TARGET_ARCH_PPC64
++ __ bind(&box_int);
++ // Allocate a HeapNumber for the result and perform int-to-double
++ // conversion. Don't touch r3 or r4 as they are needed if allocation
++ // fails.
++ __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r8, r6, r7, r9, &slow);
++ // Now we can use r3 for the result as key is not needed any more.
++ __ mr(r3, r8);
++
++ FloatingPointHelper::ConvertIntToDouble(
++ masm, value, d0);
++ __ stfd(d0, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ __ Ret();
++#endif
++ } else if (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
++ // The test is different for unsigned int values. Since we need
++ // the value to be in the range of a positive smi, we can't
++ // handle any of the high bits being set in the value.
++ Label box_int;
++ __ JumpIfNotUnsignedSmiCandidate(value, r0, &box_int);
++
++ // Tag integer as smi and return it.
++ __ SmiTag(r3, value);
++ __ Ret();
++
++ __ bind(&box_int);
++ // Allocate a HeapNumber for the result and perform int-to-double
++ // conversion. Don't use r3 and r4 as AllocateHeapNumber clobbers all
++ // registers - also when jumping due to exhausted young space.
++ __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r8, r6, r7, r9, &slow);
++ __ mr(r3, r8);
++
++ FloatingPointHelper::ConvertUnsignedIntToDouble(
++ masm, value, d0);
++ __ stfd(d0, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ __ Ret();
++
++ } else if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
++ // For the floating-point array type, we need to always allocate a
++ // HeapNumber.
++ // Allocate a HeapNumber for the result. Don't use r3 and r4 as
++ // AllocateHeapNumber clobbers all registers - also when jumping due to
++ // exhausted young space.
++ __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r5, r6, r7, r9, &slow);
++ __ stfd(d0, FieldMemOperand(r5, HeapNumber::kValueOffset));
++ __ mr(r3, r5);
++ __ Ret();
++
++ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
++ // Allocate a HeapNumber for the result. Don't use r3 and r4 as
++ // AllocateHeapNumber clobbers all registers - also when jumping due to
++ // exhausted young space.
++ __ LoadRoot(r9, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(r5, r6, r7, r9, &slow);
++ __ stfd(d0, FieldMemOperand(r5, HeapNumber::kValueOffset));
++ __ mr(r3, r5);
++ __ Ret();
++
++ } else {
++ // Tag integer as smi and return it.
++ __ SmiTag(r3, value);
++ __ Ret();
++ }
++
++ // Slow case, key and receiver still in r3 and r4.
++ __ bind(&slow);
++ __ IncrementCounter(
++ masm->isolate()->counters()->keyed_load_external_array_slow(),
++ 1, r5, r6);
++
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++
++ __ Push(r4, r3);
++
++ __ TailCallRuntime(Runtime::kKeyedGetProperty, 2, 1);
++
++ __ bind(&miss_force_generic);
++ Handle<Code> stub =
++ masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
++ __ Jump(stub, RelocInfo::CODE_TARGET);
++}
++
++
++void KeyedStoreStubCompiler::GenerateStoreExternalArray(
++ MacroAssembler* masm,
++ ElementsKind elements_kind) {
++ // ---------- S t a t e --------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -----------------------------------
++ Label slow, check_heap_number, miss_force_generic;
++
++ // Register usage.
++ Register value = r3;
++ Register key = r4;
++ Register receiver = r5;
++ // r6 mostly holds the elements array or the destination external array.
++
++ // This stub is meant to be tail-jumped to, the receiver must already
++ // have been verified by the caller to not be a smi.
++
++ // Check that the key is a smi or a heap number convertible to a smi.
++ GenerateSmiKeyCheck(masm, key, r7, r8, d1, d2, &miss_force_generic);
++
++ __ LoadP(r6, FieldMemOperand(receiver, JSObject::kElementsOffset));
++
++ // Check that the index is in range
++ __ LoadP(ip, FieldMemOperand(r6, ExternalArray::kLengthOffset));
++ __ cmpl(key, ip);
++ // Unsigned comparison catches both negative and too-large values.
++ __ bge(&miss_force_generic);
++
++ // Handle both smis and HeapNumbers in the fast path. Go to the
++ // runtime for all other kinds of values.
++ // r6: external array.
++ if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) {
++ // Double to pixel conversion is only implemented in the runtime for now.
++ __ JumpIfNotSmi(value, &slow);
++ } else {
++ __ JumpIfNotSmi(value, &check_heap_number);
++ }
++ __ SmiUntag(r8, value);
++ __ LoadP(r6, FieldMemOperand(r6, ExternalArray::kExternalPointerOffset));
++
++ // r6: base pointer of external storage.
++ // r8: value (integer).
++ // r10: scratch register
++ switch (elements_kind) {
++ case EXTERNAL_PIXEL_ELEMENTS:
++ // Clamp the value to [0..255].
++ __ ClampUint8(r8, r8);
++ __ SmiToByteArrayOffset(r10, key);
++ __ stbx(r8, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_BYTE_ELEMENTS:
++ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
++ __ SmiToByteArrayOffset(r10, key);
++ __ stbx(r8, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_SHORT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
++ __ SmiToShortArrayOffset(r10, key);
++ __ sthx(r8, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_INT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
++ __ SmiToIntArrayOffset(r10, key);
++ __ stwx(r8, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_FLOAT_ELEMENTS:
++ // Perform int-to-float conversion and store to memory.
++ __ SmiToFloatArrayOffset(r10, key);
++ // r10: efective address of the float element
++ FloatingPointHelper::ConvertIntToFloat(masm, d0, r8, r9);
++ __ stfsx(d0, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_DOUBLE_ELEMENTS:
++ __ SmiToDoubleArrayOffset(r10, key);
++ // __ add(r6, r6, r10);
++ // r6: effective address of the double element
++ FloatingPointHelper::ConvertIntToDouble(
++ masm, r8, d0);
++ __ stfdx(d0, MemOperand(r6, r10));
++ break;
++ case FAST_ELEMENTS:
++ case FAST_SMI_ELEMENTS:
++ case FAST_DOUBLE_ELEMENTS:
++ case FAST_HOLEY_ELEMENTS:
++ case FAST_HOLEY_SMI_ELEMENTS:
++ case FAST_HOLEY_DOUBLE_ELEMENTS:
++ case DICTIONARY_ELEMENTS:
++ case NON_STRICT_ARGUMENTS_ELEMENTS:
++ UNREACHABLE();
++ break;
++ }
++
++ // Entry registers are intact, r3 holds the value which is the return value.
++ __ Ret();
++
++ if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) {
++ // r6: external array.
++ __ bind(&check_heap_number);
++ __ CompareObjectType(value, r8, r9, HEAP_NUMBER_TYPE);
++ __ bne(&slow);
++
++ __ LoadP(r6, FieldMemOperand(r6, ExternalArray::kExternalPointerOffset));
++
++ // r6: base pointer of external storage.
++
++ // The WebGL specification leaves the behavior of storing NaN and
++ // +/-Infinity into integer arrays basically undefined. For more
++ // reproducible behavior, convert these to zero.
++
++ if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) {
++ __ lfd(d0, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ __ SmiToFloatArrayOffset(r8, key);
++ __ frsp(d0, d0);
++ __ stfsx(d0, MemOperand(r6, r8));
++ } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) {
++ __ lfd(d0, FieldMemOperand(r3, HeapNumber::kValueOffset));
++ __ SmiToDoubleArrayOffset(r8, key);
++ __ stfdx(d0, MemOperand(r6, r8));
++ } else {
++ // Hoisted load.
++ __ mr(r8, value);
++ __ lfd(d0, FieldMemOperand(r8, HeapNumber::kValueOffset));
++
++ __ EmitECMATruncate(r8, d0, d1, r10, r7, r9);
++ switch (elements_kind) {
++ case EXTERNAL_BYTE_ELEMENTS:
++ case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
++ __ SmiToByteArrayOffset(r10, key);
++ __ stbx(r8, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_SHORT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
++ __ SmiToShortArrayOffset(r10, key);
++ __ sthx(r8, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_INT_ELEMENTS:
++ case EXTERNAL_UNSIGNED_INT_ELEMENTS:
++ __ SmiToIntArrayOffset(r10, key);
++ __ stwx(r8, MemOperand(r6, r10));
++ break;
++ case EXTERNAL_PIXEL_ELEMENTS:
++ case EXTERNAL_FLOAT_ELEMENTS:
++ case EXTERNAL_DOUBLE_ELEMENTS:
++ case FAST_ELEMENTS:
++ case FAST_SMI_ELEMENTS:
++ case FAST_DOUBLE_ELEMENTS:
++ case FAST_HOLEY_ELEMENTS:
++ case FAST_HOLEY_SMI_ELEMENTS:
++ case FAST_HOLEY_DOUBLE_ELEMENTS:
++ case DICTIONARY_ELEMENTS:
++ case NON_STRICT_ARGUMENTS_ELEMENTS:
++ UNREACHABLE();
++ break;
++ }
++ }
++
++ // Entry registers are intact, r3 holds the value which is the return
++ // value.
++ __ Ret();
++ }
++
++ // Slow case, key and receiver still in r3 and r4.
++ __ bind(&slow);
++ __ IncrementCounter(
++ masm->isolate()->counters()->keyed_load_external_array_slow(),
++ 1, r5, r6);
++
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Handle<Code> slow_ic =
++ masm->isolate()->builtins()->KeyedStoreIC_Slow();
++ __ Jump(slow_ic, RelocInfo::CODE_TARGET);
++
++ // Miss case, call the runtime.
++ __ bind(&miss_force_generic);
++
++ // ---------- S t a t e --------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++
++ Handle<Code> miss_ic =
++ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
++ __ Jump(miss_ic, RelocInfo::CODE_TARGET);
++}
++
++
++void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss_force_generic;
++
++ // This stub is meant to be tail-jumped to, the receiver must already
++ // have been verified by the caller to not be a smi.
++
++ // Check that the key is a smi or a heap number convertible to a smi.
++ GenerateSmiKeyCheck(masm, r3, r7, r8, d1, d2, &miss_force_generic);
++
++ // Get the elements array.
++ __ LoadP(r5, FieldMemOperand(r4, JSObject::kElementsOffset));
++ __ AssertFastElements(r5);
++
++ // Check that the key is within bounds.
++ __ LoadP(r6, FieldMemOperand(r5, FixedArray::kLengthOffset));
++ __ cmpl(r3, r6);
++ __ bge(&miss_force_generic);
++
++ // Load the result and make sure it's not the hole.
++ __ addi(r6, r5, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(r7, r3);
++ __ LoadPX(r7, MemOperand(r7, r6));
++ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
++ __ cmp(r7, ip);
++ __ beq(&miss_force_generic);
++ __ mr(r3, r7);
++ __ Ret();
++
++ __ bind(&miss_force_generic);
++ Handle<Code> stub =
++ masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
++ __ Jump(stub, RelocInfo::CODE_TARGET);
++}
++
++
++void KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(
++ MacroAssembler* masm) {
++ // ----------- S t a t e -------------
++ // -- lr : return address
++ // -- r3 : key
++ // -- r4 : receiver
++ // -----------------------------------
++ Label miss_force_generic, slow_allocate_heapnumber;
++
++ Register key_reg = r3;
++ Register receiver_reg = r4;
++ Register elements_reg = r5;
++ Register heap_number_reg = r5;
++ Register indexed_double_offset = r6;
++ Register scratch = r7;
++ Register scratch2 = r8;
++ Register scratch3 = r9;
++ Register heap_number_map = r10;
++
++ // This stub is meant to be tail-jumped to, the receiver must already
++ // have been verified by the caller to not be a smi.
++
++ // Check that the key is a smi or a heap number convertible to a smi.
++ GenerateSmiKeyCheck(masm, key_reg, r7, r8, d1, d2, &miss_force_generic);
++
++ // Get the elements array.
++ __ LoadP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
++
++ // Check that the key is within bounds.
++ __ LoadP(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
++ __ cmpl(key_reg, scratch);
++ __ bge(&miss_force_generic);
++
++ // Load the upper word of the double in the fixed array and test for NaN.
++ __ SmiToDoubleArrayOffset(indexed_double_offset, key_reg);
++ __ add(indexed_double_offset, elements_reg, indexed_double_offset);
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ uint32_t upper_32_offset = FixedArray::kHeaderSize + sizeof(kHoleNanLower32);
++#else
++ uint32_t upper_32_offset = FixedArray::kHeaderSize;
++#endif
++ __ lwz(scratch, FieldMemOperand(indexed_double_offset, upper_32_offset));
++ __ Cmpi(scratch, Operand(kHoleNanUpper32), r0);
++ __ beq(&miss_force_generic);
++
++ // Non-NaN. Allocate a new heap number and copy the double value into it.
++ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
++ __ AllocateHeapNumber(heap_number_reg, scratch2, scratch3,
++ heap_number_map, &slow_allocate_heapnumber);
++
++ // Don't need to reload the upper 32 bits of the double, it's already in
++ // scratch.
++ __ stw(scratch, FieldMemOperand(heap_number_reg,
++ HeapNumber::kExponentOffset));
++#if __FLOAT_WORD_ORDER == __LITTLE_ENDIAN
++ __ lwz(scratch, FieldMemOperand(indexed_double_offset,
++ FixedArray::kHeaderSize));
++#else
++ __ lwz(scratch, FieldMemOperand(indexed_double_offset,
++ FixedArray::kHeaderSize+4));
++#endif
++ __ stw(scratch, FieldMemOperand(heap_number_reg,
++ HeapNumber::kMantissaOffset));
++
++ __ mr(r3, heap_number_reg);
++ __ Ret();
++
++ __ bind(&slow_allocate_heapnumber);
++ Handle<Code> slow_ic =
++ masm->isolate()->builtins()->KeyedLoadIC_Slow();
++ __ Jump(slow_ic, RelocInfo::CODE_TARGET);
++
++ __ bind(&miss_force_generic);
++ Handle<Code> miss_ic =
++ masm->isolate()->builtins()->KeyedLoadIC_MissForceGeneric();
++ __ Jump(miss_ic, RelocInfo::CODE_TARGET);
++}
++
++
++void KeyedStoreStubCompiler::GenerateStoreFastElement(
++ MacroAssembler* masm,
++ bool is_js_array,
++ ElementsKind elements_kind,
++ KeyedAccessGrowMode grow_mode) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -- r6 : scratch
++ // -- r7 : scratch (elements)
++ // -----------------------------------
++ Label miss_force_generic, transition_elements_kind, grow, slow;
++ Label finish_store, check_capacity;
++
++ Register value_reg = r3;
++ Register key_reg = r4;
++ Register receiver_reg = r5;
++ Register scratch = r7;
++ Register elements_reg = r6;
++ Register length_reg = r8;
++ Register scratch2 = r9;
++
++ // This stub is meant to be tail-jumped to, the receiver must already
++ // have been verified by the caller to not be a smi.
++
++ // Check that the key is a smi or a heap number convertible to a smi.
++ GenerateSmiKeyCheck(masm, key_reg, r7, r8, d1, d2, &miss_force_generic);
++
++ if (IsFastSmiElementsKind(elements_kind)) {
++ __ JumpIfNotSmi(value_reg, &transition_elements_kind);
++ }
++
++ // Check that the key is within bounds.
++ __ LoadP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
++ if (is_js_array) {
++ __ LoadP(scratch, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
++ } else {
++ __ LoadP(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
++ }
++ // Compare smis.
++ __ cmpl(key_reg, scratch);
++ if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
++ __ bge(&grow);
++ } else {
++ __ bge(&miss_force_generic);
++ }
++
++ // Make sure elements is a fast element array, not 'cow'.
++ __ CheckMap(elements_reg,
++ scratch,
++ Heap::kFixedArrayMapRootIndex,
++ &miss_force_generic,
++ DONT_DO_SMI_CHECK);
++
++ __ bind(&finish_store);
++ if (IsFastSmiElementsKind(elements_kind)) {
++ __ addi(scratch,
++ elements_reg,
++ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(scratch2, key_reg);
++ __ StorePX(value_reg, MemOperand(scratch, scratch2));
++ } else {
++ ASSERT(IsFastObjectElementsKind(elements_kind));
++ __ addi(scratch,
++ elements_reg,
++ Operand(FixedArray::kHeaderSize - kHeapObjectTag));
++ __ SmiToPtrArrayOffset(scratch2, key_reg);
++ __ StorePUX(value_reg, MemOperand(scratch, scratch2));
++ __ mr(receiver_reg, value_reg);
++ __ RecordWrite(elements_reg, // Object.
++ scratch, // Address.
++ receiver_reg, // Value.
++ kLRHasNotBeenSaved,
++ kDontSaveFPRegs);
++ }
++ // value_reg (r3) is preserved.
++ // Done.
++ __ Ret();
++
++ __ bind(&miss_force_generic);
++ Handle<Code> ic =
++ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ __ bind(&transition_elements_kind);
++ Handle<Code> ic_miss =
masm->isolate()->builtins()->KeyedStoreIC_Miss();
++ __ Jump(ic_miss, RelocInfo::CODE_TARGET);
++
++ if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
++ // Grow the array by a single element if possible.
++ __ bind(&grow);
++
++ // Make sure the array is only growing by a single element, anything else
++ // must be handled by the runtime. Flags already set by previous compare.
++ __ bne(&miss_force_generic);
++
++ // Check for the empty array, and preallocate a small backing store if
++ // possible.
++ __ LoadP(length_reg,
++ FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
++ __ LoadP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
++ __ CompareRoot(elements_reg, Heap::kEmptyFixedArrayRootIndex);
++ __ bne(&check_capacity);
++
++ int size = FixedArray::SizeFor(JSArray::kPreallocatedArrayElements);
++ __ AllocateInNewSpace(size, elements_reg, scratch, scratch2, &slow,
++ TAG_OBJECT);
++
++ __ LoadRoot(scratch, Heap::kFixedArrayMapRootIndex);
++ __ StoreP(scratch, FieldMemOperand(elements_reg, JSObject::kMapOffset), r0);
++ __ LoadSmiLiteral(scratch,
++ Smi::FromInt(JSArray::kPreallocatedArrayElements));
++ __ StoreP(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset),
++ r0);
++ __ LoadRoot(scratch, Heap::kTheHoleValueRootIndex);
++ for (int i = 1; i < JSArray::kPreallocatedArrayElements; ++i) {
++ __ StoreP(scratch,
++ FieldMemOperand(elements_reg, FixedArray::SizeFor(i)), r0);
++ }
++
++ // Store the element at index zero.
++ __ StoreP(value_reg, FieldMemOperand(elements_reg, FixedArray::SizeFor(0)),
++ r0);
++
++ // Install the new backing store in the JSArray.
++ __ StoreP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset), r0);
++ __ RecordWriteField(receiver_reg, JSObject::kElementsOffset, elements_reg,
++ scratch, kLRHasNotBeenSaved, kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
++
++ // Increment the length of the array.
++ __ LoadSmiLiteral(length_reg, Smi::FromInt(1));
++ __ StoreP(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset),
++ r0);
++ __ Ret();
++
++ __ bind(&check_capacity);
++ // Check for cow elements, in general they are not handled by this stub
++ __ CheckMap(elements_reg,
++ scratch,
++ Heap::kFixedCOWArrayMapRootIndex,
++ &miss_force_generic,
++ DONT_DO_SMI_CHECK);
++
++ __ LoadP(scratch, FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
++ __ cmpl(length_reg, scratch);
++ __ bge(&slow);
++
++ // Grow the array and finish the store.
++ __ AddSmiLiteral(length_reg, length_reg, Smi::FromInt(1), r0);
++ __ StoreP(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset),
++ r0);
++ __ b(&finish_store);
++
++ __ bind(&slow);
++ Handle<Code> ic_slow =
masm->isolate()->builtins()->KeyedStoreIC_Slow();
++ __ Jump(ic_slow, RelocInfo::CODE_TARGET);
++ }
++}
++
++
++void KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(
++ MacroAssembler* masm,
++ bool is_js_array,
++ KeyedAccessGrowMode grow_mode) {
++ // ----------- S t a t e -------------
++ // -- r3 : value
++ // -- r4 : key
++ // -- r5 : receiver
++ // -- lr : return address
++ // -- r6 : scratch
++ // -- r7 : scratch
++ // -- r8 : scratch
++ // -----------------------------------
++ Label miss_force_generic, transition_elements_kind, grow, slow;
++ Label finish_store, check_capacity;
++
++ Register value_reg = r3;
++ Register key_reg = r4;
++ Register receiver_reg = r5;
++ Register elements_reg = r6;
++ Register scratch1 = r7;
++ Register scratch2 = r8;
++ Register scratch3 = r9;
++ Register scratch4 = r10;
++ Register length_reg = r10;
++
++ // This stub is meant to be tail-jumped to, the receiver must already
++ // have been verified by the caller to not be a smi.
++
++ // Check that the key is a smi or a heap number convertible to a smi.
++ GenerateSmiKeyCheck(masm, key_reg, r7, r8, d1, d2, &miss_force_generic);
++
++ __ LoadP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
++
++ // Check that the key is within bounds.
++ if (is_js_array) {
++ __ LoadP(scratch1, FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
++ } else {
++ __ LoadP(scratch1,
++ FieldMemOperand(elements_reg, FixedArray::kLengthOffset));
++ }
++ // Compare smis, unsigned compare catches both negative and out-of-bound
++ // indexes.
++ __ cmpl(key_reg, scratch1);
++ if (grow_mode == ALLOW_JSARRAY_GROWTH) {
++ __ bge(&grow);
++ } else {
++ __ bge(&miss_force_generic);
++ }
++
++ __ bind(&finish_store);
++ __ StoreNumberToDoubleElements(value_reg,
++ key_reg,
++ receiver_reg,
++ // All registers after this are overwritten.
++ elements_reg,
++ scratch1,
++ scratch2,
++ scratch3,
++ scratch4,
++ &transition_elements_kind);
++ __ Ret();
++
++ // Handle store cache miss, replacing the ic with the generic stub.
++ __ bind(&miss_force_generic);
++ Handle<Code> ic =
++ masm->isolate()->builtins()->KeyedStoreIC_MissForceGeneric();
++ __ Jump(ic, RelocInfo::CODE_TARGET);
++
++ __ bind(&transition_elements_kind);
++ Handle<Code> ic_miss =
masm->isolate()->builtins()->KeyedStoreIC_Miss();
++ __ Jump(ic_miss, RelocInfo::CODE_TARGET);
++
++ if (is_js_array && grow_mode == ALLOW_JSARRAY_GROWTH) {
++ // Grow the array by a single element if possible.
++ __ bind(&grow);
++
++ // Make sure the array is only growing by a single element, anything else
++ // must be handled by the runtime. Flags already set by previous compare.
++ __ bne(&miss_force_generic);
++
++ // Transition on values that can't be stored in a FixedDoubleArray.
++ Label value_is_smi;
++ __ JumpIfSmi(value_reg, &value_is_smi);
++ __ LoadP(scratch1, FieldMemOperand(value_reg, HeapObject::kMapOffset));
++ __ CompareRoot(scratch1, Heap::kHeapNumberMapRootIndex);
++ __ bne(&transition_elements_kind);
++ __ bind(&value_is_smi);
++
++ // Check for the empty array, and preallocate a small backing store if
++ // possible.
++ __ LoadP(length_reg,
++ FieldMemOperand(receiver_reg, JSArray::kLengthOffset));
++ __ LoadP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
++ __ CompareRoot(elements_reg, Heap::kEmptyFixedArrayRootIndex);
++ __ bne(&check_capacity);
++
++ int size = FixedDoubleArray::SizeFor(JSArray::kPreallocatedArrayElements);
++ __ AllocateInNewSpace(size, elements_reg, scratch1, scratch2, &slow,
++ TAG_OBJECT);
++
++ // Initialize the new FixedDoubleArray. Leave elements unitialized for
++ // efficiency, they are guaranteed to be initialized before use.
++ __ LoadRoot(scratch1, Heap::kFixedDoubleArrayMapRootIndex);
++ __ StoreP(scratch1, FieldMemOperand(elements_reg, JSObject::kMapOffset),
++ r0);
++ __ LoadSmiLiteral(scratch1,
++ Smi::FromInt(JSArray::kPreallocatedArrayElements));
++ __ StoreP(scratch1,
++ FieldMemOperand(elements_reg, FixedDoubleArray::kLengthOffset),
++ r0);
++
++ // Install the new backing store in the JSArray.
++ __ StoreP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset), r0);
++ __ RecordWriteField(receiver_reg, JSObject::kElementsOffset, elements_reg,
++ scratch1, kLRHasNotBeenSaved, kDontSaveFPRegs,
++ EMIT_REMEMBERED_SET, OMIT_SMI_CHECK);
++
++ // Increment the length of the array.
++ __ LoadSmiLiteral(length_reg, Smi::FromInt(1));
++ __ StoreP(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset),
++ r0);
++ __ LoadP(elements_reg,
++ FieldMemOperand(receiver_reg, JSObject::kElementsOffset));
++ __ b(&finish_store);
++
++ __ bind(&check_capacity);
++ // Make sure that the backing store can hold additional elements.
++ __ LoadP(scratch1,
++ FieldMemOperand(elements_reg, FixedDoubleArray::kLengthOffset));
++ __ cmpl(length_reg, scratch1);
++ __ bge(&slow);
++
++ // Grow the array and finish the store.
++ __ AddSmiLiteral(length_reg, length_reg, Smi::FromInt(1), r0);
++ __ StoreP(length_reg, FieldMemOperand(receiver_reg, JSArray::kLengthOffset),
++ r0);
++ __ b(&finish_store);
++
++ __ bind(&slow);
++ Handle<Code> ic_slow =
masm->isolate()->builtins()->KeyedStoreIC_Slow();
++ __ Jump(ic_slow, RelocInfo::CODE_TARGET);
++ }
++}
++
++
++#undef __
++
++} } // namespace v8::internal
++
++#endif // V8_TARGET_ARCH_PPC
+diff -up v8-3.14.5.10/src/profile-generator.cc.ppc v8-3.14.5.10/src/profile-generator.cc
+diff -up v8-3.14.5.10/src/regexp-macro-assembler.h.ppc
v8-3.14.5.10/src/regexp-macro-assembler.h
+--- v8-3.14.5.10/src/regexp-macro-assembler.h.ppc 2012-06-13 07:51:58.000000000 -0400
++++ v8-3.14.5.10/src/regexp-macro-assembler.h 2016-06-07 14:15:46.006392913 -0400
+@@ -54,6 +54,7 @@ class RegExpMacroAssembler {
+ kIA32Implementation,
+ kARMImplementation,
+ kMIPSImplementation,
++ kPPCImplementation,
+ kX64Implementation,
+ kBytecodeImplementation
+ };
+diff -up v8-3.14.5.10/src/runtime.cc.ppc v8-3.14.5.10/src/runtime.cc
+diff -up v8-3.14.5.10/src/serialize.cc.ppc v8-3.14.5.10/src/serialize.cc
+--- v8-3.14.5.10/src/serialize.cc.ppc 2012-09-20 08:51:09.000000000 -0400
++++ v8-3.14.5.10/src/serialize.cc 2016-06-07 14:15:46.008392901 -0400
+@@ -697,6 +697,22 @@ void Deserializer::ReadObject(int space_
+ bool is_codespace = (space_number == CODE_SPACE);
+ ASSERT(HeapObject::FromAddress(address)->IsCode() == is_codespace);
+ #endif
++#if defined(_AIX) || defined(V8_TARGET_ARCH_PPC64)
++ // If we're on a platform that uses function_descriptors
++ // these jump tables make use of RelocInfo::INTERNAL_REFERENCE.
++ // As the V8 serialization code doesn't handle that relocation type
++ // we use this hack to fix up code that has function_descriptors
++ if (space_number == CODE_SPACE) {
++ Code * code = reinterpret_cast<Code*>(HeapObject::FromAddress(address));
++ for (RelocIterator it(code); !it.done(); it.next()) {
++ RelocInfo::Mode rmode = it.rinfo()->rmode();
++ if (rmode == RelocInfo::INTERNAL_REFERENCE) {
++ uintptr_t* p =
reinterpret_cast<uintptr_t*>(code->instruction_start());
++ *p = reinterpret_cast<uintptr_t>(p + 3);
++ }
++ }
++ }
++#endif
+ }
+
+ void Deserializer::ReadChunk(Object** current,
+@@ -941,15 +957,16 @@ void Deserializer::ReadChunk(Object** cu
+ // allocation point and write a pointer to it to the current object.
+ ALL_SPACES(kBackref, kPlain, kStartOfObject)
+ ALL_SPACES(kBackrefWithSkip, kPlain, kStartOfObject)
+-#if V8_TARGET_ARCH_MIPS
++#if defined(V8_TARGET_ARCH_MIPS) || \
++ defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64)
+ // Deserialize a new object from pointer found in code and write
+- // a pointer to it to the current object. Required only for MIPS, and
++ // a pointer to it to the current object. Required only for MIPS/PPC, and
+ // omitted on the other architectures because it is fully unrolled and
+ // would cause bloat.
+ ALL_SPACES(kNewObject, kFromCode, kStartOfObject)
+ // Find a recently deserialized code object using its offset from the
+ // current allocation point and write a pointer to it to the current
+- // object. Required only for MIPS.
++ // object. Required only for MIPS/PPC.
+ ALL_SPACES(kBackref, kFromCode, kStartOfObject)
+ ALL_SPACES(kBackrefWithSkip, kFromCode, kStartOfObject)
+ #endif
+@@ -1164,12 +1181,14 @@ int Serializer::RootIndex(HeapObject* he
+ for (int i = 0; i < root_index_wave_front_; i++) {
+ Object* root = heap->roots_array_start()[i];
+ if (!root->IsSmi() && root == heap_object) {
+-#if V8_TARGET_ARCH_MIPS
++#if defined(V8_TARGET_ARCH_MIPS) || \
++ defined(V8_TARGET_ARCH_PPC) || defined(V8_TARGET_ARCH_PPC64)
+ if (from == kFromCode) {
+- // In order to avoid code bloat in the deserializer we don't have
+- // support for the encoding that specifies a particular root should
+- // be written into the lui/ori instructions on MIPS. Therefore we
+- // should not generate such serialization data for MIPS.
++ // In order to avoid code bloat in the deserializer we don't
++ // have support for the encoding that specifies a particular
++ // root should be written into the lui/ori instructions on
++ // MIPS or lis/addic on PPC. Therefore we should not generate
++ // such serialization data for MIPS/PPC.
+ return kInvalidRootIndex;
+ }
+ #endif
+diff -up v8-3.14.5.10/src/simulator.h.ppc v8-3.14.5.10/src/simulator.h
+--- v8-3.14.5.10/src/simulator.h.ppc 2010-02-19 03:53:10.000000000 -0500
++++ v8-3.14.5.10/src/simulator.h 2016-06-07 14:15:46.008392901 -0400
+@@ -34,6 +34,8 @@
+ #include "x64/simulator-x64.h"
+ #elif V8_TARGET_ARCH_ARM
+ #include "arm/simulator-arm.h"
++#elif V8_TARGET_ARCH_PPC
++#include "ppc/simulator-ppc.h"
+ #elif V8_TARGET_ARCH_MIPS
+ #include "mips/simulator-mips.h"
+ #else
+diff -up v8-3.14.5.10/test/cctest/cctest.gyp.ppc v8-3.14.5.10/test/cctest/cctest.gyp
+--- v8-3.14.5.10/test/cctest/cctest.gyp.ppc 2016-06-07 14:15:45.964393164 -0400
++++ v8-3.14.5.10/test/cctest/cctest.gyp 2016-06-07 14:29:00.110622134 -0400
+@@ -118,6 +118,12 @@
+ 'test-disasm-arm.cc'
+ ],
+ }],
++ ['v8_target_arch=="ppc" or v8_target_arch=="ppc64"',
{
++ 'sources': [
++ 'test-assembler-ppc.cc',
++ 'test-disasm-ppc.cc'
++ ],
++ }],
+ ['v8_target_arch=="mipsel" or
v8_target_arch=="mips"', {
+ 'sources': [
+ 'test-assembler-mips.cc',
+@@ -134,6 +140,11 @@
+ 'test-platform-macos.cc',
+ ],
+ }],
++ [ 'OS=="aix" and v8_target_arch=="ppc64"', {
++ 'sources': [
++ 'test-api2.cc',
++ ],
++ }],
+ [ 'OS=="win"', {
+ 'sources': [
+ 'test-platform-win32.cc',
+diff -up v8-3.14.5.10/test/cctest/test-api2.cc.ppc v8-3.14.5.10/test/cctest/test-api2.cc
+--- v8-3.14.5.10/test/cctest/test-api2.cc.ppc 2016-06-07 14:15:46.010392889 -0400
++++ v8-3.14.5.10/test/cctest/test-api2.cc 2016-06-07 14:15:46.010392889 -0400
+@@ -0,0 +1,37 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++#include "v8.h"
++
++#if defined(_AIX) && defined(V8_HOST_ARCH_64_BIT)
++#define TEST_API_PART2
++// AIX gcc is unable to compile test-api.cc in its entirety due to the
++// following open issue:
++//
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=378
++// As a workaround, the file is split into two parts: test-api.cc and
++// test-api2.cc.
++#include "test-api.cc"
++#endif
+diff -up v8-3.14.5.10/test/cctest/test-api.cc.ppc v8-3.14.5.10/test/cctest/test-api.cc
+--- v8-3.14.5.10/test/cctest/test-api.cc.ppc 2016-06-07 14:15:45.906393510 -0400
++++ v8-3.14.5.10/test/cctest/test-api.cc 2016-06-07 14:15:46.010392889 -0400
+@@ -46,6 +46,18 @@
+ #include "parser.h"
+ #include "unicode-inl.h"
+
++#if defined(_AIX) && defined(V8_HOST_ARCH_64_BIT)
++// AIX gcc is unable to compile this file in its entirety due to the
++// following open issue:
++//
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=378
++// As a workaround, the file is split into two parts: test-api.cc and
++// test-api2.cc.
++#define TEST_API_IN_PARTS
++#if !defined(TEST_API_PART2)
++#define TEST_API_PART1
++#endif
++#endif
++
+ static const bool kLogThreading = false;
+
+ static bool IsNaN(double x) {
+diff -up v8-3.14.5.10/test/cctest/.test-assembler-arm.cc.swp.ppc
v8-3.14.5.10/test/cctest/.test-assembler-arm.cc.swp
+diff -up v8-3.14.5.10/test/cctest/test-assembler-ppc.cc.ppc
v8-3.14.5.10/test/cctest/test-assembler-ppc.cc
+--- v8-3.14.5.10/test/cctest/test-assembler-ppc.cc.ppc 2016-06-07 14:15:46.010392889
-0400
++++ v8-3.14.5.10/test/cctest/test-assembler-ppc.cc 2016-06-07 14:15:46.010392889 -0400
+@@ -0,0 +1,1076 @@
++// Copyright 2012 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++
++#include "v8.h"
++
++#include "disassembler.h"
++#include "factory.h"
++#include "ppc/simulator-ppc.h"
++#include "ppc/assembler-ppc-inl.h"
++#include "cctest.h"
++
++using namespace v8::internal;
++
++
++// Define these function prototypes to match JSEntryFunction in execution.cc.
++typedef Object* (*F1)(int x, int p1, int p2, int p3, int p4);
++typedef Object* (*F2)(int x, int y, int p2, int p3, int p4);
++typedef Object* (*F3)(void* p0, int p1, int p2, int p3, int p4);
++typedef Object* (*F4)(void* p0, void* p1, int p2, int p3, int p4);
++
++
++static v8::Persistent<v8::Context> env;
++
++
++static void InitializeVM() {
++ if (env.IsEmpty()) {
++ env = v8::Context::New();
++ }
++}
++
++
++#define __ assm.
++
++// Simple add parameter 1 to parameter 2 and return
++TEST(0) {
++ InitializeVM();
++ v8::HandleScope scope;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++
++ __ add(r3, r3, r4);
++ __ blr();
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry());
++ intptr_t res =
++ reinterpret_cast<intptr_t>(CALL_GENERATED_CODE(f, 3, 4, 0, 0, 0));
++ ::printf("f() = %" V8PRIdPTR "\n", res);
++ CHECK_EQ(7, static_cast<int>(res));
++}
++
++// Loop 100 times, adding loop counter to result
++TEST(1) {
++ InitializeVM();
++ v8::HandleScope scope;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++ Label L, C;
++
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++
++ __ mr(r4, r3);
++ __ li(r3, Operand(0, RelocInfo::NONE));
++ __ b(&C);
++
++ __ bind(&L);
++ __ add(r3, r3, r4);
++ __ subi(r4, r4, Operand(1));
++
++ __ bind(&C);
++ __ cmpi(r4, Operand(0, RelocInfo::NONE));
++ __ bne(&L);
++ __ blr();
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
++ intptr_t res =
++ reinterpret_cast<intptr_t>(CALL_GENERATED_CODE(f, 100, 0, 0, 0, 0));
++ ::printf("f() = %" V8PRIdPTR "\n", res);
++ CHECK_EQ(5050, static_cast<int>(res));
++}
++
++
++TEST(2) {
++ InitializeVM();
++ v8::HandleScope scope;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++ Label L, C;
++
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++
++ __ mr(r4, r3);
++ __ li(r3, Operand(1));
++ __ b(&C);
++
++ __ bind(&L);
++#if defined(V8_TARGET_ARCH_PPC64)
++ __ mulld(r3, r4, r3);
++#else
++ __ mullw(r3, r4, r3);
++#endif
++ __ subi(r4, r4, Operand(1));
++
++ __ bind(&C);
++ __ cmpi(r4, Operand(0, RelocInfo::NONE));
++ __ bne(&L);
++ __ blr();
++
++ // some relocated stuff here, not executed
++ __ RecordComment("dead code, just testing relocations");
++ __ mov(r0, Operand(FACTORY->true_value()));
++ __ RecordComment("dead code, just testing immediate operands");
++ __ mov(r0, Operand(-1));
++ __ mov(r0, Operand(0xFF000000));
++ __ mov(r0, Operand(0xF0F0F0F0));
++ __ mov(r0, Operand(0xFFF0FFFF));
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
++ intptr_t res =
++ reinterpret_cast<intptr_t>(CALL_GENERATED_CODE(f, 10, 0, 0, 0, 0));
++ ::printf("f() = %" V8PRIdPTR "\n", res);
++ CHECK_EQ(3628800, static_cast<int>(res));
++}
++
++
++
++TEST(3) {
++ InitializeVM();
++ v8::HandleScope scope;
++
++ typedef struct {
++ int i;
++ char c;
++ int16_t s;
++ } T;
++ T t;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++ Label L, C;
++
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++
++ // build a frame
++#if V8_TARGET_ARCH_PPC64
++ __ stdu(sp, MemOperand(sp, -32));
++ __ std(fp, MemOperand(sp, 24));
++#else
++ __ stwu(sp, MemOperand(sp, -16));
++ __ stw(fp, MemOperand(sp, 12));
++#endif
++ __ mr(fp, sp);
++
++ // r4 points to our struct
++ __ mr(r4, r3);
++
++ // modify field int i of struct
++ __ lwz(r3, MemOperand(r4, OFFSET_OF(T, i)));
++ __ srwi(r5, r3, Operand(1));
++ __ stw(r5, MemOperand(r4, OFFSET_OF(T, i)));
++
++ // modify field char c of struct
++ __ lbz(r5, MemOperand(r4, OFFSET_OF(T, c)));
++ __ add(r3, r5, r3);
++ __ slwi(r5, r5, Operand(2));
++ __ stb(r5, MemOperand(r4, OFFSET_OF(T, c)));
++
++ // modify field int16_t s of struct
++ __ lhz(r5, MemOperand(r4, OFFSET_OF(T, s)));
++ __ add(r3, r5, r3);
++ __ srwi(r5, r5, Operand(3));
++ __ sth(r5, MemOperand(r4, OFFSET_OF(T, s)));
++
++ // restore frame
++#if V8_TARGET_ARCH_PPC64
++ __ addi(r11, fp, Operand(32));
++ __ ld(fp, MemOperand(r11, -8));
++#else
++ __ addi(r11, fp, Operand(16));
++ __ lwz(fp, MemOperand(r11, -4));
++#endif
++ __ mr(sp, r11);
++ __ blr();
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F3 f = FUNCTION_CAST<F3>(Code::cast(code)->entry());
++ t.i = 100000;
++ t.c = 10;
++ t.s = 1000;
++ intptr_t res =
++ reinterpret_cast<intptr_t>(CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0));
++ ::printf("f() = %" V8PRIdPTR "\n", res);
++ CHECK_EQ(101010, static_cast<int>(res));
++ CHECK_EQ(100000/2, t.i);
++ CHECK_EQ(10*4, t.c);
++ CHECK_EQ(1000/8, t.s);
++}
++
++#if 0
++TEST(4) {
++ // Test the VFP floating point instructions.
++ InitializeVM();
++ v8::HandleScope scope;
++
++ typedef struct {
++ double a;
++ double b;
++ double c;
++ double d;
++ double e;
++ double f;
++ double g;
++ double h;
++ int i;
++ double m;
++ double n;
++ float x;
++ float y;
++ } T;
++ T t;
++
++ // Create a function that accepts &t, and loads, manipulates, and stores
++ // the doubles and floats.
++ Assembler assm(Isolate::Current(), NULL, 0);
++ Label L, C;
++
++ if (CpuFeatures::IsSupported(VFP3)) {
++ CpuFeatures::Scope scope(VFP3);
++
++ __ mov(ip, Operand(sp));
++ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
++ __ sub(fp, ip, Operand(4));
++
++ __ mov(r4, Operand(r0));
++ __ vldr(d6, r4, OFFSET_OF(T, a));
++ __ vldr(d7, r4, OFFSET_OF(T, b));
++ __ vadd(d5, d6, d7);
++ __ vstr(d5, r4, OFFSET_OF(T, c));
++
++ __ vmov(r2, r3, d5);
++ __ vmov(d4, r2, r3);
++ __ vstr(d4, r4, OFFSET_OF(T, b));
++
++ // Load t.x and t.y, switch values, and store back to the struct.
++ __ vldr(s0, r4, OFFSET_OF(T, x));
++ __ vldr(s31, r4, OFFSET_OF(T, y));
++ __ vmov(s16, s0);
++ __ vmov(s0, s31);
++ __ vmov(s31, s16);
++ __ vstr(s0, r4, OFFSET_OF(T, x));
++ __ vstr(s31, r4, OFFSET_OF(T, y));
++
++ // Move a literal into a register that can be encoded in the instruction.
++ __ vmov(d4, 1.0);
++ __ vstr(d4, r4, OFFSET_OF(T, e));
++
++ // Move a literal into a register that requires 64 bits to encode.
++ // 0x3ff0000010000000 = 1.000000059604644775390625
++ __ vmov(d4, 1.000000059604644775390625);
++ __ vstr(d4, r4, OFFSET_OF(T, d));
++
++ // Convert from floating point to integer.
++ __ vmov(d4, 2.0);
++ __ vcvt_s32_f64(s31, d4);
++ __ vstr(s31, r4, OFFSET_OF(T, i));
++
++ // Convert from integer to floating point.
++ __ mov(lr, Operand(42));
++ __ vmov(s31, lr);
++ __ vcvt_f64_s32(d4, s31);
++ __ vstr(d4, r4, OFFSET_OF(T, f));
++
++ // Test vabs.
++ __ vldr(d1, r4, OFFSET_OF(T, g));
++ __ vabs(d0, d1);
++ __ vstr(d0, r4, OFFSET_OF(T, g));
++ __ vldr(d2, r4, OFFSET_OF(T, h));
++ __ vabs(d0, d2);
++ __ vstr(d0, r4, OFFSET_OF(T, h));
++
++ // Test vneg.
++ __ vldr(d1, r4, OFFSET_OF(T, m));
++ __ vneg(d0, d1);
++ __ vstr(d0, r4, OFFSET_OF(T, m));
++ __ vldr(d1, r4, OFFSET_OF(T, n));
++ __ vneg(d0, d1);
++ __ vstr(d0, r4, OFFSET_OF(T, n));
++
++ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F3 f = FUNCTION_CAST<F3>(Code::cast(code)->entry());
++ t.a = 1.5;
++ t.b = 2.75;
++ t.c = 17.17;
++ t.d = 0.0;
++ t.e = 0.0;
++ t.f = 0.0;
++ t.g = -2718.2818;
++ t.h = 31415926.5;
++ t.i = 0;
++ t.m = -2718.2818;
++ t.n = 123.456;
++ t.x = 4.5;
++ t.y = 9.0;
++ Object* dummy = CALL_GENERATED_CODE(f, &t, 0, 0, 0, 0);
++ USE(dummy);
++ CHECK_EQ(4.5, t.y);
++ CHECK_EQ(9.0, t.x);
++ CHECK_EQ(-123.456, t.n);
++ CHECK_EQ(2718.2818, t.m);
++ CHECK_EQ(2, t.i);
++ CHECK_EQ(2718.2818, t.g);
++ CHECK_EQ(31415926.5, t.h);
++ CHECK_EQ(42.0, t.f);
++ CHECK_EQ(1.0, t.e);
++ CHECK_EQ(1.000000059604644775390625, t.d);
++ CHECK_EQ(4.25, t.c);
++ CHECK_EQ(4.25, t.b);
++ CHECK_EQ(1.5, t.a);
++ }
++}
++
++
++TEST(5) {
++ // Test the ARMv7 bitfield instructions.
++ InitializeVM();
++ v8::HandleScope scope;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ if (CpuFeatures::IsSupported(ARMv7)) {
++ CpuFeatures::Scope scope(ARMv7);
++ // On entry, r0 = 0xAAAAAAAA = 0b10..10101010.
++ __ ubfx(r0, r0, 1, 12); // 0b00..010101010101 = 0x555
++ __ sbfx(r0, r0, 0, 5); // 0b11..111111110101 = -11
++ __ bfc(r0, 1, 3); // 0b11..111111110001 = -15
++ __ mov(r1, Operand(7));
++ __ bfi(r0, r1, 3, 3); // 0b11..111111111001 = -7
++ __ mov(pc, Operand(lr));
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
++ int res = reinterpret_cast<int>(
++ CALL_GENERATED_CODE(f, 0xAAAAAAAA, 0, 0, 0, 0));
++ ::printf("f() = %d\n", res);
++ CHECK_EQ(-7, res);
++ }
++}
++
++
++TEST(6) {
++ // Test saturating instructions.
++ InitializeVM();
++ v8::HandleScope scope;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ if (CpuFeatures::IsSupported(ARMv7)) {
++ CpuFeatures::Scope scope(ARMv7);
++ __ usat(r1, 8, Operand(r0)); // Sat 0xFFFF to 0-255 = 0xFF.
++ __ usat(r2, 12, Operand(r0, ASR, 9)); // Sat (0xFFFF>>9) to 0-4095 = 0x7F.
++ __ usat(r3, 1, Operand(r0, LSL, 16)); // Sat (0xFFFF<<16) to 0-1 = 0x0.
++ __ addi(r0, r1, Operand(r2));
++ __ addi(r0, r0, Operand(r3));
++ __ mov(pc, Operand(lr));
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
++ int res = reinterpret_cast<int>(
++ CALL_GENERATED_CODE(f, 0xFFFF, 0, 0, 0, 0));
++ ::printf("f() = %d\n", res);
++ CHECK_EQ(382, res);
++ }
++}
++
++enum VCVTTypes {
++ s32_f64,
++ u32_f64
++};
++
++static void TestRoundingMode(VCVTTypes types,
++ VFPRoundingMode mode,
++ double value,
++ int expected,
++ bool expected_exception = false) {
++ InitializeVM();
++ v8::HandleScope scope;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ if (CpuFeatures::IsSupported(VFP3)) {
++ CpuFeatures::Scope scope(VFP3);
++
++ Label wrong_exception;
++
++ __ vmrs(r1);
++ // Set custom FPSCR.
++ __ bic(r2, r1, Operand(kVFPRoundingModeMask | kVFPExceptionMask));
++ __ orr(r2, r2, Operand(mode));
++ __ vmsr(r2);
++
++ // Load value, convert, and move back result to r0 if everything went well.
++ __ vmov(d1, value);
++ switch (types) {
++ case s32_f64:
++ __ vcvt_s32_f64(s0, d1, kFPSCRRounding);
++ break;
++
++ case u32_f64:
++ __ vcvt_u32_f64(s0, d1, kFPSCRRounding);
++ break;
++
++ default:
++ UNREACHABLE();
++ break;
++ }
++ // Check for vfp exceptions
++ __ vmrs(r2);
++ __ tst(r2, Operand(kVFPExceptionMask));
++ // Check that we behaved as expected.
++ __ b(&wrong_exception,
++ expected_exception ? eq : ne);
++ // There was no exception. Retrieve the result and return.
++ __ vmov(r0, s0);
++ __ mov(pc, Operand(lr));
++
++ // The exception behaviour is not what we expected.
++ // Load a special value and return.
++ __ bind(&wrong_exception);
++ __ mov(r0, Operand(11223344));
++ __ mov(pc, Operand(lr));
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
++ int res = reinterpret_cast<int>(
++ CALL_GENERATED_CODE(f, 0, 0, 0, 0, 0));
++ ::printf("res = %d\n", res);
++ CHECK_EQ(expected, res);
++ }
++}
++
++
++TEST(7) {
++ // Test vfp rounding modes.
++
++ // s32_f64 (double to integer).
++
++ TestRoundingMode(s32_f64, RN, 0, 0);
++ TestRoundingMode(s32_f64, RN, 0.5, 0);
++ TestRoundingMode(s32_f64, RN, -0.5, 0);
++ TestRoundingMode(s32_f64, RN, 1.5, 2);
++ TestRoundingMode(s32_f64, RN, -1.5, -2);
++ TestRoundingMode(s32_f64, RN, 123.7, 124);
++ TestRoundingMode(s32_f64, RN, -123.7, -124);
++ TestRoundingMode(s32_f64, RN, 123456.2, 123456);
++ TestRoundingMode(s32_f64, RN, -123456.2, -123456);
++ TestRoundingMode(s32_f64, RN, static_cast<double>(kMaxInt), kMaxInt);
++ TestRoundingMode(s32_f64, RN, (kMaxInt + 0.49), kMaxInt);
++ TestRoundingMode(s32_f64, RN, (kMaxInt + 1.0), kMaxInt, true);
++ TestRoundingMode(s32_f64, RN, (kMaxInt + 0.5), kMaxInt, true);
++ TestRoundingMode(s32_f64, RN, static_cast<double>(kMinInt), kMinInt);
++ TestRoundingMode(s32_f64, RN, (kMinInt - 0.5), kMinInt);
++ TestRoundingMode(s32_f64, RN, (kMinInt - 1.0), kMinInt, true);
++ TestRoundingMode(s32_f64, RN, (kMinInt - 0.51), kMinInt, true);
++
++ TestRoundingMode(s32_f64, RM, 0, 0);
++ TestRoundingMode(s32_f64, RM, 0.5, 0);
++ TestRoundingMode(s32_f64, RM, -0.5, -1);
++ TestRoundingMode(s32_f64, RM, 123.7, 123);
++ TestRoundingMode(s32_f64, RM, -123.7, -124);
++ TestRoundingMode(s32_f64, RM, 123456.2, 123456);
++ TestRoundingMode(s32_f64, RM, -123456.2, -123457);
++ TestRoundingMode(s32_f64, RM, static_cast<double>(kMaxInt), kMaxInt);
++ TestRoundingMode(s32_f64, RM, (kMaxInt + 0.5), kMaxInt);
++ TestRoundingMode(s32_f64, RM, (kMaxInt + 1.0), kMaxInt, true);
++ TestRoundingMode(s32_f64, RM, static_cast<double>(kMinInt), kMinInt);
++ TestRoundingMode(s32_f64, RM, (kMinInt - 0.5), kMinInt, true);
++ TestRoundingMode(s32_f64, RM, (kMinInt + 0.5), kMinInt);
++
++ TestRoundingMode(s32_f64, RZ, 0, 0);
++ TestRoundingMode(s32_f64, RZ, 0.5, 0);
++ TestRoundingMode(s32_f64, RZ, -0.5, 0);
++ TestRoundingMode(s32_f64, RZ, 123.7, 123);
++ TestRoundingMode(s32_f64, RZ, -123.7, -123);
++ TestRoundingMode(s32_f64, RZ, 123456.2, 123456);
++ TestRoundingMode(s32_f64, RZ, -123456.2, -123456);
++ TestRoundingMode(s32_f64, RZ, static_cast<double>(kMaxInt), kMaxInt);
++ TestRoundingMode(s32_f64, RZ, (kMaxInt + 0.5), kMaxInt);
++ TestRoundingMode(s32_f64, RZ, (kMaxInt + 1.0), kMaxInt, true);
++ TestRoundingMode(s32_f64, RZ, static_cast<double>(kMinInt), kMinInt);
++ TestRoundingMode(s32_f64, RZ, (kMinInt - 0.5), kMinInt);
++ TestRoundingMode(s32_f64, RZ, (kMinInt - 1.0), kMinInt, true);
++
++
++ // u32_f64 (double to integer).
++
++ // Negative values.
++ TestRoundingMode(u32_f64, RN, -0.5, 0);
++ TestRoundingMode(u32_f64, RN, -123456.7, 0, true);
++ TestRoundingMode(u32_f64, RN, static_cast<double>(kMinInt), 0, true);
++ TestRoundingMode(u32_f64, RN, kMinInt - 1.0, 0, true);
++
++ TestRoundingMode(u32_f64, RM, -0.5, 0, true);
++ TestRoundingMode(u32_f64, RM, -123456.7, 0, true);
++ TestRoundingMode(u32_f64, RM, static_cast<double>(kMinInt), 0, true);
++ TestRoundingMode(u32_f64, RM, kMinInt - 1.0, 0, true);
++
++ TestRoundingMode(u32_f64, RZ, -0.5, 0);
++ TestRoundingMode(u32_f64, RZ, -123456.7, 0, true);
++ TestRoundingMode(u32_f64, RZ, static_cast<double>(kMinInt), 0, true);
++ TestRoundingMode(u32_f64, RZ, kMinInt - 1.0, 0, true);
++
++ // Positive values.
++ // kMaxInt is the maximum *signed* integer: 0x7fffffff.
++ static const uint32_t kMaxUInt = 0xffffffffu;
++ TestRoundingMode(u32_f64, RZ, 0, 0);
++ TestRoundingMode(u32_f64, RZ, 0.5, 0);
++ TestRoundingMode(u32_f64, RZ, 123.7, 123);
++ TestRoundingMode(u32_f64, RZ, 123456.2, 123456);
++ TestRoundingMode(u32_f64, RZ, static_cast<double>(kMaxInt), kMaxInt);
++ TestRoundingMode(u32_f64, RZ, (kMaxInt + 0.5), kMaxInt);
++ TestRoundingMode(u32_f64, RZ, (kMaxInt + 1.0),
++ static_cast<uint32_t>(kMaxInt) + 1);
++ TestRoundingMode(u32_f64, RZ, (kMaxUInt + 0.5), kMaxUInt);
++ TestRoundingMode(u32_f64, RZ, (kMaxUInt + 1.0), kMaxUInt, true);
++
++ TestRoundingMode(u32_f64, RM, 0, 0);
++ TestRoundingMode(u32_f64, RM, 0.5, 0);
++ TestRoundingMode(u32_f64, RM, 123.7, 123);
++ TestRoundingMode(u32_f64, RM, 123456.2, 123456);
++ TestRoundingMode(u32_f64, RM, static_cast<double>(kMaxInt), kMaxInt);
++ TestRoundingMode(u32_f64, RM, (kMaxInt + 0.5), kMaxInt);
++ TestRoundingMode(u32_f64, RM, (kMaxInt + 1.0),
++ static_cast<uint32_t>(kMaxInt) + 1);
++ TestRoundingMode(u32_f64, RM, (kMaxUInt + 0.5), kMaxUInt);
++ TestRoundingMode(u32_f64, RM, (kMaxUInt + 1.0), kMaxUInt, true);
++
++ TestRoundingMode(u32_f64, RN, 0, 0);
++ TestRoundingMode(u32_f64, RN, 0.5, 0);
++ TestRoundingMode(u32_f64, RN, 1.5, 2);
++ TestRoundingMode(u32_f64, RN, 123.7, 124);
++ TestRoundingMode(u32_f64, RN, 123456.2, 123456);
++ TestRoundingMode(u32_f64, RN, static_cast<double>(kMaxInt), kMaxInt);
++ TestRoundingMode(u32_f64, RN, (kMaxInt + 0.49), kMaxInt);
++ TestRoundingMode(u32_f64, RN, (kMaxInt + 0.5),
++ static_cast<uint32_t>(kMaxInt) + 1);
++ TestRoundingMode(u32_f64, RN, (kMaxUInt + 0.49), kMaxUInt);
++ TestRoundingMode(u32_f64, RN, (kMaxUInt + 0.5), kMaxUInt, true);
++ TestRoundingMode(u32_f64, RN, (kMaxUInt + 1.0), kMaxUInt, true);
++}
++
++TEST(8) {
++ // Test VFP multi load/store with ia_w.
++ InitializeVM();
++ v8::HandleScope scope;
++
++ typedef struct {
++ double a;
++ double b;
++ double c;
++ double d;
++ double e;
++ double f;
++ double g;
++ double h;
++ } D;
++ D d;
++
++ typedef struct {
++ float a;
++ float b;
++ float c;
++ float d;
++ float e;
++ float f;
++ float g;
++ float h;
++ } F;
++ F f;
++
++ // Create a function that uses vldm/vstm to move some double and
++ // single precision values around in memory.
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ if (CpuFeatures::IsSupported(VFP2)) {
++ CpuFeatures::Scope scope(VFP2);
++
++ __ mov(ip, Operand(sp));
++ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
++ __ sub(fp, ip, Operand(4));
++
++ __ addi(r4, r0, Operand(OFFSET_OF(D, a)));
++ __ vldm(ia_w, r4, d0, d3);
++ __ vldm(ia_w, r4, d4, d7);
++
++ __ addi(r4, r0, Operand(OFFSET_OF(D, a)));
++ __ vstm(ia_w, r4, d6, d7);
++ __ vstm(ia_w, r4, d0, d5);
++
++ __ addi(r4, r1, Operand(OFFSET_OF(F, a)));
++ __ vldm(ia_w, r4, s0, s3);
++ __ vldm(ia_w, r4, s4, s7);
++
++ __ addi(r4, r1, Operand(OFFSET_OF(F, a)));
++ __ vstm(ia_w, r4, s6, s7);
++ __ vstm(ia_w, r4, s0, s5);
++
++ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F4 fn = FUNCTION_CAST<F4>(Code::cast(code)->entry());
++ d.a = 1.1;
++ d.b = 2.2;
++ d.c = 3.3;
++ d.d = 4.4;
++ d.e = 5.5;
++ d.f = 6.6;
++ d.g = 7.7;
++ d.h = 8.8;
++
++ f.a = 1.0;
++ f.b = 2.0;
++ f.c = 3.0;
++ f.d = 4.0;
++ f.e = 5.0;
++ f.f = 6.0;
++ f.g = 7.0;
++ f.h = 8.0;
++
++ Object* dummy = CALL_GENERATED_CODE(fn, &d, &f, 0, 0, 0);
++ USE(dummy);
++
++ CHECK_EQ(7.7, d.a);
++ CHECK_EQ(8.8, d.b);
++ CHECK_EQ(1.1, d.c);
++ CHECK_EQ(2.2, d.d);
++ CHECK_EQ(3.3, d.e);
++ CHECK_EQ(4.4, d.f);
++ CHECK_EQ(5.5, d.g);
++ CHECK_EQ(6.6, d.h);
++
++ CHECK_EQ(7.0, f.a);
++ CHECK_EQ(8.0, f.b);
++ CHECK_EQ(1.0, f.c);
++ CHECK_EQ(2.0, f.d);
++ CHECK_EQ(3.0, f.e);
++ CHECK_EQ(4.0, f.f);
++ CHECK_EQ(5.0, f.g);
++ CHECK_EQ(6.0, f.h);
++ }
++}
++
++
++TEST(9) {
++ // Test VFP multi load/store with ia.
++ InitializeVM();
++ v8::HandleScope scope;
++
++ typedef struct {
++ double a;
++ double b;
++ double c;
++ double d;
++ double e;
++ double f;
++ double g;
++ double h;
++ } D;
++ D d;
++
++ typedef struct {
++ float a;
++ float b;
++ float c;
++ float d;
++ float e;
++ float f;
++ float g;
++ float h;
++ } F;
++ F f;
++
++ // Create a function that uses vldm/vstm to move some double and
++ // single precision values around in memory.
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ if (CpuFeatures::IsSupported(VFP2)) {
++ CpuFeatures::Scope scope(VFP2);
++
++ __ mov(ip, Operand(sp));
++ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
++ __ sub(fp, ip, Operand(4));
++
++ __ addi(r4, r0, Operand(OFFSET_OF(D, a)));
++ __ vldm(ia, r4, d0, d3);
++ __ addi(r4, r4, Operand(4 * 8));
++ __ vldm(ia, r4, d4, d7);
++
++ __ addi(r4, r0, Operand(OFFSET_OF(D, a)));
++ __ vstm(ia, r4, d6, d7);
++ __ addi(r4, r4, Operand(2 * 8));
++ __ vstm(ia, r4, d0, d5);
++
++ __ addi(r4, r1, Operand(OFFSET_OF(F, a)));
++ __ vldm(ia, r4, s0, s3);
++ __ addi(r4, r4, Operand(4 * 4));
++ __ vldm(ia, r4, s4, s7);
++
++ __ addi(r4, r1, Operand(OFFSET_OF(F, a)));
++ __ vstm(ia, r4, s6, s7);
++ __ addi(r4, r4, Operand(2 * 4));
++ __ vstm(ia, r4, s0, s5);
++
++ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F4 fn = FUNCTION_CAST<F4>(Code::cast(code)->entry());
++ d.a = 1.1;
++ d.b = 2.2;
++ d.c = 3.3;
++ d.d = 4.4;
++ d.e = 5.5;
++ d.f = 6.6;
++ d.g = 7.7;
++ d.h = 8.8;
++
++ f.a = 1.0;
++ f.b = 2.0;
++ f.c = 3.0;
++ f.d = 4.0;
++ f.e = 5.0;
++ f.f = 6.0;
++ f.g = 7.0;
++ f.h = 8.0;
++
++ Object* dummy = CALL_GENERATED_CODE(fn, &d, &f, 0, 0, 0);
++ USE(dummy);
++
++ CHECK_EQ(7.7, d.a);
++ CHECK_EQ(8.8, d.b);
++ CHECK_EQ(1.1, d.c);
++ CHECK_EQ(2.2, d.d);
++ CHECK_EQ(3.3, d.e);
++ CHECK_EQ(4.4, d.f);
++ CHECK_EQ(5.5, d.g);
++ CHECK_EQ(6.6, d.h);
++
++ CHECK_EQ(7.0, f.a);
++ CHECK_EQ(8.0, f.b);
++ CHECK_EQ(1.0, f.c);
++ CHECK_EQ(2.0, f.d);
++ CHECK_EQ(3.0, f.e);
++ CHECK_EQ(4.0, f.f);
++ CHECK_EQ(5.0, f.g);
++ CHECK_EQ(6.0, f.h);
++ }
++}
++
++
++TEST(10) {
++ // Test VFP multi load/store with db_w.
++ InitializeVM();
++ v8::HandleScope scope;
++
++ typedef struct {
++ double a;
++ double b;
++ double c;
++ double d;
++ double e;
++ double f;
++ double g;
++ double h;
++ } D;
++ D d;
++
++ typedef struct {
++ float a;
++ float b;
++ float c;
++ float d;
++ float e;
++ float f;
++ float g;
++ float h;
++ } F;
++ F f;
++
++ // Create a function that uses vldm/vstm to move some double and
++ // single precision values around in memory.
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ if (CpuFeatures::IsSupported(VFP2)) {
++ CpuFeatures::Scope scope(VFP2);
++
++ __ mov(ip, Operand(sp));
++ __ stm(db_w, sp, r4.bit() | fp.bit() | lr.bit());
++ __ sub(fp, ip, Operand(4));
++
++ __ addi(r4, r0, Operand(OFFSET_OF(D, h) + 8));
++ __ vldm(db_w, r4, d4, d7);
++ __ vldm(db_w, r4, d0, d3);
++
++ __ addi(r4, r0, Operand(OFFSET_OF(D, h) + 8));
++ __ vstm(db_w, r4, d0, d5);
++ __ vstm(db_w, r4, d6, d7);
++
++ __ addi(r4, r1, Operand(OFFSET_OF(F, h) + 4));
++ __ vldm(db_w, r4, s4, s7);
++ __ vldm(db_w, r4, s0, s3);
++
++ __ addi(r4, r1, Operand(OFFSET_OF(F, h) + 4));
++ __ vstm(db_w, r4, s0, s5);
++ __ vstm(db_w, r4, s6, s7);
++
++ __ ldm(ia_w, sp, r4.bit() | fp.bit() | pc.bit());
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F4 fn = FUNCTION_CAST<F4>(Code::cast(code)->entry());
++ d.a = 1.1;
++ d.b = 2.2;
++ d.c = 3.3;
++ d.d = 4.4;
++ d.e = 5.5;
++ d.f = 6.6;
++ d.g = 7.7;
++ d.h = 8.8;
++
++ f.a = 1.0;
++ f.b = 2.0;
++ f.c = 3.0;
++ f.d = 4.0;
++ f.e = 5.0;
++ f.f = 6.0;
++ f.g = 7.0;
++ f.h = 8.0;
++
++ Object* dummy = CALL_GENERATED_CODE(fn, &d, &f, 0, 0, 0);
++ USE(dummy);
++
++ CHECK_EQ(7.7, d.a);
++ CHECK_EQ(8.8, d.b);
++ CHECK_EQ(1.1, d.c);
++ CHECK_EQ(2.2, d.d);
++ CHECK_EQ(3.3, d.e);
++ CHECK_EQ(4.4, d.f);
++ CHECK_EQ(5.5, d.g);
++ CHECK_EQ(6.6, d.h);
++
++ CHECK_EQ(7.0, f.a);
++ CHECK_EQ(8.0, f.b);
++ CHECK_EQ(1.0, f.c);
++ CHECK_EQ(2.0, f.d);
++ CHECK_EQ(3.0, f.e);
++ CHECK_EQ(4.0, f.f);
++ CHECK_EQ(5.0, f.g);
++ CHECK_EQ(6.0, f.h);
++ }
++}
++
++
++TEST(11) {
++ // Test instructions using the carry flag.
++ InitializeVM();
++ v8::HandleScope scope;
++
++ typedef struct {
++ int32_t a;
++ int32_t b;
++ int32_t c;
++ int32_t d;
++ } I;
++ I i;
++
++ i.a = 0xabcd0001;
++ i.b = 0xabcd0000;
++
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ // Test HeapObject untagging.
++ __ ldr(r1, MemOperand(r0, OFFSET_OF(I, a)));
++ __ mov(r1, Operand(r1, ASR, 1), SetCC);
++ __ adc(r1, r1, Operand(r1), LeaveCC, cs);
++ __ str(r1, MemOperand(r0, OFFSET_OF(I, a)));
++
++ __ ldr(r2, MemOperand(r0, OFFSET_OF(I, b)));
++ __ mov(r2, Operand(r2, ASR, 1), SetCC);
++ __ adc(r2, r2, Operand(r2), LeaveCC, cs);
++ __ str(r2, MemOperand(r0, OFFSET_OF(I, b)));
++
++ // Test corner cases.
++ __ mov(r1, Operand(0xffffffff));
++ __ mov(r2, Operand(0));
++ __ mov(r3, Operand(r1, ASR, 1), SetCC); // Set the carry.
++ __ adc(r3, r1, Operand(r2));
++ __ str(r3, MemOperand(r0, OFFSET_OF(I, c)));
++
++ __ mov(r1, Operand(0xffffffff));
++ __ mov(r2, Operand(0));
++ __ mov(r3, Operand(r2, ASR, 1), SetCC); // Unset the carry.
++ __ adc(r3, r1, Operand(r2));
++ __ str(r3, MemOperand(r0, OFFSET_OF(I, d)));
++
++ __ mov(pc, Operand(lr));
++
++ CodeDesc desc;
++ assm.GetCode(&desc);
++ Object* code = HEAP->CreateCode(
++ desc,
++ Code::ComputeFlags(Code::STUB),
++ Handle<Object>(HEAP->undefined_value()))->ToObjectChecked();
++ CHECK(code->IsCode());
++#ifdef DEBUG
++ Code::cast(code)->Print();
++#endif
++ F3 f = FUNCTION_CAST<F3>(Code::cast(code)->entry());
++ Object* dummy = CALL_GENERATED_CODE(f, &i, 0, 0, 0, 0);
++ USE(dummy);
++
++ CHECK_EQ(0xabcd0001, i.a);
++ CHECK_EQ(static_cast<int32_t>(0xabcd0000) >> 1, i.b);
++ CHECK_EQ(0x00000000, i.c);
++ CHECK_EQ(0xffffffff, i.d);
++}
++
++
++TEST(12) {
++ // Test chaining of label usages within instructions (issue 1644).
++ InitializeVM();
++ v8::HandleScope scope;
++ Assembler assm(Isolate::Current(), NULL, 0);
++
++ Label target;
++ __ b(eq, &target);
++ __ b(ne, &target);
++ __ bind(&target);
++ __ nop();
++}
++#endif // roohack
++
++#undef __
+diff -up v8-3.14.5.10/test/cctest/test-disasm-ppc.cc.ppc
v8-3.14.5.10/test/cctest/test-disasm-ppc.cc
+--- v8-3.14.5.10/test/cctest/test-disasm-ppc.cc.ppc 2016-06-07 14:15:46.011392883 -0400
++++ v8-3.14.5.10/test/cctest/test-disasm-ppc.cc 2016-06-07 14:15:46.011392883 -0400
+@@ -0,0 +1,765 @@
++// Copyright 2011 the V8 project authors. All rights reserved.
++// Redistribution and use in source and binary forms, with or without
++// modification, are permitted provided that the following conditions are
++// met:
++//
++// * Redistributions of source code must retain the above copyright
++// notice, this list of conditions and the following disclaimer.
++// * Redistributions in binary form must reproduce the above
++// copyright notice, this list of conditions and the following
++// disclaimer in the documentation and/or other materials provided
++// with the distribution.
++// * Neither the name of Google Inc. nor the names of its
++// contributors may be used to endorse or promote products derived
++// from this software without specific prior written permission.
++//
++// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
++// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
++// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
++// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
++// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
++// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
++// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
++// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
++// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
++// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++//
++
++#include <stdlib.h>
++
++#include "v8.h"
++
++#include "debug.h"
++#include "disasm.h"
++#include "disassembler.h"
++#include "macro-assembler.h"
++#include "serialize.h"
++#include "cctest.h"
++
++// todo: fix references to these
++#define lr r14
++#define pc r15
++
++using namespace v8::internal;
++
++
++static v8::Persistent<v8::Context> env;
++
++static void InitializeVM() {
++ if (env.IsEmpty()) {
++ env = v8::Context::New();
++ }
++}
++
++
++bool DisassembleAndCompare(byte* pc, const char* compare_string) {
++ disasm::NameConverter converter;
++ disasm::Disassembler disasm(converter);
++ EmbeddedVector<char, 128> disasm_buffer;
++
++ disasm.InstructionDecode(disasm_buffer, pc);
++
++ if (strcmp(compare_string, disasm_buffer.start()) != 0) {
++ fprintf(stderr,
++ "expected: \n"
++ "%s\n"
++ "disassembled: \n"
++ "%s\n\n",
++ compare_string, disasm_buffer.start());
++ return false;
++ }
++ return true;
++}
++
++
++// Set up V8 to a state where we can at least run the assembler and
++// disassembler. Declare the variables and allocate the data structures used
++// in the rest of the macros.
++#define SET_UP() \
++ InitializeVM(); \
++ v8::HandleScope scope; \
++ byte *buffer = reinterpret_cast<byte*>(malloc(4*1024)); \
++ Assembler assm(Isolate::Current(), buffer, 4*1024); \
++ bool failure = false;
++
++
++// This macro assembles one instruction using the preallocated assembler and
++// disassembles the generated instruction, comparing the output to the expected
++// value. If the comparison fails an error message is printed, but the test
++// continues to run until the end.
++#define COMPARE(asm_, compare_string) \
++ { \
++ int pc_offset = assm.pc_offset(); \
++ byte *progcounter = &buffer[pc_offset]; \
++ assm.asm_; \
++ if (!DisassembleAndCompare(progcounter, compare_string)) failure = true; \
++ }
++
++// Force emission of any pending literals into a pool.
++#define EMIT_PENDING_LITERALS() \
++ assm.CheckConstPool(true, false)
++
++
++// Verify that all invocations of the COMPARE macro passed successfully.
++// Exit with a failure if at least one of the tests failed.
++#define VERIFY_RUN() \
++if (failure) { \
++ V8_Fatal(__FILE__, __LINE__, "ARM Disassembler tests failed.\n"); \
++ }
++
++#ifdef PENGUIN_CLEANUP
++TEST(Type0) {
++ SET_UP();
++
++ COMPARE(and_(r0, r1, r2),
++ "e0010002 and r0, r1, r2");
++ COMPARE(and_(r1, r2, r3, LeaveRC),
++ "e0021003 and r1, r2, r3");
++ COMPARE(and_(r2, r3, Operand(r4), SetCC),
++ "e0132004 ands r2, r3, r4");
++ COMPARE(and_(r3, r4, Operand(r5), LeaveCC, eq),
++ "00043005 andeq r3, r4, r5");
++
++ COMPARE(eor(r4, r5, Operand(r6, LSL, 0)),
++ "e0254006 eor r4, r5, r6");
++ COMPARE(eor(r4, r5, Operand(r7, LSL, 1), SetCC),
++ "e0354087 eors r4, r5, r7, lsl #1");
++ COMPARE(eor(r4, r5, Operand(r8, LSL, 2), LeaveCC, ne),
++ "10254108 eorne r4, r5, r8, lsl #2");
++ COMPARE(eor(r4, r5, Operand(r9, LSL, 3), SetCC, cs),
++ "20354189 eorcss r4, r5, r9, lsl #3");
++
++ COMPARE(sub(r5, r6, Operand(r10, LSL, 31), LeaveCC, hs),
++ "20465f8a subcs r5, r6, r10, lsl #31");
++ COMPARE(sub(r5, r6, Operand(r10, LSL, 30), SetCC, cc),
++ "30565f0a subccs r5, r6, r10, lsl #30");
++ COMPARE(sub(r5, r6, Operand(r10, LSL, 24), LeaveCC, lo),
++ "30465c0a subcc r5, r6, r10, lsl #24");
++ COMPARE(sub(r5, r6, Operand(r10, LSL, 16), SetCC, mi),
++ "4056580a submis r5, r6, r10, lsl #16");
++
++ COMPARE(rsb(r6, r7, Operand(r11)),
++ "e067600b rsb r6, r7, fp");
++ COMPARE(rsb(r6, r7, Operand(r11, LSR, 1)),
++ "e06760ab rsb r6, r7, fp, lsr #1");
++ COMPARE(rsb(r6, r7, Operand(r11, LSR, 0), SetCC),
++ "e077602b rsbs r6, r7, r11, lsr #32");
++ COMPARE(rsb(r6, r7, Operand(r11, LSR, 31), LeaveCC, pl),
++ "50676fab rsbpl r6, r7, r11, lsr #31");
++
++ COMPARE(add(r7, r8, Operand(ip, ASR, 1)),
++ "e08870cc add r7, r8, ip, asr #1");
++ COMPARE(add(r7, r8, Operand(ip, ASR, 0)),
++ "e088704c add r7, r8, ip, asr #32");
++ COMPARE(add(r7, r8, Operand(ip), SetCC),
++ "e098700c adds r7, r8, ip");
++ COMPARE(add(r7, r8, Operand(ip, ASR, 31), SetCC, vs),
++ "60987fcc addvss r7, r8, ip, asr #31");
++
++ COMPARE(adc(r7, r11, Operand(ip, ASR, 5)),
++ "e0ab72cc adc r7, r11, ip, asr #5");
++ COMPARE(adc(r4, ip, Operand(ip, ASR, 1), LeaveCC, vc),
++ "70ac40cc adcvc r4, ip, ip, asr #1");
++ COMPARE(adc(r5, sp, Operand(ip), SetCC),
++ "e0bd500c adcs r5, sp, ip");
++ COMPARE(adc(r8, lr, Operand(ip, ASR, 31), SetCC, vc),
++ "70be8fcc adcvcs r8, lr, ip, asr #31");
++
++ COMPARE(sbc(r7, r1, Operand(ip, ROR, 1), LeaveCC, hi),
++ "80c170ec sbchi r7, r1, ip, ror #1");
++ COMPARE(sbc(r7, r9, Operand(ip, ROR, 4)),
++ "e0c9726c sbc r7, r9, ip, ror #4");
++ COMPARE(sbc(r7, r10, Operand(ip), SetCC),
++ "e0da700c sbcs r7, r10, ip");
++ COMPARE(sbc(r7, ip, Operand(ip, ROR, 31), SetCC, hi),
++ "80dc7fec sbchis r7, ip, ip, ror #31");
++
++ COMPARE(rsc(r7, r8, Operand(ip, LSL, r0)),
++ "e0e8701c rsc r7, r8, ip, lsl r0");
++ COMPARE(rsc(r7, r8, Operand(ip, LSL, r1)),
++ "e0e8711c rsc r7, r8, ip, lsl r1");
++ COMPARE(rsc(r7, r8, Operand(ip), SetCC),
++ "e0f8700c rscs r7, r8, ip");
++ COMPARE(rsc(r7, r8, Operand(ip, LSL, r3), SetCC, ls),
++ "90f8731c rsclss r7, r8, ip, lsl r3");
++
++ COMPARE(tst(r7, Operand(r5, ASR, ip), ge),
++ "a1170c55 tstge r7, r5, asr ip");
++ COMPARE(tst(r7, Operand(r6, ASR, sp)),
++ "e1170d56 tst r7, r6, asr sp");
++ COMPARE(tst(r7, Operand(r7), ge),
++ "a1170007 tstge r7, r7");
++ COMPARE(tst(r7, Operand(r8, ASR, r11), ge),
++ "a1170b58 tstge r7, r8, asr fp");
++
++ COMPARE(teq(r7, Operand(r5, ROR, r0), lt),
++ "b1370075 teqlt r7, r5, ror r0");
++ COMPARE(teq(r7, Operand(r6, ROR, lr)),
++ "e1370e76 teq r7, r6, ror lr");
++ COMPARE(teq(r7, Operand(r7), lt),
++ "b1370007 teqlt r7, r7");
++ COMPARE(teq(r7, Operand(r8, ROR, r1)),
++ "e1370178 teq r7, r8, ror r1");
++
++ COMPARE(cmp(r7, Operand(r4)),
++ "e1570004 cmp r7, r4");
++ COMPARE(cmp(r7, Operand(r6, LSL, 1), gt),
++ "c1570086 cmpgt r7, r6, lsl #1");
++ COMPARE(cmp(r7, Operand(r8, LSR, 3), gt),
++ "c15701a8 cmpgt r7, r8, lsr #3");
++ COMPARE(cmp(r7, Operand(r8, ASR, 19)),
++ "e15709c8 cmp r7, r8, asr #19");
++
++ COMPARE(cmn(r0, Operand(r4)),
++ "e1700004 cmn r0, r4");
++ COMPARE(cmn(r1, Operand(r6, ROR, 1)),
++ "e17100e6 cmn r1, r6, ror #1");
++ COMPARE(cmn(r2, Operand(r8)),
++ "e1720008 cmn r2, r8");
++ COMPARE(cmn(r3, Operand(r11), le),
++ "d173000b cmnle r3, fp");
++
++ COMPARE(orr(r7, r8, Operand(lr), LeaveCC, al),
++ "e188700e orr r7, r8, lr");
++ COMPARE(orr(r7, r8, Operand(r11)),
++ "e188700b orr r7, r8, fp");
++ COMPARE(orr(r7, r8, Operand(sp), SetCC),
++ "e198700d orrs r7, r8, sp");
++ COMPARE(orr(r7, r8, Operand(ip), SetCC, al),
++ "e198700c orrs r7, r8, ip");
++
++ COMPARE(mov(r0, Operand(r1), LeaveCC, eq),
++ "01a00001 moveq r0, r1");
++ COMPARE(mov(r0, Operand(r2)),
++ "e1a00002 mov r0, r2");
++ COMPARE(mov(r0, Operand(r3), SetCC),
++ "e1b00003 movs r0, r3");
++ COMPARE(mov(r0, Operand(r4), SetCC, pl),
++ "51b00004 movpls r0, r4");
++
++ COMPARE(bic(r0, lr, Operand(r1), LeaveCC, vs),
++ "61ce0001 bicvs r0, lr, r1");
++ COMPARE(bic(r0, r9, Operand(r2), LeaveCC, vc),
++ "71c90002 bicvc r0, r9, r2");
++ COMPARE(bic(r0, r5, Operand(r3), SetCC),
++ "e1d50003 bics r0, r5, r3");
++ COMPARE(bic(r0, r1, Operand(r4), SetCC, pl),
++ "51d10004 bicpls r0, r1, r4");
++
++ COMPARE(mvn(r10, Operand(r1)),
++ "e1e0a001 mvn r10, r1");
++ COMPARE(mvn(r9, Operand(r2)),
++ "e1e09002 mvn r9, r2");
++ COMPARE(mvn(r0, Operand(r3), SetCC),
++ "e1f00003 mvns r0, r3");
++ COMPARE(mvn(r5, Operand(r4), SetCC, cc),
++ "31f05004 mvnccs r5, r4");
++
++ // Instructions autotransformed by the assembler.
++ // mov -> mvn.
++ COMPARE(mov(r3, Operand(-1), LeaveCC, al),
++ "e3e03000 mvn r3, #0");
++ COMPARE(mov(r4, Operand(-2), SetCC, al),
++ "e3f04001 mvns r4, #1");
++ COMPARE(mov(r5, Operand(0x0ffffff0), SetCC, ne),
++ "13f052ff mvnnes r5, #-268435441");
++ COMPARE(mov(r6, Operand(-1), LeaveCC, ne),
++ "13e06000 mvnne r6, #0");
++
++ // mvn -> mov.
++ COMPARE(mvn(r3, Operand(-1), LeaveCC, al),
++ "e3a03000 mov r3, #0");
++ COMPARE(mvn(r4, Operand(-2), SetCC, al),
++ "e3b04001 movs r4, #1");
++ COMPARE(mvn(r5, Operand(0x0ffffff0), SetCC, ne),
++ "13b052ff movnes r5, #-268435441");
++ COMPARE(mvn(r6, Operand(-1), LeaveCC, ne),
++ "13a06000 movne r6, #0");
++
++ // mov -> movw.
++ if (CpuFeatures::IsSupported(ARMv7)) {
++ COMPARE(mov(r5, Operand(0x01234), LeaveCC, ne),
++ "13015234 movwne r5, #4660");
++ // We only disassemble one instruction so the eor instruction is not here.
++ COMPARE(eor(r5, r4, Operand(0x1234), LeaveCC, ne),
++ "1301c234 movwne ip, #4660");
++ // Movw can't do setcc so we don't get that here. Mov immediate with setcc
++ // is pretty strange anyway.
++ COMPARE(mov(r5, Operand(0x01234), SetCC, ne),
++ "159fc000 ldrne ip, [pc, #+0]");
++ // Emit a literal pool now, otherwise this could be dumped later, in the
++ // middle of a different test.
++ EMIT_PENDING_LITERALS();
++
++ // We only disassemble one instruction so the eor instruction is not here.
++ // The eor does the setcc so we get a movw here.
++ COMPARE(eor(r5, r4, Operand(0x1234), SetCC, ne),
++ "1301c234 movwne ip, #4660");
++
++ COMPARE(movt(r5, 0x4321, ne),
++ "13445321 movtne r5, #17185");
++ COMPARE(movw(r5, 0xabcd, eq),
++ "030a5bcd movweq r5, #43981");
++ }
++
++ // Eor doesn't have an eor-negative variant, but we can do an mvn followed by
++ // an eor to get the same effect.
++ COMPARE(eor(r5, r4, Operand(0xffffff34), SetCC, ne),
++ "13e0c0cb mvnne ip, #203");
++
++ // and <-> bic.
++ COMPARE(andi(r3, r5, Operand(0xfc03ffff)),
++ "e3c537ff bic r3, r5, #66846720");
++ COMPARE(bic(r3, r5, Operand(0xfc03ffff)),
++ "e20537ff and r3, r5, #66846720");
++
++ // sub <-> add.
++ COMPARE(add(r3, r5, Operand(-1024)),
++ "e2453b01 sub r3, r5, #1024");
++ COMPARE(sub(r3, r5, Operand(-1024)),
++ "e2853b01 add r3, r5, #1024");
++
++ // cmp <-> cmn.
++ COMPARE(cmp(r3, Operand(-1024)),
++ "e3730b01 cmn r3, #1024");
++ COMPARE(cmn(r3, Operand(-1024)),
++ "e3530b01 cmp r3, #1024");
++
++ // Miscellaneous instructions encoded as type 0.
++ COMPARE(blx(ip),
++ "e12fff3c blx ip");
++ COMPARE(bkpt(0),
++ "e1200070 bkpt 0");
++ COMPARE(bkpt(0xffff),
++ "e12fff7f bkpt 65535");
++ COMPARE(clz(r6, r7),
++ "e16f6f17 clz r6, r7");
++
++ VERIFY_RUN();
++}
++
++
++TEST(Type1) {
++ SET_UP();
++
++ COMPARE(andi(r0, r1, Operand(0x00000000)),
++ "e2010000 andi r0, r1, #0");
++ COMPARE(andi(r1, r2, Operand(0x00000001), LeaveRC),
++ "e2021001 andi r1, r2, #1");
++ COMPARE(and_(r2, r3, Operand(0x00000010), SetCC),
++ "e2132010 ands r2, r3, #16");
++ COMPARE(and_(r3, r4, Operand(0x00000100), LeaveCC, eq),
++ "02043c01 andeq r3, r4, #256");
++ COMPARE(and_(r4, r5, Operand(0x00001000), SetCC, ne),
++ "12154a01 andnes r4, r5, #4096");
++
++ COMPARE(eor(r4, r5, Operand(0x00001000)),
++ "e2254a01 eor r4, r5, #4096");
++ COMPARE(eor(r4, r4, Operand(0x00010000), LeaveCC),
++ "e2244801 eor r4, r4, #65536");
++ COMPARE(eor(r4, r3, Operand(0x00100000), SetCC),
++ "e2334601 eors r4, r3, #1048576");
++ COMPARE(eor(r4, r2, Operand(0x01000000), LeaveCC, cs),
++ "22224401 eorcs r4, r2, #16777216");
++ COMPARE(eor(r4, r1, Operand(0x10000000), SetCC, cc),
++ "32314201 eorccs r4, r1, #268435456");
++
++ VERIFY_RUN();
++}
++#endif
++
++
++TEST(Type3) {
++ SET_UP();
++
++#ifdef PENGUIN_CLEANUP
++ if (CpuFeatures::IsSupported(ARMv7)) {
++ COMPARE(ubfx(r0, r1, 5, 10),
++ "e7e902d1 ubfx r0, r1, #5, #10");
++ COMPARE(ubfx(r1, r0, 5, 10),
++ "e7e912d0 ubfx r1, r0, #5, #10");
++ COMPARE(ubfx(r0, r1, 31, 1),
++ "e7e00fd1 ubfx r0, r1, #31, #1");
++ COMPARE(ubfx(r1, r0, 31, 1),
++ "e7e01fd0 ubfx r1, r0, #31, #1");
++
++ COMPARE(sbfx(r0, r1, 5, 10),
++ "e7a902d1 sbfx r0, r1, #5, #10");
++ COMPARE(sbfx(r1, r0, 5, 10),
++ "e7a912d0 sbfx r1, r0, #5, #10");
++ COMPARE(sbfx(r0, r1, 31, 1),
++ "e7a00fd1 sbfx r0, r1, #31, #1");
++ COMPARE(sbfx(r1, r0, 31, 1),
++ "e7a01fd0 sbfx r1, r0, #31, #1");
++
++ COMPARE(bfc(r0, 5, 10),
++ "e7ce029f bfc r0, #5, #10");
++ COMPARE(bfc(r1, 5, 10),
++ "e7ce129f bfc r1, #5, #10");
++ COMPARE(bfc(r0, 31, 1),
++ "e7df0f9f bfc r0, #31, #1");
++ COMPARE(bfc(r1, 31, 1),
++ "e7df1f9f bfc r1, #31, #1");
++
++ COMPARE(bfi(r0, r1, 5, 10),
++ "e7ce0291 bfi r0, r1, #5, #10");
++ COMPARE(bfi(r1, r0, 5, 10),
++ "e7ce1290 bfi r1, r0, #5, #10");
++ COMPARE(bfi(r0, r1, 31, 1),
++ "e7df0f91 bfi r0, r1, #31, #1");
++ COMPARE(bfi(r1, r0, 31, 1),
++ "e7df1f90 bfi r1, r0, #31, #1");
++
++ COMPARE(usat(r0, 1, Operand(r1)),
++ "e6e10011 usat r0, #1, r1");
++ COMPARE(usat(r2, 7, Operand(lr)),
++ "e6e7201e usat r2, #7, lr");
++ COMPARE(usat(r3, 31, Operand(r4, LSL, 31)),
++ "e6ff3f94 usat r3, #31, r4, lsl #31");
++ COMPARE(usat(r8, 0, Operand(r5, ASR, 17)),
++ "e6e088d5 usat r8, #0, r5, asr #17");
++ }
++#endif
++
++ VERIFY_RUN();
++}
++
++
++#ifdef PENGUIN_CLEANUP
++TEST(Vfp) {
++ SET_UP();
++
++ if (CpuFeatures::IsSupported(VFP3)) {
++ CpuFeatures::Scope scope(VFP3);
++ COMPARE(vmov(d0, d1),
++ "eeb00b41 vmov.f64 d0, d1");
++ COMPARE(vmov(d3, d3, eq),
++ "0eb03b43 vmov.f64eq d3, d3");
++
++ COMPARE(vmov(s0, s31),
++ "eeb00a6f vmov.f32 s0, s31");
++ COMPARE(vmov(s31, s0),
++ "eef0fa40 vmov.f32 s31, s0");
++ COMPARE(vmov(r0, s0),
++ "ee100a10 vmov r0, s0");
++ COMPARE(vmov(r10, s31),
++ "ee1faa90 vmov r10, s31");
++ COMPARE(vmov(s0, r0),
++ "ee000a10 vmov s0, r0");
++ COMPARE(vmov(s31, r10),
++ "ee0faa90 vmov s31, r10");
++
++ COMPARE(vabs(d0, d1),
++ "eeb00bc1 vabs.f64 d0, d1");
++ COMPARE(vabs(d3, d4, mi),
++ "4eb03bc4 vabs.f64mi d3, d4");
++
++ COMPARE(vneg(d0, d1),
++ "eeb10b41 vneg.f64 d0, d1");
++ COMPARE(vneg(d3, d4, mi),
++ "4eb13b44 vneg.f64mi d3, d4");
++
++ COMPARE(vadd(d0, d1, d2),
++ "ee310b02 vadd.f64 d0, d1, d2");
++ COMPARE(vadd(d3, d4, d5, mi),
++ "4e343b05 vadd.f64mi d3, d4, d5");
++
++ COMPARE(vsub(d0, d1, d2),
++ "ee310b42 vsub.f64 d0, d1, d2");
++ COMPARE(vsub(d3, d4, d5, ne),
++ "1e343b45 vsub.f64ne d3, d4, d5");
++
++ COMPARE(vmul(d2, d1, d0),
++ "ee212b00 vmul.f64 d2, d1, d0");
++ COMPARE(vmul(d6, d4, d5, cc),
++ "3e246b05 vmul.f64cc d6, d4, d5");
++
++ COMPARE(vdiv(d2, d2, d2),
++ "ee822b02 vdiv.f64 d2, d2, d2");
++ COMPARE(vdiv(d6, d7, d7, hi),
++ "8e876b07 vdiv.f64hi d6, d7, d7");
++
++ COMPARE(vsqrt(d0, d0),
++ "eeb10bc0 vsqrt.f64 d0, d0");
++ COMPARE(vsqrt(d2, d3, ne),
++ "1eb12bc3 vsqrt.f64ne d2, d3");
++
++ COMPARE(vmov(d0, 1.0),
++ "eeb70b00 vmov.f64 d0, #1");
++ COMPARE(vmov(d2, -13.0),
++ "eeba2b0a vmov.f64 d2, #-13");
++
++ COMPARE(vldr(s0, r0, 0),
++ "ed900a00 vldr s0, [r0 + 4*0]");
++ COMPARE(vldr(s1, r1, 4),
++ "edd10a01 vldr s1, [r1 + 4*1]");
++ COMPARE(vldr(s15, r4, 16),
++ "edd47a04 vldr s15, [r4 + 4*4]");
++ COMPARE(vldr(s16, r5, 20),
++ "ed958a05 vldr s16, [r5 + 4*5]");
++ COMPARE(vldr(s31, r10, 1020),
++ "eddafaff vldr s31, [r10 + 4*255]");
++
++ COMPARE(vstr(s0, r0, 0),
++ "ed800a00 vstr s0, [r0 + 4*0]");
++ COMPARE(vstr(s1, r1, 4),
++ "edc10a01 vstr s1, [r1 + 4*1]");
++ COMPARE(vstr(s15, r8, 8),
++ "edc87a02 vstr s15, [r8 + 4*2]");
++ COMPARE(vstr(s16, r9, 12),
++ "ed898a03 vstr s16, [r9 + 4*3]");
++ COMPARE(vstr(s31, r10, 1020),
++ "edcafaff vstr s31, [r10 + 4*255]");
++
++ COMPARE(vldr(d0, r0, 0),
++ "ed900b00 vldr d0, [r0 + 4*0]");
++ COMPARE(vldr(d1, r1, 4),
++ "ed911b01 vldr d1, [r1 + 4*1]");
++ COMPARE(vldr(d15, r10, 1020),
++ "ed9afbff vldr d15, [r10 + 4*255]");
++ COMPARE(vstr(d0, r0, 0),
++ "ed800b00 vstr d0, [r0 + 4*0]");
++ COMPARE(vstr(d1, r1, 4),
++ "ed811b01 vstr d1, [r1 + 4*1]");
++ COMPARE(vstr(d15, r10, 1020),
++ "ed8afbff vstr d15, [r10 + 4*255]");
++
++ COMPARE(vmsr(r5),
++ "eee15a10 vmsr FPSCR, r5");
++ COMPARE(vmsr(r10, pl),
++ "5ee1aa10 vmsrpl FPSCR, r10");
++ COMPARE(vmsr(pc),
++ "eee1fa10 vmsr FPSCR, APSR");
++ COMPARE(vmrs(r5),
++ "eef15a10 vmrs r5, FPSCR");
++ COMPARE(vmrs(r10, ge),
++ "aef1aa10 vmrsge r10, FPSCR");
++ COMPARE(vmrs(pc),
++ "eef1fa10 vmrs APSR, FPSCR");
++
++ COMPARE(vstm(ia, r0, d1, d3),
++ "ec801b06 vstmia r0, {d1-d3}");
++ COMPARE(vldm(ia, r1, d2, d5),
++ "ec912b08 vldmia r1, {d2-d5}");
++ COMPARE(vstm(ia, r2, d0, d15),
++ "ec820b20 vstmia r2, {d0-d15}");
++ COMPARE(vldm(ia, r3, d0, d15),
++ "ec930b20 vldmia r3, {d0-d15}");
++ COMPARE(vstm(ia, r4, s1, s3),
++ "ecc40a03 vstmia r4, {s1-s3}");
++ COMPARE(vldm(ia, r5, s2, s5),
++ "ec951a04 vldmia r5, {s2-s5}");
++ COMPARE(vstm(ia, r6, s0, s31),
++ "ec860a20 vstmia r6, {s0-s31}");
++ COMPARE(vldm(ia, r7, s0, s31),
++ "ec970a20 vldmia r7, {s0-s31}");
++ }
++
++ VERIFY_RUN();
++}
++#endif
++
++#ifdef PENGUIN_CLEANUP
++TEST(LoadStore) {
++ SET_UP();
++
++ COMPARE(ldrb(r0, MemOperand(r1)),
++ "e5d10000 ldrb r0, [r1, #+0]");
++ COMPARE(ldrb(r2, MemOperand(r3, 42)),
++ "e5d3202a ldrb r2, [r3, #+42]");
++ COMPARE(ldrb(r4, MemOperand(r5, -42)),
++ "e555402a ldrb r4, [r5, #-42]");
++ COMPARE(ldrb(r6, MemOperand(r7, 42, PostIndex)),
++ "e4d7602a ldrb r6, [r7], #+42");
++ COMPARE(ldrb(r8, MemOperand(r9, -42, PostIndex)),
++ "e459802a ldrb r8, [r9], #-42");
++ COMPARE(ldrb(r10, MemOperand(r11, 42, PreIndex)),
++ "e5fba02a ldrb r10, [fp, #+42]!");
++ COMPARE(ldrb(ip, MemOperand(sp, -42, PreIndex)),
++ "e57dc02a ldrb ip, [sp, #-42]!");
++ COMPARE(ldrb(r0, MemOperand(r1, r2)),
++ "e7d10002 ldrb r0, [r1, +r2]");
++ COMPARE(ldrb(r0, MemOperand(r1, r2, NegOffset)),
++ "e7510002 ldrb r0, [r1, -r2]");
++ COMPARE(ldrb(r0, MemOperand(r1, r2, PostIndex)),
++ "e6d10002 ldrb r0, [r1], +r2");
++ COMPARE(ldrb(r0, MemOperand(r1, r2, NegPostIndex)),
++ "e6510002 ldrb r0, [r1], -r2");
++ COMPARE(ldrb(r0, MemOperand(r1, r2, PreIndex)),
++ "e7f10002 ldrb r0, [r1, +r2]!");
++ COMPARE(ldrb(r0, MemOperand(r1, r2, NegPreIndex)),
++ "e7710002 ldrb r0, [r1, -r2]!");
++
++ COMPARE(strb(r0, MemOperand(r1)),
++ "e5c10000 strb r0, [r1, #+0]");
++ COMPARE(strb(r2, MemOperand(r3, 42)),
++ "e5c3202a strb r2, [r3, #+42]");
++ COMPARE(strb(r4, MemOperand(r5, -42)),
++ "e545402a strb r4, [r5, #-42]");
++ COMPARE(strb(r6, MemOperand(r7, 42, PostIndex)),
++ "e4c7602a strb r6, [r7], #+42");
++ COMPARE(strb(r8, MemOperand(r9, -42, PostIndex)),
++ "e449802a strb r8, [r9], #-42");
++ COMPARE(strb(r10, MemOperand(r11, 42, PreIndex)),
++ "e5eba02a strb r10, [fp, #+42]!");
++ COMPARE(strb(ip, MemOperand(sp, -42, PreIndex)),
++ "e56dc02a strb ip, [sp, #-42]!");
++ COMPARE(strb(r0, MemOperand(r1, r2)),
++ "e7c10002 strb r0, [r1, +r2]");
++ COMPARE(strb(r0, MemOperand(r1, r2, NegOffset)),
++ "e7410002 strb r0, [r1, -r2]");
++ COMPARE(strb(r0, MemOperand(r1, r2, PostIndex)),
++ "e6c10002 strb r0, [r1], +r2");
++ COMPARE(strb(r0, MemOperand(r1, r2, NegPostIndex)),
++ "e6410002 strb r0, [r1], -r2");
++ COMPARE(strb(r0, MemOperand(r1, r2, PreIndex)),
++ "e7e10002 strb r0, [r1, +r2]!");
++ COMPARE(strb(r0, MemOperand(r1, r2, NegPreIndex)),
++ "e7610002 strb r0, [r1, -r2]!");
++
++ COMPARE(ldrh(r0, MemOperand(r1)),
++ "e1d100b0 ldrh r0, [r1, #+0]");
++ COMPARE(ldrh(r2, MemOperand(r3, 42)),
++ "e1d322ba ldrh r2, [r3, #+42]");
++ COMPARE(ldrh(r4, MemOperand(r5, -42)),
++ "e15542ba ldrh r4, [r5, #-42]");
++ COMPARE(ldrh(r6, MemOperand(r7, 42, PostIndex)),
++ "e0d762ba ldrh r6, [r7], #+42");
++ COMPARE(ldrh(r8, MemOperand(r9, -42, PostIndex)),
++ "e05982ba ldrh r8, [r9], #-42");
++// COMPARE(ldrh(r10, MemOperand(fp, 42, PreIndex)),
++// "e1fba2ba ldrh r10, [fp, #+42]!");
++ COMPARE(ldrh(ip, MemOperand(sp, -42, PreIndex)),
++ "e17dc2ba ldrh ip, [sp, #-42]!");
++ COMPARE(ldrh(r0, MemOperand(r1, r2)),
++ "e19100b2 ldrh r0, [r1, +r2]");
++ COMPARE(ldrh(r0, MemOperand(r1, r2, NegOffset)),
++ "e11100b2 ldrh r0, [r1, -r2]");
++ COMPARE(ldrh(r0, MemOperand(r1, r2, PostIndex)),
++ "e09100b2 ldrh r0, [r1], +r2");
++ COMPARE(ldrh(r0, MemOperand(r1, r2, NegPostIndex)),
++ "e01100b2 ldrh r0, [r1], -r2");
++ COMPARE(ldrh(r0, MemOperand(r1, r2, PreIndex)),
++ "e1b100b2 ldrh r0, [r1, +r2]!");
++ COMPARE(ldrh(r0, MemOperand(r1, r2, NegPreIndex)),
++ "e13100b2 ldrh r0, [r1, -r2]!");
++
++ COMPARE(strh(r0, MemOperand(r1)),
++ "e1c100b0 strh r0, [r1, #+0]");
++ COMPARE(strh(r2, MemOperand(r3, 42)),
++ "e1c322ba strh r2, [r3, #+42]");
++ COMPARE(strh(r4, MemOperand(r5, -42)),
++ "e14542ba strh r4, [r5, #-42]");
++ COMPARE(strh(r6, MemOperand(r7, 42, PostIndex)),
++ "e0c762ba strh r6, [r7], #+42");
++ COMPARE(strh(r8, MemOperand(r9, -42, PostIndex)),
++ "e04982ba strh r8, [r9], #-42");
++// COMPARE(strh(r10, MemOperand(fp, 42, PreIndex)),
++// "e1eba2ba strh r10, [fp, #+42]!");
++ COMPARE(strh(ip, MemOperand(sp, -42, PreIndex)),
++ "e16dc2ba strh ip, [sp, #-42]!");
++ COMPARE(strh(r0, MemOperand(r1, r2)),
++ "e18100b2 strh r0, [r1, +r2]");
++ COMPARE(strh(r0, MemOperand(r1, r2, NegOffset)),
++ "e10100b2 strh r0, [r1, -r2]");
++ COMPARE(strh(r0, MemOperand(r1, r2, PostIndex)),
++ "e08100b2 strh r0, [r1], +r2");
++ COMPARE(strh(r0, MemOperand(r1, r2, NegPostIndex)),
++ "e00100b2 strh r0, [r1], -r2");
++ COMPARE(strh(r0, MemOperand(r1, r2, PreIndex)),
++ "e1a100b2 strh r0, [r1, +r2]!");
++ COMPARE(strh(r0, MemOperand(r1, r2, NegPreIndex)),
++ "e12100b2 strh r0, [r1, -r2]!");
++
++ COMPARE(ldr(r0, MemOperand(r1)),
++ "e5910000 ldr r0, [r1, #+0]");
++ COMPARE(ldr(r2, MemOperand(r3, 42)),
++ "e593202a ldr r2, [r3, #+42]");
++ COMPARE(ldr(r4, MemOperand(r5, -42)),
++ "e515402a ldr r4, [r5, #-42]");
++ COMPARE(ldr(r6, MemOperand(r7, 42, PostIndex)),
++ "e497602a ldr r6, [r7], #+42");
++ COMPARE(ldr(r8, MemOperand(r9, -42, PostIndex)),
++ "e419802a ldr r8, [r9], #-42");
++// COMPARE(ldr(r10, MemOperand(fp, 42, PreIndex)),
++// "e5bba02a ldr r10, [fp, #+42]!");
++ COMPARE(ldr(ip, MemOperand(sp, -42, PreIndex)),
++ "e53dc02a ldr ip, [sp, #-42]!");
++ COMPARE(ldr(r0, MemOperand(r1, r2)),
++ "e7910002 ldr r0, [r1, +r2]");
++ COMPARE(ldr(r0, MemOperand(r1, r2, NegOffset)),
++ "e7110002 ldr r0, [r1, -r2]");
++ COMPARE(ldr(r0, MemOperand(r1, r2, PostIndex)),
++ "e6910002 ldr r0, [r1], +r2");
++ COMPARE(ldr(r0, MemOperand(r1, r2, NegPostIndex)),
++ "e6110002 ldr r0, [r1], -r2");
++ COMPARE(ldr(r0, MemOperand(r1, r2, PreIndex)),
++ "e7b10002 ldr r0, [r1, +r2]!");
++ COMPARE(ldr(r0, MemOperand(r1, r2, NegPreIndex)),
++ "e7310002 ldr r0, [r1, -r2]!");
++
++ COMPARE(str(r0, MemOperand(r1)),
++ "e5810000 str r0, [r1, #+0]");
++ COMPARE(str(r2, MemOperand(r3, 42)),
++ "e583202a str r2, [r3, #+42]");
++ COMPARE(str(r4, MemOperand(r5, -42)),
++ "e505402a str r4, [r5, #-42]");
++ COMPARE(str(r6, MemOperand(r7, 42, PostIndex)),
++ "e487602a str r6, [r7], #+42");
++ COMPARE(str(r8, MemOperand(r9, -42, PostIndex)),
++ "e409802a str r8, [r9], #-42");
++// COMPARE(str(r10, MemOperand(fp, 42, PreIndex)),
++// "e5aba02a str r10, [fp, #+42]!");
++ COMPARE(str(ip, MemOperand(sp, -42, PreIndex)),
++ "e52dc02a str ip, [sp, #-42]!");
++ COMPARE(str(r0, MemOperand(r1, r2)),
++ "e7810002 str r0, [r1, +r2]");
++ COMPARE(str(r0, MemOperand(r1, r2, NegOffset)),
++ "e7010002 str r0, [r1, -r2]");
++ COMPARE(str(r0, MemOperand(r1, r2, PostIndex)),
++ "e6810002 str r0, [r1], +r2");
++ COMPARE(str(r0, MemOperand(r1, r2, NegPostIndex)),
++ "e6010002 str r0, [r1], -r2");
++ COMPARE(str(r0, MemOperand(r1, r2, PreIndex)),
++ "e7a10002 str r0, [r1, +r2]!");
++ COMPARE(str(r0, MemOperand(r1, r2, NegPreIndex)),
++ "e7210002 str r0, [r1, -r2]!");
++
++ if (CpuFeatures::IsSupported(ARMv7)) {
++ CpuFeatures::Scope scope(ARMv7);
++ COMPARE(ldrd(r0, r1, MemOperand(r1)),
++ "e1c100d0 ldrd r0, [r1, #+0]");
++ COMPARE(ldrd(r2, r3, MemOperand(r3, 127)),
++ "e1c327df ldrd r2, [r3, #+127]");
++ COMPARE(ldrd(r4, r5, MemOperand(r5, -127)),
++ "e14547df ldrd r4, [r5, #-127]");
++ COMPARE(ldrd(r6, r7, MemOperand(r7, 127, PostIndex)),
++ "e0c767df ldrd r6, [r7], #+127");
++ COMPARE(ldrd(r8, r9, MemOperand(r9, -127, PostIndex)),
++ "e04987df ldrd r8, [r9], #-127");
++// COMPARE(ldrd(r10, fp, MemOperand(fp, 127, PreIndex)),
++// "e1eba7df ldrd r10, [fp, #+127]!");
++ COMPARE(ldrd(ip, sp, MemOperand(sp, -127, PreIndex)),
++ "e16dc7df ldrd ip, [sp, #-127]!");
++
++ COMPARE(strd(r0, r1, MemOperand(r1)),
++ "e1c100f0 strd r0, [r1, #+0]");
++ COMPARE(strd(r2, r3, MemOperand(r3, 127)),
++ "e1c327ff strd r2, [r3, #+127]");
++ COMPARE(strd(r4, r5, MemOperand(r5, -127)),
++ "e14547ff strd r4, [r5, #-127]");
++ COMPARE(strd(r6, r7, MemOperand(r7, 127, PostIndex)),
++ "e0c767ff strd r6, [r7], #+127");
++ COMPARE(strd(r8, r9, MemOperand(r9, -127, PostIndex)),
++ "e04987ff strd r8, [r9], #-127");
++// COMPARE(strd(r10, fp, MemOperand(fp, 127, PreIndex)),
++// "e1eba7ff strd r10, [fp, #+127]!");
++ COMPARE(strd(ip, sp, MemOperand(sp, -127, PreIndex)),
++ "e16dc7ff strd ip, [sp, #-127]!");
++ }
++
++ VERIFY_RUN();
++}
++#endif
++
+diff -up v8-3.14.5.10/test/cctest/test-hashing.cc.ppc
v8-3.14.5.10/test/cctest/test-hashing.cc
+--- v8-3.14.5.10/test/cctest/test-hashing.cc.ppc 2012-01-16 06:42:08.000000000 -0500
++++ v8-3.14.5.10/test/cctest/test-hashing.cc 2016-06-07 14:15:46.011392883 -0400
+@@ -113,6 +113,24 @@ void generate(MacroAssembler* masm, i::V
+ __ pop(kRootRegister);
+ __ jr(ra);
+ __ nop();
++#elif V8_TARGET_ARCH_PPC
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++
++ __ push(kRootRegister);
++ __ InitializeRootRegister();
++
++ __ li(r3, Operand::Zero());
++ __ li(ip, Operand(string.at(0)));
++ StringHelper::GenerateHashInit(masm, r3, ip, r0);
++ for (int i = 1; i < string.length(); i++) {
++ __ li(ip, Operand(string.at(i)));
++ StringHelper::GenerateHashAddCharacter(masm, r3, ip, r0);
++ }
++ StringHelper::GenerateHashGetHash(masm, r3, r0);
++ __ pop(kRootRegister);
++ __ blr();
+ #endif
+ }
+
+@@ -148,6 +166,16 @@ void generate(MacroAssembler* masm, uint
+ __ pop(kRootRegister);
+ __ jr(ra);
+ __ nop();
++#elif V8_TARGET_ARCH_PPC
++#if ABI_USES_FUNCTION_DESCRIPTORS
++ __ function_descriptor();
++#endif
++ __ push(kRootRegister);
++ __ InitializeRootRegister();
++ __ li(r3, Operand(key));
++ __ GetNumberHash(r3, ip);
++ __ pop(kRootRegister);
++ __ blr();
+ #endif
+ }
+
+@@ -172,7 +200,7 @@ void check(i::Vector<const char> string)
+ v8_string->set_hash_field(String::kEmptyHashField);
+ #ifdef USE_SIMULATOR
+ uint32_t codegen_hash =
+- reinterpret_cast<uint32_t>(CALL_GENERATED_CODE(hash, 0, 0, 0, 0, 0));
++ reinterpret_cast<uintptr_t>(CALL_GENERATED_CODE(hash, 0, 0, 0, 0, 0));
+ #else
+ uint32_t codegen_hash = hash();
+ #endif
+@@ -199,7 +227,7 @@ void check(uint32_t key) {
+ HASH_FUNCTION hash = FUNCTION_CAST<HASH_FUNCTION>(code->entry());
+ #ifdef USE_SIMULATOR
+ uint32_t codegen_hash =
+- reinterpret_cast<uint32_t>(CALL_GENERATED_CODE(hash, 0, 0, 0, 0, 0));
++ reinterpret_cast<uintptr_t>(CALL_GENERATED_CODE(hash, 0, 0, 0, 0, 0));
+ #else
+ uint32_t codegen_hash = hash();
+ #endif
+diff -up v8-3.14.5.10/test/cctest/test-heap.cc.ppc v8-3.14.5.10/test/cctest/test-heap.cc
+--- v8-3.14.5.10/test/cctest/test-heap.cc.ppc 2012-10-15 07:51:39.000000000 -0400
++++ v8-3.14.5.10/test/cctest/test-heap.cc 2016-06-07 14:15:46.011392883 -0400
+@@ -133,7 +133,7 @@ TEST(HeapObjects) {
+ CHECK(value->IsNumber());
+ CHECK_EQ(Smi::kMaxValue, Smi::cast(value)->value());
+
+-#ifndef V8_TARGET_ARCH_X64
++#if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_PPC64)
+ // TODO(lrn): We need a NumberFromIntptr function in order to test this.
+ value = HEAP->NumberFromInt32(Smi::kMinValue - 1)->ToObjectChecked();
+ CHECK(value->IsHeapNumber());
+diff -up v8-3.14.5.10/test/cctest/test-lockers.cc.ppc
v8-3.14.5.10/test/cctest/test-lockers.cc
+--- v8-3.14.5.10/test/cctest/test-lockers.cc.ppc 2011-11-23 07:13:52.000000000 -0500
++++ v8-3.14.5.10/test/cctest/test-lockers.cc 2016-06-07 14:15:46.011392883 -0400
+@@ -204,7 +204,7 @@ static void StartJoinAndDeleteThreads(co
+
+ // Run many threads all locking on the same isolate
+ TEST(IsolateLockingStress) {
+-#ifdef V8_TARGET_ARCH_MIPS
++#if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 50;
+ #else
+ const int kNThreads = 100;
+@@ -241,7 +241,8 @@ class IsolateNonlockingThread : public J
+
+ // Run many threads each accessing its own isolate without locking
+ TEST(MultithreadedParallelIsolates) {
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) \
++ || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 10;
+ #else
+ const int kNThreads = 50;
+@@ -279,7 +280,7 @@ class IsolateNestedLockingThread : publi
+
+ // Run many threads with nested locks
+ TEST(IsolateNestedLocking) {
+-#ifdef V8_TARGET_ARCH_MIPS
++#if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 50;
+ #else
+ const int kNThreads = 100;
+@@ -319,7 +320,8 @@ class SeparateIsolatesLocksNonexclusiveT
+
+ // Run parallel threads that lock and access different isolates in parallel
+ TEST(SeparateIsolatesLocksNonexclusive) {
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) \
++ || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 50;
+ #else
+ const int kNThreads = 100;
+@@ -393,7 +395,8 @@ class LockerUnlockerThread : public Join
+
+ // Use unlocker inside of a Locker, multiple threads.
+ TEST(LockerUnlocker) {
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) \
++ || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 50;
+ #else
+ const int kNThreads = 100;
+@@ -446,7 +449,8 @@ class LockTwiceAndUnlockThread : public
+
+ // Use Unlocker inside two Lockers.
+ TEST(LockTwiceAndUnlock) {
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) \
++ || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 50;
+ #else
+ const int kNThreads = 100;
+@@ -567,7 +571,7 @@ class LockUnlockLockThread : public Join
+
+ // Locker inside an Unlocker inside a Locker.
+ TEST(LockUnlockLockMultithreaded) {
+-#ifdef V8_TARGET_ARCH_MIPS
++#if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 50;
+ #else
+ const int kNThreads = 100;
+@@ -618,7 +622,7 @@ class LockUnlockLockDefaultIsolateThread
+
+ // Locker inside an Unlocker inside a Locker for default isolate.
+ TEST(LockUnlockLockDefaultIsolateMultithreaded) {
+-#ifdef V8_TARGET_ARCH_MIPS
++#if defined(V8_TARGET_ARCH_MIPS) || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 50;
+ #else
+ const int kNThreads = 100;
+@@ -690,7 +694,8 @@ class IsolateGenesisThread : public Join
+ // Test installing extensions in separate isolates concurrently.
+ //
http://code.google.com/p/v8/issues/detail?id=1821
+ TEST(ExtensionsRegistration) {
+-#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS)
++#if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_MIPS) \
++ || defined(V8_TARGET_ARCH_PPC)
+ const int kNThreads = 10;
+ #else
+ const int kNThreads = 40;
+diff -up v8-3.14.5.10/test/cctest/test-mark-compact.cc.ppc
v8-3.14.5.10/test/cctest/test-mark-compact.cc
+--- v8-3.14.5.10/test/cctest/test-mark-compact.cc.ppc 2016-06-07 14:15:45.967393146
-0400
++++ v8-3.14.5.10/test/cctest/test-mark-compact.cc 2016-06-07 14:30:05.926222102 -0400
+@@ -525,6 +525,18 @@ static intptr_t MemoryInUse() {
+ return memory_use;
+ }
+
++#if defined(V8_TARGET_ARCH_PPC)
++const intptr_t maxSnap64 = 3600; // ???
++const intptr_t max64 = 4000; // ???
++const intptr_t maxSnap32 = 2800; // 2624
++const intptr_t max32 = 3300; // 3136
++#else
++const intptr_t maxSnap64 = 3600; // 3396
++const intptr_t max64 = 4000; // 3948
++const intptr_t maxSnap32 = 2500; // 2400
++const intptr_t max32 = 2860; // 2760
++#endif
++
+
+ TEST(BootUpMemoryUse) {
+ intptr_t initial_memory = MemoryInUse();
+@@ -539,15 +551,15 @@ TEST(BootUpMemoryUse) {
+ intptr_t delta = MemoryInUse() - initial_memory;
+ if (sizeof(initial_memory) == 8) {
+ if (v8::internal::Snapshot::IsEnabled()) {
+- CHECK_LE(delta, 3600 * 1024); // 3396.
++ CHECK_LE(delta, maxSnap64 * 1024); // 3396.
+ } else {
+- CHECK_LE(delta, 4000 * 1024); // 3948.
++ CHECK_LE(delta, max64 * 1024); // 3948.
+ }
+ } else {
+ if (v8::internal::Snapshot::IsEnabled()) {
+- CHECK_LE(delta, 2942 * 1024); // 2400.
++ CHECK_LE(delta, maxSnap32 * 1024); // 2400.
+ } else {
+- CHECK_LE(delta, 3400 * 1024); // 2760.
++ CHECK_LE(delta, max32 * 1024); // 2760.
+ }
+ }
+ }
+diff -up v8-3.14.5.10/test/cctest/test-regexp.cc.ppc
v8-3.14.5.10/test/cctest/test-regexp.cc
+--- v8-3.14.5.10/test/cctest/test-regexp.cc.ppc 2012-08-29 11:32:24.000000000 -0400
++++ v8-3.14.5.10/test/cctest/test-regexp.cc 2016-06-07 14:15:46.012392877 -0400
+@@ -49,6 +49,11 @@
+ #include "arm/macro-assembler-arm.h"
+ #include "arm/regexp-macro-assembler-arm.h"
+ #endif
++#ifdef V8_TARGET_ARCH_PPC
++#include "ppc/assembler-ppc.h"
++#include "ppc/macro-assembler-ppc.h"
++#include "ppc/regexp-macro-assembler-ppc.h"
++#endif
+ #ifdef V8_TARGET_ARCH_MIPS
+ #include "mips/assembler-mips.h"
+ #include "mips/macro-assembler-mips.h"
+@@ -702,6 +707,8 @@ typedef RegExpMacroAssemblerIA32 ArchReg
+ typedef RegExpMacroAssemblerX64 ArchRegExpMacroAssembler;
+ #elif V8_TARGET_ARCH_ARM
+ typedef RegExpMacroAssemblerARM ArchRegExpMacroAssembler;
++#elif V8_TARGET_ARCH_PPC
++typedef RegExpMacroAssemblerPPC ArchRegExpMacroAssembler;
+ #elif V8_TARGET_ARCH_MIPS
+ typedef RegExpMacroAssemblerMIPS ArchRegExpMacroAssembler;
+ #endif
+diff -up v8-3.14.5.10/test/cctest/test-threads.cc.ppc
v8-3.14.5.10/test/cctest/test-threads.cc
+--- v8-3.14.5.10/test/cctest/test-threads.cc.ppc 2011-10-05 17:44:48.000000000 -0400
++++ v8-3.14.5.10/test/cctest/test-threads.cc 2016-06-07 14:15:46.012392877 -0400
+@@ -170,7 +170,11 @@ class ThreadIdValidationThread : public
+ };
+
+ TEST(ThreadIdValidation) {
++#ifdef V8_TARGET_ARCH_PPC
++ const int kNThreads = 50;
++#else
+ const int kNThreads = 100;
++#endif
+ i::List<ThreadIdValidationThread*> threads(kNThreads);
+ i::List<i::ThreadId> refs(kNThreads);
+ i::Semaphore* semaphore = i::OS::CreateSemaphore(0);
+diff -up v8-3.14.5.10/test/mjsunit/big-array-literal.js.ppc
v8-3.14.5.10/test/mjsunit/big-array-literal.js
+--- v8-3.14.5.10/test/mjsunit/big-array-literal.js.ppc 2012-05-11 11:02:09.000000000
-0400
++++ v8-3.14.5.10/test/mjsunit/big-array-literal.js 2016-06-07 14:15:46.012392877 -0400
+@@ -26,7 +26,8 @@
+ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ // On MacOS, this test needs a stack size of at least 538 kBytes.
+-// Flags: --stack-size=600
++// On PPC64, this test needs a stack size of at least 698 kBytes.
++// Flags: --stack-size=700
+
+ // Test that we can make large object literals that work.
+ // Also test that we can attempt to make even larger object literals without
+diff -up v8-3.14.5.10/test/mjsunit/d8-os.js.ppc v8-3.14.5.10/test/mjsunit/d8-os.js
+--- v8-3.14.5.10/test/mjsunit/d8-os.js.ppc 2012-10-10 13:07:22.000000000 -0400
++++ v8-3.14.5.10/test/mjsunit/d8-os.js 2016-06-07 14:15:46.012392877 -0400
+@@ -135,7 +135,7 @@ if (this.os && os.system) {
+ assertThrows("os.system('sleep', ['2000'], -1, 20);",
"sleep 2");
+
+ // Check that -1 means no timeout.
+- os.system('sleep', ['0.1'], -1, -1);
++ os.system('sleep', ['1'], -1, -1);
+
+ }
+
+diff -up v8-3.14.5.10/test/mjsunit/mjsunit.status.ppc
v8-3.14.5.10/test/mjsunit/mjsunit.status
+--- v8-3.14.5.10/test/mjsunit/mjsunit.status.ppc 2016-06-07 14:15:45.965393158 -0400
++++ v8-3.14.5.10/test/mjsunit/mjsunit.status 2016-06-07 14:15:46.012392877 -0400
+@@ -179,3 +179,11 @@ debug-liveedit-stack-padding: SKIP
+ debug-liveedit-restart-frame: SKIP
+ debug-liveedit-double-call: SKIP
+
++##############################################################################
++[ $arch == ppc || $arch == ppc64 ]
++
++# Stack manipulations in LiveEdit is not implemented for this arch.
++debug-liveedit-check-stack: SKIP
++debug-liveedit-stack-padding: SKIP
++debug-liveedit-restart-frame: SKIP
++debug-liveedit-double-call: SKIP
+diff -up v8-3.14.5.10/tools/gyp/v8.gyp.ppc v8-3.14.5.10/tools/gyp/v8.gyp
+--- v8-3.14.5.10/tools/gyp/v8.gyp.ppc 2016-06-07 14:15:45.965393158 -0400
++++ v8-3.14.5.10/tools/gyp/v8.gyp 2016-06-07 14:15:46.012392877 -0400
+@@ -533,6 +533,40 @@
+ '../../src/arm/stub-cache-arm.cc',
+ ],
+ }],
++ ['v8_target_arch=="ppc" or
v8_target_arch=="ppc64"', {
++ 'sources': [
++ '../../src/ppc/assembler-ppc-inl.h',
++ '../../src/ppc/assembler-ppc.cc',
++ '../../src/ppc/assembler-ppc.h',
++ '../../src/ppc/builtins-ppc.cc',
++ '../../src/ppc/code-stubs-ppc.cc',
++ '../../src/ppc/code-stubs-ppc.h',
++ '../../src/ppc/codegen-ppc.cc',
++ '../../src/ppc/codegen-ppc.h',
++ '../../src/ppc/constants-ppc.h',
++ '../../src/ppc/constants-ppc.cc',
++ '../../src/ppc/cpu-ppc.cc',
++ '../../src/ppc/debug-ppc.cc',
++ '../../src/ppc/deoptimizer-ppc.cc',
++ '../../src/ppc/disasm-ppc.cc',
++ '../../src/ppc/frames-ppc.cc',
++ '../../src/ppc/frames-ppc.h',
++ '../../src/ppc/full-codegen-ppc.cc',
++ '../../src/ppc/ic-ppc.cc',
++ '../../src/ppc/lithium-ppc.cc',
++ '../../src/ppc/lithium-ppc.h',
++ '../../src/ppc/lithium-codegen-ppc.cc',
++ '../../src/ppc/lithium-codegen-ppc.h',
++ '../../src/ppc/lithium-gap-resolver-ppc.cc',
++ '../../src/ppc/lithium-gap-resolver-ppc.h',
++ '../../src/ppc/macro-assembler-ppc.cc',
++ '../../src/ppc/macro-assembler-ppc.h',
++ '../../src/ppc/regexp-macro-assembler-ppc.cc',
++ '../../src/ppc/regexp-macro-assembler-ppc.h',
++ '../../src/ppc/simulator-ppc.cc',
++ '../../src/ppc/stub-cache-ppc.cc',
++ ],
++ }],
+ ['v8_target_arch=="ia32" or v8_target_arch=="mac" or
OS=="mac"', {
+ 'sources': [
+ '../../src/ia32/assembler-ia32-inl.h',
+@@ -706,6 +740,13 @@
+ ],
+ }
+ ],
++ ['OS=="aix"', {
++ 'sources': [
++ '../../src/platform-aix.cc',
++ '../../src/platform-posix.cc',
++ ],
++ }
++ ],
+ ['OS=="solaris"', {
+ 'link_settings': {
+ 'libraries': [
+diff -up v8-3.14.5.10/tools/run-tests.py.ppc v8-3.14.5.10/tools/run-tests.py
+--- v8-3.14.5.10/tools/run-tests.py.ppc 2016-06-07 14:15:46.012392877 -0400
++++ v8-3.14.5.10/tools/run-tests.py 2016-06-07 14:31:28.108722591 -0400
+@@ -66,6 +66,8 @@ SUPPORTED_ARCHS = ["android_arm",
+ "ia32",
+ "mipsel",
+ "mips",
++ "ppc",
++ "ppc64",
+ "x64"]
+
+
+diff -up v8-3.14.5.10/tools/testrunner/local/statusfile.py.ppc
v8-3.14.5.10/tools/testrunner/local/statusfile.py
+--- v8-3.14.5.10/tools/testrunner/local/statusfile.py.ppc 2016-06-07 14:15:46.012392877
-0400
++++ v8-3.14.5.10/tools/testrunner/local/statusfile.py 2016-06-07 14:32:41.909274026
-0400
+@@ -59,7 +59,7 @@ DEFS = {FAIL_OK: [FAIL, OKAY],
+ # Support arches, modes to be written as keywords instead of strings.
+ VARIABLES = {ALWAYS: True}
+ for var in ["debug", "release", "android_arm",
"android_ia32", "arm", "ia32",
+- "mipsel", "mips", "x64"]:
++ "mipsel", "mips", "x64", "ppc",
"ppc64"]:
+ VARIABLES[var] = var
+
+
+diff -up v8-3.14.5.10/tools/test-wrapper-gypbuild.py.ppc
v8-3.14.5.10/tools/test-wrapper-gypbuild.py
+--- v8-3.14.5.10/tools/test-wrapper-gypbuild.py.ppc 2016-06-07 14:15:45.965393158 -0400
++++ v8-3.14.5.10/tools/test-wrapper-gypbuild.py 2016-06-07 14:32:08.309478248 -0400
+@@ -66,7 +66,7 @@ def BuildOptions():
+ default='release,debug')
+ result.add_option("--arch",
+ help='The architectures to run tests for
(comma-separated)',
+- default='ia32,x64,arm')
++ default='ia32,x64,arm,ppc')
+
+ # Flags that are passed on to the wrapped test.py script:
+ result.add_option("-v", "--verbose", help="Verbose
output",
+@@ -151,7 +151,7 @@ def ProcessOptions(options):
+ print "Unknown mode %s" % mode
+ return False
+ for arch in options.arch:
+- if not arch in ['ia32', 'x64', 'arm', 'mipsel',
'mips', 'android_arm',
++ if not arch in ['ia32', 'x64', 'arm', 'mipsel',
'mips', 'ppc', 'android_arm',
+ 'android_ia32']:
+ print "Unknown architecture %s" % arch
+ return False
+diff -up v8-3.14.5.10/tools/utils.py.ppc v8-3.14.5.10/tools/utils.py
+--- v8-3.14.5.10/tools/utils.py.ppc 2011-11-17 03:34:43.000000000 -0500
++++ v8-3.14.5.10/tools/utils.py 2016-06-07 14:15:46.012392877 -0400
+@@ -63,6 +63,8 @@ def GuessOS():
+ return 'solaris'
+ elif id == 'NetBSD':
+ return 'netbsd'
++ elif id == 'AIX':
++ return 'aix'
+ else:
+ return None
+
+@@ -83,6 +85,8 @@ def GuessArchitecture():
+ return 'ia32'
+ elif id == 'amd64':
+ return 'ia32'
++ elif id == 'ppc64':
++ return 'ppc'
+ else:
+ return None
+