1// Copyright 2012 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6//     * Redistributions of source code must retain the above copyright
7//       notice, this list of conditions and the following disclaimer.
8//     * Redistributions in binary form must reproduce the above
9//       copyright notice, this list of conditions and the following
10//       disclaimer in the documentation and/or other materials provided
11//       with the distribution.
12//     * Neither the name of Google Inc. nor the names of its
13//       contributors may be used to endorse or promote products derived
14//       from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "bootstrapper.h"
31#include "code-stubs.h"
32#include "stub-cache.h"
33#include "factory.h"
34#include "gdb-jit.h"
35#include "macro-assembler.h"
36
37namespace v8 {
38namespace internal {
39
40bool CodeStub::FindCodeInCache(Code** code_out) {
41  Heap* heap = Isolate::Current()->heap();
42  int index = heap->code_stubs()->FindEntry(GetKey());
43  if (index != UnseededNumberDictionary::kNotFound) {
44    *code_out = Code::cast(heap->code_stubs()->ValueAt(index));
45    return true;
46  }
47  return false;
48}
49
50
51void CodeStub::GenerateCode(MacroAssembler* masm) {
52  // Update the static counter each time a new code stub is generated.
53  masm->isolate()->counters()->code_stubs()->Increment();
54
55  // Nested stubs are not allowed for leaves.
56  AllowStubCallsScope allow_scope(masm, false);
57
58  // Generate the code for the stub.
59  masm->set_generating_stub(true);
60  NoCurrentFrameScope scope(masm);
61  Generate(masm);
62}
63
64
65SmartArrayPointer<const char> CodeStub::GetName() {
66  char buffer[100];
67  NoAllocationStringAllocator allocator(buffer,
68                                        static_cast<unsigned>(sizeof(buffer)));
69  StringStream stream(&allocator);
70  PrintName(&stream);
71  return stream.ToCString();
72}
73
74
75void CodeStub::RecordCodeGeneration(Code* code, MacroAssembler* masm) {
76  code->set_major_key(MajorKey());
77
78  Isolate* isolate = masm->isolate();
79  SmartArrayPointer<const char> name = GetName();
80  PROFILE(isolate, CodeCreateEvent(Logger::STUB_TAG, code, *name));
81  GDBJIT(AddCode(GDBJITInterface::STUB, *name, code));
82  Counters* counters = isolate->counters();
83  counters->total_stubs_code_size()->Increment(code->instruction_size());
84
85#ifdef ENABLE_DISASSEMBLER
86  if (FLAG_print_code_stubs) {
87    code->Disassemble(*name);
88    PrintF("\n");
89  }
90#endif
91}
92
93
94int CodeStub::GetCodeKind() {
95  return Code::STUB;
96}
97
98
99Handle<Code> CodeStub::GetCode() {
100  Isolate* isolate = Isolate::Current();
101  Factory* factory = isolate->factory();
102  Heap* heap = isolate->heap();
103  Code* code;
104  if (UseSpecialCache()
105      ? FindCodeInSpecialCache(&code)
106      : FindCodeInCache(&code)) {
107    ASSERT(IsPregenerated() == code->is_pregenerated());
108    return Handle<Code>(code);
109  }
110
111  {
112    HandleScope scope(isolate);
113
114    // Generate the new code.
115    MacroAssembler masm(isolate, NULL, 256);
116    GenerateCode(&masm);
117
118    // Create the code object.
119    CodeDesc desc;
120    masm.GetCode(&desc);
121
122    // Copy the generated code into a heap object.
123    Code::Flags flags = Code::ComputeFlags(
124        static_cast<Code::Kind>(GetCodeKind()),
125        GetICState());
126    Handle<Code> new_object = factory->NewCode(
127        desc, flags, masm.CodeObject(), NeedsImmovableCode());
128    RecordCodeGeneration(*new_object, &masm);
129    FinishCode(new_object);
130
131    if (UseSpecialCache()) {
132      AddToSpecialCache(new_object);
133    } else {
134      // Update the dictionary and the root in Heap.
135      Handle<UnseededNumberDictionary> dict =
136          factory->DictionaryAtNumberPut(
137              Handle<UnseededNumberDictionary>(heap->code_stubs()),
138              GetKey(),
139              new_object);
140      heap->public_set_code_stubs(*dict);
141    }
142    code = *new_object;
143  }
144
145  Activate(code);
146  ASSERT(!NeedsImmovableCode() || heap->lo_space()->Contains(code));
147  return Handle<Code>(code, isolate);
148}
149
150
151const char* CodeStub::MajorName(CodeStub::Major major_key,
152                                bool allow_unknown_keys) {
153  switch (major_key) {
154#define DEF_CASE(name) case name: return #name "Stub";
155    CODE_STUB_LIST(DEF_CASE)
156#undef DEF_CASE
157    default:
158      if (!allow_unknown_keys) {
159        UNREACHABLE();
160      }
161      return NULL;
162  }
163}
164
165
166void CodeStub::PrintName(StringStream* stream) {
167  stream->Add("%s", MajorName(MajorKey(), false));
168}
169
170
171void ICCompareStub::AddToSpecialCache(Handle<Code> new_object) {
172  ASSERT(*known_map_ != NULL);
173  Isolate* isolate = new_object->GetIsolate();
174  Factory* factory = isolate->factory();
175  return Map::UpdateCodeCache(known_map_,
176                              factory->compare_ic_symbol(),
177                              new_object);
178}
179
180
181bool ICCompareStub::FindCodeInSpecialCache(Code** code_out) {
182  Isolate* isolate = known_map_->GetIsolate();
183  Factory* factory = isolate->factory();
184  Code::Flags flags = Code::ComputeFlags(
185      static_cast<Code::Kind>(GetCodeKind()),
186      UNINITIALIZED);
187  Handle<Object> probe(
188      known_map_->FindInCodeCache(*factory->compare_ic_symbol(), flags));
189  if (probe->IsCode()) {
190    *code_out = Code::cast(*probe);
191    return true;
192  }
193  return false;
194}
195
196
197int ICCompareStub::MinorKey() {
198  return OpField::encode(op_ - Token::EQ) | StateField::encode(state_);
199}
200
201
202void ICCompareStub::Generate(MacroAssembler* masm) {
203  switch (state_) {
204    case CompareIC::UNINITIALIZED:
205      GenerateMiss(masm);
206      break;
207    case CompareIC::SMIS:
208      GenerateSmis(masm);
209      break;
210    case CompareIC::HEAP_NUMBERS:
211      GenerateHeapNumbers(masm);
212      break;
213    case CompareIC::STRINGS:
214      GenerateStrings(masm);
215      break;
216    case CompareIC::SYMBOLS:
217      GenerateSymbols(masm);
218      break;
219    case CompareIC::OBJECTS:
220      GenerateObjects(masm);
221      break;
222    case CompareIC::KNOWN_OBJECTS:
223      ASSERT(*known_map_ != NULL);
224      GenerateKnownObjects(masm);
225      break;
226    default:
227      UNREACHABLE();
228  }
229}
230
231
232void InstanceofStub::PrintName(StringStream* stream) {
233  const char* args = "";
234  if (HasArgsInRegisters()) {
235    args = "_REGS";
236  }
237
238  const char* inline_check = "";
239  if (HasCallSiteInlineCheck()) {
240    inline_check = "_INLINE";
241  }
242
243  const char* return_true_false_object = "";
244  if (ReturnTrueFalseObject()) {
245    return_true_false_object = "_TRUEFALSE";
246  }
247
248  stream->Add("InstanceofStub%s%s%s",
249              args,
250              inline_check,
251              return_true_false_object);
252}
253
254
255void JSEntryStub::FinishCode(Handle<Code> code) {
256  Handle<FixedArray> handler_table =
257      code->GetIsolate()->factory()->NewFixedArray(1, TENURED);
258  handler_table->set(0, Smi::FromInt(handler_offset_));
259  code->set_handler_table(*handler_table);
260}
261
262
263void KeyedLoadElementStub::Generate(MacroAssembler* masm) {
264  switch (elements_kind_) {
265    case FAST_ELEMENTS:
266    case FAST_SMI_ONLY_ELEMENTS:
267      KeyedLoadStubCompiler::GenerateLoadFastElement(masm);
268      break;
269    case FAST_DOUBLE_ELEMENTS:
270      KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(masm);
271      break;
272    case EXTERNAL_BYTE_ELEMENTS:
273    case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
274    case EXTERNAL_SHORT_ELEMENTS:
275    case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
276    case EXTERNAL_INT_ELEMENTS:
277    case EXTERNAL_UNSIGNED_INT_ELEMENTS:
278    case EXTERNAL_FLOAT_ELEMENTS:
279    case EXTERNAL_DOUBLE_ELEMENTS:
280    case EXTERNAL_PIXEL_ELEMENTS:
281      KeyedLoadStubCompiler::GenerateLoadExternalArray(masm, elements_kind_);
282      break;
283    case DICTIONARY_ELEMENTS:
284      KeyedLoadStubCompiler::GenerateLoadDictionaryElement(masm);
285      break;
286    case NON_STRICT_ARGUMENTS_ELEMENTS:
287      UNREACHABLE();
288      break;
289  }
290}
291
292
293void KeyedStoreElementStub::Generate(MacroAssembler* masm) {
294  switch (elements_kind_) {
295    case FAST_ELEMENTS:
296    case FAST_SMI_ONLY_ELEMENTS: {
297      KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
298                                                       is_js_array_,
299                                                       elements_kind_,
300                                                       grow_mode_);
301    }
302      break;
303    case FAST_DOUBLE_ELEMENTS:
304      KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
305                                                             is_js_array_,
306                                                             grow_mode_);
307      break;
308    case EXTERNAL_BYTE_ELEMENTS:
309    case EXTERNAL_UNSIGNED_BYTE_ELEMENTS:
310    case EXTERNAL_SHORT_ELEMENTS:
311    case EXTERNAL_UNSIGNED_SHORT_ELEMENTS:
312    case EXTERNAL_INT_ELEMENTS:
313    case EXTERNAL_UNSIGNED_INT_ELEMENTS:
314    case EXTERNAL_FLOAT_ELEMENTS:
315    case EXTERNAL_DOUBLE_ELEMENTS:
316    case EXTERNAL_PIXEL_ELEMENTS:
317      KeyedStoreStubCompiler::GenerateStoreExternalArray(masm, elements_kind_);
318      break;
319    case DICTIONARY_ELEMENTS:
320      KeyedStoreStubCompiler::GenerateStoreDictionaryElement(masm);
321      break;
322    case NON_STRICT_ARGUMENTS_ELEMENTS:
323      UNREACHABLE();
324      break;
325  }
326}
327
328
329void ArgumentsAccessStub::PrintName(StringStream* stream) {
330  stream->Add("ArgumentsAccessStub_");
331  switch (type_) {
332    case READ_ELEMENT: stream->Add("ReadElement"); break;
333    case NEW_NON_STRICT_FAST: stream->Add("NewNonStrictFast"); break;
334    case NEW_NON_STRICT_SLOW: stream->Add("NewNonStrictSlow"); break;
335    case NEW_STRICT: stream->Add("NewStrict"); break;
336  }
337}
338
339
340void CallFunctionStub::PrintName(StringStream* stream) {
341  stream->Add("CallFunctionStub_Args%d", argc_);
342  if (ReceiverMightBeImplicit()) stream->Add("_Implicit");
343  if (RecordCallTarget()) stream->Add("_Recording");
344}
345
346
347void CallConstructStub::PrintName(StringStream* stream) {
348  stream->Add("CallConstructStub");
349  if (RecordCallTarget()) stream->Add("_Recording");
350}
351
352
353void ToBooleanStub::PrintName(StringStream* stream) {
354  stream->Add("ToBooleanStub_");
355  types_.Print(stream);
356}
357
358
359void ToBooleanStub::Types::Print(StringStream* stream) const {
360  if (IsEmpty()) stream->Add("None");
361  if (Contains(UNDEFINED)) stream->Add("Undefined");
362  if (Contains(BOOLEAN)) stream->Add("Bool");
363  if (Contains(NULL_TYPE)) stream->Add("Null");
364  if (Contains(SMI)) stream->Add("Smi");
365  if (Contains(SPEC_OBJECT)) stream->Add("SpecObject");
366  if (Contains(STRING)) stream->Add("String");
367  if (Contains(HEAP_NUMBER)) stream->Add("HeapNumber");
368}
369
370
371void ToBooleanStub::Types::TraceTransition(Types to) const {
372  if (!FLAG_trace_ic) return;
373  char buffer[100];
374  NoAllocationStringAllocator allocator(buffer,
375                                        static_cast<unsigned>(sizeof(buffer)));
376  StringStream stream(&allocator);
377  stream.Add("[ToBooleanIC (");
378  Print(&stream);
379  stream.Add("->");
380  to.Print(&stream);
381  stream.Add(")]\n");
382  stream.OutputToStdOut();
383}
384
385
386bool ToBooleanStub::Types::Record(Handle<Object> object) {
387  if (object->IsUndefined()) {
388    Add(UNDEFINED);
389    return false;
390  } else if (object->IsBoolean()) {
391    Add(BOOLEAN);
392    return object->IsTrue();
393  } else if (object->IsNull()) {
394    Add(NULL_TYPE);
395    return false;
396  } else if (object->IsSmi()) {
397    Add(SMI);
398    return Smi::cast(*object)->value() != 0;
399  } else if (object->IsSpecObject()) {
400    Add(SPEC_OBJECT);
401    return !object->IsUndetectableObject();
402  } else if (object->IsString()) {
403    Add(STRING);
404    return !object->IsUndetectableObject() &&
405        String::cast(*object)->length() != 0;
406  } else if (object->IsHeapNumber()) {
407    ASSERT(!object->IsUndetectableObject());
408    Add(HEAP_NUMBER);
409    double value = HeapNumber::cast(*object)->value();
410    return value != 0 && !isnan(value);
411  } else {
412    // We should never see an internal object at runtime here!
413    UNREACHABLE();
414    return true;
415  }
416}
417
418
419bool ToBooleanStub::Types::NeedsMap() const {
420  return Contains(ToBooleanStub::SPEC_OBJECT)
421      || Contains(ToBooleanStub::STRING)
422      || Contains(ToBooleanStub::HEAP_NUMBER);
423}
424
425
426bool ToBooleanStub::Types::CanBeUndetectable() const {
427  return Contains(ToBooleanStub::SPEC_OBJECT)
428      || Contains(ToBooleanStub::STRING);
429}
430
431
432void ElementsTransitionAndStoreStub::Generate(MacroAssembler* masm) {
433  Label fail;
434  if (!FLAG_trace_elements_transitions) {
435    if (to_ == FAST_ELEMENTS) {
436      if (from_ == FAST_SMI_ONLY_ELEMENTS) {
437        ElementsTransitionGenerator::GenerateSmiOnlyToObject(masm);
438      } else if (from_ == FAST_DOUBLE_ELEMENTS) {
439        ElementsTransitionGenerator::GenerateDoubleToObject(masm, &fail);
440      } else {
441        UNREACHABLE();
442      }
443      KeyedStoreStubCompiler::GenerateStoreFastElement(masm,
444                                                       is_jsarray_,
445                                                       FAST_ELEMENTS,
446                                                       grow_mode_);
447    } else if (from_ == FAST_SMI_ONLY_ELEMENTS && to_ == FAST_DOUBLE_ELEMENTS) {
448      ElementsTransitionGenerator::GenerateSmiOnlyToDouble(masm, &fail);
449      KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm,
450                                                             is_jsarray_,
451                                                             grow_mode_);
452    } else {
453      UNREACHABLE();
454    }
455  }
456  masm->bind(&fail);
457  KeyedStoreIC::GenerateRuntimeSetProperty(masm, strict_mode_);
458}
459
460} }  // namespace v8::internal
461