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