Version 3.31.0 (based on 50a829b3cfe8ec0b7ccd1b7e154e632c9a73e5f0) Classes: Partial fix for constructor not calling super (issues 3661, 3672). Performance and stability improvements on all platforms. git-svn-id: https://v8.googlecode.com/svn/trunk@25239 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/Makefile b/Makefile index 3b02f52..d04d9ef 100644 --- a/Makefile +++ b/Makefile
@@ -64,6 +64,10 @@ ifeq ($(verifyheap), on) GYPFLAGS += -Dv8_enable_verify_heap=1 endif +# tracemaps=on +ifeq ($(tracemaps), on) + GYPFLAGS += -Dv8_trace_maps=1 +endif # backtrace=off ifeq ($(backtrace), off) GYPFLAGS += -Dv8_enable_backtrace=0
diff --git a/build/features.gypi b/build/features.gypi index 7ce66e4..612055a 100644 --- a/build/features.gypi +++ b/build/features.gypi
@@ -39,6 +39,8 @@ 'v8_enable_verify_heap%': 0, + 'v8_trace_maps%': 0, + 'v8_use_snapshot%': 'true', 'v8_enable_verify_predictable%': 0, @@ -77,6 +79,9 @@ ['v8_enable_verify_heap==1', { 'defines': ['VERIFY_HEAP',], }], + ['v8_trace_maps==1', { + 'defines': ['TRACE_MAPS',], + }], ['v8_enable_verify_predictable==1', { 'defines': ['VERIFY_PREDICTABLE',], }],
diff --git a/build/toolchain.gypi b/build/toolchain.gypi index 20c2c94..82a10e8 100644 --- a/build/toolchain.gypi +++ b/build/toolchain.gypi
@@ -852,7 +852,8 @@ 'V8_ENABLE_CHECKS', 'OBJECT_PRINT', 'VERIFY_HEAP', - 'DEBUG' + 'DEBUG', + 'TRACE_MAPS' ], 'conditions': [ ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="netbsd" or \
diff --git a/include/v8.h b/include/v8.h index d5433a6..ddae1ed 100644 --- a/include/v8.h +++ b/include/v8.h
@@ -6110,7 +6110,7 @@ static const int kNullValueRootIndex = 7; static const int kTrueValueRootIndex = 8; static const int kFalseValueRootIndex = 9; - static const int kEmptyStringRootIndex = 154; + static const int kEmptyStringRootIndex = 155; // The external allocation limit should be below 256 MB on all architectures // to avoid that resource-constrained embedders run low on memory.
diff --git a/src/api.cc b/src/api.cc index 75fc2d7..7380cc4 100644 --- a/src/api.cc +++ b/src/api.cc
@@ -3636,7 +3636,9 @@ i::JSObject::SetAccessor(Utils::OpenHandle(obj), info), false); if (result->IsUndefined()) return false; - if (fast) i::JSObject::MigrateSlowToFast(Utils::OpenHandle(obj), 0); + if (fast) { + i::JSObject::MigrateSlowToFast(Utils::OpenHandle(obj), 0, "APISetAccessor"); + } return true; } @@ -3822,7 +3824,8 @@ // as optimized code does not always handle access checks. i::Deoptimizer::DeoptimizeGlobalObject(*obj); - i::Handle<i::Map> new_map = i::Map::Copy(i::Handle<i::Map>(obj->map())); + i::Handle<i::Map> new_map = + i::Map::Copy(i::Handle<i::Map>(obj->map()), "APITurnOnAccessCheck"); new_map->set_is_access_check_needed(true); i::JSObject::MigrateToMap(obj, new_map); }
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc index eae38be..a65cf15 100644 --- a/src/arm/assembler-arm.cc +++ b/src/arm/assembler-arm.cc
@@ -1798,71 +1798,119 @@ } -void Assembler::uxtb(Register dst, - const Operand& src, - Condition cond) { +void Assembler::sxtb(Register dst, Register src, int rotate, Condition cond) { + // Instruction details available in ARM DDI 0406C.b, A8.8.233. + // cond(31-28) | 01101010(27-20) | 1111(19-16) | + // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) + DCHECK(!dst.is(pc)); + DCHECK(!src.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6A * B20 | 0xF * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src.code()); +} + + +void Assembler::sxtab(Register dst, Register src1, Register src2, int rotate, + Condition cond) { + // Instruction details available in ARM DDI 0406C.b, A8.8.233. + // cond(31-28) | 01101010(27-20) | Rn(19-16) | + // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) + DCHECK(!dst.is(pc)); + DCHECK(!src1.is(pc)); + DCHECK(!src2.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6A * B20 | src1.code() * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src2.code()); +} + + +void Assembler::sxth(Register dst, Register src, int rotate, Condition cond) { + // Instruction details available in ARM DDI 0406C.b, A8.8.235. + // cond(31-28) | 01101011(27-20) | 1111(19-16) | + // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) + DCHECK(!dst.is(pc)); + DCHECK(!src.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6B * B20 | 0xF * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src.code()); +} + + +void Assembler::sxtah(Register dst, Register src1, Register src2, int rotate, + Condition cond) { + // Instruction details available in ARM DDI 0406C.b, A8.8.235. + // cond(31-28) | 01101011(27-20) | Rn(19-16) | + // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) + DCHECK(!dst.is(pc)); + DCHECK(!src1.is(pc)); + DCHECK(!src2.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6B * B20 | src1.code() * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src2.code()); +} + + +void Assembler::uxtb(Register dst, Register src, int rotate, Condition cond) { // Instruction details available in ARM DDI 0406C.b, A8.8.274. // cond(31-28) | 01101110(27-20) | 1111(19-16) | // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) DCHECK(!dst.is(pc)); - DCHECK(!src.rm().is(pc)); - DCHECK(!src.rm().is(no_reg)); - DCHECK(src.rs().is(no_reg)); - DCHECK((src.shift_imm_ == 0) || - (src.shift_imm_ == 8) || - (src.shift_imm_ == 16) || - (src.shift_imm_ == 24)); - // Operand maps ROR #0 to LSL #0. - DCHECK((src.shift_op() == ROR) || - ((src.shift_op() == LSL) && (src.shift_imm_ == 0))); - emit(cond | 0x6E*B20 | 0xF*B16 | dst.code()*B12 | - ((src.shift_imm_ >> 1)&0xC)*B8 | 7*B4 | src.rm().code()); + DCHECK(!src.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6E * B20 | 0xF * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src.code()); } -void Assembler::uxtab(Register dst, - Register src1, - const Operand& src2, +void Assembler::uxtab(Register dst, Register src1, Register src2, int rotate, Condition cond) { // Instruction details available in ARM DDI 0406C.b, A8.8.271. // cond(31-28) | 01101110(27-20) | Rn(19-16) | // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) DCHECK(!dst.is(pc)); DCHECK(!src1.is(pc)); - DCHECK(!src2.rm().is(pc)); - DCHECK(!src2.rm().is(no_reg)); - DCHECK(src2.rs().is(no_reg)); - DCHECK((src2.shift_imm_ == 0) || - (src2.shift_imm_ == 8) || - (src2.shift_imm_ == 16) || - (src2.shift_imm_ == 24)); - // Operand maps ROR #0 to LSL #0. - DCHECK((src2.shift_op() == ROR) || - ((src2.shift_op() == LSL) && (src2.shift_imm_ == 0))); - emit(cond | 0x6E*B20 | src1.code()*B16 | dst.code()*B12 | - ((src2.shift_imm_ >> 1) &0xC)*B8 | 7*B4 | src2.rm().code()); + DCHECK(!src2.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6E * B20 | src1.code() * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src2.code()); } -void Assembler::uxtb16(Register dst, - const Operand& src, - Condition cond) { +void Assembler::uxtb16(Register dst, Register src, int rotate, Condition cond) { // Instruction details available in ARM DDI 0406C.b, A8.8.275. // cond(31-28) | 01101100(27-20) | 1111(19-16) | // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) DCHECK(!dst.is(pc)); - DCHECK(!src.rm().is(pc)); - DCHECK(!src.rm().is(no_reg)); - DCHECK(src.rs().is(no_reg)); - DCHECK((src.shift_imm_ == 0) || - (src.shift_imm_ == 8) || - (src.shift_imm_ == 16) || - (src.shift_imm_ == 24)); - // Operand maps ROR #0 to LSL #0. - DCHECK((src.shift_op() == ROR) || - ((src.shift_op() == LSL) && (src.shift_imm_ == 0))); - emit(cond | 0x6C*B20 | 0xF*B16 | dst.code()*B12 | - ((src.shift_imm_ >> 1)&0xC)*B8 | 7*B4 | src.rm().code()); + DCHECK(!src.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6C * B20 | 0xF * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src.code()); +} + + +void Assembler::uxth(Register dst, Register src, int rotate, Condition cond) { + // Instruction details available in ARM DDI 0406C.b, A8.8.276. + // cond(31-28) | 01101111(27-20) | 1111(19-16) | + // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) + DCHECK(!dst.is(pc)); + DCHECK(!src.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6F * B20 | 0xF * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src.code()); +} + + +void Assembler::uxtah(Register dst, Register src1, Register src2, int rotate, + Condition cond) { + // Instruction details available in ARM DDI 0406C.b, A8.8.273. + // cond(31-28) | 01101111(27-20) | Rn(19-16) | + // Rd(15-12) | rotate(11-10) | 00(9-8)| 0111(7-4) | Rm(3-0) + DCHECK(!dst.is(pc)); + DCHECK(!src1.is(pc)); + DCHECK(!src2.is(pc)); + DCHECK(rotate == 0 || rotate == 8 || rotate == 16 || rotate == 24); + emit(cond | 0x6F * B20 | src1.code() * B16 | dst.code() * B12 | + ((rotate >> 1) & 0xC) * B8 | 7 * B4 | src2.code()); }
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h index 9087fab..54c9278 100644 --- a/src/arm/assembler-arm.h +++ b/src/arm/assembler-arm.h
@@ -1034,12 +1034,20 @@ void pkhtb(Register dst, Register src1, const Operand& src2, Condition cond = al); - void uxtb(Register dst, const Operand& src, Condition cond = al); - - void uxtab(Register dst, Register src1, const Operand& src2, + void sxtb(Register dst, Register src, int rotate = 0, Condition cond = al); + void sxtab(Register dst, Register src1, Register src2, int rotate = 0, + Condition cond = al); + void sxth(Register dst, Register src, int rotate = 0, Condition cond = al); + void sxtah(Register dst, Register src1, Register src2, int rotate = 0, Condition cond = al); - void uxtb16(Register dst, const Operand& src, Condition cond = al); + void uxtb(Register dst, Register src, int rotate = 0, Condition cond = al); + void uxtab(Register dst, Register src1, Register src2, int rotate = 0, + Condition cond = al); + void uxtb16(Register dst, Register src, int rotate = 0, Condition cond = al); + void uxth(Register dst, Register src, int rotate = 0, Condition cond = al); + void uxtah(Register dst, Register src1, Register src2, int rotate = 0, + Condition cond = al); // Status register access instructions
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index a0e7e4a..e0750cd 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc
@@ -2686,6 +2686,10 @@ void CallICStub::Generate(MacroAssembler* masm) { // r1 - function // r3 - slot id (Smi) + const int with_types_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); + const int generic_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); Label extra_checks_or_miss, slow_start; Label slow, non_function, wrap, cont; Label have_js_function; @@ -2724,37 +2728,70 @@ } __ bind(&extra_checks_or_miss); - Label miss; + Label uninitialized, miss; __ CompareRoot(r4, Heap::kmegamorphic_symbolRootIndex); __ b(eq, &slow_start); - __ CompareRoot(r4, Heap::kuninitialized_symbolRootIndex); - __ b(eq, &miss); - if (!FLAG_trace_ic) { - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(r4); - __ CompareObjectType(r4, r5, r5, JS_FUNCTION_TYPE); - __ b(ne, &miss); - __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); - __ LoadRoot(ip, Heap::kmegamorphic_symbolRootIndex); - __ str(ip, FieldMemOperand(r4, FixedArray::kHeaderSize)); - // We have to update statistics for runtime profiling. - const int with_types_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); - __ ldr(r4, FieldMemOperand(r2, with_types_offset)); - __ sub(r4, r4, Operand(Smi::FromInt(1))); - __ str(r4, FieldMemOperand(r2, with_types_offset)); - const int generic_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); - __ ldr(r4, FieldMemOperand(r2, generic_offset)); - __ add(r4, r4, Operand(Smi::FromInt(1))); - __ str(r4, FieldMemOperand(r2, generic_offset)); - __ jmp(&slow_start); + // The following cases attempt to handle MISS cases without going to the + // runtime. + if (FLAG_trace_ic) { + __ jmp(&miss); } - // We are here because tracing is on or we are going monomorphic. + __ CompareRoot(r4, Heap::kuninitialized_symbolRootIndex); + __ b(eq, &uninitialized); + + // We are going megamorphic. If the feedback is a JSFunction, it is fine + // to handle it here. More complex cases are dealt with in the runtime. + __ AssertNotSmi(r4); + __ CompareObjectType(r4, r5, r5, JS_FUNCTION_TYPE); + __ b(ne, &miss); + __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); + __ LoadRoot(ip, Heap::kmegamorphic_symbolRootIndex); + __ str(ip, FieldMemOperand(r4, FixedArray::kHeaderSize)); + // We have to update statistics for runtime profiling. + __ ldr(r4, FieldMemOperand(r2, with_types_offset)); + __ sub(r4, r4, Operand(Smi::FromInt(1))); + __ str(r4, FieldMemOperand(r2, with_types_offset)); + __ ldr(r4, FieldMemOperand(r2, generic_offset)); + __ add(r4, r4, Operand(Smi::FromInt(1))); + __ str(r4, FieldMemOperand(r2, generic_offset)); + __ jmp(&slow_start); + + __ bind(&uninitialized); + + // We are going monomorphic, provided we actually have a JSFunction. + __ JumpIfSmi(r1, &miss); + + // Goto miss case if we do not have a function. + __ CompareObjectType(r1, r4, r4, JS_FUNCTION_TYPE); + __ b(ne, &miss); + + // Make sure the function is not the Array() function, which requires special + // behavior on MISS. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, r4); + __ cmp(r1, r4); + __ b(eq, &miss); + + // Update stats. + __ ldr(r4, FieldMemOperand(r2, with_types_offset)); + __ add(r4, r4, Operand(Smi::FromInt(1))); + __ str(r4, FieldMemOperand(r2, with_types_offset)); + + // Store the function. + __ add(r4, r2, Operand::PointerOffsetFromSmiKey(r3)); + __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ str(r1, MemOperand(r4, 0)); + + // Update the write barrier. + __ mov(r5, r1); + __ RecordWrite(r2, r4, r5, kLRHasNotBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ jmp(&have_js_function); + + // We are here because tracing is on or we encountered a MISS case we can't + // handle here. __ bind(&miss); GenerateMiss(masm);
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index b577f59..fd1b0ef 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc
@@ -288,8 +288,8 @@ __ bind(&loop); __ ldr(temp1, MemOperand(src, 4, PostIndex)); - __ uxtb16(temp3, Operand(temp1, ROR, 0)); - __ uxtb16(temp4, Operand(temp1, ROR, 8)); + __ uxtb16(temp3, temp1); + __ uxtb16(temp4, temp1, 8); __ pkhbt(temp1, temp3, Operand(temp4, LSL, 16)); __ str(temp1, MemOperand(dest)); __ pkhtb(temp1, temp4, Operand(temp3, ASR, 16)); @@ -301,9 +301,9 @@ __ mov(chars, Operand(chars, LSL, 31), SetCC); // bit0 => ne, bit1 => cs __ b(¬_two, cc); __ ldrh(temp1, MemOperand(src, 2, PostIndex)); - __ uxtb(temp3, Operand(temp1, ROR, 8)); + __ uxtb(temp3, temp1, 8); __ mov(temp3, Operand(temp3, LSL, 16)); - __ uxtab(temp3, temp3, Operand(temp1, ROR, 0)); + __ uxtab(temp3, temp3, temp1); __ str(temp3, MemOperand(dest, 4, PostIndex)); __ bind(¬_two); __ ldrb(temp1, MemOperand(src), ne);
diff --git a/src/arm/disasm-arm.cc b/src/arm/disasm-arm.cc index dc26018..4e631b0 100644 --- a/src/arm/disasm-arm.cc +++ b/src/arm/disasm-arm.cc
@@ -1027,7 +1027,75 @@ UNREACHABLE(); break; case 1: - UNREACHABLE(); + if (instr->Bits(9, 6) == 1) { + if (instr->Bit(20) == 0) { + if (instr->Bits(19, 16) == 0xF) { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "sxtb'cond 'rd, 'rm"); + break; + case 1: + Format(instr, "sxtb'cond 'rd, 'rm, ror #8"); + break; + case 2: + Format(instr, "sxtb'cond 'rd, 'rm, ror #16"); + break; + case 3: + Format(instr, "sxtb'cond 'rd, 'rm, ror #24"); + break; + } + } else { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "sxtab'cond 'rd, 'rn, 'rm"); + break; + case 1: + Format(instr, "sxtab'cond 'rd, 'rn, 'rm, ror #8"); + break; + case 2: + Format(instr, "sxtab'cond 'rd, 'rn, 'rm, ror #16"); + break; + case 3: + Format(instr, "sxtab'cond 'rd, 'rn, 'rm, ror #24"); + break; + } + } + } else { + if (instr->Bits(19, 16) == 0xF) { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "sxth'cond 'rd, 'rm"); + break; + case 1: + Format(instr, "sxth'cond 'rd, 'rm, ror #8"); + break; + case 2: + Format(instr, "sxth'cond 'rd, 'rm, ror #16"); + break; + case 3: + Format(instr, "sxth'cond 'rd, 'rm, ror #24"); + break; + } + } else { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "sxtah'cond 'rd, 'rn, 'rm"); + break; + case 1: + Format(instr, "sxtah'cond 'rd, 'rn, 'rm, ror #8"); + break; + case 2: + Format(instr, "sxtah'cond 'rd, 'rn, 'rm, ror #16"); + break; + case 3: + Format(instr, "sxtah'cond 'rd, 'rn, 'rm, ror #24"); + break; + } + } + } + } else { + UNREACHABLE(); + } break; case 2: if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { @@ -1054,36 +1122,70 @@ } break; case 3: - if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { - if (instr->Bits(19, 16) == 0xF) { - switch (instr->Bits(11, 10)) { - case 0: - Format(instr, "uxtb'cond 'rd, 'rm"); - break; - case 1: - Format(instr, "uxtb'cond 'rd, 'rm, ror #8"); - break; - case 2: - Format(instr, "uxtb'cond 'rd, 'rm, ror #16"); - break; - case 3: - Format(instr, "uxtb'cond 'rd, 'rm, ror #24"); - break; + if ((instr->Bits(9, 6) == 1)) { + if ((instr->Bit(20) == 0)) { + if (instr->Bits(19, 16) == 0xF) { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "uxtb'cond 'rd, 'rm"); + break; + case 1: + Format(instr, "uxtb'cond 'rd, 'rm, ror #8"); + break; + case 2: + Format(instr, "uxtb'cond 'rd, 'rm, ror #16"); + break; + case 3: + Format(instr, "uxtb'cond 'rd, 'rm, ror #24"); + break; + } + } else { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "uxtab'cond 'rd, 'rn, 'rm"); + break; + case 1: + Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #8"); + break; + case 2: + Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #16"); + break; + case 3: + Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #24"); + break; + } } } else { - switch (instr->Bits(11, 10)) { - case 0: - Format(instr, "uxtab'cond 'rd, 'rn, 'rm"); - break; - case 1: - Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #8"); - break; - case 2: - Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #16"); - break; - case 3: - Format(instr, "uxtab'cond 'rd, 'rn, 'rm, ror #24"); - break; + if (instr->Bits(19, 16) == 0xF) { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "uxth'cond 'rd, 'rm"); + break; + case 1: + Format(instr, "uxth'cond 'rd, 'rm, ror #8"); + break; + case 2: + Format(instr, "uxth'cond 'rd, 'rm, ror #16"); + break; + case 3: + Format(instr, "uxth'cond 'rd, 'rm, ror #24"); + break; + } + } else { + switch (instr->Bits(11, 10)) { + case 0: + Format(instr, "uxtah'cond 'rd, 'rn, 'rm"); + break; + case 1: + Format(instr, "uxtah'cond 'rd, 'rn, 'rm, ror #8"); + break; + case 2: + Format(instr, "uxtah'cond 'rd, 'rn, 'rm, ror #16"); + break; + case 3: + Format(instr, "uxtah'cond 'rd, 'rn, 'rm, ror #24"); + break; + } } } } else {
diff --git a/src/arm/simulator-arm.cc b/src/arm/simulator-arm.cc index 972fd07..e34c311 100644 --- a/src/arm/simulator-arm.cc +++ b/src/arm/simulator-arm.cc
@@ -2629,7 +2629,89 @@ UNIMPLEMENTED(); break; case 1: - UNIMPLEMENTED(); + if (instr->Bits(9, 6) == 1) { + if (instr->Bit(20) == 0) { + if (instr->Bits(19, 16) == 0xF) { + // Sxtb. + int32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, static_cast<int8_t>(rm_val)); + } else { + // Sxtab. + int32_t rn_val = get_register(rn); + int32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, rn_val + static_cast<int8_t>(rm_val)); + } + } else { + if (instr->Bits(19, 16) == 0xF) { + // Sxth. + int32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, static_cast<int16_t>(rm_val)); + } else { + // Sxtah. + int32_t rn_val = get_register(rn); + int32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, rn_val + static_cast<int16_t>(rm_val)); + } + } + } else { + UNREACHABLE(); + } break; case 2: if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { @@ -2650,8 +2732,7 @@ rm_val = (rm_val >> 24) | (rm_val << 8); break; } - set_register(rd, - (rm_val & 0xFF) | (rm_val & 0xFF0000)); + set_register(rd, (rm_val & 0xFF) | (rm_val & 0xFF0000)); } else { UNIMPLEMENTED(); } @@ -2660,44 +2741,85 @@ } break; case 3: - if ((instr->Bit(20) == 0) && (instr->Bits(9, 6) == 1)) { - if (instr->Bits(19, 16) == 0xF) { - // Uxtb. - uint32_t rm_val = get_register(instr->RmValue()); - int32_t rotate = instr->Bits(11, 10); - switch (rotate) { - case 0: - break; - case 1: - rm_val = (rm_val >> 8) | (rm_val << 24); - break; - case 2: - rm_val = (rm_val >> 16) | (rm_val << 16); - break; - case 3: - rm_val = (rm_val >> 24) | (rm_val << 8); - break; + if ((instr->Bits(9, 6) == 1)) { + if (instr->Bit(20) == 0) { + if (instr->Bits(19, 16) == 0xF) { + // Uxtb. + uint32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, (rm_val & 0xFF)); + } else { + // Uxtab. + uint32_t rn_val = get_register(rn); + uint32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, rn_val + (rm_val & 0xFF)); } - set_register(rd, (rm_val & 0xFF)); } else { - // Uxtab. - uint32_t rn_val = get_register(rn); - uint32_t rm_val = get_register(instr->RmValue()); - int32_t rotate = instr->Bits(11, 10); - switch (rotate) { - case 0: - break; - case 1: - rm_val = (rm_val >> 8) | (rm_val << 24); - break; - case 2: - rm_val = (rm_val >> 16) | (rm_val << 16); - break; - case 3: - rm_val = (rm_val >> 24) | (rm_val << 8); - break; + if (instr->Bits(19, 16) == 0xF) { + // Uxth. + uint32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, (rm_val & 0xFFFF)); + } else { + // Uxtah. + uint32_t rn_val = get_register(rn); + uint32_t rm_val = get_register(instr->RmValue()); + int32_t rotate = instr->Bits(11, 10); + switch (rotate) { + case 0: + break; + case 1: + rm_val = (rm_val >> 8) | (rm_val << 24); + break; + case 2: + rm_val = (rm_val >> 16) | (rm_val << 16); + break; + case 3: + rm_val = (rm_val >> 24) | (rm_val << 8); + break; + } + set_register(rd, rn_val + (rm_val & 0xFFFF)); } - set_register(rd, rn_val + (rm_val & 0xFF)); } } else { UNIMPLEMENTED();
diff --git a/src/arm64/assembler-arm64.cc b/src/arm64/assembler-arm64.cc index 076e143..524be15 100644 --- a/src/arm64/assembler-arm64.cc +++ b/src/arm64/assembler-arm64.cc
@@ -44,17 +44,8 @@ // CpuFeatures implementation. void CpuFeatures::ProbeImpl(bool cross_compile) { - if (cross_compile) { - // Always align csp in cross compiled code - this is safe and ensures that - // csp will always be aligned if it is enabled by probing at runtime. - if (FLAG_enable_always_align_csp) supported_ |= 1u << ALWAYS_ALIGN_CSP; - } else { - base::CPU cpu; - if (FLAG_enable_always_align_csp && - (cpu.implementer() == base::CPU::NVIDIA || FLAG_debug_code)) { - supported_ |= 1u << ALWAYS_ALIGN_CSP; - } - } + // AArch64 has no configuration options, no further probing is required. + supported_ = 0; }
diff --git a/src/arm64/code-stubs-arm64.cc b/src/arm64/code-stubs-arm64.cc index 6583775..0d6f8ad 100644 --- a/src/arm64/code-stubs-arm64.cc +++ b/src/arm64/code-stubs-arm64.cc
@@ -3016,6 +3016,10 @@ // x1 - function // x3 - slot id (Smi) + const int with_types_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); + const int generic_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); Label extra_checks_or_miss, slow_start; Label slow, non_function, wrap, cont; Label have_js_function; @@ -3064,35 +3068,72 @@ } __ bind(&extra_checks_or_miss); - Label miss; + Label uninitialized, miss; __ JumpIfRoot(x4, Heap::kmegamorphic_symbolRootIndex, &slow_start); - __ JumpIfRoot(x4, Heap::kuninitialized_symbolRootIndex, &miss); - if (!FLAG_trace_ic) { - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(x4); - __ JumpIfNotObjectType(x4, x5, x5, JS_FUNCTION_TYPE, &miss); - __ Add(x4, feedback_vector, - Operand::UntagSmiAndScale(index, kPointerSizeLog2)); - __ LoadRoot(x5, Heap::kmegamorphic_symbolRootIndex); - __ Str(x5, FieldMemOperand(x4, FixedArray::kHeaderSize)); - // We have to update statistics for runtime profiling. - const int with_types_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); - __ Ldr(x4, FieldMemOperand(feedback_vector, with_types_offset)); - __ Subs(x4, x4, Operand(Smi::FromInt(1))); - __ Str(x4, FieldMemOperand(feedback_vector, with_types_offset)); - const int generic_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); - __ Ldr(x4, FieldMemOperand(feedback_vector, generic_offset)); - __ Adds(x4, x4, Operand(Smi::FromInt(1))); - __ Str(x4, FieldMemOperand(feedback_vector, generic_offset)); - __ B(&slow_start); + // The following cases attempt to handle MISS cases without going to the + // runtime. + if (FLAG_trace_ic) { + __ jmp(&miss); } - // We are here because tracing is on or we are going monomorphic. + __ JumpIfRoot(x4, Heap::kuninitialized_symbolRootIndex, &miss); + + // We are going megamorphic. If the feedback is a JSFunction, it is fine + // to handle it here. More complex cases are dealt with in the runtime. + __ AssertNotSmi(x4); + __ JumpIfNotObjectType(x4, x5, x5, JS_FUNCTION_TYPE, &miss); + __ Add(x4, feedback_vector, + Operand::UntagSmiAndScale(index, kPointerSizeLog2)); + __ LoadRoot(x5, Heap::kmegamorphic_symbolRootIndex); + __ Str(x5, FieldMemOperand(x4, FixedArray::kHeaderSize)); + // We have to update statistics for runtime profiling. + __ Ldr(x4, FieldMemOperand(feedback_vector, with_types_offset)); + __ Subs(x4, x4, Operand(Smi::FromInt(1))); + __ Str(x4, FieldMemOperand(feedback_vector, with_types_offset)); + __ Ldr(x4, FieldMemOperand(feedback_vector, generic_offset)); + __ Adds(x4, x4, Operand(Smi::FromInt(1))); + __ Str(x4, FieldMemOperand(feedback_vector, generic_offset)); + __ B(&slow_start); + + __ bind(&uninitialized); + + // We are going monomorphic, provided we actually have a JSFunction. + __ JumpIfSmi(function, &miss); + + // Goto miss case if we do not have a function. + __ JumpIfNotObjectType(function, x5, x5, JS_FUNCTION_TYPE, &miss); + + // Make sure the function is not the Array() function, which requires special + // behavior on MISS. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, x5); + __ Cmp(function, x5); + __ B(eq, &miss); + + // Update stats. + __ Ldr(x4, FieldMemOperand(feedback_vector, with_types_offset)); + __ Adds(x4, x4, Operand(Smi::FromInt(1))); + __ Str(x4, FieldMemOperand(feedback_vector, with_types_offset)); + + // Store the function. + __ Add(x4, feedback_vector, + Operand::UntagSmiAndScale(index, kPointerSizeLog2)); + __ Str(function, FieldMemOperand(x4, FixedArray::kHeaderSize)); + + __ Add(x4, feedback_vector, + Operand::UntagSmiAndScale(index, kPointerSizeLog2)); + __ Add(x4, x4, FixedArray::kHeaderSize - kHeapObjectTag); + __ Str(function, MemOperand(x4, 0)); + + // Update the write barrier. + __ Mov(x5, function); + __ RecordWrite(feedback_vector, x4, x5, kLRHasNotBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ B(&have_js_function); + + // We are here because tracing is on or we encountered a MISS case we can't + // handle here. __ bind(&miss); GenerateMiss(masm); @@ -4293,18 +4334,10 @@ } -static unsigned int GetProfileEntryHookCallSize(MacroAssembler* masm) { - // The entry hook is a "BumpSystemStackPointer" instruction (sub), - // followed by a "Push lr" instruction, followed by a call. - unsigned int size = - Assembler::kCallSizeWithRelocation + (2 * kInstructionSize); - if (CpuFeatures::IsSupported(ALWAYS_ALIGN_CSP)) { - // If ALWAYS_ALIGN_CSP then there will be an extra bic instruction in - // "BumpSystemStackPointer". - size += kInstructionSize; - } - return size; -} +// The entry hook is a "BumpSystemStackPointer" instruction (sub), followed by +// a "Push lr" instruction, followed by a call. +static const unsigned int kProfileEntryHookCallSize = + Assembler::kCallSizeWithRelocation + (2 * kInstructionSize); void ProfileEntryHookStub::MaybeCallEntryHook(MacroAssembler* masm) { @@ -4317,7 +4350,7 @@ __ Push(lr); __ CallStub(&stub); DCHECK(masm->SizeOfCodeGeneratedSince(&entry_hook_call_start) == - GetProfileEntryHookCallSize(masm)); + kProfileEntryHookCallSize); __ Pop(lr); } @@ -4335,7 +4368,7 @@ const int kNumSavedRegs = kCallerSaved.Count(); // Compute the function's address as the first argument. - __ Sub(x0, lr, GetProfileEntryHookCallSize(masm)); + __ Sub(x0, lr, kProfileEntryHookCallSize); #if V8_HOST_ARCH_ARM64 uintptr_t entry_hook =
diff --git a/src/arm64/lithium-codegen-arm64.cc b/src/arm64/lithium-codegen-arm64.cc index a285e7b..1b8ae1b 100644 --- a/src/arm64/lithium-codegen-arm64.cc +++ b/src/arm64/lithium-codegen-arm64.cc
@@ -557,11 +557,6 @@ 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,
diff --git a/src/arm64/macro-assembler-arm64-inl.h b/src/arm64/macro-assembler-arm64-inl.h index 4a4d644..b691e21 100644 --- a/src/arm64/macro-assembler-arm64-inl.h +++ b/src/arm64/macro-assembler-arm64-inl.h
@@ -1244,14 +1244,7 @@ void MacroAssembler::BumpSystemStackPointer(const Operand& space) { DCHECK(!csp.Is(sp_)); if (!TmpList()->IsEmpty()) { - if (CpuFeatures::IsSupported(ALWAYS_ALIGN_CSP)) { - UseScratchRegisterScope temps(this); - Register temp = temps.AcquireX(); - Sub(temp, StackPointer(), space); - Bic(csp, temp, 0xf); - } else { - Sub(csp, StackPointer(), space); - } + Sub(csp, StackPointer(), space); } else { // TODO(jbramley): Several callers rely on this not using scratch // registers, so we use the assembler directly here. However, this means @@ -1288,11 +1281,7 @@ DCHECK(emit_debug_code()); DCHECK(!csp.Is(sp_)); { InstructionAccurateScope scope(this); - if (CpuFeatures::IsSupported(ALWAYS_ALIGN_CSP)) { - bic(csp, StackPointer(), 0xf); - } else { - mov(csp, StackPointer()); - } + mov(csp, StackPointer()); } AssertStackConsistency(); }
diff --git a/src/arm64/macro-assembler-arm64.cc b/src/arm64/macro-assembler-arm64.cc index e0a2190..f2ca92b 100644 --- a/src/arm64/macro-assembler-arm64.cc +++ b/src/arm64/macro-assembler-arm64.cc
@@ -1308,7 +1308,7 @@ // Avoid emitting code when !use_real_abort() since non-real aborts cause too // much code to be generated. if (emit_debug_code() && use_real_aborts()) { - if (csp.Is(StackPointer()) || CpuFeatures::IsSupported(ALWAYS_ALIGN_CSP)) { + if (csp.Is(StackPointer())) { // Always check the alignment of csp if ALWAYS_ALIGN_CSP is true. We // can't check the alignment of csp without using a scratch register (or // clobbering the flags), but the processor (or simulator) will abort if
diff --git a/src/arm64/macro-assembler-arm64.h b/src/arm64/macro-assembler-arm64.h index cff42d7..db51156 100644 --- a/src/arm64/macro-assembler-arm64.h +++ b/src/arm64/macro-assembler-arm64.h
@@ -761,9 +761,9 @@ // it can be evidence of a potential bug because the ABI forbids accesses // below csp. // - // If StackPointer() is the system stack pointer (csp) or ALWAYS_ALIGN_CSP is - // enabled, then csp will be dereferenced to cause the processor - // (or simulator) to abort if it is not properly aligned. + // If StackPointer() is the system stack pointer (csp), then csp will be + // dereferenced to cause the processor (or simulator) to abort if it is not + // properly aligned. // // If emit_debug_code() is false, this emits no code. void AssertStackConsistency(); @@ -831,9 +831,7 @@ inline void BumpSystemStackPointer(const Operand& space); // Re-synchronizes the system stack pointer (csp) with the current stack - // pointer (according to StackPointer()). This function will ensure the - // new value of the system stack pointer is remains aligned to 16 bytes, and - // is lower than or equal to the value of the current stack pointer. + // pointer (according to StackPointer()). // // This method asserts that StackPointer() is not csp, since the call does // not make sense in that context.
diff --git a/src/ast.h b/src/ast.h index 749e579..1700916 100644 --- a/src/ast.h +++ b/src/ast.h
@@ -2576,6 +2576,12 @@ bool is_concise_method() { return IsConciseMethod(FunctionKindBits::decode(bitfield_)); } + bool is_default_constructor() { + return IsDefaultConstructor(FunctionKindBits::decode(bitfield_)); + } + bool is_default_constructor_call_super() { + return IsDefaultConstructorCallSuper(FunctionKindBits::decode(bitfield_)); + } int ast_node_count() { return ast_properties_.node_count(); } AstProperties::Flags* flags() { return ast_properties_.flags(); } @@ -2647,7 +2653,7 @@ class HasDuplicateParameters : public BitField<ParameterFlag, 3, 1> {}; class IsFunction : public BitField<IsFunctionFlag, 4, 1> {}; class IsParenthesized : public BitField<IsParenthesizedFlag, 5, 1> {}; - class FunctionKindBits : public BitField<FunctionKind, 6, 3> {}; + class FunctionKindBits : public BitField<FunctionKind, 6, 5> {}; };
diff --git a/src/base/platform/platform-win32.cc b/src/base/platform/platform-win32.cc index 1c46cf6..d68e861 100644 --- a/src/base/platform/platform-win32.cc +++ b/src/base/platform/platform-win32.cc
@@ -346,26 +346,41 @@ } -int64_t FileTimeToInt64(FILETIME ft) { - ULARGE_INTEGER result; - result.LowPart = ft.dwLowDateTime; - result.HighPart = ft.dwHighDateTime; - return static_cast<int64_t>(result.QuadPart); -} - - // Return the local timezone offset in milliseconds east of UTC. This // takes into account whether daylight saving is in effect at the time. // Only times in the 32-bit Unix range may be passed to this function. // Also, adding the time-zone offset to the input must not overflow. // The function EquivalentTime() in date.js guarantees this. int64_t Win32Time::LocalOffset(TimezoneCache* cache) { - FILETIME local; - SYSTEMTIME system_utc, system_local; - FileTimeToSystemTime(&time_.ft_, &system_utc); - SystemTimeToTzSpecificLocalTime(NULL, &system_utc, &system_local); - SystemTimeToFileTime(&system_local, &local); - return (FileTimeToInt64(local) - FileTimeToInt64(time_.ft_)) / kTimeScaler; + cache->InitializeIfNeeded(); + + Win32Time rounded_to_second(*this); + rounded_to_second.t() = + rounded_to_second.t() / 1000 / kTimeScaler * 1000 * kTimeScaler; + // Convert to local time using POSIX localtime function. + // Windows XP Service Pack 3 made SystemTimeToTzSpecificLocalTime() + // very slow. Other browsers use localtime(). + + // Convert from JavaScript milliseconds past 1/1/1970 0:00:00 to + // POSIX seconds past 1/1/1970 0:00:00. + double unchecked_posix_time = rounded_to_second.ToJSTime() / 1000; + if (unchecked_posix_time > INT_MAX || unchecked_posix_time < 0) { + return 0; + } + // Because _USE_32BIT_TIME_T is defined, time_t is a 32-bit int. + time_t posix_time = static_cast<time_t>(unchecked_posix_time); + + // Convert to local time, as struct with fields for day, hour, year, etc. + tm posix_local_time_struct; + if (localtime_s(&posix_local_time_struct, &posix_time)) return 0; + + if (posix_local_time_struct.tm_isdst > 0) { + return (cache->tzinfo_.Bias + cache->tzinfo_.DaylightBias) * -kMsPerMinute; + } else if (posix_local_time_struct.tm_isdst == 0) { + return (cache->tzinfo_.Bias + cache->tzinfo_.StandardBias) * -kMsPerMinute; + } else { + return cache->tzinfo_.Bias * -kMsPerMinute; + } }
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 264d1c6..6f37a94 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc
@@ -361,7 +361,7 @@ static void SetObjectPrototype(Handle<JSObject> object, Handle<Object> proto) { // object.__proto__ = proto; Handle<Map> old_map = Handle<Map>(object->map()); - Handle<Map> new_map = Map::Copy(old_map); + Handle<Map> new_map = Map::Copy(old_map, "SetObjectPrototype"); new_map->set_prototype(*proto); JSObject::MigrateToMap(object, new_map); } @@ -510,7 +510,8 @@ Handle<JSObject> prototype = factory->NewJSObject( isolate->object_function(), TENURED); - Handle<Map> map = Map::Copy(handle(prototype->map())); + Handle<Map> map = + Map::Copy(handle(prototype->map()), "EmptyObjectPrototype"); map->set_is_prototype_map(true); prototype->set_map(*map); @@ -908,6 +909,10 @@ Factory* factory = isolate->factory(); Heap* heap = isolate->heap(); + Handle<GlobalContextTable> global_context_table = + factory->NewGlobalContextTable(); + native_context()->set_global_context_table(*global_context_table); + Handle<String> object_name = factory->Object_string(); JSObject::AddProperty( global_object, object_name, isolate->object_function(), DONT_ENUM); @@ -1089,7 +1094,7 @@ initial_map->set_visitor_id(StaticVisitorBase::GetVisitorId(*initial_map)); // RegExp prototype object is itself a RegExp. - Handle<Map> proto_map = Map::Copy(initial_map); + Handle<Map> proto_map = Map::Copy(initial_map, "RegExpPrototype"); proto_map->set_prototype(native_context()->initial_object_prototype()); Handle<JSObject> proto = factory->NewJSObjectFromMap(proto_map); proto->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, @@ -1244,7 +1249,8 @@ } { // --- aliased arguments map - Handle<Map> map = Map::Copy(isolate->sloppy_arguments_map()); + Handle<Map> map = + Map::Copy(isolate->sloppy_arguments_map(), "AliasedArguments"); map->set_elements_kind(SLOPPY_ARGUMENTS_ELEMENTS); DCHECK_EQ(2, map->pre_allocated_property_fields()); native_context()->set_aliased_arguments_map(*map); @@ -1657,7 +1663,7 @@ array_function->shared()->DontAdaptArguments(); Handle<Map> original_map(array_function->initial_map()); - Handle<Map> initial_map = Map::Copy(original_map); + Handle<Map> initial_map = Map::Copy(original_map, "InternalArray"); initial_map->set_elements_kind(elements_kind); JSFunction::SetInitialMap(array_function, initial_map, prototype); @@ -1935,7 +1941,7 @@ // Create maps for generator functions and their prototypes. Store those // maps in the native context. Handle<Map> generator_function_map = - Map::Copy(sloppy_function_map_writable_prototype_); + Map::Copy(sloppy_function_map_writable_prototype_, "GeneratorFunction"); generator_function_map->set_prototype(*generator_function_prototype); native_context()->set_sloppy_generator_function_map( *generator_function_map); @@ -1966,7 +1972,8 @@ rw_attribs, poison_pair); Handle<Map> strict_function_map(native_context()->strict_function_map()); - Handle<Map> strict_generator_function_map = Map::Copy(strict_function_map); + Handle<Map> strict_generator_function_map = + Map::Copy(strict_function_map, "StrictGeneratorFunction"); // "arguments" and "caller" already poisoned. strict_generator_function_map->set_prototype(*generator_function_prototype); native_context()->set_strict_generator_function_map( @@ -2712,6 +2719,15 @@ AddToWeakNativeContextList(*native_context()); isolate->set_context(*native_context()); isolate->counters()->contexts_created_by_snapshot()->Increment(); +#if TRACE_MAPS + if (FLAG_trace_maps) { + Handle<JSFunction> object_fun = isolate->object_function(); + PrintF("[TraceMap: InitialMap map= %p SFI= %d_Object ]\n", + reinterpret_cast<void*>(object_fun->initial_map()), + object_fun->shared()->unique_id()); + Map::TraceAllTransitions(object_fun->initial_map()); + } +#endif Handle<GlobalObject> global_object; Handle<JSGlobalProxy> global_proxy = CreateNewGlobals( global_proxy_template, maybe_global_proxy, &global_object);
diff --git a/src/code-stubs.h b/src/code-stubs.h index 06eff69..13f8e42 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h
@@ -577,10 +577,14 @@ bool is_arrow() const { return IsArrowFunction(kind()); } bool is_generator() const { return IsGeneratorFunction(kind()); } bool is_concise_method() const { return IsConciseMethod(kind()); } + bool is_default_constructor() const { return IsDefaultConstructor(kind()); } + bool is_default_constructor_call_super() const { + return IsDefaultConstructorCallSuper(kind()); + } private: class StrictModeBits : public BitField<StrictMode, 0, 1> {}; - class FunctionKindBits : public BitField<FunctionKind, 1, 3> {}; + class FunctionKindBits : public BitField<FunctionKind, 1, 5> {}; DEFINE_CALL_INTERFACE_DESCRIPTOR(FastNewClosure); DEFINE_HYDROGEN_CODE_STUB(FastNewClosure, HydrogenCodeStub);
diff --git a/src/compiler/arm/code-generator-arm.cc b/src/compiler/arm/code-generator-arm.cc index 3433765..f4b8716 100644 --- a/src/compiler/arm/code-generator-arm.cc +++ b/src/compiler/arm/code-generator-arm.cc
@@ -299,6 +299,42 @@ DCHECK_EQ(LeaveCC, i.OutputSBit()); break; } + case kArmSxtb: + __ sxtb(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; + case kArmSxth: + __ sxth(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; + case kArmSxtab: + __ sxtab(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), + i.InputInt32(2)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; + case kArmSxtah: + __ sxtah(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), + i.InputInt32(2)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; + case kArmUxtb: + __ uxtb(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; + case kArmUxth: + __ uxth(i.OutputRegister(), i.InputRegister(0), i.InputInt32(1)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; + case kArmUxtab: + __ uxtab(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), + i.InputInt32(2)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; + case kArmUxtah: + __ uxtah(i.OutputRegister(), i.InputRegister(0), i.InputRegister(1), + i.InputInt32(2)); + DCHECK_EQ(LeaveCC, i.OutputSBit()); + break; case kArmCmp: __ cmp(i.InputRegister(0), i.InputOperand2(1)); DCHECK_EQ(SetCC, i.OutputSBit());
diff --git a/src/compiler/arm/instruction-codes-arm.h b/src/compiler/arm/instruction-codes-arm.h index c48369e..ecd0b2d 100644 --- a/src/compiler/arm/instruction-codes-arm.h +++ b/src/compiler/arm/instruction-codes-arm.h
@@ -35,6 +35,14 @@ V(ArmMvn) \ V(ArmBfc) \ V(ArmUbfx) \ + V(ArmSxtb) \ + V(ArmSxth) \ + V(ArmSxtab) \ + V(ArmSxtah) \ + V(ArmUxtb) \ + V(ArmUxth) \ + V(ArmUxtab) \ + V(ArmUxtah) \ V(ArmVcmpF64) \ V(ArmVaddF64) \ V(ArmVsubF64) \
diff --git a/src/compiler/arm/instruction-selector-arm.cc b/src/compiler/arm/instruction-selector-arm.cc index a071bbc..2bae140 100644 --- a/src/compiler/arm/instruction-selector-arm.cc +++ b/src/compiler/arm/instruction-selector-arm.cc
@@ -91,6 +91,14 @@ case kArmUdiv: case kArmBfc: case kArmUbfx: + case kArmSxtb: + case kArmSxth: + case kArmSxtab: + case kArmSxtah: + case kArmUxtb: + case kArmUxth: + case kArmUxtab: + case kArmUxtah: case kArmVcmpF64: case kArmVaddF64: case kArmVsubF64: @@ -255,8 +263,20 @@ InstructionOperand* outputs[2]; size_t output_count = 0; - if (TryMatchImmediateOrShift(selector, &opcode, m.right().node(), - &input_count, &inputs[1])) { + if (m.left().node() == m.right().node()) { + // If both inputs refer to the same operand, enforce allocating a register + // for both of them to ensure that we don't end up generating code like + // this: + // + // mov r0, r1, asr #16 + // adds r0, r0, r1, asr #16 + // bvs label + InstructionOperand* const input = g.UseRegister(m.left().node()); + opcode |= AddressingModeField::encode(kMode_Operand2_R); + inputs[input_count++] = input; + inputs[input_count++] = input; + } else if (TryMatchImmediateOrShift(selector, &opcode, m.right().node(), + &input_count, &inputs[1])) { inputs[0] = g.UseRegister(m.left().node()); input_count++; } else if (TryMatchImmediateOrShift(selector, &reverse_opcode, @@ -430,12 +450,12 @@ return; } } - if (IsSupported(ARMv7) && m.right().HasValue()) { - // Try to interpret this AND as UBFX. + if (m.right().HasValue()) { uint32_t const value = m.right().Value(); uint32_t width = base::bits::CountPopulation32(value); uint32_t msb = base::bits::CountLeadingZeros32(value); - if (width != 0 && msb + width == 32) { + // Try to interpret this AND as UBFX. + if (IsSupported(ARMv7) && width != 0 && msb + width == 32) { DCHECK_EQ(0, base::bits::CountTrailingZeros32(value)); if (m.left().IsWord32Shr()) { Int32BinopMatcher mleft(m.left().node()); @@ -450,7 +470,6 @@ g.TempImmediate(0), g.TempImmediate(width)); return; } - // Try to interpret this AND as BIC. if (g.CanBeImmediate(~value)) { Emit(kArmBic | AddressingModeField::encode(kMode_Operand2_I), @@ -458,16 +477,23 @@ g.TempImmediate(~value)); return; } - - // Try to interpret this AND as BFC. - width = 32 - width; - msb = base::bits::CountLeadingZeros32(~value); - uint32_t lsb = base::bits::CountTrailingZeros32(~value); - if (msb + width + lsb == 32) { - Emit(kArmBfc, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), - g.TempImmediate(lsb), g.TempImmediate(width)); + // Try to interpret this AND as UXTH. + if (value == 0xffff) { + Emit(kArmUxth, g.DefineAsRegister(m.node()), + g.UseRegister(m.left().node()), g.TempImmediate(0)); return; } + // Try to interpret this AND as BFC. + if (IsSupported(ARMv7)) { + width = 32 - width; + msb = base::bits::CountLeadingZeros32(~value); + uint32_t lsb = base::bits::CountTrailingZeros32(~value); + if (msb + width + lsb == 32) { + Emit(kArmBfc, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()), + g.TempImmediate(lsb), g.TempImmediate(width)); + return; + } + } } VisitBinop(this, node, kArmAnd, kArmAnd); } @@ -571,6 +597,20 @@ void InstructionSelector::VisitWord32Sar(Node* node) { + ArmOperandGenerator g(this); + Int32BinopMatcher m(node); + if (CanCover(m.node(), m.left().node()) && m.left().IsWord32Shl()) { + Int32BinopMatcher mleft(m.left().node()); + if (mleft.right().Is(16) && m.right().Is(16)) { + Emit(kArmSxth, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node()), g.TempImmediate(0)); + return; + } else if (mleft.right().Is(24) && m.right().Is(24)) { + Emit(kArmSxtb, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node()), g.TempImmediate(0)); + return; + } + } VisitShift(this, node, TryMatchASR); } @@ -583,31 +623,113 @@ void InstructionSelector::VisitInt32Add(Node* node) { ArmOperandGenerator g(this); Int32BinopMatcher m(node); - if (m.left().IsInt32Mul() && CanCover(node, m.left().node())) { - Int32BinopMatcher mleft(m.left().node()); - Emit(kArmMla, g.DefineAsRegister(node), g.UseRegister(mleft.left().node()), - g.UseRegister(mleft.right().node()), g.UseRegister(m.right().node())); - return; + if (CanCover(node, m.left().node())) { + switch (m.left().opcode()) { + case IrOpcode::kInt32Mul: { + Int32BinopMatcher mleft(m.left().node()); + Emit(kArmMla, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node()), + g.UseRegister(mleft.right().node()), + g.UseRegister(m.right().node())); + return; + } + case IrOpcode::kInt32MulHigh: { + Int32BinopMatcher mleft(m.left().node()); + Emit(kArmSmmla, g.DefineAsRegister(node), + g.UseRegister(mleft.left().node()), + g.UseRegister(mleft.right().node()), + g.UseRegister(m.right().node())); + return; + } + case IrOpcode::kWord32And: { + Int32BinopMatcher mleft(m.left().node()); + if (mleft.right().Is(0xff)) { + Emit(kArmUxtab, g.DefineAsRegister(node), + g.UseRegister(m.right().node()), + g.UseRegister(mleft.left().node()), g.TempImmediate(0)); + return; + } else if (mleft.right().Is(0xffff)) { + Emit(kArmUxtah, g.DefineAsRegister(node), + g.UseRegister(m.right().node()), + g.UseRegister(mleft.left().node()), g.TempImmediate(0)); + return; + } + } + case IrOpcode::kWord32Sar: { + Int32BinopMatcher mleft(m.left().node()); + if (CanCover(mleft.node(), mleft.left().node()) && + mleft.left().IsWord32Shl()) { + Int32BinopMatcher mleftleft(mleft.left().node()); + if (mleft.right().Is(24) && mleftleft.right().Is(24)) { + Emit(kArmSxtab, g.DefineAsRegister(node), + g.UseRegister(m.right().node()), + g.UseRegister(mleftleft.left().node()), g.TempImmediate(0)); + return; + } else if (mleft.right().Is(16) && mleftleft.right().Is(16)) { + Emit(kArmSxtah, g.DefineAsRegister(node), + g.UseRegister(m.right().node()), + g.UseRegister(mleftleft.left().node()), g.TempImmediate(0)); + return; + } + } + } + default: + break; + } } - if (m.right().IsInt32Mul() && CanCover(node, m.right().node())) { - Int32BinopMatcher mright(m.right().node()); - Emit(kArmMla, g.DefineAsRegister(node), g.UseRegister(mright.left().node()), - g.UseRegister(mright.right().node()), g.UseRegister(m.left().node())); - return; - } - if (m.left().IsInt32MulHigh() && CanCover(node, m.left().node())) { - Int32BinopMatcher mleft(m.left().node()); - Emit(kArmSmmla, g.DefineAsRegister(node), - g.UseRegister(mleft.left().node()), - g.UseRegister(mleft.right().node()), g.UseRegister(m.right().node())); - return; - } - if (m.right().IsInt32MulHigh() && CanCover(node, m.right().node())) { - Int32BinopMatcher mright(m.right().node()); - Emit(kArmSmmla, g.DefineAsRegister(node), - g.UseRegister(mright.left().node()), - g.UseRegister(mright.right().node()), g.UseRegister(m.left().node())); - return; + if (CanCover(node, m.right().node())) { + switch (m.right().opcode()) { + case IrOpcode::kInt32Mul: { + Int32BinopMatcher mright(m.right().node()); + Emit(kArmMla, g.DefineAsRegister(node), + g.UseRegister(mright.left().node()), + g.UseRegister(mright.right().node()), + g.UseRegister(m.left().node())); + return; + } + case IrOpcode::kInt32MulHigh: { + Int32BinopMatcher mright(m.right().node()); + Emit(kArmSmmla, g.DefineAsRegister(node), + g.UseRegister(mright.left().node()), + g.UseRegister(mright.right().node()), + g.UseRegister(m.left().node())); + return; + } + case IrOpcode::kWord32And: { + Int32BinopMatcher mright(m.right().node()); + if (mright.right().Is(0xff)) { + Emit(kArmUxtab, g.DefineAsRegister(node), + g.UseRegister(m.left().node()), + g.UseRegister(mright.left().node()), g.TempImmediate(0)); + return; + } else if (mright.right().Is(0xffff)) { + Emit(kArmUxtah, g.DefineAsRegister(node), + g.UseRegister(m.left().node()), + g.UseRegister(mright.left().node()), g.TempImmediate(0)); + return; + } + } + case IrOpcode::kWord32Sar: { + Int32BinopMatcher mright(m.right().node()); + if (CanCover(mright.node(), mright.left().node()) && + mright.left().IsWord32Shl()) { + Int32BinopMatcher mrightleft(mright.left().node()); + if (mright.right().Is(24) && mrightleft.right().Is(24)) { + Emit(kArmSxtab, g.DefineAsRegister(node), + g.UseRegister(m.left().node()), + g.UseRegister(mrightleft.left().node()), g.TempImmediate(0)); + return; + } else if (mright.right().Is(16) && mrightleft.right().Is(16)) { + Emit(kArmSxtah, g.DefineAsRegister(node), + g.UseRegister(m.left().node()), + g.UseRegister(mrightleft.left().node()), g.TempImmediate(0)); + return; + } + } + } + default: + break; + } } VisitBinop(this, node, kArmAdd, kArmAdd); }
diff --git a/src/compiler/arm64/instruction-selector-arm64.cc b/src/compiler/arm64/instruction-selector-arm64.cc index 1040131..151d1aa 100644 --- a/src/compiler/arm64/instruction-selector-arm64.cc +++ b/src/compiler/arm64/instruction-selector-arm64.cc
@@ -542,6 +542,17 @@ void InstructionSelector::VisitWord64Shl(Node* node) { + Arm64OperandGenerator g(this); + Int64BinopMatcher m(node); + if ((m.left().IsChangeInt32ToInt64() || m.left().IsChangeUint32ToUint64()) && + m.right().IsInRange(32, 63)) { + // There's no need to sign/zero-extend to 64-bit if we shift out the upper + // 32 bits anyway. + Emit(kArm64Lsl, g.DefineAsRegister(node), + g.UseRegister(m.left().node()->InputAt(0)), + g.UseImmediate(m.right().node())); + return; + } VisitRRO(this, kArm64Lsl, node, kShift64Imm); } @@ -884,6 +895,18 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) { Arm64OperandGenerator g(this); + Node* value = node->InputAt(0); + if (CanCover(node, value)) { + Int64BinopMatcher m(value); + if ((m.IsWord64Sar() && m.right().HasValue() && + (m.right().Value() == 32)) || + (m.IsWord64Shr() && m.right().IsInRange(32, 63))) { + Emit(kArm64Lsr, g.DefineAsRegister(node), g.UseRegister(m.left().node()), + g.UseImmediate(m.right().node())); + return; + } + } + Emit(kArm64Mov32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0))); }
diff --git a/src/compiler/code-generator.cc b/src/compiler/code-generator.cc index 6b04eef..708b18e 100644 --- a/src/compiler/code-generator.cc +++ b/src/compiler/code-generator.cc
@@ -422,7 +422,8 @@ if (type == kMachBool || type == kMachInt32 || type == kMachInt8 || type == kMachInt16) { translation->StoreInt32StackSlot(op->index()); - } else if (type == kMachUint32) { + } else if (type == kMachUint32 || type == kMachUint16 || + type == kMachUint8) { translation->StoreUint32StackSlot(op->index()); } else if ((type & kRepMask) == kRepTagged) { translation->StoreStackSlot(op->index()); @@ -437,7 +438,8 @@ if (type == kMachBool || type == kMachInt32 || type == kMachInt8 || type == kMachInt16) { translation->StoreInt32Register(converter.ToRegister(op)); - } else if (type == kMachUint32) { + } else if (type == kMachUint32 || type == kMachUint16 || + type == kMachUint8) { translation->StoreUint32Register(converter.ToRegister(op)); } else if ((type & kRepMask) == kRepTagged) { translation->StoreRegister(converter.ToRegister(op));
diff --git a/src/compiler/js-generic-lowering.cc b/src/compiler/js-generic-lowering.cc index fb18ba1..87b2604 100644 --- a/src/compiler/js-generic-lowering.cc +++ b/src/compiler/js-generic-lowering.cc
@@ -223,7 +223,7 @@ linkage()->GetRuntimeCallDescriptor(f, nargs, properties); Node* ref = ExternalConstant(ExternalReference(f, isolate())); Node* arity = Int32Constant(nargs); - PatchInsertInput(node, 0, jsgraph()->CEntryStubConstant()); + PatchInsertInput(node, 0, jsgraph()->CEntryStubConstant(fun->result_size)); PatchInsertInput(node, nargs + 1, ref); PatchInsertInput(node, nargs + 2, arity); PatchOperator(node, common()->Call(desc));
diff --git a/src/compiler/js-graph.cc b/src/compiler/js-graph.cc index da6d66d..b8a7f97 100644 --- a/src/compiler/js-graph.cc +++ b/src/compiler/js-graph.cc
@@ -17,12 +17,16 @@ } -Node* JSGraph::CEntryStubConstant() { - if (!c_entry_stub_constant_.is_set()) { - c_entry_stub_constant_.set( - ImmovableHeapConstant(CEntryStub(isolate(), 1).GetCode())); +Node* JSGraph::CEntryStubConstant(int result_size) { + if (result_size == 1) { + if (!c_entry_stub_constant_.is_set()) { + c_entry_stub_constant_.set( + ImmovableHeapConstant(CEntryStub(isolate(), 1).GetCode())); + } + return c_entry_stub_constant_.get(); } - return c_entry_stub_constant_.get(); + + return ImmovableHeapConstant(CEntryStub(isolate(), result_size).GetCode()); }
diff --git a/src/compiler/js-graph.h b/src/compiler/js-graph.h index 83e103d..e1a7b69 100644 --- a/src/compiler/js-graph.h +++ b/src/compiler/js-graph.h
@@ -32,7 +32,7 @@ cache_(zone()) {} // Canonicalized global constants. - Node* CEntryStubConstant(); + Node* CEntryStubConstant(int result_size); Node* UndefinedConstant(); Node* TheHoleConstant(); Node* TrueConstant();
diff --git a/src/compiler/linkage.cc b/src/compiler/linkage.cc index a97e484..16529b4 100644 --- a/src/compiler/linkage.cc +++ b/src/compiler/linkage.cc
@@ -186,6 +186,7 @@ case Runtime::kRegExpCompile: case Runtime::kRegExpExecMultiple: case Runtime::kResolvePossiblyDirectEval: + case Runtime::kRunMicrotasks: case Runtime::kSetPrototype: case Runtime::kSetScriptBreakPoint: case Runtime::kSparseJoinWithSeparator:
diff --git a/src/compiler/node-matchers.h b/src/compiler/node-matchers.h index a55e7bf..b1147a7 100644 --- a/src/compiler/node-matchers.h +++ b/src/compiler/node-matchers.h
@@ -5,6 +5,8 @@ #ifndef V8_COMPILER_NODE_MATCHERS_H_ #define V8_COMPILER_NODE_MATCHERS_H_ +#include "src/compiler/generic-node.h" +#include "src/compiler/generic-node-inl.h" #include "src/compiler/node.h" #include "src/compiler/operator.h" #include "src/unique.h" @@ -116,7 +118,7 @@ // right hand sides of a binary operation and can put constants on the right // if they appear on the left hand side of a commutative operation. template <typename Left, typename Right> -struct BinopMatcher FINAL : public NodeMatcher { +struct BinopMatcher : public NodeMatcher { explicit BinopMatcher(Node* node) : NodeMatcher(node), left_(InputAt(0)), right_(InputAt(1)) { if (HasProperty(Operator::kCommutative)) PutConstantOnRight(); @@ -128,12 +130,17 @@ bool IsFoldable() const { return left().HasValue() && right().HasValue(); } bool LeftEqualsRight() const { return left().node() == right().node(); } + protected: + void SwapInputs() { + std::swap(left_, right_); + node()->ReplaceInput(0, left().node()); + node()->ReplaceInput(1, right().node()); + } + private: void PutConstantOnRight() { if (left().HasValue() && !right().HasValue()) { - std::swap(left_, right_); - node()->ReplaceInput(0, left().node()); - node()->ReplaceInput(1, right().node()); + SwapInputs(); } } @@ -150,6 +157,189 @@ typedef BinopMatcher<Float64Matcher, Float64Matcher> Float64BinopMatcher; typedef BinopMatcher<NumberMatcher, NumberMatcher> NumberBinopMatcher; +struct Int32AddMatcher : public Int32BinopMatcher { + explicit Int32AddMatcher(Node* node) + : Int32BinopMatcher(node), scale_exponent_(-1) { + PutScaledInputOnLeft(); + } + + bool HasScaledInput() const { return scale_exponent_ != -1; } + Node* ScaledInput() const { + DCHECK(HasScaledInput()); + return left().node()->InputAt(0); + } + int ScaleExponent() const { + DCHECK(HasScaledInput()); + return scale_exponent_; + } + + private: + int GetInputScaleExponent(Node* node) const { + if (node->opcode() == IrOpcode::kWord32Shl) { + Int32BinopMatcher m(node); + if (m.right().HasValue()) { + int32_t value = m.right().Value(); + if (value >= 0 && value <= 3) { + return value; + } + } + } else if (node->opcode() == IrOpcode::kInt32Mul) { + Int32BinopMatcher m(node); + if (m.right().HasValue()) { + int32_t value = m.right().Value(); + if (value == 1) { + return 0; + } else if (value == 2) { + return 1; + } else if (value == 4) { + return 2; + } else if (value == 8) { + return 3; + } + } + } + return -1; + } + + void PutScaledInputOnLeft() { + scale_exponent_ = GetInputScaleExponent(right().node()); + if (scale_exponent_ >= 0) { + int left_scale_exponent = GetInputScaleExponent(left().node()); + if (left_scale_exponent == -1) { + SwapInputs(); + } else { + scale_exponent_ = left_scale_exponent; + } + } else { + scale_exponent_ = GetInputScaleExponent(left().node()); + if (scale_exponent_ == -1) { + if (right().opcode() == IrOpcode::kInt32Add && + left().opcode() != IrOpcode::kInt32Add) { + SwapInputs(); + } + } + } + } + + int scale_exponent_; +}; + +struct ScaledWithOffsetMatcher { + explicit ScaledWithOffsetMatcher(Node* node) + : matches_(false), + scaled_(NULL), + scale_exponent_(0), + offset_(NULL), + constant_(NULL) { + if (node->opcode() != IrOpcode::kInt32Add) return; + + // The Int32AddMatcher canonicalizes the order of constants and scale + // factors that are used as inputs, so instead of enumerating all possible + // patterns by brute force, checking for node clusters using the following + // templates in the following order suffices to find all of the interesting + // cases (S = scaled input, O = offset input, C = constant input): + // (S + (O + C)) + // (S + (O + O)) + // (S + C) + // (S + O) + // ((S + C) + O) + // ((S + O) + C) + // ((O + C) + O) + // ((O + O) + C) + // (O + C) + // (O + O) + Int32AddMatcher base_matcher(node); + Node* left = base_matcher.left().node(); + Node* right = base_matcher.right().node(); + if (base_matcher.HasScaledInput() && left->OwnedBy(node)) { + scaled_ = base_matcher.ScaledInput(); + scale_exponent_ = base_matcher.ScaleExponent(); + if (right->opcode() == IrOpcode::kInt32Add && right->OwnedBy(node)) { + Int32AddMatcher right_matcher(right); + if (right_matcher.right().HasValue()) { + // (S + (O + C)) + offset_ = right_matcher.left().node(); + constant_ = right_matcher.right().node(); + } else { + // (S + (O + O)) + offset_ = right; + } + } else if (base_matcher.right().HasValue()) { + // (S + C) + constant_ = right; + } else { + // (S + O) + offset_ = right; + } + } else { + if (left->opcode() == IrOpcode::kInt32Add && left->OwnedBy(node)) { + Int32AddMatcher left_matcher(left); + Node* left_left = left_matcher.left().node(); + Node* left_right = left_matcher.right().node(); + if (left_matcher.HasScaledInput() && left_left->OwnedBy(left)) { + scaled_ = left_matcher.ScaledInput(); + scale_exponent_ = left_matcher.ScaleExponent(); + if (left_matcher.right().HasValue()) { + // ((S + C) + O) + constant_ = left_right; + offset_ = right; + } else if (base_matcher.right().HasValue()) { + // ((S + O) + C) + offset_ = left_right; + constant_ = right; + } else { + // (O + O) + scaled_ = left; + offset_ = right; + } + } else { + if (left_matcher.right().HasValue()) { + // ((O + C) + O) + scaled_ = left_left; + constant_ = left_right; + offset_ = right; + } else if (base_matcher.right().HasValue()) { + // ((O + O) + C) + scaled_ = left_left; + offset_ = left_right; + constant_ = right; + } else { + // (O + O) + scaled_ = left; + offset_ = right; + } + } + } else { + if (base_matcher.right().HasValue()) { + // (O + C) + offset_ = left; + constant_ = right; + } else { + // (O + O) + offset_ = left; + scaled_ = right; + } + } + } + matches_ = true; + } + + bool matches() const { return matches_; } + Node* scaled() const { return scaled_; } + int scale_exponent() const { return scale_exponent_; } + Node* offset() const { return offset_; } + Node* constant() const { return constant_; } + + private: + bool matches_; + + protected: + Node* scaled_; + int scale_exponent_; + Node* offset_; + Node* constant_; +}; + } // namespace compiler } // namespace internal } // namespace v8
diff --git a/src/compiler/schedule.h b/src/compiler/schedule.h index b65a507..95b93b3 100644 --- a/src/compiler/schedule.h +++ b/src/compiler/schedule.h
@@ -265,8 +265,6 @@ private: friend class Scheduler; - friend class CodeGenerator; - friend class ScheduleVisualizer; friend class BasicBlockInstrumentor; void AddSuccessor(BasicBlock* block, BasicBlock* succ);
diff --git a/src/compiler/scheduler.cc b/src/compiler/scheduler.cc index 36ed088..af8fba0 100644 --- a/src/compiler/scheduler.cc +++ b/src/compiler/scheduler.cc
@@ -685,7 +685,7 @@ stack_.resize(schedule_->BasicBlockCount() - previous_block_count_); previous_block_count_ = schedule_->BasicBlockCount(); int stack_depth = Push(stack_, 0, entry, kBlockUnvisited1); - int num_loops = 0; + int num_loops = static_cast<int>(loops_.size()); while (stack_depth > 0) { int current = stack_depth - 1; @@ -717,7 +717,7 @@ } // If no loops were encountered, then the order we computed was correct. - if (num_loops != 0) { + if (num_loops > static_cast<int>(loops_.size())) { // Otherwise, compute the loop information from the backedges in order // to perform a traversal that groups loop bodies together. ComputeLoopInfo(stack_, num_loops, &backedges_); @@ -725,7 +725,7 @@ // Initialize the "loop stack". Note the entry could be a loop header. LoopInfo* loop = HasLoopNumber(entry) ? &loops_[GetLoopNumber(entry)] : NULL; - order = NULL; + order = insert_after; // Perform an iterative post-order traversal, visiting loop bodies before // edges that lead out of loops. Visits each block once, but linking loop @@ -737,7 +737,7 @@ BasicBlock* block = frame->block; BasicBlock* succ = NULL; - if (frame->index < block->SuccessorCount()) { + if (block != end && frame->index < block->SuccessorCount()) { // Process the next normal successor. succ = block->SuccessorAt(frame->index++); } else if (HasLoopNumber(block)) {
diff --git a/src/compiler/x64/instruction-selector-x64.cc b/src/compiler/x64/instruction-selector-x64.cc index c70944b..e46357a 100644 --- a/src/compiler/x64/instruction-selector-x64.cc +++ b/src/compiler/x64/instruction-selector-x64.cc
@@ -364,8 +364,79 @@ VisitWord64Shift(this, node, kX64Ror); } +namespace { + +AddressingMode GenerateMemoryOperandInputs(X64OperandGenerator* g, Node* scaled, + int scale_exponent, Node* offset, + Node* constant, + InstructionOperand* inputs[], + size_t* input_count) { + AddressingMode mode = kMode_MRI; + if (offset != NULL) { + inputs[(*input_count)++] = g->UseRegister(offset); + if (scaled != NULL) { + DCHECK(scale_exponent >= 0 && scale_exponent <= 3); + inputs[(*input_count)++] = g->UseRegister(scaled); + if (constant != NULL) { + inputs[(*input_count)++] = g->UseImmediate(constant); + static const AddressingMode kMRnI_modes[] = {kMode_MR1I, kMode_MR2I, + kMode_MR4I, kMode_MR8I}; + mode = kMRnI_modes[scale_exponent]; + } else { + static const AddressingMode kMRn_modes[] = {kMode_MR1, kMode_MR2, + kMode_MR4, kMode_MR8}; + mode = kMRn_modes[scale_exponent]; + } + } else { + DCHECK(constant != NULL); + inputs[(*input_count)++] = g->UseImmediate(constant); + mode = kMode_MRI; + } + } else { + DCHECK(scaled != NULL); + DCHECK(scale_exponent >= 0 && scale_exponent <= 3); + inputs[(*input_count)++] = g->UseRegister(scaled); + if (constant != NULL) { + inputs[(*input_count)++] = g->UseImmediate(constant); + static const AddressingMode kMnI_modes[] = {kMode_M1I, kMode_M2I, + kMode_M4I, kMode_M8I}; + mode = kMnI_modes[scale_exponent]; + } else { + static const AddressingMode kMn_modes[] = {kMode_M1, kMode_M2, kMode_M4, + kMode_M8}; + mode = kMn_modes[scale_exponent]; + } + } + return mode; +} + +} // namespace + void InstructionSelector::VisitInt32Add(Node* node) { + // Try to match the Add to a leal pattern + ScaledWithOffsetMatcher m(node); + X64OperandGenerator g(this); + if (m.matches() && (m.constant() == NULL || g.CanBeImmediate(m.constant()))) { + InstructionOperand* inputs[4]; + size_t input_count = 0; + + AddressingMode mode = GenerateMemoryOperandInputs( + &g, m.scaled(), m.scale_exponent(), m.offset(), m.constant(), inputs, + &input_count); + + DCHECK_NE(0, static_cast<int>(input_count)); + DCHECK_GE(arraysize(inputs), input_count); + + InstructionOperand* outputs[1]; + outputs[0] = g.DefineAsRegister(node); + + InstructionCode opcode = AddressingModeField::encode(mode) | kX64Lea32; + + Emit(opcode, 1, outputs, input_count, inputs); + return; + } + VisitBinop(this, node, kX64Add32); }
diff --git a/src/contexts.cc b/src/contexts.cc index 537d92d..37db84d 100644 --- a/src/contexts.cc +++ b/src/contexts.cc
@@ -11,6 +11,48 @@ namespace v8 { namespace internal { + +Handle<GlobalContextTable> GlobalContextTable::Extend( + Handle<GlobalContextTable> table, Handle<Context> global_context) { + Handle<GlobalContextTable> result; + int used = table->used(); + int length = table->length(); + CHECK(used >= 0 && length > 0 && used < length); + if (used + 1 == length) { + CHECK(length < Smi::kMaxValue / 2); + result = Handle<GlobalContextTable>::cast( + FixedArray::CopySize(table, length * 2)); + } else { + result = table; + } + result->set_used(used + 1); + + DCHECK(global_context->IsGlobalContext()); + result->set(used + 1, *global_context); + return result; +} + + +bool GlobalContextTable::Lookup(Handle<GlobalContextTable> table, + Handle<String> name, LookupResult* result) { + for (int i = 0; i < table->used(); i++) { + Handle<Context> context = GetContext(table, i); + DCHECK(context->IsGlobalContext()); + Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension())); + int slot_index = ScopeInfo::ContextSlotIndex( + scope_info, name, &result->mode, &result->init_flag, + &result->maybe_assigned_flag); + + if (slot_index >= 0) { + result->context_index = i; + result->slot_index = slot_index; + return true; + } + } + return false; +} + + Context* Context::declaration_context() { Context* current = this; while (!current->IsFunctionContext() && !current->IsNativeContext()) { @@ -102,6 +144,53 @@ return attrs; } +static void GetAttributesAndBindingFlags(VariableMode mode, + InitializationFlag init_flag, + PropertyAttributes* attributes, + BindingFlags* binding_flags) { + switch (mode) { + case INTERNAL: // Fall through. + case VAR: + *attributes = NONE; + *binding_flags = MUTABLE_IS_INITIALIZED; + break; + case LET: + *attributes = NONE; + *binding_flags = (init_flag == kNeedsInitialization) + ? MUTABLE_CHECK_INITIALIZED + : MUTABLE_IS_INITIALIZED; + break; + case CONST_LEGACY: + *attributes = READ_ONLY; + *binding_flags = (init_flag == kNeedsInitialization) + ? IMMUTABLE_CHECK_INITIALIZED + : IMMUTABLE_IS_INITIALIZED; + break; + case CONST: + *attributes = READ_ONLY; + *binding_flags = (init_flag == kNeedsInitialization) + ? IMMUTABLE_CHECK_INITIALIZED_HARMONY + : IMMUTABLE_IS_INITIALIZED_HARMONY; + break; + case MODULE: + *attributes = READ_ONLY; + *binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY; + break; + case DYNAMIC: + case DYNAMIC_GLOBAL: + case DYNAMIC_LOCAL: + case TEMPORARY: + // Note: Fixed context slots are statically allocated by the compiler. + // Statically allocated variables always have a statically known mode, + // which is the mode with which they were declared when added to the + // scope. Thus, the DYNAMIC mode (which corresponds to dynamically + // declared variables that were introduced through declaration nodes) + // must not appear here. + UNREACHABLE(); + break; + } +} + Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, @@ -122,8 +211,6 @@ PrintF(")\n"); } - bool visited_global_context = false; - do { if (FLAG_trace_contexts) { PrintF(" - looking in context %p", reinterpret_cast<void*>(*context)); @@ -132,19 +219,6 @@ PrintF("\n"); } - if (follow_context_chain && FLAG_harmony_scoping && - !visited_global_context && - (context->IsGlobalContext() || context->IsNativeContext())) { - // For lexical scoping, on a top level, we might resolve to the - // lexical bindings introduced by later scrips. Therefore we need to - // switch to the the last added global context during lookup here. - context = Handle<Context>(context->global_object()->global_context()); - visited_global_context = true; - if (FLAG_trace_contexts) { - PrintF(" - switching to current global context %p\n", - reinterpret_cast<void*>(*context)); - } - } // 1. Check global objects, subjects of with, and extension objects. if (context->IsNativeContext() || @@ -152,6 +226,30 @@ (context->IsFunctionContext() && context->has_extension())) { Handle<JSReceiver> object( JSReceiver::cast(context->extension()), isolate); + + if (context->IsNativeContext()) { + if (FLAG_trace_contexts) { + PrintF(" - trying other global contexts\n"); + } + // Try other global contexts. + Handle<GlobalContextTable> global_contexts( + context->global_object()->native_context()->global_context_table()); + GlobalContextTable::LookupResult r; + if (GlobalContextTable::Lookup(global_contexts, name, &r)) { + if (FLAG_trace_contexts) { + Handle<Context> c = GlobalContextTable::GetContext(global_contexts, + r.context_index); + PrintF("=> found property in global context %d: %p\n", + r.context_index, reinterpret_cast<void*>(*c)); + } + *index = r.slot_index; + GetAttributesAndBindingFlags(r.mode, r.init_flag, attributes, + binding_flags); + return GlobalContextTable::GetContext(global_contexts, + r.context_index); + } + } + // Context extension objects needs to behave as if they have no // prototype. So even if we want to follow prototype chains, we need // to only do a local lookup for context extension objects. @@ -206,45 +304,8 @@ slot_index, mode); } *index = slot_index; - // Note: Fixed context slots are statically allocated by the compiler. - // Statically allocated variables always have a statically known mode, - // which is the mode with which they were declared when added to the - // scope. Thus, the DYNAMIC mode (which corresponds to dynamically - // declared variables that were introduced through declaration nodes) - // must not appear here. - switch (mode) { - case INTERNAL: // Fall through. - case VAR: - *attributes = NONE; - *binding_flags = MUTABLE_IS_INITIALIZED; - break; - case LET: - *attributes = NONE; - *binding_flags = (init_flag == kNeedsInitialization) - ? MUTABLE_CHECK_INITIALIZED : MUTABLE_IS_INITIALIZED; - break; - case CONST_LEGACY: - *attributes = READ_ONLY; - *binding_flags = (init_flag == kNeedsInitialization) - ? IMMUTABLE_CHECK_INITIALIZED : IMMUTABLE_IS_INITIALIZED; - break; - case CONST: - *attributes = READ_ONLY; - *binding_flags = (init_flag == kNeedsInitialization) - ? IMMUTABLE_CHECK_INITIALIZED_HARMONY : - IMMUTABLE_IS_INITIALIZED_HARMONY; - break; - case MODULE: - *attributes = READ_ONLY; - *binding_flags = IMMUTABLE_IS_INITIALIZED_HARMONY; - break; - case DYNAMIC: - case DYNAMIC_GLOBAL: - case DYNAMIC_LOCAL: - case TEMPORARY: - UNREACHABLE(); - break; - } + GetAttributesAndBindingFlags(mode, init_flag, attributes, + binding_flags); return context; }
diff --git a/src/contexts.h b/src/contexts.h index dc77861..716682d 100644 --- a/src/contexts.h +++ b/src/contexts.h
@@ -183,7 +183,57 @@ V(SET_ITERATOR_MAP_INDEX, Map, set_iterator_map) \ V(ITERATOR_SYMBOL_INDEX, Symbol, iterator_symbol) \ V(UNSCOPABLES_SYMBOL_INDEX, Symbol, unscopables_symbol) \ - V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) + V(ARRAY_VALUES_ITERATOR_INDEX, JSFunction, array_values_iterator) \ + V(GLOBAL_CONTEXT_TABLE_INDEX, GlobalContextTable, global_context_table) + + +// A table of all global contexts. Every loaded top-level script with top-level +// lexical declarations contributes its GlobalContext into this table. +// +// The table is a fixed array, its first slot is the current used count and +// the subsequent slots 1..used contain GlobalContexts. +class GlobalContextTable : public FixedArray { + public: + // Conversions. + static GlobalContextTable* cast(Object* context) { + DCHECK(context->IsGlobalContextTable()); + return reinterpret_cast<GlobalContextTable*>(context); + } + + struct LookupResult { + int context_index; + int slot_index; + VariableMode mode; + InitializationFlag init_flag; + MaybeAssignedFlag maybe_assigned_flag; + }; + + int used() const { return Smi::cast(get(kUsedSlot))->value(); } + + void set_used(int used) { set(kUsedSlot, Smi::FromInt(used)); } + + static Handle<Context> GetContext(Handle<GlobalContextTable> table, int i) { + DCHECK(i < table->used()); + return Handle<Context>::cast(FixedArray::get(table, i + 1)); + } + + // Lookup a variable `name` in a GlobalContextTable. + // If it returns true, the variable is found and `result` contains + // valid information about its location. + // If it returns false, `result` is untouched. + MUST_USE_RESULT + static bool Lookup(Handle<GlobalContextTable> table, Handle<String> name, + LookupResult* result); + + MUST_USE_RESULT + static Handle<GlobalContextTable> Extend(Handle<GlobalContextTable> table, + Handle<Context> global_context); + + private: + static const int kUsedSlot = 0; + + DISALLOW_IMPLICIT_CONSTRUCTORS(GlobalContextTable); +}; // JSFunctions are pairs (context, function code), sometimes also called // closures. A Context object is used to represent function contexts and @@ -229,6 +279,8 @@ // // Finally, with Harmony scoping, the JSFunction representing a top level // script will have the GlobalContext rather than a FunctionContext. +// Global contexts from all top-level scripts are gathered in +// GlobalContextTable. class Context: public FixedArray { public: @@ -360,6 +412,7 @@ ITERATOR_SYMBOL_INDEX, UNSCOPABLES_SYMBOL_INDEX, ARRAY_VALUES_ITERATOR_INDEX, + GLOBAL_CONTEXT_TABLE_INDEX, // Properties from here are treated as weak references by the full GC. // Scavenge treats them as strong references.
diff --git a/src/factory.cc b/src/factory.cc index 72974a3..796fd13 100644 --- a/src/factory.cc +++ b/src/factory.cc
@@ -708,6 +708,16 @@ } +Handle<GlobalContextTable> Factory::NewGlobalContextTable() { + Handle<FixedArray> array = NewFixedArray(1); + array->set_map_no_write_barrier(*global_context_table_map()); + Handle<GlobalContextTable> context_table = + Handle<GlobalContextTable>::cast(array); + context_table->set_used(0); + return context_table; +} + + Handle<Context> Factory::NewModuleContext(Handle<ScopeInfo> scope_info) { Handle<FixedArray> array = NewFixedArray(scope_info->ContextLength(), TENURED); @@ -2077,6 +2087,9 @@ share->set_inferred_name(*empty_string(), SKIP_WRITE_BARRIER); Handle<TypeFeedbackVector> feedback_vector = NewTypeFeedbackVector(0, 0); share->set_feedback_vector(*feedback_vector, SKIP_WRITE_BARRIER); +#if TRACE_MAPS + share->set_unique_id(isolate()->GetNextUniqueSharedFunctionInfoId()); +#endif share->set_profiler_ticks(0); share->set_ast_node_count(0); share->set_counters(0);
diff --git a/src/factory.h b/src/factory.h index 9f9813c..6a9ee55 100644 --- a/src/factory.h +++ b/src/factory.h
@@ -229,6 +229,9 @@ Handle<Context> NewGlobalContext(Handle<JSFunction> function, Handle<ScopeInfo> scope_info); + // Create an empty global context table. + Handle<GlobalContextTable> NewGlobalContextTable(); + // Create a module context. Handle<Context> NewModuleContext(Handle<ScopeInfo> scope_info);
diff --git a/src/flag-definitions.h b/src/flag-definitions.h index ca82ce7..5f2a672 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h
@@ -438,11 +438,6 @@ DEFINE_BOOL(force_long_branches, false, "force all emitted branches to be in long mode (MIPS only)") -// cpu-arm64.cc -DEFINE_BOOL(enable_always_align_csp, true, - "enable alignment of csp to 16 bytes on platforms which prefer " - "the register to always be aligned (ARM64 only)") - // bootstrapper.cc DEFINE_STRING(expose_natives_as, NULL, "expose natives in global object") DEFINE_STRING(expose_debug_as, NULL, "expose debug in global object") @@ -620,6 +615,9 @@ // objects.cc DEFINE_BOOL(use_verbose_printer, true, "allows verbose printing") +#if TRACE_MAPS +DEFINE_BOOL(trace_maps, false, "trace map creation") +#endif // parser.cc DEFINE_BOOL(allow_natives_syntax, false, "allow natives syntax")
diff --git a/src/full-codegen.cc b/src/full-codegen.cc index 58e5e97..01f2faf 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc
@@ -1575,11 +1575,7 @@ __ Push(isolate()->factory()->the_hole_value()); } - if (lit->constructor() != NULL) { - VisitForStackValue(lit->constructor()); - } else { - __ Push(isolate()->factory()->undefined_value()); - } + VisitForStackValue(lit->constructor()); __ Push(script()); __ Push(Smi::FromInt(lit->start_position()));
diff --git a/src/globals.h b/src/globals.h index c6ba010..7fa4317 100644 --- a/src/globals.h +++ b/src/globals.h
@@ -776,7 +776,9 @@ kArrowFunction = 1, kGeneratorFunction = 2, kConciseMethod = 4, - kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod + kConciseGeneratorMethod = kGeneratorFunction | kConciseMethod, + kDefaultConstructor = 8, + kDefaultConstructorCallSuper = 16 }; @@ -785,7 +787,9 @@ kind == FunctionKind::kArrowFunction || kind == FunctionKind::kGeneratorFunction || kind == FunctionKind::kConciseMethod || - kind == FunctionKind::kConciseGeneratorMethod; + kind == FunctionKind::kConciseGeneratorMethod || + kind == FunctionKind::kDefaultConstructor || + kind == FunctionKind::kDefaultConstructorCallSuper; } @@ -805,6 +809,18 @@ DCHECK(IsValidFunctionKind(kind)); return kind & FunctionKind::kConciseMethod; } + + +inline bool IsDefaultConstructor(FunctionKind kind) { + DCHECK(IsValidFunctionKind(kind)); + return kind & FunctionKind::kDefaultConstructor; +} + + +inline bool IsDefaultConstructorCallSuper(FunctionKind kind) { + DCHECK(IsValidFunctionKind(kind)); + return kind & FunctionKind::kDefaultConstructorCallSuper; +} } } // namespace v8::internal namespace i = v8::internal;
diff --git a/src/heap/gc-idle-time-handler.cc b/src/heap/gc-idle-time-handler.cc index b352f44..accb87c 100644 --- a/src/heap/gc-idle-time-handler.cc +++ b/src/heap/gc-idle-time-handler.cc
@@ -14,6 +14,7 @@ const size_t GCIdleTimeHandler::kMinTimeForFinalizeSweeping = 100; const int GCIdleTimeHandler::kMaxMarkCompactsInIdleRound = 7; const int GCIdleTimeHandler::kIdleScavengeThreshold = 5; +const double GCIdleTimeHandler::kHighContextDisposalRate = 100; void GCIdleTimeAction::Print() { @@ -129,14 +130,12 @@ // (2) If the new space is almost full and we can affort a Scavenge or if the // next Scavenge will very likely take long, then a Scavenge is performed. // (3) If there is currently no MarkCompact idle round going on, we start a -// new idle round if enough garbage was created or we received a context -// disposal event. Otherwise we do not perform garbage collection to keep -// system utilization low. +// new idle round if enough garbage was created. Otherwise we do not perform +// garbage collection to keep system utilization low. // (4) If incremental marking is done, we perform a full garbage collection -// if context was disposed or if we are allowed to still do full garbage -// collections during this idle round or if we are not allowed to start -// incremental marking. Otherwise we do not perform garbage collection to -// keep system utilization low. +// if we are allowed to still do full garbage collections during this idle +// round or if we are not allowed to start incremental marking. Otherwise we +// do not perform garbage collection to keep system utilization low. // (5) If sweeping is in progress and we received a large enough idle time // request, we finalize sweeping here. // (6) If incremental marking is in progress, we perform a marking step. Note, @@ -145,8 +144,8 @@ HeapState heap_state) { if (idle_time_in_ms == 0) { if (heap_state.incremental_marking_stopped) { - if (heap_state.size_of_objects < kSmallHeapSize && - heap_state.contexts_disposed > 0) { + if (heap_state.contexts_disposed > 0 && + heap_state.contexts_disposal_rate < kHighContextDisposalRate) { return GCIdleTimeAction::FullGC(); } } @@ -162,7 +161,7 @@ } if (IsMarkCompactIdleRoundFinished()) { - if (EnoughGarbageSinceLastIdleRound() || heap_state.contexts_disposed > 0) { + if (EnoughGarbageSinceLastIdleRound()) { StartIdleRound(); } else { return GCIdleTimeAction::Done(); @@ -170,11 +169,8 @@ } if (heap_state.incremental_marking_stopped) { - // TODO(jochen): Remove context disposal dependant logic. if (ShouldDoMarkCompact(idle_time_in_ms, heap_state.size_of_objects, - heap_state.mark_compact_speed_in_bytes_per_ms) || - (heap_state.size_of_objects < kSmallHeapSize && - heap_state.contexts_disposed > 0)) { + heap_state.mark_compact_speed_in_bytes_per_ms)) { // If there are no more than two GCs left in this idle round and we are // allowed to do a full GC, then make those GCs full in order to compact // the code space. @@ -182,10 +178,9 @@ // can get rid of this special case and always start incremental marking. int remaining_mark_sweeps = kMaxMarkCompactsInIdleRound - mark_compacts_since_idle_round_started_; - if (heap_state.contexts_disposed > 0 || - (idle_time_in_ms > kMaxFrameRenderingIdleTime && - (remaining_mark_sweeps <= 2 || - !heap_state.can_start_incremental_marking))) { + if (idle_time_in_ms > kMaxFrameRenderingIdleTime && + (remaining_mark_sweeps <= 2 || + !heap_state.can_start_incremental_marking)) { return GCIdleTimeAction::FullGC(); } }
diff --git a/src/heap/gc-idle-time-handler.h b/src/heap/gc-idle-time-handler.h index edd5e42..cae6936 100644 --- a/src/heap/gc-idle-time-handler.h +++ b/src/heap/gc-idle-time-handler.h
@@ -106,10 +106,6 @@ // Number of scavenges that will trigger start of new idle round. static const int kIdleScavengeThreshold; - // Heap size threshold below which we prefer mark-compact over incremental - // step. - static const size_t kSmallHeapSize = 4 * kPointerSize * MB; - // That is the maximum idle time we will have during frame rendering. static const size_t kMaxFrameRenderingIdleTime = 16; @@ -117,8 +113,12 @@ // lower bound for the scavenger speed. static const size_t kInitialConservativeScavengeSpeed = 100 * KB; + // If contexts are disposed at a higher rate a full gc is triggered. + static const double kHighContextDisposalRate; + struct HeapState { int contexts_disposed; + double contexts_disposal_rate; size_t size_of_objects; bool incremental_marking_stopped; bool can_start_incremental_marking;
diff --git a/src/heap/gc-tracer.cc b/src/heap/gc-tracer.cc index 8a40b53..6d7231e 100644 --- a/src/heap/gc-tracer.cc +++ b/src/heap/gc-tracer.cc
@@ -26,6 +26,11 @@ } +GCTracer::ContextDisposalEvent::ContextDisposalEvent(double time) { + time_ = time; +} + + GCTracer::Event::Event(Type type, const char* gc_reason, const char* collector_reason) : type(type), @@ -207,6 +212,11 @@ } +void GCTracer::AddContextDisposalTime(double time) { + context_disposal_events_.push_front(ContextDisposalEvent(time)); +} + + void GCTracer::AddIncrementalMarkingStep(double duration, intptr_t bytes) { cumulative_incremental_marking_steps_++; cumulative_incremental_marking_bytes_ += bytes; @@ -319,6 +329,7 @@ PrintF("semi_space_copy_rate=%.1f%% ", heap_->semi_space_copied_rate_); PrintF("new_space_allocation_throughput=%" V8_PTR_PREFIX "d ", NewSpaceAllocationThroughputInBytesPerMillisecond()); + PrintF("context_disposal_rate=%.1f ", ContextDisposalRateInMilliseconds()); if (current_.type == Event::SCAVENGER) { PrintF("steps_count=%d ", current_.incremental_marking_steps); @@ -476,5 +487,21 @@ return static_cast<intptr_t>(bytes / durations); } + + +double GCTracer::ContextDisposalRateInMilliseconds() const { + if (context_disposal_events_.size() == 0) return 0.0; + + double begin = base::OS::TimeCurrentMillis(); + double end = 0.0; + ContextDisposalEventBuffer::const_iterator iter = + context_disposal_events_.begin(); + while (iter != context_disposal_events_.end()) { + end = iter->time_; + ++iter; + } + + return (begin - end) / context_disposal_events_.size(); +} } } // namespace v8::internal
diff --git a/src/heap/gc-tracer.h b/src/heap/gc-tracer.h index 4e70f07..3e6a8a7 100644 --- a/src/heap/gc-tracer.h +++ b/src/heap/gc-tracer.h
@@ -145,6 +145,19 @@ intptr_t allocation_in_bytes_; }; + + class ContextDisposalEvent { + public: + // Default constructor leaves the event uninitialized. + ContextDisposalEvent() {} + + explicit ContextDisposalEvent(double time); + + // Time when context disposal event happened. + double time_; + }; + + class Event { public: enum Type { SCAVENGER = 0, MARK_COMPACTOR = 1, START = 2 }; @@ -241,6 +254,9 @@ typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer; + typedef RingBuffer<ContextDisposalEvent, kRingBufferMaxSize> + ContextDisposalEventBuffer; + explicit GCTracer(Heap* heap); // Start collecting data. @@ -253,6 +269,8 @@ // Log an allocation throughput event. void AddNewSpaceAllocationTime(double duration, intptr_t allocation_in_bytes); + void AddContextDisposalTime(double time); + // Log an incremental marking step. void AddIncrementalMarkingStep(double duration, intptr_t bytes); @@ -322,6 +340,12 @@ // Returns 0 if no events have been recorded. intptr_t NewSpaceAllocationThroughputInBytesPerMillisecond() const; + // Computes the context disposal rate in milliseconds. It takes the time + // frame of the first recorded context disposal to the current time and + // divides it by the number of recorded events. + // Returns 0 if no events have been recorded. + double ContextDisposalRateInMilliseconds() const; + private: // Print one detailed trace line in name=value format. // TODO(ernstm): Move to Heap. @@ -359,6 +383,8 @@ // RingBuffer for allocation events. AllocationEventBuffer allocation_events_; + ContextDisposalEventBuffer context_disposal_events_; + // Cumulative number of incremental marking steps since creation of tracer. int cumulative_incremental_marking_steps_;
diff --git a/src/heap/heap.cc b/src/heap/heap.cc index 166dd3a..9c57ea3 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc
@@ -865,6 +865,7 @@ } flush_monomorphic_ics_ = true; AgeInlineCaches(); + tracer()->AddContextDisposalTime(base::OS::TimeCurrentMillis()); return ++contexts_disposed_; } @@ -2608,6 +2609,7 @@ ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, block_context) ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, module_context) ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, global_context) + ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, global_context_table) ALLOCATE_VARSIZE_MAP(FIXED_ARRAY_TYPE, native_context) native_context_map()->set_dictionary_map(true); @@ -4363,6 +4365,8 @@ GCIdleTimeHandler::HeapState heap_state; heap_state.contexts_disposed = contexts_disposed_; + heap_state.contexts_disposal_rate = + tracer()->ContextDisposalRateInMilliseconds(); heap_state.size_of_objects = static_cast<size_t>(SizeOfObjects()); heap_state.incremental_marking_stopped = incremental_marking()->IsStopped(); // TODO(ulan): Start incremental marking only for large heaps.
diff --git a/src/heap/heap.h b/src/heap/heap.h index ee1fca9..184cb42 100644 --- a/src/heap/heap.h +++ b/src/heap/heap.h
@@ -149,6 +149,7 @@ V(Map, block_context_map, BlockContextMap) \ V(Map, module_context_map, ModuleContextMap) \ V(Map, global_context_map, GlobalContextMap) \ + V(Map, global_context_table_map, GlobalContextTableMap) \ V(Map, undefined_map, UndefinedMap) \ V(Map, the_hole_map, TheHoleMap) \ V(Map, null_map, NullMap) \
diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 233ca42..9101576 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h
@@ -7562,6 +7562,10 @@ bool is_arrow() const { return IsArrowFunction(kind()); } bool is_generator() const { return IsGeneratorFunction(kind()); } bool is_concise_method() const { return IsConciseMethod(kind()); } + bool is_default_constructor() const { return IsDefaultConstructor(kind()); } + bool is_default_constructor_call_super() const { + return IsDefaultConstructorCallSuper(kind()); + } FunctionKind kind() const { return FunctionKindField::decode(bit_field_); } StrictMode strict_mode() const { return StrictModeField::decode(bit_field_); } @@ -7581,10 +7585,10 @@ virtual bool IsDeletable() const OVERRIDE { return true; } - class FunctionKindField : public BitField<FunctionKind, 0, 3> {}; - class PretenureField : public BitField<bool, 3, 1> {}; - class HasNoLiteralsField : public BitField<bool, 4, 1> {}; - class StrictModeField : public BitField<StrictMode, 5, 1> {}; + class FunctionKindField : public BitField<FunctionKind, 0, 5> {}; + class PretenureField : public BitField<bool, 5, 1> {}; + class HasNoLiteralsField : public BitField<bool, 6, 1> {}; + class StrictModeField : public BitField<StrictMode, 7, 1> {}; Handle<SharedFunctionInfo> shared_info_; uint32_t bit_field_;
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index caef04c..91df47a 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc
@@ -2214,6 +2214,10 @@ // edi - function // edx - slot id Isolate* isolate = masm->isolate(); + const int with_types_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); + const int generic_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); Label extra_checks_or_miss, slow_start; Label slow, non_function, wrap, cont; Label have_js_function; @@ -2253,35 +2257,66 @@ } __ bind(&extra_checks_or_miss); - Label miss; + Label uninitialized, miss; __ mov(ecx, FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize)); __ cmp(ecx, Immediate(TypeFeedbackVector::MegamorphicSentinel(isolate))); __ j(equal, &slow_start); - __ cmp(ecx, Immediate(TypeFeedbackVector::UninitializedSentinel(isolate))); - __ j(equal, &miss); - if (!FLAG_trace_ic) { - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(ecx); - __ CmpObjectType(ecx, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &miss); - __ mov(FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize), - Immediate(TypeFeedbackVector::MegamorphicSentinel(isolate))); - // We have to update statistics for runtime profiling. - const int with_types_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); - __ sub(FieldOperand(ebx, with_types_offset), Immediate(Smi::FromInt(1))); - const int generic_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); - __ add(FieldOperand(ebx, generic_offset), Immediate(Smi::FromInt(1))); - __ jmp(&slow_start); + // The following cases attempt to handle MISS cases without going to the + // runtime. + if (FLAG_trace_ic) { + __ jmp(&miss); } - // We are here because tracing is on or we are going monomorphic. + __ cmp(ecx, Immediate(TypeFeedbackVector::UninitializedSentinel(isolate))); + __ j(equal, &uninitialized); + + // We are going megamorphic. If the feedback is a JSFunction, it is fine + // to handle it here. More complex cases are dealt with in the runtime. + __ AssertNotSmi(ecx); + __ CmpObjectType(ecx, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &miss); + __ mov( + FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize), + Immediate(TypeFeedbackVector::MegamorphicSentinel(isolate))); + // We have to update statistics for runtime profiling. + __ sub(FieldOperand(ebx, with_types_offset), Immediate(Smi::FromInt(1))); + __ add(FieldOperand(ebx, generic_offset), Immediate(Smi::FromInt(1))); + __ jmp(&slow_start); + + __ bind(&uninitialized); + + // We are going monomorphic, provided we actually have a JSFunction. + __ JumpIfSmi(edi, &miss); + + // Goto miss case if we do not have a function. + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &miss); + + // Make sure the function is not the Array() function, which requires special + // behavior on MISS. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); + __ cmp(edi, ecx); + __ j(equal, &miss); + + // Update stats. + __ add(FieldOperand(ebx, with_types_offset), Immediate(Smi::FromInt(1))); + + // Store the function. + __ mov( + FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize), + edi); + + // Update the write barrier. + __ mov(eax, edi); + __ RecordWriteArray(ebx, eax, edx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ jmp(&have_js_function); + + // We are here because tracing is on or we encountered a MISS case we can't + // handle here. __ bind(&miss); GenerateMiss(masm);
diff --git a/src/ic/ic.cc b/src/ic/ic.cc index a9369ed..e83a4b2 100644 --- a/src/ic/ic.cc +++ b/src/ic/ic.cc
@@ -661,6 +661,21 @@ bool use_ic = MigrateDeprecated(object) ? false : FLAG_use_ic; + if (FLAG_harmony_scoping && object->IsGlobalObject() && name->IsString()) { + // Look up in global context table. + Handle<String> str_name = Handle<String>::cast(name); + Handle<GlobalObject> global = Handle<GlobalObject>::cast(object); + Handle<GlobalContextTable> global_contexts( + global->native_context()->global_context_table()); + + GlobalContextTable::LookupResult lookup_result; + if (GlobalContextTable::Lookup(global_contexts, str_name, &lookup_result)) { + return FixedArray::get(GlobalContextTable::GetContext( + global_contexts, lookup_result.context_index), + lookup_result.slot_index); + } + } + // Named lookup in the object. LookupIterator it(object, name); LookupForRead(&it); @@ -1363,6 +1378,25 @@ MaybeHandle<Object> StoreIC::Store(Handle<Object> object, Handle<Name> name, Handle<Object> value, JSReceiver::StoreFromKeyed store_mode) { + if (FLAG_harmony_scoping && object->IsGlobalObject() && name->IsString()) { + // Look up in global context table. + Handle<String> str_name = Handle<String>::cast(name); + Handle<GlobalObject> global = Handle<GlobalObject>::cast(object); + Handle<GlobalContextTable> global_contexts( + global->native_context()->global_context_table()); + + GlobalContextTable::LookupResult lookup_result; + if (GlobalContextTable::Lookup(global_contexts, str_name, &lookup_result)) { + Handle<Context> global_context = GlobalContextTable::GetContext( + global_contexts, lookup_result.context_index); + if (lookup_result.mode == CONST) { + return TypeError("harmony_const_assign", object, name); + } + global_context->set(lookup_result.slot_index, *value); + return value; + } + } + // TODO(verwaest): Let SetProperty do the migration, since storing a property // might deprecate the current map again, if value does not fit. if (MigrateDeprecated(object) || object->IsJSProxy()) {
diff --git a/src/isolate.cc b/src/isolate.cc index 2595d2f..2bf2e3b 100644 --- a/src/isolate.cc +++ b/src/isolate.cc
@@ -1650,6 +1650,9 @@ optimizing_compiler_thread_(NULL), stress_deopt_count_(0), next_optimization_id_(0), +#if TRACE_MAPS + next_unique_sfi_id_(0), +#endif use_counter_callback_(NULL), basic_block_profiler_(NULL) { { @@ -2356,7 +2359,8 @@ for (unsigned i = 0; i < arraysize(nested); ++i) { Handle<String> name = factory()->InternalizeUtf8String(nested[i]); Handle<JSObject> obj = factory()->NewJSObjectFromMap(map); - JSObject::NormalizeProperties(obj, KEEP_INOBJECT_PROPERTIES, 8); + JSObject::NormalizeProperties(obj, KEEP_INOBJECT_PROPERTIES, 8, + "SetupSymbolRegistry"); JSObject::SetProperty(registry, name, obj, STRICT).Assert(); } }
diff --git a/src/isolate.h b/src/isolate.h index 3551632..5d001f4 100644 --- a/src/isolate.h +++ b/src/isolate.h
@@ -1109,6 +1109,10 @@ std::string GetTurboCfgFileName(); +#if TRACE_MAPS + int GetNextUniqueSharedFunctionInfoId() { return next_unique_sfi_id_++; } +#endif + private: explicit Isolate(bool enable_serializer); @@ -1311,6 +1315,10 @@ int next_optimization_id_; +#if TRACE_MAPS + int next_unique_sfi_id_; +#endif + // List of callbacks when a Call completes. List<CallCompletedCallback> call_completed_callbacks_;
diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index c8db844..5b34cc2 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc
@@ -2829,8 +2829,12 @@ void CallICStub::Generate(MacroAssembler* masm) { - // r1 - function - // r3 - slot id (Smi) + // a1 - function + // a3 - slot id (Smi) + const int with_types_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); + const int generic_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); Label extra_checks_or_miss, slow_start; Label slow, non_function, wrap, cont; Label have_js_function; @@ -2869,38 +2873,71 @@ } __ bind(&extra_checks_or_miss); - Label miss; + Label uninitialized, miss; __ LoadRoot(at, Heap::kmegamorphic_symbolRootIndex); __ Branch(&slow_start, eq, t0, Operand(at)); - __ LoadRoot(at, Heap::kuninitialized_symbolRootIndex); - __ Branch(&miss, eq, t0, Operand(at)); - if (!FLAG_trace_ic) { - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(t0); - __ GetObjectType(t0, t1, t1); - __ Branch(&miss, ne, t1, Operand(JS_FUNCTION_TYPE)); - __ sll(t0, a3, kPointerSizeLog2 - kSmiTagSize); - __ Addu(t0, a2, Operand(t0)); - __ LoadRoot(at, Heap::kmegamorphic_symbolRootIndex); - __ sw(at, FieldMemOperand(t0, FixedArray::kHeaderSize)); - // We have to update statistics for runtime profiling. - const int with_types_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); - __ lw(t0, FieldMemOperand(a2, with_types_offset)); - __ Subu(t0, t0, Operand(Smi::FromInt(1))); - __ sw(t0, FieldMemOperand(a2, with_types_offset)); - const int generic_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); - __ lw(t0, FieldMemOperand(a2, generic_offset)); - __ Addu(t0, t0, Operand(Smi::FromInt(1))); - __ sw(t0, FieldMemOperand(a2, generic_offset)); - __ Branch(&slow_start); + // The following cases attempt to handle MISS cases without going to the + // runtime. + if (FLAG_trace_ic) { + __ Branch(&miss); } - // We are here because tracing is on or we are going monomorphic. + __ LoadRoot(at, Heap::kuninitialized_symbolRootIndex); + __ Branch(&uninitialized, eq, t0, Operand(at)); + + // We are going megamorphic. If the feedback is a JSFunction, it is fine + // to handle it here. More complex cases are dealt with in the runtime. + __ AssertNotSmi(t0); + __ GetObjectType(t0, t1, t1); + __ Branch(&miss, ne, t1, Operand(JS_FUNCTION_TYPE)); + __ sll(t0, a3, kPointerSizeLog2 - kSmiTagSize); + __ Addu(t0, a2, Operand(t0)); + __ LoadRoot(at, Heap::kmegamorphic_symbolRootIndex); + __ sw(at, FieldMemOperand(t0, FixedArray::kHeaderSize)); + // We have to update statistics for runtime profiling. + __ lw(t0, FieldMemOperand(a2, with_types_offset)); + __ Subu(t0, t0, Operand(Smi::FromInt(1))); + __ sw(t0, FieldMemOperand(a2, with_types_offset)); + __ lw(t0, FieldMemOperand(a2, generic_offset)); + __ Addu(t0, t0, Operand(Smi::FromInt(1))); + __ Branch(USE_DELAY_SLOT, &slow_start); + __ sw(t0, FieldMemOperand(a2, generic_offset)); // In delay slot. + + __ bind(&uninitialized); + + // We are going monomorphic, provided we actually have a JSFunction. + __ JumpIfSmi(a1, &miss); + + // Goto miss case if we do not have a function. + __ GetObjectType(a1, t0, t0); + __ Branch(&miss, ne, t0, Operand(JS_FUNCTION_TYPE)); + + // Make sure the function is not the Array() function, which requires special + // behavior on MISS. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, t0); + __ Branch(&miss, eq, a1, Operand(t0)); + + // Update stats. + __ lw(t0, FieldMemOperand(a2, with_types_offset)); + __ Addu(t0, t0, Operand(Smi::FromInt(1))); + __ sw(t0, FieldMemOperand(a2, with_types_offset)); + + // Store the function. + __ sll(t0, a3, kPointerSizeLog2 - kSmiTagSize); + __ Addu(t0, a2, Operand(t0)); + __ Addu(t0, t0, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ sw(a1, MemOperand(t0, 0)); + + // Update the write barrier. + __ mov(t1, a1); + __ RecordWrite(a2, t0, t1, kRAHasNotBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ Branch(&have_js_function); + + // We are here because tracing is on or we encountered a MISS case we can't + // handle here. __ bind(&miss); GenerateMiss(masm);
diff --git a/src/mips64/code-stubs-mips64.cc b/src/mips64/code-stubs-mips64.cc index ef812c7..8f39d02 100644 --- a/src/mips64/code-stubs-mips64.cc +++ b/src/mips64/code-stubs-mips64.cc
@@ -2892,6 +2892,10 @@ void CallICStub::Generate(MacroAssembler* masm) { // a1 - function // a3 - slot id (Smi) + const int with_types_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); + const int generic_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); Label extra_checks_or_miss, slow_start; Label slow, non_function, wrap, cont; Label have_js_function; @@ -2930,38 +2934,71 @@ } __ bind(&extra_checks_or_miss); - Label miss; + Label uninitialized, miss; __ LoadRoot(at, Heap::kmegamorphic_symbolRootIndex); __ Branch(&slow_start, eq, a4, Operand(at)); - __ LoadRoot(at, Heap::kuninitialized_symbolRootIndex); - __ Branch(&miss, eq, a4, Operand(at)); - if (!FLAG_trace_ic) { - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(a4); - __ GetObjectType(a4, a5, a5); - __ Branch(&miss, ne, a5, Operand(JS_FUNCTION_TYPE)); - __ dsrl(a4, a3, 32 - kPointerSizeLog2); - __ Daddu(a4, a2, Operand(a4)); - __ LoadRoot(at, Heap::kmegamorphic_symbolRootIndex); - __ sd(at, FieldMemOperand(a4, FixedArray::kHeaderSize)); - // We have to update statistics for runtime profiling. - const int with_types_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); - __ ld(a4, FieldMemOperand(a2, with_types_offset)); - __ Dsubu(a4, a4, Operand(Smi::FromInt(1))); - __ sd(a4, FieldMemOperand(a2, with_types_offset)); - const int generic_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); - __ ld(a4, FieldMemOperand(a2, generic_offset)); - __ Daddu(a4, a4, Operand(Smi::FromInt(1))); - __ Branch(USE_DELAY_SLOT, &slow_start); - __ sd(a4, FieldMemOperand(a2, generic_offset)); // In delay slot. + // The following cases attempt to handle MISS cases without going to the + // runtime. + if (FLAG_trace_ic) { + __ Branch(&miss); } - // We are here because tracing is on or we are going monomorphic. + __ LoadRoot(at, Heap::kuninitialized_symbolRootIndex); + __ Branch(&uninitialized, eq, a4, Operand(at)); + + // We are going megamorphic. If the feedback is a JSFunction, it is fine + // to handle it here. More complex cases are dealt with in the runtime. + __ AssertNotSmi(a4); + __ GetObjectType(a4, a5, a5); + __ Branch(&miss, ne, a5, Operand(JS_FUNCTION_TYPE)); + __ dsrl(a4, a3, 32 - kPointerSizeLog2); + __ Daddu(a4, a2, Operand(a4)); + __ LoadRoot(at, Heap::kmegamorphic_symbolRootIndex); + __ sd(at, FieldMemOperand(a4, FixedArray::kHeaderSize)); + // We have to update statistics for runtime profiling. + __ ld(a4, FieldMemOperand(a2, with_types_offset)); + __ Dsubu(a4, a4, Operand(Smi::FromInt(1))); + __ sd(a4, FieldMemOperand(a2, with_types_offset)); + __ ld(a4, FieldMemOperand(a2, generic_offset)); + __ Daddu(a4, a4, Operand(Smi::FromInt(1))); + __ Branch(USE_DELAY_SLOT, &slow_start); + __ sd(a4, FieldMemOperand(a2, generic_offset)); // In delay slot. + + __ bind(&uninitialized); + + // We are going monomorphic, provided we actually have a JSFunction. + __ JumpIfSmi(a1, &miss); + + // Goto miss case if we do not have a function. + __ GetObjectType(a1, a4, a4); + __ Branch(&miss, ne, a4, Operand(JS_FUNCTION_TYPE)); + + // Make sure the function is not the Array() function, which requires special + // behavior on MISS. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, a4); + __ Branch(&miss, eq, a1, Operand(a4)); + + // Update stats. + __ ld(a4, FieldMemOperand(a2, with_types_offset)); + __ Daddu(a4, a4, Operand(Smi::FromInt(1))); + __ sd(a4, FieldMemOperand(a2, with_types_offset)); + + // Store the function. + __ dsrl(a4, a3, 32 - kPointerSizeLog2); + __ Daddu(a4, a2, Operand(a4)); + __ Daddu(a4, a4, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); + __ sd(a1, MemOperand(a4, 0)); + + // Update the write barrier. + __ mov(a5, a1); + __ RecordWrite(a2, a4, a5, kRAHasNotBeenSaved, kDontSaveFPRegs, + EMIT_REMEMBERED_SET, OMIT_SMI_CHECK); + __ Branch(&have_js_function); + + // We are here because tracing is on or we encountered a MISS case we can't + // handle here. __ bind(&miss); GenerateMiss(masm);
diff --git a/src/objects-inl.h b/src/objects-inl.h index 0288bfb..40ce81a 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h
@@ -767,6 +767,14 @@ } +bool Object::IsGlobalContextTable() const { + if (!Object::IsHeapObject()) return false; + Map* map = HeapObject::cast(this)->map(); + Heap* heap = map->GetHeap(); + return map == heap->global_context_table_map(); +} + + bool Object::IsScopeInfo() const { return Object::IsHeapObject() && HeapObject::cast(this)->map() == @@ -5492,6 +5500,9 @@ ACCESSORS(SharedFunctionInfo, construct_stub, Code, kConstructStubOffset) ACCESSORS(SharedFunctionInfo, feedback_vector, TypeFeedbackVector, kFeedbackVectorOffset) +#if TRACE_MAPS +SMI_ACCESSORS(SharedFunctionInfo, unique_id, kUniqueIdOffset) +#endif ACCESSORS(SharedFunctionInfo, instance_class_name, Object, kInstanceClassNameOffset) ACCESSORS(SharedFunctionInfo, function_data, Object, kFunctionDataOffset) @@ -5684,6 +5695,11 @@ BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_generator, kIsGenerator) BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_concise_method, kIsConciseMethod) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, is_default_constructor, + kIsDefaultConstructor) +BOOL_ACCESSORS(SharedFunctionInfo, compiler_hints, + is_default_constructor_call_super, + kIsDefaultConstructorCallSuper) ACCESSORS(CodeCache, default_cache, FixedArray, kDefaultCacheOffset) ACCESSORS(CodeCache, normal_type_cache, Object, kNormalTypeCacheOffset)
diff --git a/src/objects-printer.cc b/src/objects-printer.cc index ba05b47..2fb924c 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc
@@ -1129,4 +1129,38 @@ #endif // OBJECT_PRINT +#if TRACE_MAPS + + +void Name::NameShortPrint() { + if (this->IsString()) { + PrintF("%s", String::cast(this)->ToCString().get()); + } else { + DCHECK(this->IsSymbol()); + Symbol* s = Symbol::cast(this); + if (s->name()->IsUndefined()) { + PrintF("#<%s>", s->PrivateSymbolToName()); + } else { + PrintF("<%s>", String::cast(s->name())->ToCString().get()); + } + } +} + + +int Name::NameShortPrint(Vector<char> str) { + if (this->IsString()) { + return SNPrintF(str, "%s", String::cast(this)->ToCString().get()); + } else { + DCHECK(this->IsSymbol()); + Symbol* s = Symbol::cast(this); + if (s->name()->IsUndefined()) { + return SNPrintF(str, "#<%s>", s->PrivateSymbolToName()); + } else { + return SNPrintF(str, "<%s>", String::cast(s->name())->ToCString().get()); + } + } +} + + +#endif // TRACE_MAPS } } // namespace v8::internal
diff --git a/src/objects.cc b/src/objects.cc index 258390c..2b5b567 100644 --- a/src/objects.cc +++ b/src/objects.cc
@@ -708,6 +708,13 @@ // the hole value. Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); DCHECK(new_map->is_dictionary_map()); +#if TRACE_MAPS + if (FLAG_trace_maps) { + PrintF("[TraceMaps: GlobalDeleteNormalized from= %p to= %p ]\n", + reinterpret_cast<void*>(object->map()), + reinterpret_cast<void*>(*new_map)); + } +#endif JSObject::MigrateToMap(object, new_map); } Handle<PropertyCell> cell(PropertyCell::cast(dictionary->ValueAt(entry))); @@ -2150,7 +2157,7 @@ PropertyAttributes attributes, const char* reason) { Isolate* isolate = map->GetIsolate(); - Handle<Map> new_map = Copy(map); + Handle<Map> new_map = Copy(map, reason); DescriptorArray* descriptors = new_map->instance_descriptors(); int length = descriptors->number_of_descriptors(); @@ -2459,8 +2466,8 @@ // Check the state of the root map. Handle<Map> root_map(old_map->FindRootMap(), isolate); if (!old_map->EquivalentToForTransition(*root_map)) { - return CopyGeneralizeAllRepresentations( - old_map, modify_index, store_mode, "not equivalent"); + return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, + "GenAll_NotEquivalent"); } int root_nof = root_map->NumberOfOwnDescriptors(); if (modify_index < root_nof) { @@ -2469,8 +2476,8 @@ (old_details.type() == FIELD && (!new_field_type->NowIs(old_descriptors->GetFieldType(modify_index)) || !new_representation.fits_into(old_details.representation())))) { - return CopyGeneralizeAllRepresentations( - old_map, modify_index, store_mode, "root modification"); + return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, + "GenAll_RootModification"); } } @@ -2493,8 +2500,8 @@ if ((tmp_type == CALLBACKS || old_type == CALLBACKS) && (tmp_type != old_type || tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) { - return CopyGeneralizeAllRepresentations( - old_map, modify_index, store_mode, "incompatible"); + return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, + "GenAll_Incompatible"); } Representation old_representation = old_details.representation(); Representation tmp_representation = tmp_details.representation(); @@ -2559,8 +2566,8 @@ if ((tmp_details.type() == CALLBACKS || old_details.type() == CALLBACKS) && (tmp_details.type() != old_details.type() || tmp_descriptors->GetValue(i) != old_descriptors->GetValue(i))) { - return CopyGeneralizeAllRepresentations( - old_map, modify_index, store_mode, "incompatible"); + return CopyGeneralizeAllRepresentations(old_map, modify_index, store_mode, + "GenAll_Incompatible"); } target_map = tmp_map; } @@ -4260,11 +4267,12 @@ void JSObject::NormalizeProperties(Handle<JSObject> object, PropertyNormalizationMode mode, - int expected_additional_properties) { + int expected_additional_properties, + const char* reason) { if (!object->HasFastProperties()) return; Handle<Map> map(object->map()); - Handle<Map> new_map = Map::Normalize(map, mode); + Handle<Map> new_map = Map::Normalize(map, mode, reason); MigrateFastToSlow(object, new_map, expected_additional_properties); } @@ -4372,7 +4380,8 @@ void JSObject::MigrateSlowToFast(Handle<JSObject> object, - int unused_property_fields) { + int unused_property_fields, + const char* reason) { if (object->HasFastProperties()) return; DCHECK(!object->IsGlobalObject()); Isolate* isolate = object->GetIsolate(); @@ -4414,6 +4423,14 @@ Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); new_map->set_dictionary_map(false); +#if TRACE_MAPS + if (FLAG_trace_maps) { + PrintF("[TraceMaps: SlowToFast from= %p to= %p reason= %s ]\n", + reinterpret_cast<void*>(object->map()), + reinterpret_cast<void*>(*new_map), reason); + } +#endif + if (instance_descriptor_length == 0) { DisallowHeapAllocation no_gc; DCHECK_LE(unused_property_fields, inobject_props); @@ -5074,7 +5091,7 @@ !(object->IsJSGlobalProxy() && holder->IsJSGlobalObject())) { return it.isolate()->factory()->true_value(); } - NormalizeProperties(holder, mode, 0); + NormalizeProperties(holder, mode, 0, "DeletingProperty"); Handle<Object> result = DeleteNormalizedProperty(holder, name, delete_mode); ReoptimizeIfPrototype(holder); @@ -5292,7 +5309,7 @@ // Do a map transition, other objects with this map may still // be extensible. // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. - Handle<Map> new_map = Map::Copy(handle(object->map())); + Handle<Map> new_map = Map::Copy(handle(object->map()), "PreventExtensions"); new_map->set_is_extensible(false); JSObject::MigrateToMap(object, new_map); @@ -5404,11 +5421,11 @@ } else { DCHECK(old_map->is_dictionary_map() || !old_map->is_prototype_map()); // Slow path: need to normalize properties for safety - NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0); + NormalizeProperties(object, CLEAR_INOBJECT_PROPERTIES, 0, "SlowFreeze"); // Create a new map, since other objects with this map may be extensible. // TODO(adamk): Extend the NormalizedMapCache to handle non-extensible maps. - Handle<Map> new_map = Map::Copy(handle(object->map())); + Handle<Map> new_map = Map::Copy(handle(object->map()), "SlowCopyForFreeze"); new_map->freeze(); new_map->set_is_extensible(false); new_map->set_elements_kind(DICTIONARY_ELEMENTS); @@ -5450,7 +5467,7 @@ } else if (object->HasFastProperties() && old_map->CanHaveMoreTransitions()) { new_map = Map::CopyForObserved(old_map); } else { - new_map = Map::Copy(old_map); + new_map = Map::Copy(old_map, "SlowObserved"); new_map->set_is_observed(); } JSObject::MigrateToMap(object, new_map); @@ -6167,13 +6184,20 @@ ? KEEP_INOBJECT_PROPERTIES : CLEAR_INOBJECT_PROPERTIES; // Normalize object to make this operation simple. - NormalizeProperties(object, mode, 0); + NormalizeProperties(object, mode, 0, "SetPropertyCallback"); // For the global object allocate a new map to invalidate the global inline // caches which have a global property cell reference directly in the code. if (object->IsGlobalObject()) { Handle<Map> new_map = Map::CopyDropDescriptors(handle(object->map())); DCHECK(new_map->is_dictionary_map()); +#if TRACE_MAPS + if (FLAG_trace_maps) { + PrintF("[TraceMaps: GlobalPropertyCallback from= %p to= %p ]\n", + reinterpret_cast<void*>(object->map()), + reinterpret_cast<void*>(*new_map)); + } +#endif JSObject::MigrateToMap(object, new_map); // When running crankshaft, changing the map is not enough. We @@ -6494,8 +6518,8 @@ } -Handle<Map> Map::Normalize(Handle<Map> fast_map, - PropertyNormalizationMode mode) { +Handle<Map> Map::Normalize(Handle<Map> fast_map, PropertyNormalizationMode mode, + const char* reason) { DCHECK(!fast_map->is_dictionary_map()); Isolate* isolate = fast_map->GetIsolate(); @@ -6534,6 +6558,13 @@ cache->Set(fast_map, new_map); isolate->counters()->normalized_maps()->Increment(); } +#if TRACE_MAPS + if (FLAG_trace_maps) { + PrintF("[TraceMaps: Normalize from= %p to= %p reason= %s ]\n", + reinterpret_cast<void*>(*fast_map), + reinterpret_cast<void*>(*new_map), reason); + } +#endif } fast_map->NotifyLeafMapLayoutChange(); return new_map; @@ -6616,11 +6647,41 @@ } +#if TRACE_MAPS + +// static +void Map::TraceTransition(const char* what, Map* from, Map* to, Name* name) { + if (FLAG_trace_maps) { + PrintF("[TraceMaps: %s from= %p to= %p name= ", what, + reinterpret_cast<void*>(from), reinterpret_cast<void*>(to)); + name->NameShortPrint(); + PrintF(" ]\n"); + } +} + + +// static +void Map::TraceAllTransitions(Map* map) { + if (!map->HasTransitionArray()) return; + TransitionArray* transitions = map->transitions(); + for (int i = 0; i < transitions->number_of_transitions(); ++i) { + Map* target = transitions->GetTarget(i); + Map::TraceTransition("Transition", map, target, transitions->GetKey(i)); + Map::TraceAllTransitions(target); + } +} + +#endif // TRACE_MAPS + + void Map::ConnectTransition(Handle<Map> parent, Handle<Map> child, Handle<Name> name, SimpleTransitionFlag flag) { parent->set_owns_descriptors(false); if (parent->is_prototype_map()) { DCHECK(child->is_prototype_map()); +#if TRACE_MAPS + Map::TraceTransition("NoTransition", *parent, *child, *name); +#endif } else { Handle<TransitionArray> transitions = TransitionArray::Insert(parent, name, child, flag); @@ -6629,6 +6690,9 @@ parent->set_transitions(*transitions); } child->SetBackPointer(*parent); +#if TRACE_MAPS + Map::TraceTransition("Transition", *parent, *child, *name); +#endif } } @@ -6637,6 +6701,7 @@ Handle<DescriptorArray> descriptors, TransitionFlag flag, MaybeHandle<Name> maybe_name, + const char* reason, SimpleTransitionFlag simple_flag) { DCHECK(descriptors->IsSortedNoDuplicates()); @@ -6658,6 +6723,16 @@ } } } +#if TRACE_MAPS + if (FLAG_trace_maps && + // Mirror conditions above that did not call ConnectTransition(). + (map->is_prototype_map() || + !(flag == INSERT_TRANSITION && map->CanHaveMoreTransitions()))) { + PrintF("[TraceMaps: ReplaceDescriptors from= %p to= %p reason= %s ]\n", + reinterpret_cast<void*>(*map), reinterpret_cast<void*>(*result), + reason); + } +#endif return result; } @@ -6726,7 +6801,7 @@ // In case the map did not own its own descriptors, a split is forced by // copying the map; creating a new descriptor array cell. // Create a new free-floating map only if we are not allowed to store it. - Handle<Map> new_map = Copy(map); + Handle<Map> new_map = Copy(map, "CopyAsElementsKind"); new_map->set_elements_kind(kind); @@ -6750,7 +6825,7 @@ new_map = CopyDropDescriptors(map); } else { DCHECK(!map->is_prototype_map()); - new_map = Copy(map); + new_map = Copy(map, "CopyForObserved"); } new_map->set_is_observed(); @@ -6766,18 +6841,20 @@ } -Handle<Map> Map::Copy(Handle<Map> map) { +Handle<Map> Map::Copy(Handle<Map> map, const char* reason) { Handle<DescriptorArray> descriptors(map->instance_descriptors()); int number_of_own_descriptors = map->NumberOfOwnDescriptors(); Handle<DescriptorArray> new_descriptors = DescriptorArray::CopyUpTo(descriptors, number_of_own_descriptors); return CopyReplaceDescriptors(map, new_descriptors, OMIT_TRANSITION, - MaybeHandle<Name>(), SPECIAL_TRANSITION); + MaybeHandle<Name>(), reason, + SPECIAL_TRANSITION); } Handle<Map> Map::Create(Isolate* isolate, int inobject_properties) { - Handle<Map> copy = Copy(handle(isolate->object_function()->initial_map())); + Handle<Map> copy = + Copy(handle(isolate->object_function()->initial_map()), "MapCreate"); // Check that we do not overflow the instance size when adding the extra // inobject properties. If the instance size overflows, we allocate as many @@ -6808,7 +6885,7 @@ handle(map->instance_descriptors(), isolate), num_descriptors, FROZEN); Handle<Map> new_map = CopyReplaceDescriptors( map, new_desc, INSERT_TRANSITION, isolate->factory()->frozen_symbol(), - SPECIAL_TRANSITION); + "CopyForFreeze", SPECIAL_TRANSITION); new_map->freeze(); new_map->set_is_extensible(false); new_map->set_elements_kind(DICTIONARY_ELEMENTS); @@ -6898,7 +6975,17 @@ Handle<Map> result; if (!maybe_map.ToHandle(&result)) { - return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); +#if TRACE_MAPS + if (FLAG_trace_maps) { + Vector<char> name_buffer = Vector<char>::New(100); + name->NameShortPrint(name_buffer); + Vector<char> buffer = Vector<char>::New(128); + SNPrintF(buffer, "TooManyFastProperties %s", name_buffer.start()); + return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, buffer.start()); + } +#endif + return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, + "TooManyFastProperties"); } return result; @@ -6912,8 +6999,8 @@ // For now, give up on transitioning and just create a unique map. // TODO(verwaest/ishell): Cache transitions with different attributes. - return CopyGeneralizeAllRepresentations(map, descriptor, FORCE_FIELD, - attributes, "attributes mismatch"); + return CopyGeneralizeAllRepresentations( + map, descriptor, FORCE_FIELD, attributes, "GenAll_AttributesMismatch"); } @@ -6928,7 +7015,7 @@ if (map->is_dictionary_map()) { // For global objects, property cells are inlined. We need to change the // map. - if (map->IsGlobalObjectMap()) return Copy(map); + if (map->IsGlobalObjectMap()) return Copy(map, "GlobalAccessor"); return map; } @@ -6951,12 +7038,12 @@ Handle<Object> maybe_pair(descriptors->GetValue(descriptor), isolate); if (!maybe_pair->IsAccessorPair()) { - return Map::Normalize(map, mode); + return Map::Normalize(map, mode, "TransitionToAccessorFromNonPair"); } Handle<AccessorPair> pair = Handle<AccessorPair>::cast(maybe_pair); if (pair->get(component) != *accessor) { - return Map::Normalize(map, mode); + return Map::Normalize(map, mode, "TransitionToDifferentAccessor"); } return transition; @@ -6967,33 +7054,33 @@ int descriptor = old_descriptors->SearchWithCache(*name, *map); if (descriptor != DescriptorArray::kNotFound) { if (descriptor != map->LastAdded()) { - return Map::Normalize(map, mode); + return Map::Normalize(map, mode, "AccessorsOverwritingNonLast"); } PropertyDetails old_details = old_descriptors->GetDetails(descriptor); if (old_details.type() != CALLBACKS) { - return Map::Normalize(map, mode); + return Map::Normalize(map, mode, "AccessorsOverwritingNonAccessors"); } if (old_details.attributes() != attributes) { - return Map::Normalize(map, mode); + return Map::Normalize(map, mode, "AccessorsWithAttributes"); } Handle<Object> maybe_pair(old_descriptors->GetValue(descriptor), isolate); if (!maybe_pair->IsAccessorPair()) { - return Map::Normalize(map, mode); + return Map::Normalize(map, mode, "AccessorsOverwritingNonPair"); } Object* current = Handle<AccessorPair>::cast(maybe_pair)->get(component); if (current == *accessor) return map; if (!current->IsTheHole()) { - return Map::Normalize(map, mode); + return Map::Normalize(map, mode, "AccessorsOverwritingAccessors"); } pair = AccessorPair::Copy(Handle<AccessorPair>::cast(maybe_pair)); } else if (map->NumberOfOwnDescriptors() >= kMaxNumberOfDescriptors || map->TooManyFastProperties(CERTAINLY_NOT_STORE_FROM_KEYED)) { - return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES); + return Map::Normalize(map, CLEAR_INOBJECT_PROPERTIES, "TooManyAccessors"); } else { pair = isolate->factory()->NewAccessorPair(); } @@ -7024,7 +7111,7 @@ new_descriptors->Append(descriptor); return CopyReplaceDescriptors(map, new_descriptors, flag, - descriptor->GetKey(), + descriptor->GetKey(), "CopyAddDescriptor", SIMPLE_PROPERTY_TRANSITION); } @@ -7121,7 +7208,8 @@ (insertion_index == descriptors->number_of_descriptors() - 1) ? SIMPLE_PROPERTY_TRANSITION : PROPERTY_TRANSITION; - return CopyReplaceDescriptors(map, new_descriptors, flag, key, simple_flag); + return CopyReplaceDescriptors(map, new_descriptors, flag, key, + "CopyReplaceDescriptor", simple_flag); } @@ -9414,14 +9502,15 @@ if (object->IsJSGlobalProxy()) return; if (mode == FAST_PROTOTYPE && !object->map()->is_prototype_map()) { // First normalize to ensure all JSFunctions are CONSTANT. - JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0); + JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, 0, + "NormalizeAsPrototype"); } if (!object->HasFastProperties()) { - JSObject::MigrateSlowToFast(object, 0); + JSObject::MigrateSlowToFast(object, 0, "OptimizeAsPrototype"); } if (mode == FAST_PROTOTYPE && object->HasFastProperties() && !object->map()->is_prototype_map()) { - Handle<Map> new_map = Map::Copy(handle(object->map())); + Handle<Map> new_map = Map::Copy(handle(object->map()), "CopyAsPrototype"); JSObject::MigrateToMap(object, new_map); object->map()->set_is_prototype_map(true); } @@ -9491,7 +9580,7 @@ // into the initial map where it belongs. function->set_prototype_or_initial_map(*value); } else { - Handle<Map> new_map = Map::Copy(initial_map); + Handle<Map> new_map = Map::Copy(initial_map, "SetInstancePrototype"); JSFunction::SetInitialMap(function, new_map, value); // If the function is used as the global Array function, cache the @@ -9531,7 +9620,7 @@ // Copy the map so this does not affect unrelated functions. // Remove map transitions because they point to maps with a // different prototype. - Handle<Map> new_map = Map::Copy(handle(function->map())); + Handle<Map> new_map = Map::Copy(handle(function->map()), "SetPrototype"); JSObject::MigrateToMap(function, new_map); new_map->set_constructor(*value); @@ -9579,6 +9668,13 @@ map->set_prototype(*prototype); function->set_prototype_or_initial_map(*map); map->set_constructor(*function); +#if TRACE_MAPS + if (FLAG_trace_maps) { + PrintF("[TraceMaps: InitialMap map= %p SFI= %d_%s ]\n", + reinterpret_cast<void*>(*map), function->shared()->unique_id(), + function->shared()->DebugName()->ToCString().get()); + } +#endif } @@ -11700,7 +11796,7 @@ Handle<Object> prototype) { Handle<Map> new_map = GetPrototypeTransition(map, prototype); if (new_map.is_null()) { - new_map = Copy(map); + new_map = Copy(map, "TransitionToPrototype"); PutPrototypeTransition(map, prototype, new_map); new_map->set_prototype(*prototype); } @@ -14677,6 +14773,25 @@ } +void GlobalObject::InvalidatePropertyCell(Handle<GlobalObject> global, + Handle<Name> name) { + DCHECK(!global->HasFastProperties()); + Isolate* isolate = global->GetIsolate(); + int entry = global->property_dictionary()->FindEntry(name); + if (entry != NameDictionary::kNotFound) { + Handle<PropertyCell> cell( + PropertyCell::cast(global->property_dictionary()->ValueAt(entry))); + + Handle<Object> value(cell->value(), isolate); + Handle<PropertyCell> new_cell = isolate->factory()->NewPropertyCell(value); + global->property_dictionary()->ValueAtPut(entry, *new_cell); + + Handle<Object> hole = global->GetIsolate()->factory()->the_hole_value(); + PropertyCell::SetValueInferType(cell, hole); + } +} + + Handle<PropertyCell> JSGlobalObject::EnsurePropertyCell( Handle<JSGlobalObject> global, Handle<Name> name) { @@ -14899,16 +15014,19 @@ Handle<SharedFunctionInfo> shared(context->closure()->shared()); StringSharedKey key(src, shared, FLAG_use_strict ? STRICT : SLOPPY, RelocInfo::kNoPosition); - int entry = cache->FindEntry(&key); - if (entry != kNotFound) { + { Handle<Object> k = key.AsHandle(isolate); - cache->set(EntryToIndex(entry), *k); - cache->set(EntryToIndex(entry) + 1, *value); - return cache; + DisallowHeapAllocation no_allocation_scope; + int entry = cache->FindEntry(&key); + if (entry != kNotFound) { + cache->set(EntryToIndex(entry), *k); + cache->set(EntryToIndex(entry) + 1, *value); + return cache; + } } cache = EnsureCapacity(cache, 1, &key); - entry = cache->FindInsertionEntry(key.Hash()); + int entry = cache->FindInsertionEntry(key.Hash()); Handle<Object> k = isolate->factory()->NewNumber(static_cast<double>(key.Hash())); cache->set(EntryToIndex(entry), *k); @@ -14924,16 +15042,19 @@ int scope_position) { Isolate* isolate = cache->GetIsolate(); StringSharedKey key(src, outer_info, value->strict_mode(), scope_position); - int entry = cache->FindEntry(&key); - if (entry != kNotFound) { + { Handle<Object> k = key.AsHandle(isolate); - cache->set(EntryToIndex(entry), *k); - cache->set(EntryToIndex(entry) + 1, *value); - return cache; + DisallowHeapAllocation no_allocation_scope; + int entry = cache->FindEntry(&key); + if (entry != kNotFound) { + cache->set(EntryToIndex(entry), *k); + cache->set(EntryToIndex(entry) + 1, *value); + return cache; + } } cache = EnsureCapacity(cache, 1, &key); - entry = cache->FindInsertionEntry(key.Hash()); + int entry = cache->FindInsertionEntry(key.Hash()); Handle<Object> k = isolate->factory()->NewNumber(static_cast<double>(key.Hash())); cache->set(EntryToIndex(entry), *k);
diff --git a/src/objects.h b/src/objects.h index 9333e9e..31d0a16 100644 --- a/src/objects.h +++ b/src/objects.h
@@ -87,6 +87,7 @@ // - JSFunctionResultCache // - ScopeInfo // - TransitionArray +// - GlobalContextTable // - FixedDoubleArray // - ExternalArray // - ExternalUint8ClampedArray @@ -944,6 +945,7 @@ V(FixedDoubleArray) \ V(ConstantPoolArray) \ V(Context) \ + V(GlobalContextTable) \ V(NativeContext) \ V(ScopeInfo) \ V(JSFunction) \ @@ -2049,7 +2051,8 @@ // an initial capacity for holding these properties. static void NormalizeProperties(Handle<JSObject> object, PropertyNormalizationMode mode, - int expected_additional_properties); + int expected_additional_properties, + const char* reason); // Convert and update the elements backing store to be a // SeededNumberDictionary dictionary. Returns the backing after conversion. @@ -2058,7 +2061,7 @@ // Transform slow named properties to fast variants. static void MigrateSlowToFast(Handle<JSObject> object, - int unused_property_fields); + int unused_property_fields, const char* reason); // Access fast-case object properties at index. static Handle<Object> FastPropertyAt(Handle<JSObject> object, @@ -5878,7 +5881,8 @@ int descriptor_number, Handle<Object> value); - static Handle<Map> Normalize(Handle<Map> map, PropertyNormalizationMode mode); + static Handle<Map> Normalize(Handle<Map> map, PropertyNormalizationMode mode, + const char* reason); // Returns the constructor name (the name (possibly, inferred name) of the // function that was used to instantiate the object). @@ -6087,7 +6091,7 @@ // Returns a copy of the map, with all transitions dropped from the // instance descriptors. - static Handle<Map> Copy(Handle<Map> map); + static Handle<Map> Copy(Handle<Map> map, const char* reason); static Handle<Map> Create(Isolate* isolate, int inobject_properties); // Returns the next free property index (only valid for FAST MODE). @@ -6315,6 +6319,11 @@ // The "shared" flags of both this map and |other| are ignored. bool EquivalentToForNormalization(Map* other, PropertyNormalizationMode mode); +#if TRACE_MAPS + static void TraceTransition(const char* what, Map* from, Map* to, Name* name); + static void TraceAllTransitions(Map* map); +#endif + private: static void ConnectElementsTransition(Handle<Map> parent, Handle<Map> child); static void ConnectTransition(Handle<Map> parent, Handle<Map> child, @@ -6336,6 +6345,7 @@ Handle<DescriptorArray> descriptors, TransitionFlag flag, MaybeHandle<Name> maybe_name, + const char* reason, SimpleTransitionFlag simple_flag); static Handle<Map> CopyReplaceDescriptor(Handle<Map> map, Handle<DescriptorArray> descriptors, @@ -6709,6 +6719,13 @@ // available. DECL_ACCESSORS(feedback_vector, TypeFeedbackVector) +#if TRACE_MAPS + // [unique_id] - For --trace-maps purposes, an identifier that's persistent + // even if the GC moves this SharedFunctionInfo. + inline int unique_id() const; + inline void set_unique_id(int value); +#endif + // [instance class name]: class name for instances. DECL_ACCESSORS(instance_class_name, Object) @@ -6855,6 +6872,13 @@ // Indicates that this function is a concise method. DECL_BOOLEAN_ACCESSORS(is_concise_method) + // Indicates that this function is a default constructor. + DECL_BOOLEAN_ACCESSORS(is_default_constructor) + + // Indicates that this function is a default constructor that needs to call + // super. + DECL_BOOLEAN_ACCESSORS(is_default_constructor_call_super) + // Indicates that this function is an asm function. DECL_BOOLEAN_ACCESSORS(asm_function) @@ -6955,10 +6979,16 @@ static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize; static const int kFeedbackVectorOffset = kInferredNameOffset + kPointerSize; +#if TRACE_MAPS + static const int kUniqueIdOffset = kFeedbackVectorOffset + kPointerSize; + static const int kLastPointerFieldOffset = kUniqueIdOffset; +#else + static const int kLastPointerFieldOffset = kFeedbackVectorOffset; +#endif + #if V8_HOST_ARCH_32_BIT // Smi fields. - static const int kLengthOffset = - kFeedbackVectorOffset + kPointerSize; + static const int kLengthOffset = kLastPointerFieldOffset + kPointerSize; static const int kFormalParameterCountOffset = kLengthOffset + kPointerSize; static const int kExpectedNofPropertiesOffset = kFormalParameterCountOffset + kPointerSize; @@ -6994,8 +7024,7 @@ // word is not set and thus this word cannot be treated as pointer // to HeapObject during old space traversal. #if V8_TARGET_LITTLE_ENDIAN - static const int kLengthOffset = - kFeedbackVectorOffset + kPointerSize; + static const int kLengthOffset = kLastPointerFieldOffset + kPointerSize; static const int kFormalParameterCountOffset = kLengthOffset + kIntSize; @@ -7029,7 +7058,7 @@ #elif V8_TARGET_BIG_ENDIAN static const int kFormalParameterCountOffset = - kFeedbackVectorOffset + kPointerSize; + kLastPointerFieldOffset + kPointerSize; static const int kLengthOffset = kFormalParameterCountOffset + kIntSize; static const int kNumLiteralsOffset = kLengthOffset + kIntSize; @@ -7062,7 +7091,7 @@ static const int kAlignedSize = POINTER_SIZE_ALIGN(kSize); typedef FixedBodyDescriptor<kNameOffset, - kFeedbackVectorOffset + kPointerSize, + kLastPointerFieldOffset + kPointerSize, kSize> BodyDescriptor; // Bit positions in start_position_and_type. @@ -7092,12 +7121,14 @@ kIsArrow, kIsGenerator, kIsConciseMethod, + kIsDefaultConstructor, + kIsDefaultConstructorCallSuper, kIsAsmFunction, kDeserialized, kCompilerHintsCount // Pseudo entry }; - class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 3> {}; + class FunctionKindBits : public BitField<FunctionKind, kIsArrow, 5> {}; class DeoptCountBits : public BitField<int, 0, 4> {}; class OptReenableTriesBits : public BitField<int, 4, 18> {}; @@ -7538,6 +7569,9 @@ DECLARE_CAST(GlobalObject) + static void InvalidatePropertyCell(Handle<GlobalObject> object, + Handle<Name> name); + // Layout description. static const int kBuiltinsOffset = JSObject::kHeaderSize; static const int kNativeContextOffset = kBuiltinsOffset + kPointerSize; @@ -8578,6 +8612,10 @@ DECLARE_CAST(Name) DECLARE_PRINTER(Name) +#if TRACE_MAPS + void NameShortPrint(); + int NameShortPrint(Vector<char> str); +#endif // Layout description. static const int kHashFieldSlot = HeapObject::kHeaderSize; @@ -8680,6 +8718,10 @@ const char* PrivateSymbolToName() const; +#if TRACE_MAPS + friend class Name; // For PrivateSymbolToName. +#endif + DISALLOW_IMPLICIT_CONSTRUCTORS(Symbol); };
diff --git a/src/parser.cc b/src/parser.cc index c5bf0d9..e34854f 100644 --- a/src/parser.cc +++ b/src/parser.cc
@@ -275,6 +275,62 @@ } +FunctionLiteral* Parser::DefaultConstructor(bool call_super, Scope* scope, + int pos, int end_pos) { + int materialized_literal_count = -1; + int expected_property_count = -1; + int handler_count = 0; + int parameter_count = 0; + AstProperties ast_properties; + BailoutReason dont_optimize_reason = kNoReason; + const AstRawString* name = ast_value_factory()->empty_string(); + FunctionKind kind = call_super ? FunctionKind::kDefaultConstructorCallSuper + : FunctionKind::kDefaultConstructor; + + Scope* function_scope = NewScope(scope, FUNCTION_SCOPE); + function_scope->SetStrictMode(STRICT); + // Set start and end position to the same value + function_scope->set_start_position(pos); + function_scope->set_end_position(pos); + ZoneList<Statement*>* body = NULL; + + { + AstNodeFactory<AstConstructionVisitor> function_factory( + ast_value_factory()); + FunctionState function_state(&function_state_, &scope_, function_scope, + &function_factory); + + body = new (zone()) ZoneList<Statement*>(1, zone()); + if (call_super) { + Expression* prop = SuperReference(function_scope, factory(), pos); + ZoneList<Expression*>* args = + new (zone()) ZoneList<Expression*>(0, zone()); + Call* call = factory()->NewCall(prop, args, pos); + body->Add(factory()->NewExpressionStatement(call, pos), zone()); + } + + materialized_literal_count = function_state.materialized_literal_count(); + expected_property_count = function_state.expected_property_count(); + handler_count = function_state.handler_count(); + + ast_properties = *factory()->visitor()->ast_properties(); + dont_optimize_reason = factory()->visitor()->dont_optimize_reason(); + } + + FunctionLiteral* function_literal = factory()->NewFunctionLiteral( + name, ast_value_factory(), function_scope, body, + materialized_literal_count, expected_property_count, handler_count, + parameter_count, FunctionLiteral::kNoDuplicateParameters, + FunctionLiteral::ANONYMOUS_EXPRESSION, FunctionLiteral::kIsFunction, + FunctionLiteral::kNotParenthesized, kind, pos); + + function_literal->set_ast_properties(&ast_properties); + function_literal->set_dont_optimize_reason(dont_optimize_reason); + + return function_literal; +} + + // ---------------------------------------------------------------------------- // Target is a support class to facilitate manipulation of the // Parser's target_stack_ (the stack of potential 'break' and @@ -648,6 +704,13 @@ start_position, end_position); } + +Expression* ParserTraits::DefaultConstructor(bool call_super, Scope* scope, + int pos, int end_pos) { + return parser_->DefaultConstructor(call_super, scope, pos, end_pos); +} + + Literal* ParserTraits::ExpressionFromLiteral( Token::Value token, int pos, Scanner* scanner, @@ -1004,6 +1067,11 @@ Expression* expression = ParseExpression(false, &ok); DCHECK(expression->IsFunctionLiteral()); result = expression->AsFunctionLiteral(); + } else if (shared_info->is_default_constructor() || + shared_info->is_default_constructor_call_super()) { + result = DefaultConstructor( + shared_info->is_default_constructor_call_super(), scope, + shared_info->start_position(), shared_info->end_position()); } else { result = ParseFunctionLiteral(raw_name, Scanner::Location::invalid(), false, // Strict mode name already checked.
diff --git a/src/parser.h b/src/parser.h index db9071a..c815a45 100644 --- a/src/parser.h +++ b/src/parser.h
@@ -547,7 +547,8 @@ ZoneList<ObjectLiteral::Property*>* properties, int start_position, int end_position, AstNodeFactory<AstConstructionVisitor>* factory); - + Expression* DefaultConstructor(bool call_super, Scope* scope, int pos, + int end_pos); Literal* ExpressionFromLiteral( Token::Value token, int pos, Scanner* scanner, AstNodeFactory<AstConstructionVisitor>* factory); @@ -804,6 +805,9 @@ Scope* NewScope(Scope* parent, ScopeType type); + FunctionLiteral* DefaultConstructor(bool call_super, Scope* scope, int pos, + int end_pos); + // Skip over a lazy function, either using cached data if we have it, or // by parsing the function with PreParser. Consumes the ending }. void SkipLazyFunctionBody(const AstRawString* function_name,
diff --git a/src/preparser.h b/src/preparser.h index ddf9cec..7c9791f 100644 --- a/src/preparser.h +++ b/src/preparser.h
@@ -1325,6 +1325,12 @@ return PreParserExpression::Default(); } + static PreParserExpression DefaultConstructor(bool call_super, + PreParserScope* scope, int pos, + int end_pos) { + return PreParserExpression::Default(); + } + static PreParserExpression ExpressionFromLiteral( Token::Value token, int pos, Scanner* scanner, PreParserFactory* factory) { @@ -2747,12 +2753,14 @@ return this->EmptyExpression(); } + bool has_extends = false; ExpressionT extends = this->EmptyExpression(); if (Check(Token::EXTENDS)) { typename Traits::Type::ScopePtr scope = this->NewScope(scope_, BLOCK_SCOPE); BlockState block_state(&scope_, Traits::Type::ptr_to_scope(scope)); scope_->SetStrictMode(STRICT); extends = this->ParseLeftHandSideExpression(CHECK_OK); + has_extends = true; } // TODO(arv): Implement scopes and name binding in class body only. @@ -2791,6 +2799,11 @@ int end_pos = peek_position(); Expect(Token::RBRACE, CHECK_OK); + if (!has_seen_constructor) { + constructor = + this->DefaultConstructor(has_extends, scope_, pos, end_pos + 1); + } + return this->ClassExpression(name, extends, constructor, properties, pos, end_pos + 1, factory()); }
diff --git a/src/runtime/runtime-classes.cc b/src/runtime/runtime-classes.cc index cc4e09b..30ff918 100644 --- a/src/runtime/runtime-classes.cc +++ b/src/runtime/runtime-classes.cc
@@ -62,7 +62,7 @@ DCHECK(args.length() == 6); CONVERT_ARG_HANDLE_CHECKED(Object, name, 0); CONVERT_ARG_HANDLE_CHECKED(Object, super_class, 1); - CONVERT_ARG_HANDLE_CHECKED(Object, constructor, 2); + CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 2); CONVERT_ARG_HANDLE_CHECKED(Script, script, 3); CONVERT_SMI_ARG_CHECKED(start_position, 4); CONVERT_SMI_ARG_CHECKED(end_position, 5); @@ -104,52 +104,45 @@ Handle<String> name_string = name->IsString() ? Handle<String>::cast(name) : isolate->factory()->empty_string(); + constructor->shared()->set_name(*name_string); - Handle<JSFunction> ctor; - if (constructor->IsSpecFunction()) { - ctor = Handle<JSFunction>::cast(constructor); - JSFunction::SetPrototype(ctor, prototype); - PropertyAttributes attribs = - static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); - RETURN_FAILURE_ON_EXCEPTION( - isolate, - JSObject::SetOwnPropertyIgnoreAttributes( - ctor, isolate->factory()->prototype_string(), prototype, attribs)); - } else { - // TODO(arv): This should not use an empty function but a function that - // calls super. - Handle<Code> code(isolate->builtins()->builtin(Builtins::kEmptyFunction)); - ctor = isolate->factory()->NewFunction(name_string, code, prototype, true); - } - + JSFunction::SetPrototype(constructor, prototype); + PropertyAttributes attribs = + static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); + RETURN_FAILURE_ON_EXCEPTION( + isolate, JSObject::SetOwnPropertyIgnoreAttributes( + constructor, isolate->factory()->prototype_string(), + prototype, attribs)); Handle<Symbol> home_object_symbol(isolate->heap()->home_object_symbol()); RETURN_FAILURE_ON_EXCEPTION( isolate, JSObject::SetOwnPropertyIgnoreAttributes( - ctor, home_object_symbol, prototype, DONT_ENUM)); + constructor, home_object_symbol, prototype, DONT_ENUM)); if (!constructor_parent.is_null()) { RETURN_FAILURE_ON_EXCEPTION( - isolate, JSObject::SetPrototype(ctor, constructor_parent, false)); + isolate, + JSObject::SetPrototype(constructor, constructor_parent, false)); } JSObject::AddProperty(prototype, isolate->factory()->constructor_string(), - ctor, DONT_ENUM); + constructor, DONT_ENUM); // Install private properties that are used to construct the FunctionToString. RETURN_FAILURE_ON_EXCEPTION( + isolate, Object::SetProperty(constructor, + isolate->factory()->class_script_symbol(), + script, STRICT)); + RETURN_FAILURE_ON_EXCEPTION( isolate, - Object::SetProperty(ctor, isolate->factory()->class_script_symbol(), - script, STRICT)); + Object::SetProperty( + constructor, isolate->factory()->class_start_position_symbol(), + handle(Smi::FromInt(start_position), isolate), STRICT)); RETURN_FAILURE_ON_EXCEPTION( isolate, Object::SetProperty( - ctor, isolate->factory()->class_start_position_symbol(), - handle(Smi::FromInt(start_position), isolate), STRICT)); - RETURN_FAILURE_ON_EXCEPTION( - isolate, - Object::SetProperty(ctor, isolate->factory()->class_end_position_symbol(), - handle(Smi::FromInt(end_position), isolate), STRICT)); + constructor, isolate->factory()->class_end_position_symbol(), + handle(Smi::FromInt(end_position), isolate), STRICT)); - return *ctor; + return *constructor; }
diff --git a/src/runtime/runtime-debug.cc b/src/runtime/runtime-debug.cc index 95ac77b..00ac921 100644 --- a/src/runtime/runtime-debug.cc +++ b/src/runtime/runtime-debug.cc
@@ -2083,7 +2083,8 @@ static Handle<JSObject> NewJSObjectWithNullProto(Isolate* isolate) { Handle<JSObject> result = isolate->factory()->NewJSObject(isolate->object_function()); - Handle<Map> new_map = Map::Copy(Handle<Map>(result->map())); + Handle<Map> new_map = + Map::Copy(Handle<Map>(result->map()), "ObjectWithNullProto"); new_map->set_prototype(*isolate->factory()->null_value()); JSObject::MigrateToMap(result, new_map); return result;
diff --git a/src/runtime/runtime-literals.cc b/src/runtime/runtime-literals.cc index 23b5b19..c6efd02 100644 --- a/src/runtime/runtime-literals.cc +++ b/src/runtime/runtime-literals.cc
@@ -109,7 +109,7 @@ if (should_normalize) { // TODO(verwaest): We might not want to ever normalize here. JSObject::NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES, - length / 2); + length / 2, "Boilerplate"); } // TODO(verwaest): Support tracking representations in the boilerplate. for (int index = 0; index < length; index += 2) { @@ -166,7 +166,8 @@ // constant function properties. if (should_transform && !has_function_literal) { JSObject::MigrateSlowToFast(boilerplate, - boilerplate->map()->unused_property_fields()); + boilerplate->map()->unused_property_fields(), + "FastLiteral"); } return boilerplate;
diff --git a/src/runtime/runtime-object.cc b/src/runtime/runtime-object.cc index 74cb8cb..b2a736d 100644 --- a/src/runtime/runtime-object.cc +++ b/src/runtime/runtime-object.cc
@@ -469,7 +469,7 @@ bool needs_access_checks = old_map->is_access_check_needed(); if (needs_access_checks) { // Copy map so it won't interfere constructor's initial map. - Handle<Map> new_map = Map::Copy(old_map); + Handle<Map> new_map = Map::Copy(old_map, "DisableAccessChecks"); new_map->set_is_access_check_needed(false); JSObject::MigrateToMap(Handle<JSObject>::cast(object), new_map); } @@ -484,7 +484,7 @@ Handle<Map> old_map(object->map()); RUNTIME_ASSERT(!old_map->is_access_check_needed()); // Copy map so it won't interfere constructor's initial map. - Handle<Map> new_map = Map::Copy(old_map); + Handle<Map> new_map = Map::Copy(old_map, "EnableAccessChecks"); new_map->set_is_access_check_needed(true); JSObject::MigrateToMap(object, new_map); return isolate->heap()->undefined_value(); @@ -499,7 +499,8 @@ // Conservative upper limit to prevent fuzz tests from going OOM. RUNTIME_ASSERT(properties <= 100000); if (object->HasFastProperties() && !object->IsJSGlobalProxy()) { - JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties); + JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties, + "OptimizeForAdding"); } return *object; } @@ -1152,7 +1153,8 @@ DCHECK(args.length() == 1); CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); if (object->IsJSObject() && !object->IsGlobalObject()) { - JSObject::MigrateSlowToFast(Handle<JSObject>::cast(object), 0); + JSObject::MigrateSlowToFast(Handle<JSObject>::cast(object), 0, + "RuntimeToFastProperties"); } return *object; } @@ -1456,7 +1458,7 @@ bool fast = obj->HasFastProperties(); RETURN_FAILURE_ON_EXCEPTION( isolate, JSObject::DefineAccessor(obj, name, getter, setter, attr)); - if (fast) JSObject::MigrateSlowToFast(obj, 0); + if (fast) JSObject::MigrateSlowToFast(obj, 0, "RuntimeDefineAccessor"); return isolate->heap()->undefined_value(); }
diff --git a/src/runtime/runtime-scopes.cc b/src/runtime/runtime-scopes.cc index c935cda..5ee9b25 100644 --- a/src/runtime/runtime-scopes.cc +++ b/src/runtime/runtime-scopes.cc
@@ -27,6 +27,14 @@ Handle<String> name, Handle<Object> value, PropertyAttributes attr, bool is_var, bool is_const, bool is_function) { + Handle<GlobalContextTable> global_contexts( + global->native_context()->global_context_table()); + GlobalContextTable::LookupResult lookup; + if (GlobalContextTable::Lookup(global_contexts, name, &lookup) && + IsLexicalVariableMode(lookup.mode)) { + return ThrowRedeclarationError(isolate, name); + } + // Do the lookup own properties only, see ES5 erratum. LookupIterator it(global, name, LookupIterator::HIDDEN_SKIP_INTERCEPTOR); Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it); @@ -347,7 +355,7 @@ isolate->factory()->NewFixedArray(mapped_count + 2, NOT_TENURED); parameter_map->set_map(isolate->heap()->sloppy_arguments_elements_map()); - Handle<Map> map = Map::Copy(handle(result->map())); + Handle<Map> map = Map::Copy(handle(result->map()), "NewSloppyArguments"); map->set_elements_kind(SLOPPY_ARGUMENTS_ELEMENTS); result->set_map(*map); @@ -507,6 +515,35 @@ pretenure_flag); } +static Object* FindNameClash(Handle<ScopeInfo> scope_info, + Handle<GlobalObject> global_object, + Handle<GlobalContextTable> global_context) { + Isolate* isolate = scope_info->GetIsolate(); + for (int var = 0; var < scope_info->ContextLocalCount(); var++) { + Handle<String> name(scope_info->ContextLocalName(var)); + VariableMode mode = scope_info->ContextLocalMode(var); + GlobalContextTable::LookupResult lookup; + if (GlobalContextTable::Lookup(global_context, name, &lookup)) { + if (IsLexicalVariableMode(mode) || IsLexicalVariableMode(lookup.mode)) { + return ThrowRedeclarationError(isolate, name); + } + } + + if (IsLexicalVariableMode(mode)) { + LookupIterator it(global_object, name, + LookupIterator::HIDDEN_SKIP_INTERCEPTOR); + Maybe<PropertyAttributes> maybe = JSReceiver::GetPropertyAttributes(&it); + if (!maybe.has_value) return isolate->heap()->exception(); + if ((maybe.value & DONT_DELETE) != 0) { + return ThrowRedeclarationError(isolate, name); + } + + GlobalObject::InvalidatePropertyCell(global_object, name); + } + } + return isolate->heap()->undefined_value(); +} + RUNTIME_FUNCTION(Runtime_NewGlobalContext) { HandleScope scope(isolate); @@ -514,12 +551,25 @@ CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 0); CONVERT_ARG_HANDLE_CHECKED(ScopeInfo, scope_info, 1); + Handle<GlobalObject> global_object(function->context()->global_object()); + Handle<Context> native_context(global_object->native_context()); + Handle<GlobalContextTable> global_context_table( + native_context->global_context_table()); + + Handle<String> clashed_name; + Object* name_clash_result = + FindNameClash(scope_info, global_object, global_context_table); + if (isolate->has_pending_exception()) return name_clash_result; + Handle<Context> result = isolate->factory()->NewGlobalContext(function, scope_info); DCHECK(function->context() == isolate->context()); DCHECK(function->context()->global_object() == result->global_object()); - result->global_object()->set_global_context(*result); + + Handle<GlobalContextTable> new_global_context_table = + GlobalContextTable::Extend(global_context_table, result); + native_context->set_global_context_table(*new_global_context_table); return *result; }
diff --git a/src/scanner.cc b/src/scanner.cc index e63239d..ddcd937 100644 --- a/src/scanner.cc +++ b/src/scanner.cc
@@ -1138,24 +1138,6 @@ } -bool Scanner::ScanLiteralUnicodeEscape() { - DCHECK(c0_ == '\\'); - AddLiteralChar(c0_); - Advance(); - int hex_digits_read = 0; - if (c0_ == 'u') { - AddLiteralChar(c0_); - while (hex_digits_read < 4) { - Advance(); - if (!IsHexDigit(c0_)) break; - AddLiteralChar(c0_); - ++hex_digits_read; - } - } - return hex_digits_read == 4; -} - - bool Scanner::ScanRegExpFlags() { // Scan regular expression flags. LiteralScope literal(this); @@ -1163,10 +1145,7 @@ if (c0_ != '\\') { AddLiteralCharAdvance(); } else { - if (!ScanLiteralUnicodeEscape()) { - return false; - } - Advance(); + return false; } } literal.Complete();
diff --git a/src/scanner.h b/src/scanner.h index 387d331..e626f20 100644 --- a/src/scanner.h +++ b/src/scanner.h
@@ -637,10 +637,6 @@ // Decodes a Unicode escape-sequence which is part of an identifier. // If the escape sequence cannot be decoded the result is kBadChar. uc32 ScanIdentifierUnicodeEscape(); - // Scans a Unicode escape-sequence and adds its characters, - // uninterpreted, to the current literal. Used for parsing RegExp - // flags. - bool ScanLiteralUnicodeEscape(); // Return the current source position. int source_pos() {
diff --git a/src/version.cc b/src/version.cc index 601a599..6136739 100644 --- a/src/version.cc +++ b/src/version.cc
@@ -33,8 +33,8 @@ // NOTE these macros are used by some of the tool scripts and the build // system so their names cannot be changed without changing the scripts. #define MAJOR_VERSION 3 -#define MINOR_VERSION 30 -#define BUILD_NUMBER 37 +#define MINOR_VERSION 31 +#define BUILD_NUMBER 0 #define PATCH_LEVEL 0 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 5ea5f72..f17389c 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc
@@ -2087,6 +2087,10 @@ // rdi - function // rdx - slot id Isolate* isolate = masm->isolate(); + const int with_types_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); + const int generic_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); Label extra_checks_or_miss, slow_start; Label slow, non_function, wrap, cont; Label have_js_function; @@ -2128,34 +2132,64 @@ } __ bind(&extra_checks_or_miss); - Label miss; + Label uninitialized, miss; __ movp(rcx, FieldOperand(rbx, rdx, times_pointer_size, FixedArray::kHeaderSize)); __ Cmp(rcx, TypeFeedbackVector::MegamorphicSentinel(isolate)); __ j(equal, &slow_start); - __ Cmp(rcx, TypeFeedbackVector::UninitializedSentinel(isolate)); - __ j(equal, &miss); - if (!FLAG_trace_ic) { - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(rcx); - __ CmpObjectType(rcx, JS_FUNCTION_TYPE, rcx); - __ j(not_equal, &miss); - __ Move(FieldOperand(rbx, rdx, times_pointer_size, FixedArray::kHeaderSize), - TypeFeedbackVector::MegamorphicSentinel(isolate)); - // We have to update statistics for runtime profiling. - const int with_types_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); - __ SmiAddConstant(FieldOperand(rbx, with_types_offset), Smi::FromInt(-1)); - const int generic_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); - __ SmiAddConstant(FieldOperand(rbx, generic_offset), Smi::FromInt(1)); - __ jmp(&slow_start); + // The following cases attempt to handle MISS cases without going to the + // runtime. + if (FLAG_trace_ic) { + __ jmp(&miss); } - // We are here because tracing is on or we are going monomorphic. + __ Cmp(rcx, TypeFeedbackVector::UninitializedSentinel(isolate)); + __ j(equal, &uninitialized); + + // We are going megamorphic. If the feedback is a JSFunction, it is fine + // to handle it here. More complex cases are dealt with in the runtime. + __ AssertNotSmi(rcx); + __ CmpObjectType(rcx, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &miss); + __ Move(FieldOperand(rbx, rdx, times_pointer_size, FixedArray::kHeaderSize), + TypeFeedbackVector::MegamorphicSentinel(isolate)); + // We have to update statistics for runtime profiling. + __ SmiAddConstant(FieldOperand(rbx, with_types_offset), Smi::FromInt(-1)); + __ SmiAddConstant(FieldOperand(rbx, generic_offset), Smi::FromInt(1)); + __ jmp(&slow_start); + + __ bind(&uninitialized); + + // We are going monomorphic, provided we actually have a JSFunction. + __ JumpIfSmi(rdi, &miss); + + // Goto miss case if we do not have a function. + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &miss); + + // Make sure the function is not the Array() function, which requires special + // behavior on MISS. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, rcx); + __ cmpp(rdi, rcx); + __ j(equal, &miss); + + // Update stats. + __ SmiAddConstant(FieldOperand(rbx, with_types_offset), Smi::FromInt(1)); + + // Store the function. + __ movp(FieldOperand(rbx, rdx, times_pointer_size, FixedArray::kHeaderSize), + rdi); + + // Update the write barrier. + __ movp(rax, rdi); + __ RecordWriteArray(rbx, rax, rdx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ jmp(&have_js_function); + + // We are here because tracing is on or we encountered a MISS case we can't + // handle here. __ bind(&miss); GenerateMiss(masm);
diff --git a/src/x87/code-stubs-x87.cc b/src/x87/code-stubs-x87.cc index 202dec6..8925d4f 100644 --- a/src/x87/code-stubs-x87.cc +++ b/src/x87/code-stubs-x87.cc
@@ -1900,6 +1900,10 @@ // edi - function // edx - slot id Isolate* isolate = masm->isolate(); + const int with_types_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); + const int generic_offset = + FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); Label extra_checks_or_miss, slow_start; Label slow, non_function, wrap, cont; Label have_js_function; @@ -1939,35 +1943,66 @@ } __ bind(&extra_checks_or_miss); - Label miss; + Label uninitialized, miss; __ mov(ecx, FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize)); __ cmp(ecx, Immediate(TypeFeedbackVector::MegamorphicSentinel(isolate))); __ j(equal, &slow_start); - __ cmp(ecx, Immediate(TypeFeedbackVector::UninitializedSentinel(isolate))); - __ j(equal, &miss); - if (!FLAG_trace_ic) { - // We are going megamorphic. If the feedback is a JSFunction, it is fine - // to handle it here. More complex cases are dealt with in the runtime. - __ AssertNotSmi(ecx); - __ CmpObjectType(ecx, JS_FUNCTION_TYPE, ecx); - __ j(not_equal, &miss); - __ mov(FieldOperand(ebx, edx, times_half_pointer_size, - FixedArray::kHeaderSize), - Immediate(TypeFeedbackVector::MegamorphicSentinel(isolate))); - // We have to update statistics for runtime profiling. - const int with_types_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kWithTypesIndex); - __ sub(FieldOperand(ebx, with_types_offset), Immediate(Smi::FromInt(1))); - const int generic_offset = - FixedArray::OffsetOfElementAt(TypeFeedbackVector::kGenericCountIndex); - __ add(FieldOperand(ebx, generic_offset), Immediate(Smi::FromInt(1))); - __ jmp(&slow_start); + // The following cases attempt to handle MISS cases without going to the + // runtime. + if (FLAG_trace_ic) { + __ jmp(&miss); } - // We are here because tracing is on or we are going monomorphic. + __ cmp(ecx, Immediate(TypeFeedbackVector::UninitializedSentinel(isolate))); + __ j(equal, &uninitialized); + + // We are going megamorphic. If the feedback is a JSFunction, it is fine + // to handle it here. More complex cases are dealt with in the runtime. + __ AssertNotSmi(ecx); + __ CmpObjectType(ecx, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &miss); + __ mov( + FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize), + Immediate(TypeFeedbackVector::MegamorphicSentinel(isolate))); + // We have to update statistics for runtime profiling. + __ sub(FieldOperand(ebx, with_types_offset), Immediate(Smi::FromInt(1))); + __ add(FieldOperand(ebx, generic_offset), Immediate(Smi::FromInt(1))); + __ jmp(&slow_start); + + __ bind(&uninitialized); + + // We are going monomorphic, provided we actually have a JSFunction. + __ JumpIfSmi(edi, &miss); + + // Goto miss case if we do not have a function. + __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx); + __ j(not_equal, &miss); + + // Make sure the function is not the Array() function, which requires special + // behavior on MISS. + __ LoadGlobalFunction(Context::ARRAY_FUNCTION_INDEX, ecx); + __ cmp(edi, ecx); + __ j(equal, &miss); + + // Update stats. + __ add(FieldOperand(ebx, with_types_offset), Immediate(Smi::FromInt(1))); + + // Store the function. + __ mov( + FieldOperand(ebx, edx, times_half_pointer_size, FixedArray::kHeaderSize), + edi); + + // Update the write barrier. + __ mov(eax, edi); + __ RecordWriteArray(ebx, eax, edx, kDontSaveFPRegs, EMIT_REMEMBERED_SET, + OMIT_SMI_CHECK); + __ jmp(&have_js_function); + + // We are here because tracing is on or we encountered a MISS case we can't + // handle here. __ bind(&miss); GenerateMiss(masm); @@ -2862,6 +2897,25 @@ } +void ToNumberStub::Generate(MacroAssembler* masm) { + // The ToNumber stub takes one argument in eax. + Label check_heap_number, call_builtin; + __ JumpIfNotSmi(eax, &check_heap_number, Label::kNear); + __ Ret(); + + __ bind(&check_heap_number); + __ CompareMap(eax, masm->isolate()->factory()->heap_number_map()); + __ j(not_equal, &call_builtin, Label::kNear); + __ Ret(); + + __ bind(&call_builtin); + __ pop(ecx); // Pop return address. + __ push(eax); + __ push(ecx); // Push return address. + __ InvokeBuiltin(Builtins::TO_NUMBER, JUMP_FUNCTION); +} + + void StringHelper::GenerateFlatOneByteStringEquals(MacroAssembler* masm, Register left, Register right,
diff --git a/src/x87/lithium-codegen-x87.cc b/src/x87/lithium-codegen-x87.cc index f8872d7..284a666 100644 --- a/src/x87/lithium-codegen-x87.cc +++ b/src/x87/lithium-codegen-x87.cc
@@ -4063,8 +4063,8 @@ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ push(temp_result); __ CallRuntimeSaveDoubles(Runtime::kMathSqrtRT); - RecordSafepointWithRegisters( - instr->pointer_map(), 0, Safepoint::kNoLazyDeopt); + RecordSafepointWithRegisters(instr->pointer_map(), 1, + Safepoint::kNoLazyDeopt); __ StoreToSafepointRegisterSlot(temp_result, eax); } X87PrepareToWrite(result_reg); @@ -4278,7 +4278,7 @@ __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); __ push(temp_result); __ CallRuntimeSaveDoubles(Runtime::kMathExpRT); - RecordSafepointWithRegisters(instr->pointer_map(), 0, + RecordSafepointWithRegisters(instr->pointer_map(), 1, Safepoint::kNoLazyDeopt); __ StoreToSafepointRegisterSlot(temp_result, eax); }
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status index 0b76995..9e85a0c 100644 --- a/test/cctest/cctest.status +++ b/test/cctest/cctest.status
@@ -81,10 +81,7 @@ ############################################################################## # TurboFan compiler failures. - # TODO(sigurds): The schedule is borked with multiple inlinees, - # and cannot handle free-floating loops yet - 'test-run-inlining/InlineTwiceDependentDiamond': [SKIP], - 'test-run-inlining/InlineTwiceDependentDiamondDifferent': [SKIP], + # TODO(mstarzinger): The scheduler cannot handle free-floating loops yet. 'test-run-inlining/InlineLoop': [SKIP], # Some tests are just too slow to run for now.
diff --git a/test/cctest/compiler/test-scheduler.cc b/test/cctest/compiler/test-scheduler.cc index aed8f8b..659aacd 100644 --- a/test/cctest/compiler/test-scheduler.cc +++ b/test/cctest/compiler/test-scheduler.cc
@@ -1816,6 +1816,48 @@ } +TEST(NestedFloatingDiamondWithLoop) { + HandleAndZoneScope scope; + Graph graph(scope.main_zone()); + CommonOperatorBuilder common(scope.main_zone()); + SimplifiedOperatorBuilder simplified(scope.main_zone()); + MachineOperatorBuilder machine; + + Node* start = graph.NewNode(common.Start(2)); + graph.SetStart(start); + + Node* p0 = graph.NewNode(common.Parameter(0), start); + + Node* fv = graph.NewNode(common.Int32Constant(7)); + Node* br = graph.NewNode(common.Branch(), p0, graph.start()); + Node* t = graph.NewNode(common.IfTrue(), br); + Node* f = graph.NewNode(common.IfFalse(), br); + + Node* loop = graph.NewNode(common.Loop(2), f, start); + Node* ind = graph.NewNode(common.Phi(kMachAnyTagged, 2), p0, p0, loop); + + // TODO(mstarzinger): Make scheduler deal with non-empty loops here. + // Node* add = graph.NewNode(machine.IntAdd(), ind, fv); + + Node* br1 = graph.NewNode(common.Branch(), ind, loop); + Node* t1 = graph.NewNode(common.IfTrue(), br1); + Node* f1 = graph.NewNode(common.IfFalse(), br1); + + loop->ReplaceInput(1, t1); // close loop. + ind->ReplaceInput(1, ind); // close induction variable. + + Node* m = graph.NewNode(common.Merge(2), t, f1); + Node* phi = graph.NewNode(common.Phi(kMachAnyTagged, 2), fv, ind, m); + + Node* ret = graph.NewNode(common.Return(), phi, start, start); + Node* end = graph.NewNode(common.End(), ret, start); + + graph.SetEnd(end); + + ComputeAndVerifySchedule(19, &graph); +} + + TEST(LoopedFloatingDiamond1) { HandleAndZoneScope scope; Graph graph(scope.main_zone());
diff --git a/test/cctest/test-assembler-arm.cc b/test/cctest/test-assembler-arm.cc index b6e260e..2bcf022 100644 --- a/test/cctest/test-assembler-arm.cc +++ b/test/cctest/test-assembler-arm.cc
@@ -1375,14 +1375,14 @@ __ pkhtb(r2, r0, Operand(r1, ASR, 8)); __ str(r2, MemOperand(r4, OFFSET_OF(T, dst1))); - __ uxtb16(r2, Operand(r0, ROR, 8)); + __ uxtb16(r2, r0, 8); __ str(r2, MemOperand(r4, OFFSET_OF(T, dst2))); - __ uxtb(r2, Operand(r0, ROR, 8)); + __ uxtb(r2, r0, 8); __ str(r2, MemOperand(r4, OFFSET_OF(T, dst3))); __ ldr(r0, MemOperand(r4, OFFSET_OF(T, src2))); - __ uxtab(r2, r0, Operand(r1, ROR, 8)); + __ uxtab(r2, r0, r1, 8); __ str(r2, MemOperand(r4, OFFSET_OF(T, dst4))); __ ldm(ia_w, sp, r4.bit() | pc.bit()); @@ -1606,6 +1606,214 @@ } +TEST(sxtb) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ sxtb(r1, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<int8_t>(x)), r); + USE(dummy); + } +} + + +TEST(sxtab) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ sxtab(r1, r2, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(), y = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<int8_t>(x)) + y, r); + USE(dummy); + } +} + + +TEST(sxth) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ sxth(r1, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<int16_t>(x)), r); + USE(dummy); + } +} + + +TEST(sxtah) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ sxtah(r1, r2, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(), y = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<int16_t>(x)) + y, r); + USE(dummy); + } +} + + +TEST(uxtb) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ uxtb(r1, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<uint8_t>(x)), r); + USE(dummy); + } +} + + +TEST(uxtab) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ uxtab(r1, r2, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(), y = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<uint8_t>(x)) + y, r); + USE(dummy); + } +} + + +TEST(uxth) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ uxth(r1, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, 0, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<uint16_t>(x)), r); + USE(dummy); + } +} + + +TEST(uxtah) { + CcTest::InitializeVM(); + Isolate* const isolate = CcTest::i_isolate(); + HandleScope scope(isolate); + RandomNumberGenerator* const rng = isolate->random_number_generator(); + Assembler assm(isolate, nullptr, 0); + __ uxtah(r1, r2, r1); + __ str(r1, MemOperand(r0)); + __ bx(lr); + CodeDesc desc; + assm.GetCode(&desc); + Handle<Code> code = isolate->factory()->NewCode( + desc, Code::ComputeFlags(Code::STUB), Handle<Code>()); +#ifdef OBJECT_PRINT + code->Print(std::cout); +#endif + F3 f = FUNCTION_CAST<F3>(code->entry()); + for (size_t i = 0; i < 128; ++i) { + int32_t r, x = rng->NextInt(), y = rng->NextInt(); + Object* dummy = CALL_GENERATED_CODE(f, &r, x, y, 0, 0); + CHECK_EQ(static_cast<int32_t>(static_cast<uint16_t>(x)) + y, r); + USE(dummy); + } +} + + TEST(code_relative_offset) { // Test extracting the offset of a label from the beginning of the code // in a register.
diff --git a/test/cctest/test-decls.cc b/test/cctest/test-decls.cc index f2ccdab..0c13a68 100644 --- a/test/cctest/test-decls.cc +++ b/test/cctest/test-decls.cc
@@ -644,6 +644,44 @@ } +TEST(CrossScriptReferences_Simple) { + i::FLAG_harmony_scoping = true; + i::FLAG_use_strict = true; + + v8::Isolate* isolate = CcTest::isolate(); + HandleScope scope(isolate); + + { + SimpleContext context; + context.Check("let x = 1; x", EXPECT_RESULT, Number::New(isolate, 1)); + context.Check("let x = 5; x", EXPECT_EXCEPTION); + } +} + + +TEST(CrossScriptReferences_Simple2) { + i::FLAG_harmony_scoping = true; + i::FLAG_use_strict = true; + + v8::Isolate* isolate = CcTest::isolate(); + HandleScope scope(isolate); + + for (int k = 0; k < 100; k++) { + SimpleContext context; + bool cond = (k % 2) == 0; + if (cond) { + context.Check("let x = 1; x", EXPECT_RESULT, Number::New(isolate, 1)); + context.Check("let z = 4; z", EXPECT_RESULT, Number::New(isolate, 4)); + } else { + context.Check("let z = 1; z", EXPECT_RESULT, Number::New(isolate, 1)); + context.Check("let x = 4; x", EXPECT_RESULT, Number::New(isolate, 4)); + } + context.Check("let y = 2; x", EXPECT_RESULT, + Number::New(isolate, cond ? 1 : 4)); + } +} + + TEST(CrossScriptReferencesHarmony) { i::FLAG_use_strict = true; i::FLAG_harmony_scoping = true; @@ -704,12 +742,12 @@ SimpleContext context; context.Check(firsts[i], EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); - // TODO(rossberg): All tests should actually be errors in Harmony, - // but we currently do not detect the cases where the first declaration - // is not lexical. - context.Check(seconds[j], - i < 2 ? EXPECT_RESULT : EXPECT_ERROR, - Number::New(CcTest::isolate(), 2)); + bool success_case = i < 2 && j < 2; + Local<Value> success_result; + if (success_case) success_result = Number::New(CcTest::isolate(), 2); + + context.Check(seconds[j], success_case ? EXPECT_RESULT : EXPECT_EXCEPTION, + success_result); } } } @@ -740,9 +778,86 @@ EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); context.Check( "'use strict';" - "g({});" + "g({});0", + EXPECT_RESULT, Number::New(CcTest::isolate(), 0)); + context.Check("f({})", EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); + context.Check("h({})", EXPECT_RESULT, number_string); + } +} + + +TEST(CrossScriptGlobal) { + i::FLAG_harmony_scoping = true; + + HandleScope handle_scope(CcTest::isolate()); + { + SimpleContext context; + + context.Check( + "var global = this;" + "global.x = 255;" "x", + EXPECT_RESULT, Number::New(CcTest::isolate(), 255)); + context.Check( + "'use strict';" + "let x = 1;" + "global.x", + EXPECT_RESULT, Number::New(CcTest::isolate(), 255)); + context.Check("global.x = 15; x", EXPECT_RESULT, + Number::New(CcTest::isolate(), 1)); + context.Check("x = 221; global.x", EXPECT_RESULT, + Number::New(CcTest::isolate(), 15)); + context.Check( + "z = 15;" + "function f() { return z; };" + "for (var k = 0; k < 3; k++) { f(); }" + "f()", EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); + context.Check( + "'use strict';" + "let z = 5; f()", + EXPECT_RESULT, Number::New(CcTest::isolate(), 5)); + context.Check( + "function f() { konst = 10; return konst; };" + "f()", + EXPECT_RESULT, Number::New(CcTest::isolate(), 10)); + context.Check( + "'use strict';" + "const konst = 255;" + "f()", + EXPECT_EXCEPTION); + } +} + + +TEST(CrossScriptStaticLookupUndeclared) { + i::FLAG_harmony_scoping = true; + + HandleScope handle_scope(CcTest::isolate()); + + { + SimpleContext context; + Local<String> undefined_string = String::NewFromUtf8( + CcTest::isolate(), "undefined", String::kInternalizedString); + Local<String> number_string = String::NewFromUtf8( + CcTest::isolate(), "number", String::kInternalizedString); + + context.Check( + "function f(o) { return x; }" + "function g(o) { x = 15; }" + "function h(o) { return typeof x; }", + EXPECT_RESULT, Undefined(CcTest::isolate())); + context.Check("h({})", EXPECT_RESULT, undefined_string); + context.Check( + "'use strict';" + "let x = 1;" + "f({})", + EXPECT_RESULT, Number::New(CcTest::isolate(), 1)); + context.Check( + "'use strict';" + "g({});x", + EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); + context.Check("h({})", EXPECT_RESULT, number_string); context.Check("f({})", EXPECT_RESULT, Number::New(CcTest::isolate(), 15)); context.Check("h({})", EXPECT_RESULT, number_string); }
diff --git a/test/cctest/test-disasm-arm.cc b/test/cctest/test-disasm-arm.cc index 39356b1..095c636 100644 --- a/test/cctest/test-disasm-arm.cc +++ b/test/cctest/test-disasm-arm.cc
@@ -410,14 +410,32 @@ "e6843895 pkhbt r3, r4, r5, lsl #17"); COMPARE(pkhtb(r3, r4, Operand(r5, ASR, 17)), "e68438d5 pkhtb r3, r4, r5, asr #17"); - COMPARE(uxtb(r9, Operand(r10, ROR, 0)), - "e6ef907a uxtb r9, r10"); - COMPARE(uxtb(r3, Operand(r4, ROR, 8)), - "e6ef3474 uxtb r3, r4, ror #8"); - COMPARE(uxtab(r3, r4, Operand(r5, ROR, 8)), - "e6e43475 uxtab r3, r4, r5, ror #8"); - COMPARE(uxtb16(r3, Operand(r4, ROR, 8)), - "e6cf3474 uxtb16 r3, r4, ror #8"); + + COMPARE(sxtb(r1, r7, 0, eq), "06af1077 sxtbeq r1, r7"); + COMPARE(sxtb(r0, r0, 8, ne), "16af0470 sxtbne r0, r0, ror #8"); + COMPARE(sxtb(r9, r10, 16), "e6af987a sxtb r9, r10, ror #16"); + COMPARE(sxtb(r4, r3, 24), "e6af4c73 sxtb r4, r3, ror #24"); + + COMPARE(sxtab(r3, r4, r5), "e6a43075 sxtab r3, r4, r5"); + + COMPARE(sxth(r5, r0), "e6bf5070 sxth r5, r0"); + COMPARE(sxth(r5, r9, 8), "e6bf5479 sxth r5, r9, ror #8"); + COMPARE(sxth(r5, r9, 16, hi), "86bf5879 sxthhi r5, r9, ror #16"); + COMPARE(sxth(r8, r9, 24, cc), "36bf8c79 sxthcc r8, r9, ror #24"); + + COMPARE(sxtah(r3, r4, r5, 16), "e6b43875 sxtah r3, r4, r5, ror #16"); + + COMPARE(uxtb(r9, r10), "e6ef907a uxtb r9, r10"); + COMPARE(uxtb(r3, r4, 8), "e6ef3474 uxtb r3, r4, ror #8"); + + COMPARE(uxtab(r3, r4, r5, 8), "e6e43475 uxtab r3, r4, r5, ror #8"); + + COMPARE(uxtb16(r3, r4, 8), "e6cf3474 uxtb16 r3, r4, ror #8"); + + COMPARE(uxth(r9, r10), "e6ff907a uxth r9, r10"); + COMPARE(uxth(r3, r4, 8), "e6ff3474 uxth r3, r4, ror #8"); + + COMPARE(uxtah(r3, r4, r5, 24), "e6f43c75 uxtah r3, r4, r5, ror #24"); } COMPARE(smmla(r0, r1, r2, r3), "e7503211 smmla r0, r1, r2, r3");
diff --git a/test/cctest/test-parsing.cc b/test/cctest/test-parsing.cc index 1909b3e..79d7654 100644 --- a/test/cctest/test-parsing.cc +++ b/test/cctest/test-parsing.cc
@@ -4302,7 +4302,9 @@ "var foob\\u123r = 0;", "var \\u123roo = 0;", "\"foob\\u123rr\"", - "/regex/g\\u123r", + // No escapes allowed in regexp flags + "/regex/\\u0069g", + "/regex/\\u006g", NULL}; RunParserSyncTest(context_data, data, kError); }
diff --git a/test/mjsunit/asm/sign-extend.js b/test/mjsunit/asm/sign-extend.js new file mode 100644 index 0000000..62d8d34 --- /dev/null +++ b/test/mjsunit/asm/sign-extend.js
@@ -0,0 +1,45 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var stdlib = this; +var buffer = new ArrayBuffer(64 * 1024); +var foreign = {} + + +var sext8 = (function Module(stdlib, foreign, heap) { + "use asm"; + function sext8(i) { + i = i|0; + i = i << 24 >> 24; + return i|0; + } + return { sext8: sext8 }; +})(stdlib, foreign, buffer).sext8; + +assertEquals(-128, sext8(128)); +assertEquals(-1, sext8(-1)); +assertEquals(-1, sext8(255)); +assertEquals(0, sext8(0)); +assertEquals(0, sext8(256)); +assertEquals(42, sext8(42)); +assertEquals(127, sext8(127)); + + +var sext16 = (function Module(stdlib, foreign, heap) { + "use asm"; + function sext16(i) { + i = i|0; + i = i << 16 >> 16; + return i|0; + } + return { sext16: sext16 }; +})(stdlib, foreign, buffer).sext16; + +assertEquals(-32768, sext16(32768)); +assertEquals(-1, sext16(-1)); +assertEquals(-1, sext16(65535)); +assertEquals(0, sext16(0)); +assertEquals(0, sext16(65536)); +assertEquals(128, sext16(128)); +assertEquals(32767, sext16(32767));
diff --git a/test/mjsunit/asm/zero-extend.js b/test/mjsunit/asm/zero-extend.js new file mode 100644 index 0000000..a1f9da6 --- /dev/null +++ b/test/mjsunit/asm/zero-extend.js
@@ -0,0 +1,37 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var stdlib = this; +var buffer = new ArrayBuffer(64 * 1024); +var foreign = {} + + +var zext8 = (function Module(stdlib, foreign, heap) { + "use asm"; + function zext8(i) { + i = i|0; + return i & 0xff; + } + return { zext8: zext8 }; +})(stdlib, foreign, buffer).zext8; + +assertEquals(0, zext8(0)); +assertEquals(0, zext8(0x100)); +assertEquals(0xff, zext8(-1)); +assertEquals(0xff, zext8(0xff)); + + +var zext16 = (function Module(stdlib, foreign, heap) { + "use asm"; + function zext16(i) { + i = i|0; + return i & 0xffff; + } + return { zext16: zext16 }; +})(stdlib, foreign, buffer).zext16; + +assertEquals(0, zext16(0)); +assertEquals(0, zext16(0x10000)); +assertEquals(0xffff, zext16(-1)); +assertEquals(0xffff, zext16(0xffff));
diff --git a/test/mjsunit/compiler/division-by-constant.js b/test/mjsunit/compiler/division-by-constant.js index 0778e95..d3f3ac3 100644 --- a/test/mjsunit/compiler/division-by-constant.js +++ b/test/mjsunit/compiler/division-by-constant.js
@@ -101,6 +101,7 @@ // ----------------------------------------------------------------------------- + function TestDivisionLike(ref, construct, values, divisor) { // Define the function to test. var OptFun = new Function("dividend", construct(divisor)); @@ -111,12 +112,14 @@ %OptimizeFunctionOnNextCall(OptFun); OptFun(13); - // Check results. - values.forEach(function(dividend) { +function dude(dividend) { // Avoid deopt caused by overflow, we do not want to test this here. if (dividend === -2147483648 && divisor === -1) return; assertEquals(ref(dividend, divisor), OptFun(dividend)); - }); + } + + // Check results. + values.forEach(dude); } function Test(ref, construct) {
diff --git a/test/mjsunit/compiler/regress-uint8-deopt.js b/test/mjsunit/compiler/regress-uint8-deopt.js new file mode 100644 index 0000000..ba2823f --- /dev/null +++ b/test/mjsunit/compiler/regress-uint8-deopt.js
@@ -0,0 +1,17 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --turbo-asm --turbo-deoptimization --allow-natives-syntax + +function Module(heap) { + "use asm"; + var a = new Uint8Array(heap); + function f() { + var x = a[0] | 0; + %DeoptimizeFunction(f); + return x; + } + return f; +} +assertEquals(0, Module(new ArrayBuffer(1))());
diff --git a/test/mjsunit/harmony/classes.js b/test/mjsunit/harmony/classes.js index 59371e4..8748f62 100644 --- a/test/mjsunit/harmony/classes.js +++ b/test/mjsunit/harmony/classes.js
@@ -19,8 +19,16 @@ assertEquals(Function.prototype, Object.getPrototypeOf(D)); assertEquals('D', D.name); + class D2 { constructor() {} } + assertEquals('D2', D2.name); + + // TODO(arv): The logic for the name of anonymous functions in ES6 requires + // the below to be 'E'; var E = class {} - assertEquals('', E.name); + assertEquals('', E.name); // Should be 'E'. + + var F = class { constructor() {} }; + assertEquals('', F.name); // Should be 'F'. })(); @@ -589,6 +597,33 @@ })(); +(function TestDefaultConstructorNoCrash() { + // Regression test for https://code.google.com/p/v8/issues/detail?id=3661 + class C {} + assertEquals(undefined, C()); + assertEquals(undefined, C(1)); + assertTrue(new C() instanceof C); + assertTrue(new C(1) instanceof C); +})(); + + +(function TestDefaultConstructor() { + var calls = 0; + class Base { + constructor() { + calls++; + } + } + class Derived extends Base {} + var object = new Derived; + assertEquals(1, calls); + + calls = 0; + Derived(); + assertEquals(1, calls); +})(); + + /* TODO(arv): Implement (function TestNameBindingInConstructor() { class C {
diff --git a/test/mjsunit/regress/regress-136048.js b/test/mjsunit/regress/regress-136048.js index c9972e9..21ae622 100644 --- a/test/mjsunit/regress/regress-136048.js +++ b/test/mjsunit/regress/regress-136048.js
@@ -26,9 +26,9 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. try { - /foo/\u0069 + eval("/foo/\\u0069") } catch (e) { assertEquals( - "SyntaxError: Invalid flags supplied to RegExp constructor '\\u0069'", + "SyntaxError: Invalid regular expression flags", e.toString()); }
diff --git a/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc b/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc index 6e4306de..f922d62 100644 --- a/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc +++ b/test/unittests/compiler/arm/instruction-selector-arm-unittest.cc
@@ -1558,6 +1558,150 @@ } +TEST_F(InstructionSelectorTest, Int32AddWithWord32And) { + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add(m.Word32And(p0, m.Int32Constant(0xff)), p1); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmUxtab, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add(p1, m.Word32And(p0, m.Int32Constant(0xff))); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmUxtab, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add(m.Word32And(p0, m.Int32Constant(0xffff)), p1); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmUxtah, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add(p1, m.Word32And(p0, m.Int32Constant(0xffff))); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmUxtah, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } +} + + +TEST_F(InstructionSelectorTest, Int32AddWithWord32SarWithWord32Shl) { + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add( + m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(24)), m.Int32Constant(24)), + p1); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmSxtab, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add( + p1, + m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(24)), m.Int32Constant(24))); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmSxtab, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add( + m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(16)), m.Int32Constant(16)), + p1); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmSxtah, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const r = m.Int32Add( + p1, + m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(16)), m.Int32Constant(16))); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmSxtah, s[0]->arch_opcode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(2))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } +} + + TEST_F(InstructionSelectorTest, Int32SubWithInt32Mul) { StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32, kMachInt32); m.Return( @@ -1943,6 +2087,72 @@ } +TEST_F(InstructionSelectorTest, Word32AndWith0xffff) { + { + StreamBuilder m(this, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const r = m.Word32And(p0, m.Int32Constant(0xffff)); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmUxth, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const r = m.Word32And(m.Int32Constant(0xffff), p0); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmUxth, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } +} + + +TEST_F(InstructionSelectorTest, Word32SarWithWord32Shl) { + { + StreamBuilder m(this, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const r = + m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(24)), m.Int32Constant(24)); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmSxtb, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } + { + StreamBuilder m(this, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const r = + m.Word32Sar(m.Word32Shl(p0, m.Int32Constant(16)), m.Int32Constant(16)); + m.Return(r); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArmSxth, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(0, s.ToInt32(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(r), s.ToVreg(s[0]->Output())); + } +} + + TEST_F(InstructionSelectorTest, Word32ShrWithWord32AndWithImmediateForARMv7) { TRACED_FORRANGE(int32_t, lsb, 0, 31) { TRACED_FORRANGE(int32_t, width, 1, 32 - lsb) {
diff --git a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc index 96b8a83..eee9c58 100644 --- a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc +++ b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc
@@ -1229,6 +1229,76 @@ ::testing::ValuesIn(kShiftInstructions)); +TEST_F(InstructionSelectorTest, Word64ShlWithChangeInt32ToInt64) { + TRACED_FORRANGE(int64_t, x, 32, 63) { + StreamBuilder m(this, kMachInt64, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const n = m.Word64Shl(m.ChangeInt32ToInt64(p0), m.Int64Constant(x)); + m.Return(n); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Lsl, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(x, s.ToInt64(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); + } +} + + +TEST_F(InstructionSelectorTest, Word64ShlWithChangeUint32ToUint64) { + TRACED_FORRANGE(int64_t, x, 32, 63) { + StreamBuilder m(this, kMachInt64, kMachUint32); + Node* const p0 = m.Parameter(0); + Node* const n = m.Word64Shl(m.ChangeUint32ToUint64(p0), m.Int64Constant(x)); + m.Return(n); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Lsl, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(x, s.ToInt64(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(n), s.ToVreg(s[0]->Output())); + } +} + + +TEST_F(InstructionSelectorTest, TruncateInt64ToInt32WithWord64Sar) { + StreamBuilder m(this, kMachInt32, kMachInt64); + Node* const p = m.Parameter(0); + Node* const t = m.TruncateInt64ToInt32(m.Word64Sar(p, m.Int64Constant(32))); + m.Return(t); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Lsr, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(32, s.ToInt64(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(t), s.ToVreg(s[0]->OutputAt(0))); +} + + +TEST_F(InstructionSelectorTest, TruncateInt64ToInt32WithWord64Shr) { + TRACED_FORRANGE(int64_t, x, 32, 63) { + StreamBuilder m(this, kMachInt32, kMachInt64); + Node* const p = m.Parameter(0); + Node* const t = m.TruncateInt64ToInt32(m.Word64Shr(p, m.Int64Constant(x))); + m.Return(t); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kArm64Lsr, s[0]->arch_opcode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(x, s.ToInt64(s[0]->InputAt(1))); + ASSERT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(s.ToVreg(t), s.ToVreg(s[0]->OutputAt(0))); + } +} + + // ----------------------------------------------------------------------------- // Mul and Div instructions.
diff --git a/test/unittests/compiler/node-matchers-unittest.cc b/test/unittests/compiler/node-matchers-unittest.cc new file mode 100644 index 0000000..843a44e --- /dev/null +++ b/test/unittests/compiler/node-matchers-unittest.cc
@@ -0,0 +1,317 @@ +// Copyright 2014 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "src/compiler/common-operator.h" +#include "src/compiler/graph.h" +#include "src/compiler/machine-operator.h" +#include "src/compiler/node.h" +#include "src/compiler/node-matchers.h" +#include "src/compiler/opcodes.h" + +#include "test/unittests/compiler/graph-unittest.h" +#include "test/unittests/test-utils.h" + +namespace v8 { +namespace internal { +namespace compiler { + +class NodeMatcherTest : public GraphTest { + public: + NodeMatcherTest() {} + virtual ~NodeMatcherTest() {} + + MachineOperatorBuilder* machine() { return &machine_; } + + private: + MachineOperatorBuilder machine_; +}; + +namespace { + +void CheckScaledWithOffsetMatch(ScaledWithOffsetMatcher* matcher, Node* scaled, + int scale_exponent, Node* offset, + Node* constant) { + EXPECT_TRUE(matcher->matches()); + EXPECT_EQ(scaled, matcher->scaled()); + EXPECT_EQ(scale_exponent, matcher->scale_exponent()); + EXPECT_EQ(offset, matcher->offset()); + EXPECT_EQ(constant, matcher->constant()); +} +}; + + +TEST_F(NodeMatcherTest, ScaledWithOffsetMatcher) { + graph()->SetStart(graph()->NewNode(common()->Start(0))); + + const Operator* c0_op = common()->Int32Constant(0); + Node* c0 = graph()->NewNode(c0_op); + USE(c0); + const Operator* c1_op = common()->Int32Constant(1); + Node* c1 = graph()->NewNode(c1_op); + USE(c1); + const Operator* c2_op = common()->Int32Constant(2); + Node* c2 = graph()->NewNode(c2_op); + USE(c2); + const Operator* c3_op = common()->Int32Constant(3); + Node* c3 = graph()->NewNode(c3_op); + USE(c3); + const Operator* c4_op = common()->Int32Constant(4); + Node* c4 = graph()->NewNode(c4_op); + USE(c4); + const Operator* c8_op = common()->Int32Constant(8); + Node* c8 = graph()->NewNode(c8_op); + USE(c8); + + const Operator* o0_op = common()->Parameter(0); + Node* o0 = graph()->NewNode(o0_op, graph()->start()); + USE(o0); + const Operator* o1_op = common()->Parameter(1); + Node* o1 = graph()->NewNode(o1_op, graph()->start()); + USE(o0); + + const Operator* p1_op = common()->Parameter(3); + Node* p1 = graph()->NewNode(p1_op, graph()->start()); + USE(p1); + + const Operator* a_op = machine()->Int32Add(); + USE(a_op); + + const Operator* m_op = machine()->Int32Mul(); + Node* m1 = graph()->NewNode(m_op, p1, c1); + Node* m2 = graph()->NewNode(m_op, p1, c2); + Node* m4 = graph()->NewNode(m_op, p1, c4); + Node* m8 = graph()->NewNode(m_op, p1, c8); + Node* m3 = graph()->NewNode(m_op, p1, c3); + + const Operator* s_op = machine()->Word32Shl(); + Node* s0 = graph()->NewNode(s_op, p1, c0); + Node* s1 = graph()->NewNode(s_op, p1, c1); + Node* s2 = graph()->NewNode(s_op, p1, c2); + Node* s3 = graph()->NewNode(s_op, p1, c3); + Node* s4 = graph()->NewNode(s_op, p1, c4); + + // 1 INPUT + + // Only relevant test cases is checking for non-match. + ScaledWithOffsetMatcher match0(c0); + EXPECT_FALSE(match0.matches()); + + // 2 INPUT + + // (O0 + O1) -> [O0, 0, O1, NULL] + ScaledWithOffsetMatcher match1(graph()->NewNode(a_op, o0, o1)); + CheckScaledWithOffsetMatch(&match1, o1, 0, o0, NULL); + + // (O0 + C0) -> [NULL, 0, O0, C0] + ScaledWithOffsetMatcher match2(graph()->NewNode(a_op, o0, c0)); + CheckScaledWithOffsetMatch(&match2, NULL, 0, o0, c0); + + // (C0 + O0) -> [NULL, 0, O0, C0] + ScaledWithOffsetMatcher match3(graph()->NewNode(a_op, c0, o0)); + CheckScaledWithOffsetMatch(&match3, NULL, 0, o0, c0); + + // (O0 + M1) -> [p1, 0, O0, NULL] + ScaledWithOffsetMatcher match4(graph()->NewNode(a_op, o0, m1)); + CheckScaledWithOffsetMatch(&match4, p1, 0, o0, NULL); + + // (M1 + O0) -> [p1, 0, O0, NULL] + m1 = graph()->NewNode(m_op, p1, c1); + ScaledWithOffsetMatcher match5(graph()->NewNode(a_op, m1, o0)); + CheckScaledWithOffsetMatch(&match5, p1, 0, o0, NULL); + + // (C0 + M1) -> [P1, 0, NULL, C0] + m1 = graph()->NewNode(m_op, p1, c1); + ScaledWithOffsetMatcher match6(graph()->NewNode(a_op, c0, m1)); + CheckScaledWithOffsetMatch(&match6, p1, 0, NULL, c0); + + // (M1 + C0) -> [P1, 0, NULL, C0] + m1 = graph()->NewNode(m_op, p1, c1); + ScaledWithOffsetMatcher match7(graph()->NewNode(a_op, m1, c0)); + CheckScaledWithOffsetMatch(&match7, p1, 0, NULL, c0); + + // (O0 + S0) -> [p1, 0, O0, NULL] + ScaledWithOffsetMatcher match8(graph()->NewNode(a_op, o0, s0)); + CheckScaledWithOffsetMatch(&match8, p1, 0, o0, NULL); + + // (S0 + O0) -> [p1, 0, O0, NULL] + s0 = graph()->NewNode(s_op, p1, c0); + ScaledWithOffsetMatcher match9(graph()->NewNode(a_op, s0, o0)); + CheckScaledWithOffsetMatch(&match9, p1, 0, o0, NULL); + + // (C0 + S0) -> [P1, 0, NULL, C0] + s0 = graph()->NewNode(s_op, p1, c0); + ScaledWithOffsetMatcher match10(graph()->NewNode(a_op, c0, s0)); + CheckScaledWithOffsetMatch(&match10, p1, 0, NULL, c0); + + // (S0 + C0) -> [P1, 0, NULL, C0] + s0 = graph()->NewNode(s_op, p1, c0); + ScaledWithOffsetMatcher match11(graph()->NewNode(a_op, s0, c0)); + CheckScaledWithOffsetMatch(&match11, p1, 0, NULL, c0); + + // (O0 + M2) -> [p1, 1, O0, NULL] + ScaledWithOffsetMatcher match12(graph()->NewNode(a_op, o0, m2)); + CheckScaledWithOffsetMatch(&match12, p1, 1, o0, NULL); + + // (M2 + O0) -> [p1, 1, O0, NULL] + m2 = graph()->NewNode(m_op, p1, c2); + ScaledWithOffsetMatcher match13(graph()->NewNode(a_op, m2, o0)); + CheckScaledWithOffsetMatch(&match13, p1, 1, o0, NULL); + + // (C0 + M2) -> [P1, 1, NULL, C0] + m2 = graph()->NewNode(m_op, p1, c2); + ScaledWithOffsetMatcher match14(graph()->NewNode(a_op, c0, m2)); + CheckScaledWithOffsetMatch(&match14, p1, 1, NULL, c0); + + // (M2 + C0) -> [P1, 1, NULL, C0] + m2 = graph()->NewNode(m_op, p1, c2); + ScaledWithOffsetMatcher match15(graph()->NewNode(a_op, m2, c0)); + CheckScaledWithOffsetMatch(&match15, p1, 1, NULL, c0); + + // (O0 + S1) -> [p1, 1, O0, NULL] + ScaledWithOffsetMatcher match16(graph()->NewNode(a_op, o0, s1)); + CheckScaledWithOffsetMatch(&match16, p1, 1, o0, NULL); + + // (S1 + O0) -> [p1, 1, O0, NULL] + s1 = graph()->NewNode(s_op, p1, c1); + ScaledWithOffsetMatcher match17(graph()->NewNode(a_op, s1, o0)); + CheckScaledWithOffsetMatch(&match17, p1, 1, o0, NULL); + + // (C0 + S1) -> [P1, 1, NULL, C0] + s1 = graph()->NewNode(s_op, p1, c1); + ScaledWithOffsetMatcher match18(graph()->NewNode(a_op, c0, s1)); + CheckScaledWithOffsetMatch(&match18, p1, 1, NULL, c0); + + // (S1 + C0) -> [P1, 1, NULL, C0] + s1 = graph()->NewNode(s_op, p1, c1); + ScaledWithOffsetMatcher match19(graph()->NewNode(a_op, s1, c0)); + CheckScaledWithOffsetMatch(&match19, p1, 1, NULL, c0); + + // (O0 + M4) -> [p1, 2, O0, NULL] + ScaledWithOffsetMatcher match20(graph()->NewNode(a_op, o0, m4)); + CheckScaledWithOffsetMatch(&match20, p1, 2, o0, NULL); + + // (M4 + O0) -> [p1, 2, O0, NULL] + m4 = graph()->NewNode(m_op, p1, c4); + ScaledWithOffsetMatcher match21(graph()->NewNode(a_op, m4, o0)); + CheckScaledWithOffsetMatch(&match21, p1, 2, o0, NULL); + + // (C0 + M4) -> [p1, 2, NULL, C0] + m4 = graph()->NewNode(m_op, p1, c4); + ScaledWithOffsetMatcher match22(graph()->NewNode(a_op, c0, m4)); + CheckScaledWithOffsetMatch(&match22, p1, 2, NULL, c0); + + // (M4 + C0) -> [p1, 2, NULL, C0] + m4 = graph()->NewNode(m_op, p1, c4); + ScaledWithOffsetMatcher match23(graph()->NewNode(a_op, m4, c0)); + CheckScaledWithOffsetMatch(&match23, p1, 2, NULL, c0); + + // (O0 + S2) -> [p1, 2, O0, NULL] + ScaledWithOffsetMatcher match24(graph()->NewNode(a_op, o0, s2)); + CheckScaledWithOffsetMatch(&match24, p1, 2, o0, NULL); + + // (S2 + O0) -> [p1, 2, O0, NULL] + s2 = graph()->NewNode(s_op, p1, c2); + ScaledWithOffsetMatcher match25(graph()->NewNode(a_op, s2, o0)); + CheckScaledWithOffsetMatch(&match25, p1, 2, o0, NULL); + + // (C0 + S2) -> [p1, 2, NULL, C0] + s2 = graph()->NewNode(s_op, p1, c2); + ScaledWithOffsetMatcher match26(graph()->NewNode(a_op, c0, s2)); + CheckScaledWithOffsetMatch(&match26, p1, 2, NULL, c0); + + // (S2 + C0) -> [p1, 2, NULL, C0] + s2 = graph()->NewNode(s_op, p1, c2); + ScaledWithOffsetMatcher match27(graph()->NewNode(a_op, s2, c0)); + CheckScaledWithOffsetMatch(&match27, p1, 2, NULL, c0); + + // (O0 + M8) -> [p1, 2, O0, NULL] + ScaledWithOffsetMatcher match28(graph()->NewNode(a_op, o0, m8)); + CheckScaledWithOffsetMatch(&match28, p1, 3, o0, NULL); + + // (M8 + O0) -> [p1, 2, O0, NULL] + m8 = graph()->NewNode(m_op, p1, c8); + ScaledWithOffsetMatcher match29(graph()->NewNode(a_op, m8, o0)); + CheckScaledWithOffsetMatch(&match29, p1, 3, o0, NULL); + + // (C0 + M8) -> [p1, 2, NULL, C0] + m8 = graph()->NewNode(m_op, p1, c8); + ScaledWithOffsetMatcher match30(graph()->NewNode(a_op, c0, m8)); + CheckScaledWithOffsetMatch(&match30, p1, 3, NULL, c0); + + // (M8 + C0) -> [p1, 2, NULL, C0] + m8 = graph()->NewNode(m_op, p1, c8); + ScaledWithOffsetMatcher match31(graph()->NewNode(a_op, m8, c0)); + CheckScaledWithOffsetMatch(&match31, p1, 3, NULL, c0); + + // (O0 + S3) -> [p1, 2, O0, NULL] + ScaledWithOffsetMatcher match32(graph()->NewNode(a_op, o0, s3)); + CheckScaledWithOffsetMatch(&match32, p1, 3, o0, NULL); + + // (S3 + O0) -> [p1, 2, O0, NULL] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match33(graph()->NewNode(a_op, s3, o0)); + CheckScaledWithOffsetMatch(&match33, p1, 3, o0, NULL); + + // (C0 + S3) -> [p1, 2, NULL, C0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match34(graph()->NewNode(a_op, c0, s3)); + CheckScaledWithOffsetMatch(&match34, p1, 3, NULL, c0); + + // (S3 + C0) -> [p1, 2, NULL, C0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match35(graph()->NewNode(a_op, s3, c0)); + CheckScaledWithOffsetMatch(&match35, p1, 3, NULL, c0); + + // 2 INPUT - NEGATIVE CASES + + // (M3 + O1) -> [O0, 0, M3, NULL] + ScaledWithOffsetMatcher match36(graph()->NewNode(a_op, o1, m3)); + CheckScaledWithOffsetMatch(&match36, m3, 0, o1, NULL); + + // (S4 + O1) -> [O0, 0, S4, NULL] + ScaledWithOffsetMatcher match37(graph()->NewNode(a_op, o1, s4)); + CheckScaledWithOffsetMatch(&match37, s4, 0, o1, NULL); + + // 3 INPUT + + // (C0 + S3) + O0 -> [p1, 2, o0, c0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match38( + graph()->NewNode(a_op, graph()->NewNode(a_op, c0, s3), o0)); + CheckScaledWithOffsetMatch(&match38, p1, 3, o0, c0); + + // (O0 + C0) + S3 -> [p1, 2, o0, c0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match39( + graph()->NewNode(a_op, graph()->NewNode(a_op, o0, c0), s3)); + CheckScaledWithOffsetMatch(&match39, p1, 3, o0, c0); + + // (S3 + O0) + C0 -> [p1, 2, o0, c0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match40( + graph()->NewNode(a_op, graph()->NewNode(a_op, s3, o0), c0)); + CheckScaledWithOffsetMatch(&match40, p1, 3, o0, c0); + + // C0 + (S3 + O0) -> [p1, 2, o0, c0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match41( + graph()->NewNode(a_op, c0, graph()->NewNode(a_op, s3, o0))); + CheckScaledWithOffsetMatch(&match41, p1, 3, o0, c0); + + // O0 + (C0 + S3) -> [p1, 2, o0, c0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match42( + graph()->NewNode(a_op, o0, graph()->NewNode(a_op, c0, s3))); + CheckScaledWithOffsetMatch(&match42, p1, 3, o0, c0); + + // S3 + (O0 + C0) -> [p1, 2, o0, c0] + s3 = graph()->NewNode(s_op, p1, c3); + ScaledWithOffsetMatcher match43( + graph()->NewNode(a_op, s3, graph()->NewNode(a_op, o0, c0))); + CheckScaledWithOffsetMatch(&match43, p1, 3, o0, c0); +} + +} // namespace compiler +} // namespace internal +} // namespace v8
diff --git a/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc b/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc index 48c074e..f4070ec 100644 --- a/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc +++ b/test/unittests/compiler/x64/instruction-selector-x64-unittest.cc
@@ -252,10 +252,366 @@ m.Return(m.Int32Add(a0, p0)); Stream s = m.Build(); ASSERT_EQ(2U, s.size()); - EXPECT_EQ(kX64Add32, s[0]->arch_opcode()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); ASSERT_EQ(2U, s[0]->InputCount()); - EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(0))); - EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); +} + + +TEST_F(InstructionSelectorTest, Int32AddConstantAsLea) { + StreamBuilder m(this, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(p0, c0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddCommutedConstantAsLea) { + StreamBuilder m(this, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(c0, p0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MRI, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_TRUE(s[0]->InputAt(1)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2Mul) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + m.Return(m.Int32Add(p0, s0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddCommutedScaled2Mul) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + m.Return(m.Int32Add(s0, p0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2Shl) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Word32Shl(p1, m.Int32Constant(1)); + m.Return(m.Int32Add(p0, s0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddCommutedScaled2Shl) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Word32Shl(p1, m.Int32Constant(1)); + m.Return(m.Int32Add(s0, p0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled4Mul) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(4)); + m.Return(m.Int32Add(p0, s0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR4, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled4Shl) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Word32Shl(p1, m.Int32Constant(2)); + m.Return(m.Int32Add(p0, s0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR4, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled8Mul) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(8)); + m.Return(m.Int32Add(p0, s0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR8, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled8Shl) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Word32Shl(p1, m.Int32Constant(3)); + m.Return(m.Int32Add(p0, s0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR8, s[0]->addressing_mode()); + ASSERT_EQ(2U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstant) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle1) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(p0, m.Int32Add(s0, c0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle2) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(s0, m.Int32Add(c0, p0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle3) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(m.Int32Add(s0, c0), p0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle4) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(m.Int32Add(c0, p0), s0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2MulWithConstantShuffle5) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(2)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(m.Int32Add(p0, s0), c0)); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled2ShlWithConstant) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Word32Shl(p1, m.Int32Constant(1)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR2I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled4MulWithConstant) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(4)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR4I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled4ShlWithConstant) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Word32Shl(p1, m.Int32Constant(2)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR4I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled8MulWithConstant) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Int32Mul(p1, m.Int32Constant(8)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR8I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); +} + + +TEST_F(InstructionSelectorTest, Int32AddScaled8ShlWithConstant) { + StreamBuilder m(this, kMachInt32, kMachInt32, kMachInt32); + Node* const p0 = m.Parameter(0); + Node* const p1 = m.Parameter(1); + Node* const s0 = m.Word32Shl(p1, m.Int32Constant(3)); + Node* const c0 = m.Int32Constant(15); + m.Return(m.Int32Add(c0, m.Int32Add(p0, s0))); + Stream s = m.Build(); + ASSERT_EQ(1U, s.size()); + EXPECT_EQ(kX64Lea32, s[0]->arch_opcode()); + EXPECT_EQ(kMode_MR8I, s[0]->addressing_mode()); + ASSERT_EQ(3U, s[0]->InputCount()); + EXPECT_EQ(s.ToVreg(p0), s.ToVreg(s[0]->InputAt(0))); + EXPECT_EQ(s.ToVreg(p1), s.ToVreg(s[0]->InputAt(1))); + EXPECT_TRUE(s[0]->InputAt(2)->IsImmediate()); }
diff --git a/test/unittests/heap/gc-idle-time-handler-unittest.cc b/test/unittests/heap/gc-idle-time-handler-unittest.cc index 55dd6c6..977882a 100644 --- a/test/unittests/heap/gc-idle-time-handler-unittest.cc +++ b/test/unittests/heap/gc-idle-time-handler-unittest.cc
@@ -22,6 +22,7 @@ GCIdleTimeHandler::HeapState DefaultHeapState() { GCIdleTimeHandler::HeapState result; result.contexts_disposed = 0; + result.contexts_disposal_rate = GCIdleTimeHandler::kHighContextDisposalRate; result.size_of_objects = kSizeOfObjects; result.incremental_marking_stopped = false; result.can_start_incremental_marking = true; @@ -179,10 +180,34 @@ } -TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) { +TEST_F(GCIdleTimeHandlerTest, ContextDisposeLowRate) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.contexts_disposed = 1; heap_state.incremental_marking_stopped = true; + int idle_time_ms = 0; + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_NOTHING, action.type); +} + + +TEST_F(GCIdleTimeHandlerTest, ContextDisposeHighRate) { + GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); + heap_state.contexts_disposed = 1; + heap_state.contexts_disposal_rate = + GCIdleTimeHandler::kHighContextDisposalRate - 1; + heap_state.incremental_marking_stopped = true; + int idle_time_ms = 0; + GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); + EXPECT_EQ(DO_FULL_GC, action.type); +} + + +TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) { + GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); + heap_state.contexts_disposed = 1; + heap_state.contexts_disposal_rate = 1.0; + heap_state.incremental_marking_stopped = true; + heap_state.can_start_incremental_marking = false; size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; int idle_time_ms = static_cast<int>((heap_state.size_of_objects + speed - 1) / speed); @@ -194,8 +219,8 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeZeroIdleTime) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.contexts_disposed = 1; + heap_state.contexts_disposal_rate = 1.0; heap_state.incremental_marking_stopped = true; - heap_state.size_of_objects = GCIdleTimeHandler::kSmallHeapSize / 2; int idle_time_ms = 0; GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state); EXPECT_EQ(DO_FULL_GC, action.type); @@ -205,6 +230,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime1) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.contexts_disposed = 1; + heap_state.contexts_disposal_rate = 1.0; heap_state.incremental_marking_stopped = true; size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1); @@ -216,6 +242,7 @@ TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeSmallIdleTime2) { GCIdleTimeHandler::HeapState heap_state = DefaultHeapState(); heap_state.contexts_disposed = 1; + heap_state.contexts_disposal_rate = 1.0; size_t speed = heap_state.mark_compact_speed_in_bytes_per_ms; int idle_time_ms = static_cast<int>(heap_state.size_of_objects / speed - 1); GCIdleTimeAction action = handler()->Compute(idle_time_ms, heap_state);
diff --git a/test/unittests/unittests.gyp b/test/unittests/unittests.gyp index a881e46..b16d2c5 100644 --- a/test/unittests/unittests.gyp +++ b/test/unittests/unittests.gyp
@@ -49,7 +49,7 @@ 'compiler/js-operator-unittest.cc', 'compiler/js-typed-lowering-unittest.cc', 'compiler/machine-operator-reducer-unittest.cc', - 'compiler/machine-operator-unittest.cc', + 'compiler/node-matchers-unittest.cc', 'compiler/node-test-utils.cc', 'compiler/node-test-utils.h', 'compiler/register-allocator-unittest.cc',
diff --git a/tools/trace-maps-processor.py b/tools/trace-maps-processor.py new file mode 100755 index 0000000..bf8c8a8 --- /dev/null +++ b/tools/trace-maps-processor.py
@@ -0,0 +1,172 @@ +#!/usr/bin/env python +# Copyright 2014 the V8 project authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import sys + + +action = sys.argv[1] + +if action in ["help", "-h", "--help"] or len(sys.argv) != 3: + print("Usage: %s <action> <inputfile>, where action can be: \n" + "help Print this message\n" + "plain Print ASCII tree to stdout\n" + "dot Print dot file to stdout\n" + "count Count most frequent transition reasons\n" % sys.argv[0]) + sys.exit(0) + + +filename = sys.argv[2] +maps = {} +root_maps = [] +transitions = {} +annotations = {} + + +class Map(object): + + def __init__(self, pointer, origin): + self.pointer = pointer + self.origin = origin + + def __str__(self): + return "%s (%s)" % (self.pointer, self.origin) + + +class Transition(object): + + def __init__(self, from_map, to_map, reason): + self.from_map = from_map + self.to_map = to_map + self.reason = reason + + +def RegisterNewMap(raw_map): + if raw_map in annotations: + annotations[raw_map] += 1 + else: + annotations[raw_map] = 0 + return AnnotateExistingMap(raw_map) + + +def AnnotateExistingMap(raw_map): + return "%s_%d" % (raw_map, annotations[raw_map]) + + +def AddMap(pointer, origin): + pointer = RegisterNewMap(pointer) + maps[pointer] = Map(pointer, origin) + return pointer + + +def AddTransition(from_map, to_map, reason): + from_map = AnnotateExistingMap(from_map) + to_map = AnnotateExistingMap(to_map) + if from_map not in transitions: + transitions[from_map] = {} + targets = transitions[from_map] + if to_map in targets: + # Some events get printed twice, that's OK. In some cases, ignore the + # second output... + old_reason = targets[to_map].reason + if old_reason.startswith("ReplaceDescriptors"): + return + # ...and in others use it for additional detail. + if reason in []: + targets[to_map].reason = reason + return + # Unexpected duplicate events? Warn. + print("// warning: already have a transition from %s to %s, reason: %s" % + (from_map, to_map, targets[to_map].reason)) + return + targets[to_map] = Transition(from_map, to_map, reason) + + +with open(filename, "r") as f: + last_to_map = "" + for line in f: + if not line.startswith("[TraceMaps: "): continue + words = line.split(" ") + event = words[1] + if event == "InitialMap": + assert words[2] == "map=" + assert words[4] == "SFI=" + new_map = AddMap(words[3], "SFI#%s" % words[5]) + root_maps.append(new_map) + continue + if words[2] == "from=" and words[4] == "to=": + from_map = words[3] + to_map = words[5] + if from_map not in annotations: + print("// warning: unknown from_map %s" % from_map) + new_map = AddMap(from_map, "<unknown>") + root_maps.append(new_map) + if to_map != last_to_map: + AddMap(to_map, "<transition> (%s)" % event) + last_to_map = to_map + if event in ["Transition", "NoTransition"]: + assert words[6] == "name=", line + reason = "%s: %s" % (event, words[7]) + elif event in ["Normalize", "ReplaceDescriptors", "SlowToFast"]: + assert words[6] == "reason=", line + reason = "%s: %s" % (event, words[7]) + if words[8].strip() != "]": + reason = "%s_%s" % (reason, words[8]) + else: + reason = event + AddTransition(from_map, to_map, reason) + continue + + +def PlainPrint(m, indent, label): + print("%s%s (%s)" % (indent, m, label)) + if m in transitions: + for t in transitions[m]: + PlainPrint(t, indent + " ", transitions[m][t].reason) + + +def CountTransitions(m): + if m not in transitions: return 0 + return len(transitions[m]) + + +def DotPrint(m, label): + print("m%s [label=\"%s\"]" % (m[2:], label)) + if m in transitions: + for t in transitions[m]: + # GraphViz doesn't like node labels looking like numbers, so use + # "m..." instead of "0x...". + print("m%s -> m%s" % (m[2:], t[2:])) + reason = transitions[m][t].reason + reason = reason.replace("\\", "BACKSLASH") + reason = reason.replace("\"", "\\\"") + DotPrint(t, reason) + + +if action == "plain": + root_maps = sorted(root_maps, key=CountTransitions, reverse=True) + for m in root_maps: + PlainPrint(m, "", maps[m].origin) + +elif action == "dot": + print("digraph g {") + for m in root_maps: + DotPrint(m, maps[m].origin) + print("}") + +elif action == "count": + reasons = {} + for s in transitions: + for t in transitions[s]: + reason = transitions[s][t].reason + if reason not in reasons: + reasons[reason] = 1 + else: + reasons[reason] += 1 + reasons_list = [] + for r in reasons: + reasons_list.append("%8d %s" % (reasons[r], r)) + reasons_list.sort(reverse=True) + for r in reasons_list[:20]: + print r