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 37 namespace v8 { 38 namespace internal { 39 40 bool 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 51 void 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 65 SmartArrayPointer<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 75 void 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 94 int CodeStub::GetCodeKind() { 95 return Code::STUB; 96 } 97 98 99 Handle<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 151 const 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 166 void CodeStub::PrintName(StringStream* stream) { 167 stream->Add("%s", MajorName(MajorKey(), false)); 168 } 169 170 171 void 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 181 bool 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 197 int ICCompareStub::MinorKey() { 198 return OpField::encode(op_ - Token::EQ) | StateField::encode(state_); 199 } 200 201 202 void 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 232 void 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 255 void 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 263 void 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 293 void 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 329 void 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 340 void 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 347 void CallConstructStub::PrintName(StringStream* stream) { 348 stream->Add("CallConstructStub"); 349 if (RecordCallTarget()) stream->Add("_Recording"); 350 } 351 352 353 void ToBooleanStub::PrintName(StringStream* stream) { 354 stream->Add("ToBooleanStub_"); 355 types_.Print(stream); 356 } 357 358 359 void 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 371 void 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 386 bool 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 419 bool ToBooleanStub::Types::NeedsMap() const { 420 return Contains(ToBooleanStub::SPEC_OBJECT) 421 || Contains(ToBooleanStub::STRING) 422 || Contains(ToBooleanStub::HEAP_NUMBER); 423 } 424 425 426 bool ToBooleanStub::Types::CanBeUndetectable() const { 427 return Contains(ToBooleanStub::SPEC_OBJECT) 428 || Contains(ToBooleanStub::STRING); 429 } 430 431 432 void 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