1// Copyright 2013 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/crankshaft/lithium-codegen.h"
6
7#include <sstream>
8
9#if V8_TARGET_ARCH_IA32
10#include "src/crankshaft/ia32/lithium-ia32.h"  // NOLINT
11#include "src/crankshaft/ia32/lithium-codegen-ia32.h"  // NOLINT
12#elif V8_TARGET_ARCH_X64
13#include "src/crankshaft/x64/lithium-x64.h"  // NOLINT
14#include "src/crankshaft/x64/lithium-codegen-x64.h"  // NOLINT
15#elif V8_TARGET_ARCH_ARM
16#include "src/crankshaft/arm/lithium-arm.h"  // NOLINT
17#include "src/crankshaft/arm/lithium-codegen-arm.h"  // NOLINT
18#elif V8_TARGET_ARCH_ARM64
19#include "src/crankshaft/arm64/lithium-arm64.h"  // NOLINT
20#include "src/crankshaft/arm64/lithium-codegen-arm64.h"  // NOLINT
21#elif V8_TARGET_ARCH_MIPS
22#include "src/crankshaft/mips/lithium-mips.h"  // NOLINT
23#include "src/crankshaft/mips/lithium-codegen-mips.h"  // NOLINT
24#elif V8_TARGET_ARCH_MIPS64
25#include "src/crankshaft/mips64/lithium-mips64.h"  // NOLINT
26#include "src/crankshaft/mips64/lithium-codegen-mips64.h"  // NOLINT
27#elif V8_TARGET_ARCH_X87
28#include "src/crankshaft/x87/lithium-x87.h"  // NOLINT
29#include "src/crankshaft/x87/lithium-codegen-x87.h"  // NOLINT
30#elif V8_TARGET_ARCH_PPC
31#include "src/crankshaft/ppc/lithium-ppc.h"          // NOLINT
32#include "src/crankshaft/ppc/lithium-codegen-ppc.h"  // NOLINT
33#else
34#error Unsupported target architecture.
35#endif
36
37namespace v8 {
38namespace internal {
39
40
41HGraph* LCodeGenBase::graph() const {
42  return chunk()->graph();
43}
44
45
46LCodeGenBase::LCodeGenBase(LChunk* chunk, MacroAssembler* assembler,
47                           CompilationInfo* info)
48    : chunk_(static_cast<LPlatformChunk*>(chunk)),
49      masm_(assembler),
50      info_(info),
51      zone_(info->zone()),
52      status_(UNUSED),
53      current_block_(-1),
54      current_instruction_(-1),
55      instructions_(chunk->instructions()),
56      deoptimizations_(4, info->zone()),
57      deoptimization_literals_(8, info->zone()),
58      translations_(info->zone()),
59      inlined_function_count_(0),
60      last_lazy_deopt_pc_(0),
61      osr_pc_offset_(-1) {}
62
63
64bool LCodeGenBase::GenerateBody() {
65  DCHECK(is_generating());
66  bool emit_instructions = true;
67  LCodeGen* codegen = static_cast<LCodeGen*>(this);
68  for (current_instruction_ = 0;
69       !is_aborted() && current_instruction_ < instructions_->length();
70       current_instruction_++) {
71    LInstruction* instr = instructions_->at(current_instruction_);
72
73    // Don't emit code for basic blocks with a replacement.
74    if (instr->IsLabel()) {
75      emit_instructions = !LLabel::cast(instr)->HasReplacement() &&
76          (!FLAG_unreachable_code_elimination ||
77           instr->hydrogen_value()->block()->IsReachable());
78      if (FLAG_code_comments && !emit_instructions) {
79        Comment(
80            ";;; <@%d,#%d> -------------------- B%d (unreachable/replaced) "
81            "--------------------",
82            current_instruction_,
83            instr->hydrogen_value()->id(),
84            instr->hydrogen_value()->block()->block_id());
85      }
86    }
87    if (!emit_instructions) continue;
88
89    if (FLAG_code_comments && instr->HasInterestingComment(codegen)) {
90      Comment(";;; <@%d,#%d> %s",
91              current_instruction_,
92              instr->hydrogen_value()->id(),
93              instr->Mnemonic());
94    }
95
96    GenerateBodyInstructionPre(instr);
97
98    HValue* value = instr->hydrogen_value();
99    if (!value->position().IsUnknown()) {
100      RecordAndWritePosition(
101        chunk()->graph()->SourcePositionToScriptPosition(value->position()));
102    }
103
104    instr->CompileToNative(codegen);
105
106    GenerateBodyInstructionPost(instr);
107  }
108  EnsureSpaceForLazyDeopt(Deoptimizer::patch_size());
109  last_lazy_deopt_pc_ = masm()->pc_offset();
110  return !is_aborted();
111}
112
113
114void LCodeGenBase::CheckEnvironmentUsage() {
115#ifdef DEBUG
116  bool dead_block = false;
117  for (int i = 0; i < instructions_->length(); i++) {
118    LInstruction* instr = instructions_->at(i);
119    HValue* hval = instr->hydrogen_value();
120    if (instr->IsLabel()) dead_block = LLabel::cast(instr)->HasReplacement();
121    if (dead_block || !hval->block()->IsReachable()) continue;
122
123    HInstruction* hinstr = HInstruction::cast(hval);
124    if (!hinstr->CanDeoptimize() && instr->HasEnvironment()) {
125      V8_Fatal(__FILE__, __LINE__, "CanDeoptimize is wrong for %s (%s)",
126               hinstr->Mnemonic(), instr->Mnemonic());
127    }
128
129    if (instr->HasEnvironment() && !instr->environment()->has_been_used()) {
130      V8_Fatal(__FILE__, __LINE__, "unused environment for %s (%s)",
131               hinstr->Mnemonic(), instr->Mnemonic());
132    }
133  }
134#endif
135}
136
137
138void LCodeGenBase::Comment(const char* format, ...) {
139  if (!FLAG_code_comments) return;
140  char buffer[4 * KB];
141  StringBuilder builder(buffer, arraysize(buffer));
142  va_list arguments;
143  va_start(arguments, format);
144  builder.AddFormattedList(format, arguments);
145  va_end(arguments);
146
147  // Copy the string before recording it in the assembler to avoid
148  // issues when the stack allocated buffer goes out of scope.
149  size_t length = builder.position();
150  Vector<char> copy = Vector<char>::New(static_cast<int>(length) + 1);
151  MemCopy(copy.start(), builder.Finalize(), copy.length());
152  masm()->RecordComment(copy.start());
153}
154
155
156void LCodeGenBase::DeoptComment(const Deoptimizer::DeoptInfo& deopt_info) {
157  masm()->RecordDeoptReason(deopt_info.deopt_reason, deopt_info.position);
158}
159
160
161int LCodeGenBase::GetNextEmittedBlock() const {
162  for (int i = current_block_ + 1; i < graph()->blocks()->length(); ++i) {
163    if (!graph()->blocks()->at(i)->IsReachable()) continue;
164    if (!chunk_->GetLabel(i)->HasReplacement()) return i;
165  }
166  return -1;
167}
168
169
170void LCodeGenBase::Abort(BailoutReason reason) {
171  info()->AbortOptimization(reason);
172  status_ = ABORTED;
173}
174
175
176void LCodeGenBase::Retry(BailoutReason reason) {
177  info()->RetryOptimization(reason);
178  status_ = ABORTED;
179}
180
181
182void LCodeGenBase::AddDeprecationDependency(Handle<Map> map) {
183  if (map->is_deprecated()) return Retry(kMapBecameDeprecated);
184  chunk_->AddDeprecationDependency(map);
185}
186
187
188void LCodeGenBase::AddStabilityDependency(Handle<Map> map) {
189  if (!map->is_stable()) return Retry(kMapBecameUnstable);
190  chunk_->AddStabilityDependency(map);
191}
192
193
194int LCodeGenBase::DefineDeoptimizationLiteral(Handle<Object> literal) {
195  int result = deoptimization_literals_.length();
196  for (int i = 0; i < deoptimization_literals_.length(); ++i) {
197    if (deoptimization_literals_[i].is_identical_to(literal)) return i;
198  }
199  deoptimization_literals_.Add(literal, zone());
200  return result;
201}
202
203
204void LCodeGenBase::WriteTranslationFrame(LEnvironment* environment,
205                                         Translation* translation) {
206  int translation_size = environment->translation_size();
207  // The output frame height does not include the parameters.
208  int height = translation_size - environment->parameter_count();
209
210  switch (environment->frame_type()) {
211    case JS_FUNCTION: {
212      int shared_id = DefineDeoptimizationLiteral(
213          environment->entry() ? environment->entry()->shared()
214                               : info()->shared_info());
215      translation->BeginJSFrame(environment->ast_id(), shared_id, height);
216      if (info()->closure().is_identical_to(environment->closure())) {
217        translation->StoreJSFrameFunction();
218      } else {
219        int closure_id = DefineDeoptimizationLiteral(environment->closure());
220        translation->StoreLiteral(closure_id);
221      }
222      break;
223    }
224    case JS_CONSTRUCT: {
225      int shared_id = DefineDeoptimizationLiteral(
226          environment->entry() ? environment->entry()->shared()
227                               : info()->shared_info());
228      translation->BeginConstructStubFrame(shared_id, translation_size);
229      if (info()->closure().is_identical_to(environment->closure())) {
230        translation->StoreJSFrameFunction();
231      } else {
232        int closure_id = DefineDeoptimizationLiteral(environment->closure());
233        translation->StoreLiteral(closure_id);
234      }
235      break;
236    }
237    case JS_GETTER: {
238      DCHECK(translation_size == 1);
239      DCHECK(height == 0);
240      int shared_id = DefineDeoptimizationLiteral(
241          environment->entry() ? environment->entry()->shared()
242                               : info()->shared_info());
243      translation->BeginGetterStubFrame(shared_id);
244      if (info()->closure().is_identical_to(environment->closure())) {
245        translation->StoreJSFrameFunction();
246      } else {
247        int closure_id = DefineDeoptimizationLiteral(environment->closure());
248        translation->StoreLiteral(closure_id);
249      }
250      break;
251    }
252    case JS_SETTER: {
253      DCHECK(translation_size == 2);
254      DCHECK(height == 0);
255      int shared_id = DefineDeoptimizationLiteral(
256          environment->entry() ? environment->entry()->shared()
257                               : info()->shared_info());
258      translation->BeginSetterStubFrame(shared_id);
259      if (info()->closure().is_identical_to(environment->closure())) {
260        translation->StoreJSFrameFunction();
261      } else {
262        int closure_id = DefineDeoptimizationLiteral(environment->closure());
263        translation->StoreLiteral(closure_id);
264      }
265      break;
266    }
267    case ARGUMENTS_ADAPTOR: {
268      int shared_id = DefineDeoptimizationLiteral(
269          environment->entry() ? environment->entry()->shared()
270                               : info()->shared_info());
271      translation->BeginArgumentsAdaptorFrame(shared_id, translation_size);
272      if (info()->closure().is_identical_to(environment->closure())) {
273        translation->StoreJSFrameFunction();
274      } else {
275        int closure_id = DefineDeoptimizationLiteral(environment->closure());
276        translation->StoreLiteral(closure_id);
277      }
278      break;
279    }
280    case STUB:
281      translation->BeginCompiledStubFrame(translation_size);
282      break;
283  }
284}
285
286
287void LCodeGenBase::PopulateDeoptimizationData(Handle<Code> code) {
288  int length = deoptimizations_.length();
289  if (length == 0) return;
290  Handle<DeoptimizationInputData> data =
291      DeoptimizationInputData::New(isolate(), length, TENURED);
292
293  Handle<ByteArray> translations =
294      translations_.CreateByteArray(isolate()->factory());
295  data->SetTranslationByteArray(*translations);
296  data->SetInlinedFunctionCount(Smi::FromInt(inlined_function_count_));
297  data->SetOptimizationId(Smi::FromInt(info_->optimization_id()));
298  if (info_->IsOptimizing()) {
299    // Reference to shared function info does not change between phases.
300    AllowDeferredHandleDereference allow_handle_dereference;
301    data->SetSharedFunctionInfo(*info_->shared_info());
302  } else {
303    data->SetSharedFunctionInfo(Smi::FromInt(0));
304  }
305  data->SetWeakCellCache(Smi::FromInt(0));
306
307  Handle<FixedArray> literals =
308      factory()->NewFixedArray(deoptimization_literals_.length(), TENURED);
309  {
310    AllowDeferredHandleDereference copy_handles;
311    for (int i = 0; i < deoptimization_literals_.length(); i++) {
312      literals->set(i, *deoptimization_literals_[i]);
313    }
314    data->SetLiteralArray(*literals);
315  }
316
317  data->SetOsrAstId(Smi::FromInt(info_->osr_ast_id().ToInt()));
318  data->SetOsrPcOffset(Smi::FromInt(osr_pc_offset_));
319
320  // Populate the deoptimization entries.
321  for (int i = 0; i < length; i++) {
322    LEnvironment* env = deoptimizations_[i];
323    data->SetAstId(i, env->ast_id());
324    data->SetTranslationIndex(i, Smi::FromInt(env->translation_index()));
325    data->SetArgumentsStackHeight(i,
326                                  Smi::FromInt(env->arguments_stack_height()));
327    data->SetPc(i, Smi::FromInt(env->pc_offset()));
328  }
329  code->set_deoptimization_data(*data);
330}
331
332
333void LCodeGenBase::PopulateDeoptimizationLiteralsWithInlinedFunctions() {
334  DCHECK_EQ(0, deoptimization_literals_.length());
335  for (Handle<SharedFunctionInfo> function : chunk()->inlined_functions()) {
336    DefineDeoptimizationLiteral(function);
337  }
338  inlined_function_count_ = deoptimization_literals_.length();
339
340  // Define deoptimization literals for all unoptimized code objects of inlined
341  // functions. This ensures unoptimized code is kept alive by optimized code.
342  AllowDeferredHandleDereference allow_shared_function_info_dereference;
343  for (Handle<SharedFunctionInfo> function : chunk()->inlined_functions()) {
344    DefineDeoptimizationLiteral(handle(function->code()));
345  }
346}
347
348
349Deoptimizer::DeoptInfo LCodeGenBase::MakeDeoptInfo(
350    LInstruction* instr, Deoptimizer::DeoptReason deopt_reason) {
351  Deoptimizer::DeoptInfo deopt_info(instr->hydrogen_value()->position(),
352                                    instr->Mnemonic(), deopt_reason);
353  HEnterInlined* enter_inlined = instr->environment()->entry();
354  deopt_info.inlining_id = enter_inlined ? enter_inlined->inlining_id() : 0;
355  return deopt_info;
356}
357}  // namespace internal
358}  // namespace v8
359