| // Copyright 2013 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/crankshaft/lithium-codegen.h" |
| |
| #include <sstream> |
| |
| #if V8_TARGET_ARCH_IA32 |
| #include "src/crankshaft/ia32/lithium-ia32.h" // NOLINT |
| #include "src/crankshaft/ia32/lithium-codegen-ia32.h" // NOLINT |
| #elif V8_TARGET_ARCH_X64 |
| #include "src/crankshaft/x64/lithium-x64.h" // NOLINT |
| #include "src/crankshaft/x64/lithium-codegen-x64.h" // NOLINT |
| #elif V8_TARGET_ARCH_ARM |
| #include "src/crankshaft/arm/lithium-arm.h" // NOLINT |
| #include "src/crankshaft/arm/lithium-codegen-arm.h" // NOLINT |
| #elif V8_TARGET_ARCH_ARM64 |
| #include "src/crankshaft/arm64/lithium-arm64.h" // NOLINT |
| #include "src/crankshaft/arm64/lithium-codegen-arm64.h" // NOLINT |
| #elif V8_TARGET_ARCH_MIPS |
| #include "src/crankshaft/mips/lithium-mips.h" // NOLINT |
| #include "src/crankshaft/mips/lithium-codegen-mips.h" // NOLINT |
| #elif V8_TARGET_ARCH_MIPS64 |
| #include "src/crankshaft/mips64/lithium-mips64.h" // NOLINT |
| #include "src/crankshaft/mips64/lithium-codegen-mips64.h" // NOLINT |
| #elif V8_TARGET_ARCH_X87 |
| #include "src/crankshaft/x87/lithium-x87.h" // NOLINT |
| #include "src/crankshaft/x87/lithium-codegen-x87.h" // NOLINT |
| #elif V8_TARGET_ARCH_PPC |
| #include "src/crankshaft/ppc/lithium-ppc.h" // NOLINT |
| #include "src/crankshaft/ppc/lithium-codegen-ppc.h" // NOLINT |
| #elif V8_TARGET_ARCH_S390 |
| #include "src/crankshaft/s390/lithium-s390.h" // NOLINT |
| #include "src/crankshaft/s390/lithium-codegen-s390.h" // NOLINT |
| #else |
| #error Unsupported target architecture. |
| #endif |
| |
| namespace v8 { |
| namespace internal { |
| |
| |
| HGraph* LCodeGenBase::graph() const { |
| return chunk()->graph(); |
| } |
| |
| |
| LCodeGenBase::LCodeGenBase(LChunk* chunk, MacroAssembler* assembler, |
| CompilationInfo* info) |
| : chunk_(static_cast<LPlatformChunk*>(chunk)), |
| masm_(assembler), |
| info_(info), |
| zone_(info->zone()), |
| status_(UNUSED), |
| current_block_(-1), |
| current_instruction_(-1), |
| instructions_(chunk->instructions()), |
| deoptimizations_(4, info->zone()), |
| deoptimization_literals_(8, info->zone()), |
| translations_(info->zone()), |
| inlined_function_count_(0), |
| last_lazy_deopt_pc_(0), |
| osr_pc_offset_(-1) {} |
| |
| |
| bool LCodeGenBase::GenerateBody() { |
| DCHECK(is_generating()); |
| bool emit_instructions = true; |
| LCodeGen* codegen = static_cast<LCodeGen*>(this); |
| for (current_instruction_ = 0; |
| !is_aborted() && current_instruction_ < instructions_->length(); |
| current_instruction_++) { |
| LInstruction* instr = instructions_->at(current_instruction_); |
| |
| // Don't emit code for basic blocks with a replacement. |
| if (instr->IsLabel()) { |
| emit_instructions = !LLabel::cast(instr)->HasReplacement() && |
| (!FLAG_unreachable_code_elimination || |
| instr->hydrogen_value()->block()->IsReachable()); |
| if (FLAG_code_comments && !emit_instructions) { |
| Comment( |
| ";;; <@%d,#%d> -------------------- B%d (unreachable/replaced) " |
| "--------------------", |
| current_instruction_, |
| instr->hydrogen_value()->id(), |
| instr->hydrogen_value()->block()->block_id()); |
| } |
| } |
| if (!emit_instructions) continue; |
| |
| if (FLAG_code_comments && instr->HasInterestingComment(codegen)) { |
| Comment(";;; <@%d,#%d> %s", |
| current_instruction_, |
| instr->hydrogen_value()->id(), |
| instr->Mnemonic()); |
| } |
| |
| GenerateBodyInstructionPre(instr); |
| |
| HValue* value = instr->hydrogen_value(); |
| if (!value->position().IsUnknown()) { |
| RecordAndWritePosition( |
| chunk()->graph()->SourcePositionToScriptPosition(value->position())); |
| } |
| |
| instr->CompileToNative(codegen); |
| |
| GenerateBodyInstructionPost(instr); |
| } |
| EnsureSpaceForLazyDeopt(Deoptimizer::patch_size()); |
| last_lazy_deopt_pc_ = masm()->pc_offset(); |
| return !is_aborted(); |
| } |
| |
| |
| void LCodeGenBase::CheckEnvironmentUsage() { |
| #ifdef DEBUG |
| bool dead_block = false; |
| for (int i = 0; i < instructions_->length(); i++) { |
| LInstruction* instr = instructions_->at(i); |
| HValue* hval = instr->hydrogen_value(); |
| if (instr->IsLabel()) dead_block = LLabel::cast(instr)->HasReplacement(); |
| if (dead_block || !hval->block()->IsReachable()) continue; |
| |
| HInstruction* hinstr = HInstruction::cast(hval); |
| if (!hinstr->CanDeoptimize() && instr->HasEnvironment()) { |
| V8_Fatal(__FILE__, __LINE__, "CanDeoptimize is wrong for %s (%s)", |
| hinstr->Mnemonic(), instr->Mnemonic()); |
| } |
| |
| if (instr->HasEnvironment() && !instr->environment()->has_been_used()) { |
| V8_Fatal(__FILE__, __LINE__, "unused environment for %s (%s)", |
| hinstr->Mnemonic(), instr->Mnemonic()); |
| } |
| } |
| #endif |
| } |
| |
| |
| void LCodeGenBase::Comment(const char* format, ...) { |
| if (!FLAG_code_comments) return; |
| char buffer[4 * KB]; |
| StringBuilder builder(buffer, arraysize(buffer)); |
| va_list arguments; |
| va_start(arguments, format); |
| builder.AddFormattedList(format, arguments); |
| va_end(arguments); |
| |
| // Copy the string before recording it in the assembler to avoid |
| // issues when the stack allocated buffer goes out of scope. |
| size_t length = builder.position(); |
| Vector<char> copy = Vector<char>::New(static_cast<int>(length) + 1); |
| MemCopy(copy.start(), builder.Finalize(), copy.length()); |
| masm()->RecordComment(copy.start()); |
| } |
| |
| |
| void LCodeGenBase::DeoptComment(const Deoptimizer::DeoptInfo& deopt_info) { |
| SourcePosition position = deopt_info.position; |
| int raw_position = position.IsUnknown() ? 0 : position.raw(); |
| masm()->RecordDeoptReason(deopt_info.deopt_reason, raw_position); |
| } |
| |
| |
| int LCodeGenBase::GetNextEmittedBlock() const { |
| for (int i = current_block_ + 1; i < graph()->blocks()->length(); ++i) { |
| if (!graph()->blocks()->at(i)->IsReachable()) continue; |
| if (!chunk_->GetLabel(i)->HasReplacement()) return i; |
| } |
| return -1; |
| } |
| |
| |
| void LCodeGenBase::Abort(BailoutReason reason) { |
| info()->AbortOptimization(reason); |
| status_ = ABORTED; |
| } |
| |
| |
| void LCodeGenBase::Retry(BailoutReason reason) { |
| info()->RetryOptimization(reason); |
| status_ = ABORTED; |
| } |
| |
| |
| void LCodeGenBase::AddDeprecationDependency(Handle<Map> map) { |
| if (map->is_deprecated()) return Retry(kMapBecameDeprecated); |
| chunk_->AddDeprecationDependency(map); |
| } |
| |
| |
| void LCodeGenBase::AddStabilityDependency(Handle<Map> map) { |
| if (!map->is_stable()) return Retry(kMapBecameUnstable); |
| chunk_->AddStabilityDependency(map); |
| } |
| |
| |
| int LCodeGenBase::DefineDeoptimizationLiteral(Handle<Object> literal) { |
| int result = deoptimization_literals_.length(); |
| for (int i = 0; i < deoptimization_literals_.length(); ++i) { |
| if (deoptimization_literals_[i].is_identical_to(literal)) return i; |
| } |
| deoptimization_literals_.Add(literal, zone()); |
| return result; |
| } |
| |
| |
| void LCodeGenBase::WriteTranslationFrame(LEnvironment* environment, |
| Translation* translation) { |
| int translation_size = environment->translation_size(); |
| // The output frame height does not include the parameters. |
| int height = translation_size - environment->parameter_count(); |
| |
| switch (environment->frame_type()) { |
| case JS_FUNCTION: { |
| int shared_id = DefineDeoptimizationLiteral( |
| environment->entry() ? environment->entry()->shared() |
| : info()->shared_info()); |
| translation->BeginJSFrame(environment->ast_id(), shared_id, height); |
| if (info()->closure().is_identical_to(environment->closure())) { |
| translation->StoreJSFrameFunction(); |
| } else { |
| int closure_id = DefineDeoptimizationLiteral(environment->closure()); |
| translation->StoreLiteral(closure_id); |
| } |
| break; |
| } |
| case JS_CONSTRUCT: { |
| int shared_id = DefineDeoptimizationLiteral( |
| environment->entry() ? environment->entry()->shared() |
| : info()->shared_info()); |
| translation->BeginConstructStubFrame(shared_id, translation_size); |
| if (info()->closure().is_identical_to(environment->closure())) { |
| translation->StoreJSFrameFunction(); |
| } else { |
| int closure_id = DefineDeoptimizationLiteral(environment->closure()); |
| translation->StoreLiteral(closure_id); |
| } |
| break; |
| } |
| case JS_GETTER: { |
| DCHECK_EQ(1, translation_size); |
| DCHECK_EQ(0, height); |
| int shared_id = DefineDeoptimizationLiteral( |
| environment->entry() ? environment->entry()->shared() |
| : info()->shared_info()); |
| translation->BeginGetterStubFrame(shared_id); |
| if (info()->closure().is_identical_to(environment->closure())) { |
| translation->StoreJSFrameFunction(); |
| } else { |
| int closure_id = DefineDeoptimizationLiteral(environment->closure()); |
| translation->StoreLiteral(closure_id); |
| } |
| break; |
| } |
| case JS_SETTER: { |
| DCHECK_EQ(2, translation_size); |
| DCHECK_EQ(0, height); |
| int shared_id = DefineDeoptimizationLiteral( |
| environment->entry() ? environment->entry()->shared() |
| : info()->shared_info()); |
| translation->BeginSetterStubFrame(shared_id); |
| if (info()->closure().is_identical_to(environment->closure())) { |
| translation->StoreJSFrameFunction(); |
| } else { |
| int closure_id = DefineDeoptimizationLiteral(environment->closure()); |
| translation->StoreLiteral(closure_id); |
| } |
| break; |
| } |
| case TAIL_CALLER_FUNCTION: { |
| DCHECK_EQ(0, translation_size); |
| int shared_id = DefineDeoptimizationLiteral( |
| environment->entry() ? environment->entry()->shared() |
| : info()->shared_info()); |
| translation->BeginTailCallerFrame(shared_id); |
| if (info()->closure().is_identical_to(environment->closure())) { |
| translation->StoreJSFrameFunction(); |
| } else { |
| int closure_id = DefineDeoptimizationLiteral(environment->closure()); |
| translation->StoreLiteral(closure_id); |
| } |
| break; |
| } |
| case ARGUMENTS_ADAPTOR: { |
| int shared_id = DefineDeoptimizationLiteral( |
| environment->entry() ? environment->entry()->shared() |
| : info()->shared_info()); |
| translation->BeginArgumentsAdaptorFrame(shared_id, translation_size); |
| if (info()->closure().is_identical_to(environment->closure())) { |
| translation->StoreJSFrameFunction(); |
| } else { |
| int closure_id = DefineDeoptimizationLiteral(environment->closure()); |
| translation->StoreLiteral(closure_id); |
| } |
| break; |
| } |
| case STUB: |
| translation->BeginCompiledStubFrame(translation_size); |
| break; |
| } |
| } |
| |
| |
| void LCodeGenBase::PopulateDeoptimizationData(Handle<Code> code) { |
| int length = deoptimizations_.length(); |
| if (length == 0) return; |
| Handle<DeoptimizationInputData> data = |
| DeoptimizationInputData::New(isolate(), length, TENURED); |
| |
| Handle<ByteArray> translations = |
| translations_.CreateByteArray(isolate()->factory()); |
| data->SetTranslationByteArray(*translations); |
| data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_)); |
| data->SetOptimizationId(Smi::FromInt(info_->optimization_id())); |
| if (info_->IsOptimizing()) { |
| // Reference to shared function info does not change between phases. |
| AllowDeferredHandleDereference allow_handle_dereference; |
| data->SetSharedFunctionInfo(*info_->shared_info()); |
| } else { |
| data->SetSharedFunctionInfo(Smi::FromInt(0)); |
| } |
| data->SetWeakCellCache(Smi::FromInt(0)); |
| |
| Handle<FixedArray> literals = |
| factory()->NewFixedArray(deoptimization_literals_.length(), TENURED); |
| { |
| AllowDeferredHandleDereference copy_handles; |
| for (int i = 0; i < deoptimization_literals_.length(); i++) { |
| literals->set(i, *deoptimization_literals_[i]); |
| } |
| data->SetLiteralArray(*literals); |
| } |
| |
| data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id().ToInt())); |
| data->SetOsrPcOffset(Smi::FromInt(osr_pc_offset_)); |
| |
| // Populate the deoptimization entries. |
| for (int i = 0; i < length; i++) { |
| LEnvironment* env = deoptimizations_[i]; |
| data->SetAstId(i, env->ast_id()); |
| data->SetTranslationIndex(i, Smi::FromInt(env->translation_index())); |
| data->SetArgumentsStackHeight(i, |
| Smi::FromInt(env->arguments_stack_height())); |
| data->SetPc(i, Smi::FromInt(env->pc_offset())); |
| } |
| code->set_deoptimization_data(*data); |
| } |
| |
| |
| void LCodeGenBase::PopulateDeoptimizationLiteralsWithInlinedFunctions() { |
| DCHECK_EQ(0, deoptimization_literals_.length()); |
| for (Handle<SharedFunctionInfo> function : chunk()->inlined_functions()) { |
| DefineDeoptimizationLiteral(function); |
| } |
| inlined_function_count_ = deoptimization_literals_.length(); |
| |
| // Define deoptimization literals for all unoptimized code objects of inlined |
| // functions. This ensures unoptimized code is kept alive by optimized code. |
| AllowDeferredHandleDereference allow_shared_function_info_dereference; |
| for (Handle<SharedFunctionInfo> function : chunk()->inlined_functions()) { |
| DefineDeoptimizationLiteral(handle(function->code())); |
| } |
| } |
| |
| |
| Deoptimizer::DeoptInfo LCodeGenBase::MakeDeoptInfo( |
| LInstruction* instr, Deoptimizer::DeoptReason deopt_reason) { |
| Deoptimizer::DeoptInfo deopt_info(instr->hydrogen_value()->position(), |
| instr->Mnemonic(), deopt_reason); |
| HEnterInlined* enter_inlined = instr->environment()->entry(); |
| deopt_info.inlining_id = enter_inlined ? enter_inlined->inlining_id() : 0; |
| return deopt_info; |
| } |
| } // namespace internal |
| } // namespace v8 |