From bdeaf7514a151a37f58aaad227d0d8ebd4b16d27 Mon Sep 17 00:00:00 2001 From: "danno@chromium.org" Date: Wed, 23 Oct 2013 13:48:04 +0000 Subject: [PATCH] Enable preaging of code objects when --optimize-for-size. This change means that code which is never executed is garbage collected immediately, and code which is only executed once is collected more quickly (limiting heap growth), however, code which is re-executed is reset to the young age, thus being kept around for the same number of GC generations as currently. BUG=280984 R=danno@chromium.org, hpayer@chromium.org Review URL: https://codereview.chromium.org/23480031 Patch from Ross McIlroy . git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17343 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/assembler-arm-inl.h | 7 ++++ src/arm/builtins-arm.cc | 33 +++++++++++++++++++ src/arm/full-codegen-arm.cc | 11 +------ src/arm/lithium-codegen-arm.cc | 16 +--------- src/arm/macro-assembler-arm.cc | 27 ++++++++++++++++ src/arm/macro-assembler-arm.h | 2 ++ src/assembler.cc | 7 ++++ src/assembler.h | 2 ++ src/builtins.h | 8 +++++ src/codegen.cc | 4 +-- src/compiler.cc | 2 +- src/compiler.h | 6 ++-- src/factory.cc | 6 ++-- src/factory.h | 3 +- src/frames.h | 7 ++++ src/full-codegen.cc | 2 -- src/heap.cc | 13 ++++++-- src/heap.h | 12 ++++--- src/ia32/assembler-ia32-inl.h | 11 ++++++- src/ia32/assembler-ia32.cc | 3 +- src/ia32/builtins-ia32.cc | 38 ++++++++++++++++++++++ src/ia32/codegen-ia32.cc | 1 - src/ia32/full-codegen-ia32.cc | 5 +-- src/ia32/lithium-codegen-ia32.cc | 9 +----- src/ia32/macro-assembler-ia32.cc | 24 ++++++++++++++ src/ia32/macro-assembler-ia32.h | 3 ++ src/mips/assembler-mips-inl.h | 7 ++++ src/mips/builtins-mips.cc | 43 +++++++++++++++++++++++++ src/mips/full-codegen-mips.cc | 7 +--- src/mips/lithium-codegen-mips.cc | 16 +--------- src/mips/macro-assembler-mips.cc | 34 ++++++++++++++++++++ src/mips/macro-assembler-mips.h | 3 ++ src/objects.cc | 44 ++++++++++++++++++++----- src/objects.h | 14 ++++++-- src/serialize.cc | 4 +++ src/x64/assembler-x64-inl.h | 11 ++++++- src/x64/assembler-x64.cc | 5 ++- src/x64/builtins-x64.cc | 36 +++++++++++++++++++++ src/x64/codegen-x64.cc | 9 ++---- src/x64/full-codegen-x64.cc | 5 +-- src/x64/lithium-codegen-x64.cc | 9 +----- src/x64/macro-assembler-x64.cc | 27 +++++++++++++++- src/x64/macro-assembler-x64.h | 3 ++ test/cctest/test-heap.cc | 69 ++++++++++++++++++++++++++++++++++++++++ 44 files changed, 496 insertions(+), 112 deletions(-) diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h index a1d1e1b..e3b39f4 100644 --- a/src/arm/assembler-arm-inl.h +++ b/src/arm/assembler-arm-inl.h @@ -208,6 +208,13 @@ void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) { static const int kNoCodeAgeSequenceLength = 3; + +Handle RelocInfo::code_age_stub_handle(Assembler* origin) { + UNREACHABLE(); // This should never be reached on Arm. + return Handle(); +} + + Code* RelocInfo::code_age_stub() { ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); return Code::GetCodeFromTargetAddress( diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 13c0e44..60f5290 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -826,6 +826,39 @@ CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR) #undef DEFINE_CODE_AGE_BUILTIN_GENERATOR +void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { + // For now, as in GenerateMakeCodeYoungAgainCommon, we are relying on the fact + // that make_code_young doesn't do any garbage collection which allows us to + // save/restore the registers without worrying about which of them contain + // pointers. + + // The following registers must be saved and restored when calling through to + // the runtime: + // r0 - contains return address (beginning of patch sequence) + // r1 - isolate + FrameScope scope(masm, StackFrame::MANUAL); + __ stm(db_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit()); + __ PrepareCallCFunction(1, 0, r2); + __ mov(r1, Operand(ExternalReference::isolate_address(masm->isolate()))); + __ CallCFunction(ExternalReference::get_mark_code_as_executed_function( + masm->isolate()), 2); + __ ldm(ia_w, sp, r0.bit() | r1.bit() | fp.bit() | lr.bit()); + + // Perform prologue operations usually performed by the young code stub. + __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); + __ add(fp, sp, Operand(2 * kPointerSize)); + + // Jump to point after the code-age stub. + __ add(r0, r0, Operand(kNoCodeAgeSequenceLength * Assembler::kInstrSize)); + __ mov(pc, r0); +} + + +void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { + GenerateMakeCodeYoungAgainCommon(masm); +} + + void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 3becc96..34b8921 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -163,16 +163,7 @@ void FullCodeGenerator::Generate() { FrameScope frame_scope(masm_, StackFrame::MANUAL); info->set_prologue_offset(masm_->pc_offset()); - { - PredictableCodeSizeScope predictible_code_size_scope( - masm_, kNoCodeAgeSequenceLength * Assembler::kInstrSize); - // The following three instructions must remain together and unmodified - // for code aging to work properly. - __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); - __ nop(ip.code()); - // Adjust FP to point to saved FP. - __ add(fp, sp, Operand(2 * kPointerSize)); - } + __ Prologue(BUILD_FUNCTION_FRAME); info->AddNoFrameRange(0, masm_->pc_offset()); { Comment cmnt(masm_, "[ Allocate locals"); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 60e21b7..fc7d0fd 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -133,21 +133,7 @@ bool LCodeGen::GeneratePrologue() { info()->set_prologue_offset(masm_->pc_offset()); if (NeedsEagerFrame()) { - if (info()->IsStub()) { - __ stm(db_w, sp, cp.bit() | fp.bit() | lr.bit()); - __ Push(Smi::FromInt(StackFrame::STUB)); - // Adjust FP to point to saved FP. - __ add(fp, sp, Operand(2 * kPointerSize)); - } else { - PredictableCodeSizeScope predictible_code_size_scope( - masm_, kNoCodeAgeSequenceLength * Assembler::kInstrSize); - // The following three instructions must remain together and unmodified - // for code aging to work properly. - __ stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); - __ nop(ip.code()); - // Adjust FP to point to saved FP. - __ add(fp, sp, Operand(2 * kPointerSize)); - } + __ Prologue(info()->IsStub() ? BUILD_STUB_FRAME : BUILD_FUNCTION_FRAME); frame_is_built_ = true; info_->AddNoFrameRange(0, masm_->pc_offset()); } diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 7ca61d2..a149dee 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -916,6 +916,33 @@ void MacroAssembler::LoadNumberAsInt32(Register object, } +void MacroAssembler::Prologue(PrologueFrameMode frame_mode) { + if (frame_mode == BUILD_STUB_FRAME) { + stm(db_w, sp, cp.bit() | fp.bit() | lr.bit()); + Push(Smi::FromInt(StackFrame::STUB)); + // Adjust FP to point to saved FP. + add(fp, sp, Operand(2 * kPointerSize)); + } else { + PredictableCodeSizeScope predictible_code_size_scope( + this, kNoCodeAgeSequenceLength * Assembler::kInstrSize); + // The following three instructions must remain together and unmodified + // for code aging to work properly. + if (FLAG_optimize_for_size && FLAG_age_code) { + // Pre-age the code. + Code* stub = Code::GetPreAgedCodeAgeStub(isolate()); + add(r0, pc, Operand(-8)); + ldr(pc, MemOperand(pc, -4)); + dd(reinterpret_cast(stub->instruction_start())); + } else { + stm(db_w, sp, r1.bit() | cp.bit() | fp.bit() | lr.bit()); + nop(ip.code()); + // Adjust FP to point to saved FP. + add(fp, sp, Operand(2 * kPointerSize)); + } + } +} + + void MacroAssembler::EnterFrame(StackFrame::Type type) { // r0-r3: preserved stm(db_w, sp, cp.bit() | fp.bit() | lr.bit()); diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index c709e5c..7cf5d9a 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -539,6 +539,8 @@ class MacroAssembler: public Assembler { LowDwVfpRegister double_scratch1, Label* not_int32); + // Generates function and stub prologue code. + void Prologue(PrologueFrameMode frame_mode); // Enter exit frame. // stack_space - extra stack space, used for alignment before call to C. diff --git a/src/assembler.cc b/src/assembler.cc index 0a049a4..9ed4360 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -1088,6 +1088,13 @@ ExternalReference ExternalReference::get_make_code_young_function( } +ExternalReference ExternalReference::get_mark_code_as_executed_function( + Isolate* isolate) { + return ExternalReference(Redirect( + isolate, FUNCTION_ADDR(Code::MarkCodeAsExecuted))); +} + + ExternalReference ExternalReference::date_cache_stamp(Isolate* isolate) { return ExternalReference(isolate->date_cache()->stamp_address()); } diff --git a/src/assembler.h b/src/assembler.h index e34b13a..f0b7fed 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -401,6 +401,7 @@ class RelocInfo BASE_EMBEDDED { INLINE(Handle target_cell_handle()); INLINE(void set_target_cell(Cell* cell, WriteBarrierMode mode = UPDATE_WRITE_BARRIER)); + INLINE(Handle code_age_stub_handle(Assembler* origin)); INLINE(Code* code_age_stub()); INLINE(void set_code_age_stub(Code* stub)); @@ -727,6 +728,7 @@ class ExternalReference BASE_EMBEDDED { static ExternalReference date_cache_stamp(Isolate* isolate); static ExternalReference get_make_code_young_function(Isolate* isolate); + static ExternalReference get_mark_code_as_executed_function(Isolate* isolate); // New heap objects tracking support. static ExternalReference record_object_allocation_function(Isolate* isolate); diff --git a/src/builtins.h b/src/builtins.h index 7364878..c1c8a5d 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -204,6 +204,11 @@ enum BuiltinExtraArguments { Code::kNoExtraICState) \ V(StackCheck, BUILTIN, UNINITIALIZED, \ Code::kNoExtraICState) \ + \ + V(MarkCodeAsExecutedOnce, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ + V(MarkCodeAsExecutedTwice, BUILTIN, UNINITIALIZED, \ + Code::kNoExtraICState) \ CODE_AGE_LIST_WITH_ARG(DECLARE_CODE_AGE_BUILTIN, V) // Define list of builtin handlers implemented in assembly. @@ -413,6 +418,9 @@ class Builtins { CODE_AGE_LIST(DECLARE_CODE_AGE_BUILTIN_GENERATOR) #undef DECLARE_CODE_AGE_BUILTIN_GENERATOR + static void Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm); + static void Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm); + static void InitBuiltinFunctionTable(); bool initialized_; diff --git a/src/codegen.cc b/src/codegen.cc index 7b2f81b..573ddc6 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -113,12 +113,12 @@ Handle CodeGenerator::MakeCodeEpilogue(MacroAssembler* masm, masm->GetCode(&desc); Handle code = isolate->factory()->NewCode(desc, flags, masm->CodeObject(), - false, is_crankshafted); + false, is_crankshafted, + info->prologue_offset()); isolate->counters()->total_compiled_code_size()->Increment( code->instruction_size()); isolate->heap()->IncrementCodeGeneratedBytes(is_crankshafted, code->instruction_size()); - code->set_prologue_offset(info->prologue_offset()); return code; } diff --git a/src/compiler.cc b/src/compiler.cc index 1ce6dab..5e4d17b 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -112,7 +112,7 @@ void CompilationInfo::Initialize(Isolate* isolate, zone_ = zone; deferred_handles_ = NULL; code_stub_ = NULL; - prologue_offset_ = kPrologueOffsetNotSet; + prologue_offset_ = Code::kPrologueOffsetNotSet; opt_count_ = shared_info().is_null() ? 0 : shared_info()->opt_count(); no_frame_ranges_ = isolate->cpu_profiler()->is_profiling() ? new List(2) : NULL; diff --git a/src/compiler.h b/src/compiler.h index d166c2a..2d9e52a 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -35,8 +35,6 @@ namespace v8 { namespace internal { -static const int kPrologueOffsetNotSet = -1; - class ScriptDataImpl; class HydrogenCodeStub; @@ -269,12 +267,12 @@ class CompilationInfo { void set_bailout_reason(BailoutReason reason) { bailout_reason_ = reason; } int prologue_offset() const { - ASSERT_NE(kPrologueOffsetNotSet, prologue_offset_); + ASSERT_NE(Code::kPrologueOffsetNotSet, prologue_offset_); return prologue_offset_; } void set_prologue_offset(int prologue_offset) { - ASSERT_EQ(kPrologueOffsetNotSet, prologue_offset_); + ASSERT_EQ(Code::kPrologueOffsetNotSet, prologue_offset_); prologue_offset_ = prologue_offset; } diff --git a/src/factory.cc b/src/factory.cc index 4927cac..1dd246f 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -1015,10 +1015,12 @@ Handle Factory::NewCode(const CodeDesc& desc, Code::Flags flags, Handle self_ref, bool immovable, - bool crankshafted) { + bool crankshafted, + int prologue_offset) { CALL_HEAP_FUNCTION(isolate(), isolate()->heap()->CreateCode( - desc, flags, self_ref, immovable, crankshafted), + desc, flags, self_ref, immovable, crankshafted, + prologue_offset), Code); } diff --git a/src/factory.h b/src/factory.h index 68980c5..ee25bf2 100644 --- a/src/factory.h +++ b/src/factory.h @@ -380,7 +380,8 @@ class Factory { Code::Flags flags, Handle self_reference, bool immovable = false, - bool crankshafted = false); + bool crankshafted = false, + int prologue_offset = Code::kPrologueOffsetNotSet); Handle CopyCode(Handle code); diff --git a/src/frames.h b/src/frames.h index 2bbbd98..d2dbfe2 100644 --- a/src/frames.h +++ b/src/frames.h @@ -922,6 +922,13 @@ class StackFrameLocator BASE_EMBEDDED { }; +// Used specify the type of prologue to generate. +enum PrologueFrameMode { + BUILD_FUNCTION_FRAME, + BUILD_STUB_FRAME +}; + + // Reads all frames on the current stack and copies them into the current // zone memory. Vector CreateStackMap(Isolate* isolate, Zone* zone); diff --git a/src/full-codegen.cc b/src/full-codegen.cc index f87cf88..fec9ee5 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -345,8 +345,6 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) { code->set_has_deoptimization_support(info->HasDeoptimizationSupport()); code->set_handler_table(*cgen.handler_table()); #ifdef ENABLE_DEBUGGER_SUPPORT - code->set_has_debug_break_slots( - info->isolate()->debugger()->IsDebuggerActive()); code->set_compiled_optimizable(info->IsOptimizable()); #endif // ENABLE_DEBUGGER_SUPPORT code->set_allow_osr_at_loop_nesting_level(0); diff --git a/src/heap.cc b/src/heap.cc index 2e46048..1c55c60 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -4140,7 +4140,8 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc, Code::Flags flags, Handle self_reference, bool immovable, - bool crankshafted) { + bool crankshafted, + int prologue_offset) { // Allocate ByteArray before the Code object, so that we do not risk // leaving uninitialized Code object (and breaking the heap). ByteArray* reloc_info; @@ -4190,10 +4191,18 @@ MaybeObject* Heap::CreateCode(const CodeDesc& desc, code->set_handler_table(empty_fixed_array(), SKIP_WRITE_BARRIER); code->set_gc_metadata(Smi::FromInt(0)); code->set_ic_age(global_ic_age_); - code->set_prologue_offset(kPrologueOffsetNotSet); + code->set_prologue_offset(prologue_offset); if (code->kind() == Code::OPTIMIZED_FUNCTION) { code->set_marked_for_deoptimization(false); } + +#ifdef ENABLE_DEBUGGER_SUPPORT + if (code->kind() == Code::FUNCTION) { + code->set_has_debug_break_slots( + isolate_->debugger()->IsDebuggerActive()); + } +#endif + // Allow self references to created code object by patching the handle to // point to the newly allocated Code object. if (!self_reference.is_null()) { diff --git a/src/heap.h b/src/heap.h index 0bd2c40..92e8089 100644 --- a/src/heap.h +++ b/src/heap.h @@ -1125,11 +1125,13 @@ class Heap { // self_reference. This allows generated code to reference its own Code // object by containing this pointer. // Please note this function does not perform a garbage collection. - MUST_USE_RESULT MaybeObject* CreateCode(const CodeDesc& desc, - Code::Flags flags, - Handle self_reference, - bool immovable = false, - bool crankshafted = false); + MUST_USE_RESULT MaybeObject* CreateCode( + const CodeDesc& desc, + Code::Flags flags, + Handle self_reference, + bool immovable = false, + bool crankshafted = false, + int prologue_offset = Code::kPrologueOffsetNotSet); MUST_USE_RESULT MaybeObject* CopyCode(Code* code); diff --git a/src/ia32/assembler-ia32-inl.h b/src/ia32/assembler-ia32-inl.h index 5a35b20..05cc23a 100644 --- a/src/ia32/assembler-ia32-inl.h +++ b/src/ia32/assembler-ia32-inl.h @@ -47,6 +47,7 @@ namespace internal { static const byte kCallOpcode = 0xE8; +static const int kNoCodeAgeSequenceLength = 5; // The modes possibly affected by apply must be in kApplyMask. @@ -190,6 +191,13 @@ void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) { } +Handle RelocInfo::code_age_stub_handle(Assembler* origin) { + ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); + ASSERT(*pc_ == kCallOpcode); + return Memory::Object_Handle_at(pc_ + 1); +} + + Code* RelocInfo::code_age_stub() { ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); ASSERT(*pc_ == kCallOpcode); @@ -379,7 +387,8 @@ void Assembler::emit(Handle handle) { void Assembler::emit(uint32_t x, RelocInfo::Mode rmode, TypeFeedbackId id) { if (rmode == RelocInfo::CODE_TARGET && !id.IsNone()) { RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, id.ToInt()); - } else if (!RelocInfo::IsNone(rmode)) { + } else if (!RelocInfo::IsNone(rmode) + && rmode != RelocInfo::CODE_AGE_SEQUENCE) { RecordRelocInfo(rmode); } emit(x); diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc index 8cf73c3..cfeaca6 100644 --- a/src/ia32/assembler-ia32.cc +++ b/src/ia32/assembler-ia32.cc @@ -1414,7 +1414,8 @@ void Assembler::call(Handle code, TypeFeedbackId ast_id) { positions_recorder()->WriteRecordedPositions(); EnsureSpace ensure_space(this); - ASSERT(RelocInfo::IsCodeTarget(rmode)); + ASSERT(RelocInfo::IsCodeTarget(rmode) + || rmode == RelocInfo::CODE_AGE_SEQUENCE); EMIT(0xE8); emit(code, rmode, ast_id); } diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 24a2e6e..e5e6ec5 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -563,6 +563,44 @@ CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR) #undef DEFINE_CODE_AGE_BUILTIN_GENERATOR +void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { + // For now, as in GenerateMakeCodeYoungAgainCommon, we are relying on the fact + // that make_code_young doesn't do any garbage collection which allows us to + // save/restore the registers without worrying about which of them contain + // pointers. + __ pushad(); + __ mov(eax, Operand(esp, 8 * kPointerSize)); + __ sub(eax, Immediate(Assembler::kCallInstructionLength)); + { // NOLINT + FrameScope scope(masm, StackFrame::MANUAL); + __ PrepareCallCFunction(2, ebx); + __ mov(Operand(esp, 1 * kPointerSize), + Immediate(ExternalReference::isolate_address(masm->isolate()))); + __ mov(Operand(esp, 0), eax); + __ CallCFunction( + ExternalReference::get_mark_code_as_executed_function(masm->isolate()), + 2); + } + __ popad(); + + // Perform prologue operations usually performed by the young code stub. + __ pop(eax); // Pop return address into scratch register. + __ push(ebp); // Caller's frame pointer. + __ mov(ebp, esp); + __ push(esi); // Callee's context. + __ push(edi); // Callee's JS Function. + __ push(eax); // Push return address after frame prologue. + + // Jump to point after the code-age stub. + __ ret(0); +} + + +void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { + GenerateMakeCodeYoungAgainCommon(masm); +} + + void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) { // Enter an internal frame. { diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index da88146..226b6b0 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -1117,7 +1117,6 @@ void MathExpGenerator::EmitMathExp(MacroAssembler* masm, #undef __ -static const int kNoCodeAgeSequenceLength = 5; static byte* GetNoCodeAgeSequence(uint32_t* length) { static bool initialized = false; diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 725a6f8..704fb4e 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -158,10 +158,7 @@ void FullCodeGenerator::Generate() { FrameScope frame_scope(masm_, StackFrame::MANUAL); info->set_prologue_offset(masm_->pc_offset()); - __ push(ebp); // Caller's frame pointer. - __ mov(ebp, esp); - __ push(esi); // Callee's context. - __ push(edi); // Callee's JS Function. + __ Prologue(BUILD_FUNCTION_FRAME); info->AddNoFrameRange(0, masm_->pc_offset()); { Comment cmnt(masm_, "[ Allocate locals"); diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index c231fe1..ebeaaa8 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -188,15 +188,8 @@ bool LCodeGen::GeneratePrologue() { if (NeedsEagerFrame()) { ASSERT(!frame_is_built_); frame_is_built_ = true; - __ push(ebp); // Caller's frame pointer. - __ mov(ebp, esp); + __ Prologue(info()->IsStub() ? BUILD_STUB_FRAME : BUILD_FUNCTION_FRAME); info()->AddNoFrameRange(0, masm_->pc_offset()); - __ push(esi); // Callee's context. - if (info()->IsStub()) { - __ push(Immediate(Smi::FromInt(StackFrame::STUB))); - } else { - __ push(edi); // Callee's JS function. - } } if (info()->IsOptimizing() && diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index 805861e..ed69fd0 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -1013,6 +1013,30 @@ void MacroAssembler::AssertNotSmi(Register object) { } +void MacroAssembler::Prologue(PrologueFrameMode frame_mode) { + if (frame_mode == BUILD_STUB_FRAME) { + push(ebp); // Caller's frame pointer. + mov(ebp, esp); + push(esi); // Callee's context. + push(Immediate(Smi::FromInt(StackFrame::STUB))); + } else { + PredictableCodeSizeScope predictible_code_size_scope(this, + kNoCodeAgeSequenceLength); + if (FLAG_optimize_for_size && FLAG_age_code) { + // Pre-age the code. + call(isolate()->builtins()->MarkCodeAsExecutedOnce(), + RelocInfo::CODE_AGE_SEQUENCE); + Nop(kNoCodeAgeSequenceLength - Assembler::kCallInstructionLength); + } else { + push(ebp); // Caller's frame pointer. + mov(ebp, esp); + push(esi); // Callee's context. + push(edi); // Callee's JS function. + } + } +} + + void MacroAssembler::EnterFrame(StackFrame::Type type) { push(ebp); mov(ebp, esp); diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index a6d782e..30f8a8d 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -225,6 +225,9 @@ class MacroAssembler: public Assembler { void DebugBreak(); #endif + // Generates function and stub prologue code. + void Prologue(PrologueFrameMode frame_mode); + // Enter specific kind of exit frame. Expects the number of // arguments in register eax and sets up the number of arguments in // register edi and the pointer to the first argument in register diff --git a/src/mips/assembler-mips-inl.h b/src/mips/assembler-mips-inl.h index 2fa6804..de91051 100644 --- a/src/mips/assembler-mips-inl.h +++ b/src/mips/assembler-mips-inl.h @@ -261,6 +261,13 @@ void RelocInfo::set_target_cell(Cell* cell, WriteBarrierMode mode) { static const int kNoCodeAgeSequenceLength = 7; + +Handle RelocInfo::code_age_stub_handle(Assembler* origin) { + UNREACHABLE(); // This should never be reached on Arm. + return Handle(); +} + + Code* RelocInfo::code_age_stub() { ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); return Code::GetCodeFromTargetAddress( diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index ed7764b..0b49583 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -857,6 +857,49 @@ CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR) #undef DEFINE_CODE_AGE_BUILTIN_GENERATOR +void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { + // For now, as in GenerateMakeCodeYoungAgainCommon, we are relying on the fact + // that make_code_young doesn't do any garbage collection which allows us to + // save/restore the registers without worrying about which of them contain + // pointers. + + __ mov(a0, ra); + // Adjust a0 to point to the head of the PlatformCodeAge sequence + __ Subu(a0, a0, + Operand((kNoCodeAgeSequenceLength - 1) * Assembler::kInstrSize)); + // Restore the original return address of the function + __ mov(ra, at); + + // The following registers must be saved and restored when calling through to + // the runtime: + // a0 - contains return address (beginning of patch sequence) + // a1 - isolate + RegList saved_regs = + (a0.bit() | a1.bit() | ra.bit() | fp.bit()) & ~sp.bit(); + FrameScope scope(masm, StackFrame::MANUAL); + __ MultiPush(saved_regs); + __ PrepareCallCFunction(1, 0, a2); + __ li(a1, Operand(ExternalReference::isolate_address(masm->isolate()))); + __ CallCFunction( + ExternalReference::get_mark_code_as_executed_function(masm->isolate()), + 2); + __ MultiPop(saved_regs); + + // Perform prologue operations usually performed by the young code stub. + __ Push(ra, fp, cp, a1); + __ Addu(fp, sp, Operand(2 * kPointerSize)); + + // Jump to point after the code-age stub. + __ Addu(a0, a0, Operand((kNoCodeAgeSequenceLength) * Assembler::kInstrSize)); + __ Jump(a0); +} + + +void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { + GenerateMakeCodeYoungAgainCommon(masm); +} + + void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) { { FrameScope scope(masm, StackFrame::INTERNAL); diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index b9e282f..cbd0788 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -171,12 +171,7 @@ void FullCodeGenerator::Generate() { FrameScope frame_scope(masm_, StackFrame::MANUAL); info->set_prologue_offset(masm_->pc_offset()); - // The following three instructions must remain together and unmodified for - // code aging to work properly. - __ Push(ra, fp, cp, a1); - __ nop(Assembler::CODE_AGE_SEQUENCE_NOP); - // Adjust fp to point to caller's fp. - __ Addu(fp, sp, Operand(2 * kPointerSize)); + __ Prologue(BUILD_FUNCTION_FRAME); info->AddNoFrameRange(0, masm_->pc_offset()); { Comment cmnt(masm_, "[ Allocate locals"); diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index 2c6e5a5..f54d4a5 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -133,21 +133,7 @@ bool LCodeGen::GeneratePrologue() { info()->set_prologue_offset(masm_->pc_offset()); if (NeedsEagerFrame()) { - if (info()->IsStub()) { - __ Push(ra, fp, cp); - __ Push(Smi::FromInt(StackFrame::STUB)); - // Adjust FP to point to saved FP. - __ Addu(fp, sp, Operand(2 * kPointerSize)); - } else { - // The following three instructions must remain together and unmodified - // for code aging to work properly. - __ Push(ra, fp, cp, a1); - // Add unused nop to ensure prologue sequence is identical for - // full-codegen and lithium-codegen. - __ nop(Assembler::CODE_AGE_SEQUENCE_NOP); - // Adj. FP to point to saved FP. - __ Addu(fp, sp, Operand(2 * kPointerSize)); - } + __ Prologue(info()->IsStub() ? BUILD_STUB_FRAME : BUILD_FUNCTION_FRAME); frame_is_built_ = true; info_->AddNoFrameRange(0, masm_->pc_offset()); } diff --git a/src/mips/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index e42ba6c..52d8a4c 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -4585,6 +4585,40 @@ void MacroAssembler::LoadNumberAsInt32(Register object, } +void MacroAssembler::Prologue(PrologueFrameMode frame_mode) { + if (frame_mode == BUILD_STUB_FRAME) { + Push(ra, fp, cp); + Push(Smi::FromInt(StackFrame::STUB)); + // Adjust FP to point to saved FP. + Addu(fp, sp, Operand(2 * kPointerSize)); + } else { + PredictableCodeSizeScope predictible_code_size_scope( + this, kNoCodeAgeSequenceLength * Assembler::kInstrSize); + // The following three instructions must remain together and unmodified + // for code aging to work properly. + if (FLAG_optimize_for_size && FLAG_age_code) { + // Pre-age the code. + Code* stub = Code::GetPreAgedCodeAgeStub(isolate()); + nop(Assembler::CODE_AGE_MARKER_NOP); + // Save the function's original return address + // (it will be clobbered by Call(t9)) + mov(at, ra); + // Load the stub address to t9 and call it + li(t9, + Operand(reinterpret_cast(stub->instruction_start()))); + Call(t9); + // Record the stub address in the empty space for GetCodeAgeAndParity() + dd(reinterpret_cast(stub->instruction_start())); + } else { + Push(ra, fp, cp, a1); + nop(Assembler::CODE_AGE_SEQUENCE_NOP); + // Adjust fp to point to caller's fp. + Addu(fp, sp, Operand(2 * kPointerSize)); + } + } +} + + void MacroAssembler::EnterFrame(StackFrame::Type type) { addiu(sp, sp, -5 * kPointerSize); li(t8, Operand(Smi::FromInt(type))); diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h index 84e60ba..0805bb9 100644 --- a/src/mips/macro-assembler-mips.h +++ b/src/mips/macro-assembler-mips.h @@ -1498,6 +1498,9 @@ class MacroAssembler: public Assembler { And(reg, reg, Operand(mask)); } + // Generates function and stub prologue code. + void Prologue(PrologueFrameMode frame_mode); + // Activation support. void EnterFrame(StackFrame::Type type); void LeaveFrame(StackFrame::Type type); diff --git a/src/objects.cc b/src/objects.cc index e0f160b..2889014 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -10384,6 +10384,10 @@ void Code::CopyFrom(const CodeDesc& desc) { } else if (RelocInfo::IsRuntimeEntry(mode)) { Address p = it.rinfo()->target_runtime_entry(origin); it.rinfo()->set_target_runtime_entry(p, SKIP_WRITE_BARRIER); + } else if (mode == RelocInfo::CODE_AGE_SEQUENCE) { + Handle p = it.rinfo()->code_age_stub_handle(origin); + Code* code = Code::cast(*p); + it.rinfo()->set_code_age_stub(code); } else { it.rinfo()->apply(delta); } @@ -10625,6 +10629,12 @@ void Code::MakeCodeAgeSequenceYoung(byte* sequence, Isolate* isolate) { } +void Code::MarkCodeAsExecuted(byte* sequence, Isolate* isolate) { + PatchPlatformCodeAge(isolate, sequence, kExecutedOnceCodeAge, + NO_MARKING_PARITY); +} + + void Code::MakeOlder(MarkingParity current_parity) { byte* sequence = FindCodeAgeSequence(); if (sequence != NULL) { @@ -10642,18 +10652,14 @@ void Code::MakeOlder(MarkingParity current_parity) { bool Code::IsOld() { - byte* sequence = FindCodeAgeSequence(); - if (sequence == NULL) return false; - Age age; - MarkingParity parity; - GetCodeAgeAndParity(sequence, &age, &parity); - return age >= kSexagenarianCodeAge; + Age age = GetAge(); + return age >= kIsOldCodeAge; } byte* Code::FindCodeAgeSequence() { return FLAG_age_code && - prologue_offset() != kPrologueOffsetNotSet && + prologue_offset() != Code::kPrologueOffsetNotSet && (kind() == OPTIMIZED_FUNCTION || (kind() == FUNCTION && !has_debug_break_slots())) ? instruction_start() + prologue_offset() @@ -10661,7 +10667,7 @@ byte* Code::FindCodeAgeSequence() { } -int Code::GetAge() { +Code::Age Code::GetAge() { byte* sequence = FindCodeAgeSequence(); if (sequence == NULL) { return Code::kNoAge; @@ -10693,6 +10699,20 @@ void Code::GetCodeAgeAndParity(Code* code, Age* age, } CODE_AGE_LIST(HANDLE_CODE_AGE) #undef HANDLE_CODE_AGE + stub = *builtins->MarkCodeAsExecutedOnce(); + if (code == stub) { + // Treat that's never been executed as old immediatly. + *age = kIsOldCodeAge; + *parity = NO_MARKING_PARITY; + return; + } + stub = *builtins->MarkCodeAsExecutedTwice(); + if (code == stub) { + // Pre-age code that has only been executed once. + *age = kPreAgedCodeAge; + *parity = NO_MARKING_PARITY; + return; + } UNREACHABLE(); } @@ -10709,6 +10729,14 @@ Code* Code::GetCodeAgeStub(Isolate* isolate, Age age, MarkingParity parity) { } CODE_AGE_LIST(HANDLE_CODE_AGE) #undef HANDLE_CODE_AGE + case kNotExecutedCodeAge: { + ASSERT(parity == NO_MARKING_PARITY); + return *builtins->MarkCodeAsExecutedOnce(); + } + case kExecutedOnceCodeAge: { + ASSERT(parity == NO_MARKING_PARITY); + return *builtins->MarkCodeAsExecutedTwice(); + } default: UNREACHABLE(); break; diff --git a/src/objects.h b/src/objects.h index ec78d27..1dfa737 100644 --- a/src/objects.h +++ b/src/objects.h @@ -4978,6 +4978,8 @@ class Code: public HeapObject { static const ExtraICState kNoExtraICState = 0; + static const int kPrologueOffsetNotSet = -1; + #ifdef ENABLE_DISASSEMBLER // Printing static const char* ICState2String(InlineCacheState state); @@ -5297,11 +5299,15 @@ class Code: public HeapObject { #define DECLARE_CODE_AGE_ENUM(X) k##X##CodeAge, enum Age { + kNotExecutedCodeAge = -2, + kExecutedOnceCodeAge = -1, kNoAge = 0, CODE_AGE_LIST(DECLARE_CODE_AGE_ENUM) kAfterLastCodeAge, kLastCodeAge = kAfterLastCodeAge - 1, - kCodeAgeCount = kAfterLastCodeAge - 1 + kCodeAgeCount = kAfterLastCodeAge - 1, + kIsOldCodeAge = kSexagenarianCodeAge, + kPreAgedCodeAge = kIsOldCodeAge - 1 }; #undef DECLARE_CODE_AGE_ENUM @@ -5310,10 +5316,14 @@ class Code: public HeapObject { // relatively safe to flush this code object and replace it with the lazy // compilation stub. static void MakeCodeAgeSequenceYoung(byte* sequence, Isolate* isolate); + static void MarkCodeAsExecuted(byte* sequence, Isolate* isolate); void MakeOlder(MarkingParity); static bool IsYoungSequence(byte* sequence); bool IsOld(); - int GetAge(); + Age GetAge(); + static inline Code* GetPreAgedCodeAgeStub(Isolate* isolate) { + return GetCodeAgeStub(isolate, kNotExecutedCodeAge, NO_MARKING_PARITY); + } void PrintDeoptLocation(int bailout_id); bool CanDeoptAt(Address pc); diff --git a/src/serialize.cc b/src/serialize.cc index b536163..b3b63fe 100644 --- a/src/serialize.cc +++ b/src/serialize.cc @@ -589,6 +589,10 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) { UNCLASSIFIED, 65, "uint32_bias"); + Add(ExternalReference::get_mark_code_as_executed_function(isolate).address(), + UNCLASSIFIED, + 66, + "Code::MarkCodeAsExecuted"); // Add a small set of deopt entry addresses to encoder without generating the // deopt table code, which isn't possible at deserialization time. diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h index 07d0703..4887899 100644 --- a/src/x64/assembler-x64-inl.h +++ b/src/x64/assembler-x64-inl.h @@ -43,6 +43,7 @@ namespace internal { static const byte kCallOpcode = 0xE8; +static const int kNoCodeAgeSequenceLength = 6; void Assembler::emitl(uint32_t x) { @@ -79,7 +80,8 @@ void Assembler::emitw(uint16_t x) { void Assembler::emit_code_target(Handle target, RelocInfo::Mode rmode, TypeFeedbackId ast_id) { - ASSERT(RelocInfo::IsCodeTarget(rmode)); + ASSERT(RelocInfo::IsCodeTarget(rmode) || + rmode == RelocInfo::CODE_AGE_SEQUENCE); if (rmode == RelocInfo::CODE_TARGET && !ast_id.IsNone()) { RecordRelocInfo(RelocInfo::CODE_TARGET_WITH_ID, ast_id.ToInt()); } else { @@ -392,6 +394,13 @@ bool RelocInfo::IsPatchedDebugBreakSlotSequence() { } +Handle RelocInfo::code_age_stub_handle(Assembler* origin) { + ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); + ASSERT(*pc_ == kCallOpcode); + return origin->code_target_object_handle_at(pc_ + 1); +} + + Code* RelocInfo::code_age_stub() { ASSERT(rmode_ == RelocInfo::CODE_AGE_SEQUENCE); ASSERT(*pc_ == kCallOpcode); diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc index a92196d..4989c1e 100644 --- a/src/x64/assembler-x64.cc +++ b/src/x64/assembler-x64.cc @@ -3004,8 +3004,8 @@ void Assembler::dd(uint32_t data) { void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { ASSERT(!RelocInfo::IsNone(rmode)); - // Don't record external references unless the heap will be serialized. if (rmode == RelocInfo::EXTERNAL_REFERENCE) { + // Don't record external references unless the heap will be serialized. #ifdef DEBUG if (!Serializer::enabled()) { Serializer::TooLateToEnableNow(); @@ -3014,6 +3014,9 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { if (!Serializer::enabled() && !emit_debug_code()) { return; } + } else if (rmode == RelocInfo::CODE_AGE_SEQUENCE) { + // Don't record psuedo relocation info for code age sequence mode. + return; } RelocInfo rinfo(pc_, rmode, data, NULL); reloc_info_writer.Write(&rinfo); diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index df6acb6..2f5e0c1 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -627,6 +627,42 @@ CODE_AGE_LIST(DEFINE_CODE_AGE_BUILTIN_GENERATOR) #undef DEFINE_CODE_AGE_BUILTIN_GENERATOR +void Builtins::Generate_MarkCodeAsExecutedOnce(MacroAssembler* masm) { + // For now, as in GenerateMakeCodeYoungAgainCommon, we are relying on the fact + // that make_code_young doesn't do any garbage collection which allows us to + // save/restore the registers without worrying about which of them contain + // pointers. + __ Pushad(); + __ movq(arg_reg_2, ExternalReference::isolate_address(masm->isolate())); + __ movq(arg_reg_1, Operand(rsp, kNumSafepointRegisters * kPointerSize)); + __ subq(arg_reg_1, Immediate(Assembler::kShortCallInstructionLength)); + { // NOLINT + FrameScope scope(masm, StackFrame::MANUAL); + __ PrepareCallCFunction(1); + __ CallCFunction( + ExternalReference::get_mark_code_as_executed_function(masm->isolate()), + 1); + } + __ Popad(); + + // Perform prologue operations usually performed by the young code stub. + __ pop(r10); // Pop return address into scratch register. + __ push(rbp); // Caller's frame pointer. + __ movq(rbp, rsp); + __ push(rsi); // Callee's context. + __ push(rdi); // Callee's JS Function. + __ push(r10); // Push return address after frame prologue. + + // Jump to point after the code-age stub. + __ ret(0); +} + + +void Builtins::Generate_MarkCodeAsExecutedTwice(MacroAssembler* masm) { + GenerateMakeCodeYoungAgainCommon(masm); +} + + void Builtins::Generate_NotifyStubFailure(MacroAssembler* masm) { // Enter an internal frame. { diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 5373827..2002608 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -675,8 +675,6 @@ void MathExpGenerator::EmitMathExp(MacroAssembler* masm, #undef __ -static const int kNoCodeAgeSequenceLength = 6; - static byte* GetNoCodeAgeSequence(uint32_t* length) { static bool initialized = false; static byte sequence[kNoCodeAgeSequenceLength]; @@ -733,11 +731,8 @@ void Code::PatchPlatformCodeAge(Isolate* isolate, Code* stub = GetCodeAgeStub(isolate, age, parity); CodePatcher patcher(sequence, young_length); patcher.masm()->call(stub->instruction_start()); - for (int i = 0; - i < kNoCodeAgeSequenceLength - Assembler::kShortCallInstructionLength; - i++) { - patcher.masm()->nop(); - } + patcher.masm()->Nop( + kNoCodeAgeSequenceLength - Assembler::kShortCallInstructionLength); } } diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index f9ac84d..02ba67b 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -152,10 +152,7 @@ void FullCodeGenerator::Generate() { FrameScope frame_scope(masm_, StackFrame::MANUAL); info->set_prologue_offset(masm_->pc_offset()); - __ push(rbp); // Caller's frame pointer. - __ movq(rbp, rsp); - __ push(rsi); // Callee's context. - __ push(rdi); // Callee's JS Function. + __ Prologue(BUILD_FUNCTION_FRAME); info->AddNoFrameRange(0, masm_->pc_offset()); { Comment cmnt(masm_, "[ Allocate locals"); diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index ea885f0..6c8e377 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -143,14 +143,7 @@ bool LCodeGen::GeneratePrologue() { if (NeedsEagerFrame()) { ASSERT(!frame_is_built_); frame_is_built_ = true; - __ push(rbp); // Caller's frame pointer. - __ movq(rbp, rsp); - __ push(rsi); // Callee's context. - if (info()->IsStub()) { - __ Push(Smi::FromInt(StackFrame::STUB)); - } else { - __ push(rdi); // Callee's JS function. - } + __ Prologue(info()->IsStub() ? BUILD_STUB_FRAME : BUILD_FUNCTION_FRAME); info()->AddNoFrameRange(0, masm_->pc_offset()); } diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index 98d07fe..c4d0ffa 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -2635,7 +2635,8 @@ void MacroAssembler::Call(Handle code_object, #ifdef DEBUG int end_position = pc_offset() + CallSize(code_object); #endif - ASSERT(RelocInfo::IsCodeTarget(rmode)); + ASSERT(RelocInfo::IsCodeTarget(rmode) || + rmode == RelocInfo::CODE_AGE_SEQUENCE); call(code_object, rmode, ast_id); #ifdef DEBUG CHECK_EQ(end_position, pc_offset()); @@ -3649,6 +3650,30 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected, } +void MacroAssembler::Prologue(PrologueFrameMode frame_mode) { + if (frame_mode == BUILD_STUB_FRAME) { + push(rbp); // Caller's frame pointer. + movq(rbp, rsp); + push(rsi); // Callee's context. + Push(Smi::FromInt(StackFrame::STUB)); + } else { + PredictableCodeSizeScope predictible_code_size_scope(this, + kNoCodeAgeSequenceLength); + if (FLAG_optimize_for_size && FLAG_age_code) { + // Pre-age the code. + Call(isolate()->builtins()->MarkCodeAsExecutedOnce(), + RelocInfo::CODE_AGE_SEQUENCE); + Nop(kNoCodeAgeSequenceLength - Assembler::kShortCallInstructionLength); + } else { + push(rbp); // Caller's frame pointer. + movq(rbp, rsp); + push(rsi); // Callee's context. + push(rdi); // Callee's JS function. + } + } +} + + void MacroAssembler::EnterFrame(StackFrame::Type type) { push(rbp); movq(rbp, rsp); diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 238513a..df0ac5d 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -282,6 +282,9 @@ class MacroAssembler: public Assembler { void DebugBreak(); #endif + // Generates function and stub prologue code. + void Prologue(PrologueFrameMode frame_mode); + // Enter specific kind of exit frame; either in normal or // debug mode. Expects the number of arguments in register rax and // sets up the number of arguments in register rdi and the pointer diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc index 9f4ad46..74c2b75 100644 --- a/test/cctest/test-heap.cc +++ b/test/cctest/test-heap.cc @@ -1031,6 +1031,7 @@ TEST(TestCodeFlushing) { // If we do not flush code this test is invalid. if (!FLAG_flush_code) return; i::FLAG_allow_natives_syntax = true; + i::FLAG_optimize_for_size = false; CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); @@ -1076,10 +1077,76 @@ TEST(TestCodeFlushing) { } +TEST(TestCodeFlushingPreAged) { + // If we do not flush code this test is invalid. + if (!FLAG_flush_code) return; + i::FLAG_allow_natives_syntax = true; + i::FLAG_optimize_for_size = true; + CcTest::InitializeVM(); + Isolate* isolate = Isolate::Current(); + Factory* factory = isolate->factory(); + v8::HandleScope scope(CcTest::isolate()); + const char* source = "function foo() {" + " var x = 42;" + " var y = 42;" + " var z = x + y;" + "};" + "foo()"; + Handle foo_name = factory->InternalizeUtf8String("foo"); + + // Compile foo, but don't run it. + { v8::HandleScope scope(CcTest::isolate()); + CompileRun(source); + } + + // Check function is compiled. + Object* func_value = Isolate::Current()->context()->global_object()-> + GetProperty(*foo_name)->ToObjectChecked(); + CHECK(func_value->IsJSFunction()); + Handle function(JSFunction::cast(func_value)); + CHECK(function->shared()->is_compiled()); + + // The code has been run so will survive at least one GC. + CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + CHECK(function->shared()->is_compiled()); + + // The code was only run once, so it should be pre-aged and collected on the + // next GC. + CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + CHECK(!function->shared()->is_compiled() || function->IsOptimized()); + + // Execute the function again twice, and ensure it is reset to the young age. + { v8::HandleScope scope(CcTest::isolate()); + CompileRun("foo();" + "foo();"); + } + + // The code will survive at least two GC now that it is young again. + CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + CHECK(function->shared()->is_compiled()); + + // Simulate several GCs that use full marking. + const int kAgingThreshold = 6; + for (int i = 0; i < kAgingThreshold; i++) { + CcTest::heap()->CollectAllGarbage(Heap::kAbortIncrementalMarkingMask); + } + + // foo should no longer be in the compilation cache + CHECK(!function->shared()->is_compiled() || function->IsOptimized()); + CHECK(!function->is_compiled() || function->IsOptimized()); + // Call foo to get it recompiled. + CompileRun("foo()"); + CHECK(function->shared()->is_compiled()); + CHECK(function->is_compiled()); +} + + TEST(TestCodeFlushingIncremental) { // If we do not flush code this test is invalid. if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; i::FLAG_allow_natives_syntax = true; + i::FLAG_optimize_for_size = false; CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); @@ -1148,6 +1215,7 @@ TEST(TestCodeFlushingIncrementalScavenge) { // If we do not flush code this test is invalid. if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; i::FLAG_allow_natives_syntax = true; + i::FLAG_optimize_for_size = false; CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); @@ -1216,6 +1284,7 @@ TEST(TestCodeFlushingIncrementalAbort) { // If we do not flush code this test is invalid. if (!FLAG_flush_code || !FLAG_flush_code_incrementally) return; i::FLAG_allow_natives_syntax = true; + i::FLAG_optimize_for_size = false; CcTest::InitializeVM(); Isolate* isolate = CcTest::i_isolate(); Factory* factory = isolate->factory(); -- 2.7.4