Inline the inobject property case for named property loads.

Review URL: http://codereview.chromium.org/99120

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1806 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent bffdfa35
...@@ -116,6 +116,9 @@ class DeferredCode: public ZoneObject { ...@@ -116,6 +116,9 @@ class DeferredCode: public ZoneObject {
JumpTarget* enter() { return &enter_; } JumpTarget* enter() { return &enter_; }
void BindExit() { exit_.Bind(0); } void BindExit() { exit_.Bind(0); }
void BindExit(Result* result) { exit_.Bind(result, 1); } void BindExit(Result* result) { exit_.Bind(result, 1); }
void BindExit(Result* result0, Result* result1) {
exit_.Bind(result0, result1, 2);
}
void BindExit(Result* result0, Result* result1, Result* result2) { void BindExit(Result* result0, Result* result1, Result* result2) {
exit_.Bind(result0, result1, result2, 3); exit_.Bind(result0, result1, result2, 3);
} }
......
...@@ -377,9 +377,7 @@ void BreakLocationIterator::SetDebugBreakAtIC() { ...@@ -377,9 +377,7 @@ void BreakLocationIterator::SetDebugBreakAtIC() {
// is set the patching performed by the runtime system will take place in // is set the patching performed by the runtime system will take place in
// the code copy and will therefore have no effect on the running code // the code copy and will therefore have no effect on the running code
// keeping it from using the inlined code. // keeping it from using the inlined code.
if (code->is_keyed_load_stub() && KeyedLoadIC::HasInlinedVersion(pc())) { if (code->is_keyed_load_stub()) KeyedLoadIC::ClearInlinedVersion(pc());
KeyedLoadIC::ClearInlinedVersion(pc());
}
} }
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include "bootstrapper.h" #include "bootstrapper.h"
#include "codegen-inl.h" #include "codegen-inl.h"
#include "debug.h" #include "debug.h"
#include "ic-inl.h"
#include "parser.h" #include "parser.h"
#include "register-allocator-inl.h" #include "register-allocator-inl.h"
#include "runtime.h" #include "runtime.h"
...@@ -3410,7 +3411,10 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions( ...@@ -3410,7 +3411,10 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
? RelocInfo::CODE_TARGET ? RelocInfo::CODE_TARGET
: RelocInfo::CODE_TARGET_CONTEXT; : RelocInfo::CODE_TARGET_CONTEXT;
Result answer = frame_->CallLoadIC(mode); Result answer = frame_->CallLoadIC(mode);
// A test eax instruction following the call signals that the inobject
// property case was inlined. Ensure that there is not a test eax
// instruction here.
__ nop();
// Discard the global object. The result is in answer. // Discard the global object. The result is in answer.
frame_->Drop(); frame_->Drop();
return answer; return answer;
...@@ -5233,6 +5237,47 @@ bool CodeGenerator::HasValidEntryRegisters() { ...@@ -5233,6 +5237,47 @@ bool CodeGenerator::HasValidEntryRegisters() {
#endif #endif
class DeferredReferenceGetNamedValue: public DeferredCode {
public:
DeferredReferenceGetNamedValue(CodeGenerator* cgen, Handle<String> name)
: DeferredCode(cgen), name_(name) {
set_comment("[ DeferredReferenceGetNamedValue");
}
virtual void Generate();
Label* patch_site() { return &patch_site_; }
private:
Label patch_site_;
Handle<String> name_;
};
void DeferredReferenceGetNamedValue::Generate() {
CodeGenerator* cgen = generator();
Result receiver(cgen);
enter()->Bind(&receiver);
cgen->frame()->Push(&receiver);
cgen->frame()->Push(name_);
Result answer = cgen->frame()->CallLoadIC(RelocInfo::CODE_TARGET);
// The call must be followed by a test eax instruction to indicate
// that the inobject property case was inlined.
ASSERT(answer.is_register() && answer.reg().is(eax));
// Store the delta to the map check instruction here in the test
// instruction.
int delta_to_patch_site = __ SizeOfCodeGeneratedSince(patch_site());
// Here we use masm_-> instead of the double underscore macro because
// this is the instruction that gets patched and coverage code gets in
// the way.
masm_->test(answer.reg(), Immediate(-delta_to_patch_site));
__ IncrementCounter(&Counters::named_load_inline_miss, 1);
receiver = cgen->frame()->Pop();
exit_.Jump(&receiver, &answer);
}
class DeferredReferenceGetKeyedValue: public DeferredCode { class DeferredReferenceGetKeyedValue: public DeferredCode {
public: public:
DeferredReferenceGetKeyedValue(CodeGenerator* generator, bool is_global) DeferredReferenceGetKeyedValue(CodeGenerator* generator, bool is_global)
...@@ -5334,16 +5379,63 @@ void Reference::GetValue(TypeofState typeof_state) { ...@@ -5334,16 +5379,63 @@ void Reference::GetValue(TypeofState typeof_state) {
// thrown below, we must distinguish between the two kinds of // thrown below, we must distinguish between the two kinds of
// loads (typeof expression loads must not throw a reference // loads (typeof expression loads must not throw a reference
// error). // error).
Variable* var = expression_->AsVariableProxy()->AsVariable();
bool is_global = var != NULL;
ASSERT(!is_global || var->is_global());
if (is_global || cgen_->scope()->is_global_scope()) {
// Do not inline the inobject property case for loads from the
// global object or loads in toplevel code.
Comment cmnt(masm, "[ Load from named Property"); Comment cmnt(masm, "[ Load from named Property");
cgen_->frame()->Push(GetName()); cgen_->frame()->Push(GetName());
Variable* var = expression_->AsVariableProxy()->AsVariable(); RelocInfo::Mode mode = is_global
ASSERT(var == NULL || var->is_global()); ? RelocInfo::CODE_TARGET_CONTEXT
RelocInfo::Mode mode = (var == NULL) : RelocInfo::CODE_TARGET;
? RelocInfo::CODE_TARGET
: RelocInfo::CODE_TARGET_CONTEXT;
Result answer = cgen_->frame()->CallLoadIC(mode); Result answer = cgen_->frame()->CallLoadIC(mode);
// A test eax instruction following the call signals that the
// inobject property case was inlined. Ensure that there is not
// a test eax instruction here.
__ nop();
cgen_->frame()->Push(&answer); cgen_->frame()->Push(&answer);
} else {
// Inline the inobject property case.
Comment cmnt(masm, "[ Inlined named property load");
DeferredReferenceGetNamedValue* deferred =
new DeferredReferenceGetNamedValue(cgen_, GetName());
Result receiver = cgen_->frame()->Pop();
receiver.ToRegister();
// Check that the receiver is a heap object.
__ test(receiver.reg(), Immediate(kSmiTagMask));
deferred->enter()->Branch(zero, &receiver, not_taken);
// Preallocate the value register to ensure that there is no
// spill emitted between the patch site label and the offset in
// the load instruction.
Result value = cgen_->allocator()->Allocate();
ASSERT(value.is_valid());
__ bind(deferred->patch_site());
// This is the map check instruction that will be patched.
// Initially use an invalid map to force a failure.
__ cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
Immediate(Factory::null_value()));
deferred->enter()->Branch(not_equal, &receiver, not_taken);
// The delta from the patch label to the load offset must be
// statically known.
ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
LoadIC::kOffsetToLoadInstruction);
// The initial (invalid) offset has to be large enough to force
// a 32-bit instruction encoding to allow patching with an
// arbitrary offset. Use kMaxInt (minus kHeapObjectTag).
int offset = kMaxInt;
__ mov(value.reg(), FieldOperand(receiver.reg(), offset));
__ IncrementCounter(&Counters::named_load_inline, 1);
deferred->BindExit(&receiver, &value);
cgen_->frame()->Push(&receiver);
cgen_->frame()->Push(&value);
}
break; break;
} }
......
...@@ -729,36 +729,66 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { ...@@ -729,36 +729,66 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
static const byte kTestEaxByte = 0xA9; static const byte kTestEaxByte = 0xA9;
bool KeyedLoadIC::HasInlinedVersion(Address address) { void LoadIC::ClearInlinedVersion(Address address) {
Address test_instruction_address = address + 4; // 4 = stub address // Reset the map check of the inlined inobject property load (if
return *test_instruction_address == kTestEaxByte; // present) to guarantee failure by holding an invalid map (the null
// value). The offset can be patched to anything.
PatchInlinedLoad(address, Heap::null_value(), kMaxInt);
} }
void KeyedLoadIC::ClearInlinedVersion(Address address) { void KeyedLoadIC::ClearInlinedVersion(Address address) {
// Insert null as the map to check for to make sure the map check fails // Insert null as the map to check for to make sure the map check fails
// sending control flow to the IC instead of the inlined version. // sending control flow to the IC instead of the inlined version.
PatchInlinedMapCheck(address, Heap::null_value()); PatchInlinedLoad(address, Heap::null_value());
}
bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
// The address of the instruction following the call.
Address test_instruction_address = address + 4;
// If the instruction following the call is not a test eax, nothing
// was inlined.
if (*test_instruction_address != kTestEaxByte) return false;
Address delta_address = test_instruction_address + 1;
// The delta to the start of the map check instruction.
int delta = *reinterpret_cast<int*>(delta_address);
// The map address is the last 4 bytes of the 7-byte
// operand-immediate compare instruction, so we add 3 to get the
// offset to the last 4 bytes.
Address map_address = test_instruction_address + delta + 3;
*(reinterpret_cast<Object**>(map_address)) = map;
// The offset is in the last 4 bytes of a six byte
// memory-to-register move instruction, so we add 2 to get the
// offset to the last 4 bytes.
Address offset_address =
test_instruction_address + delta + kOffsetToLoadInstruction + 2;
*reinterpret_cast<int*>(offset_address) = offset - kHeapObjectTag;
return true;
} }
void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) { bool KeyedLoadIC::PatchInlinedLoad(Address address, Object* map) {
Address test_instruction_address = address + 4; // 4 = stub address Address test_instruction_address = address + 4; // 4 = stub address
// The keyed load has a fast inlined case if the IC call instruction // The keyed load has a fast inlined case if the IC call instruction
// is immediately followed by a test instruction. // is immediately followed by a test instruction.
if (*test_instruction_address == kTestEaxByte) { if (*test_instruction_address != kTestEaxByte) return false;
// Fetch the offset from the test instruction to the map cmp // Fetch the offset from the test instruction to the map cmp
// instruction. This offset is stored in the last 4 bytes of the // instruction. This offset is stored in the last 4 bytes of the 5
// 5 byte test instruction. // byte test instruction.
Address offset_address = test_instruction_address + 1; Address delta_address = test_instruction_address + 1;
int offset_value = *(reinterpret_cast<int*>(offset_address)); int delta = *reinterpret_cast<int*>(delta_address);
// Compute the map address. The map address is in the last 4 // Compute the map address. The map address is in the last 4 bytes
// bytes of the 7-byte operand-immediate compare instruction, so // of the 7-byte operand-immediate compare instruction, so we add 3
// we add 3 to the offset to get the map address. // to the offset to get the map address.
Address map_address = test_instruction_address + offset_value + 3; Address map_address = test_instruction_address + delta + 3;
// Patch the map check. // Patch the map check.
(*(reinterpret_cast<Object**>(map_address))) = value; *(reinterpret_cast<Object**>(map_address)) = map;
} return true;
} }
......
...@@ -42,7 +42,7 @@ static char TransitionMarkFromState(IC::State state) { ...@@ -42,7 +42,7 @@ static char TransitionMarkFromState(IC::State state) {
switch (state) { switch (state) {
case UNINITIALIZED: return '0'; case UNINITIALIZED: return '0';
case UNINITIALIZED_IN_LOOP: return 'L'; case UNINITIALIZED_IN_LOOP: return 'L';
case PREMONOMORPHIC: return '0'; case PREMONOMORPHIC: return 'P';
case MONOMORPHIC: return '1'; case MONOMORPHIC: return '1';
case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; case MONOMORPHIC_PROTOTYPE_FAILURE: return '^';
case MEGAMORPHIC: return 'N'; case MEGAMORPHIC: return 'N';
...@@ -244,6 +244,7 @@ void KeyedLoadIC::Clear(Address address, Code* target) { ...@@ -244,6 +244,7 @@ void KeyedLoadIC::Clear(Address address, Code* target) {
void LoadIC::Clear(Address address, Code* target) { void LoadIC::Clear(Address address, Code* target) {
if (target->ic_state() == UNINITIALIZED) return; if (target->ic_state() == UNINITIALIZED) return;
ClearInlinedVersion(address);
SetTargetAtAddress(address, initialize_stub()); SetTargetAtAddress(address, initialize_stub());
} }
...@@ -523,6 +524,31 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) { ...@@ -523,6 +524,31 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
LOG(SuspectReadEvent(*name, *object)); LOG(SuspectReadEvent(*name, *object));
} }
bool can_be_inlined =
FLAG_use_ic &&
state == PREMONOMORPHIC &&
lookup.IsValid() &&
lookup.IsLoaded() &&
lookup.IsCacheable() &&
lookup.holder() == *object &&
lookup.type() == FIELD &&
!object->IsAccessCheckNeeded();
if (can_be_inlined) {
Map* map = lookup.holder()->map();
// Property's index in the properties array. If negative we have
// an inobject property.
int index = lookup.GetFieldIndex() - map->inobject_properties();
if (index < 0) {
// Index is an offset from the end of the object.
int offset = map->instance_size() + (index * kPointerSize);
if (PatchInlinedLoad(address(), map, offset)) {
set_target(megamorphic_stub());
return lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
}
}
}
// Update inline cache and stub cache. // Update inline cache and stub cache.
if (FLAG_use_ic && lookup.IsLoaded()) { if (FLAG_use_ic && lookup.IsLoaded()) {
UpdateCaches(&lookup, state, object, name); UpdateCaches(&lookup, state, object, name);
...@@ -734,7 +760,7 @@ Object* KeyedLoadIC::Load(State state, ...@@ -734,7 +760,7 @@ Object* KeyedLoadIC::Load(State state,
!object->IsJSValue() && !object->IsJSValue() &&
!JSObject::cast(*object)->HasIndexedInterceptor()) { !JSObject::cast(*object)->HasIndexedInterceptor()) {
Map* map = JSObject::cast(*object)->map(); Map* map = JSObject::cast(*object)->map();
PatchInlinedMapCheck(address(), map); PatchInlinedLoad(address(), map);
} }
} }
......
...@@ -216,6 +216,11 @@ class LoadIC: public IC { ...@@ -216,6 +216,11 @@ class LoadIC: public IC {
static void GenerateStringLength(MacroAssembler* masm); static void GenerateStringLength(MacroAssembler* masm);
static void GenerateFunctionPrototype(MacroAssembler* masm); static void GenerateFunctionPrototype(MacroAssembler* masm);
// The offset from the inlined patch site to the start of the
// inlined load instruction. It is 7 bytes (test eax, imm) plus
// 6 bytes (jne slow_label).
static const int kOffsetToLoadInstruction = 13;
private: private:
static void Generate(MacroAssembler* masm, const ExternalReference& f); static void Generate(MacroAssembler* masm, const ExternalReference& f);
...@@ -238,6 +243,12 @@ class LoadIC: public IC { ...@@ -238,6 +243,12 @@ class LoadIC: public IC {
} }
static void Clear(Address address, Code* target); static void Clear(Address address, Code* target);
// Clear the use of the inlined version.
static void ClearInlinedVersion(Address address);
static bool PatchInlinedLoad(Address address, Object* map, int index);
friend class IC; friend class IC;
}; };
...@@ -254,9 +265,6 @@ class KeyedLoadIC: public IC { ...@@ -254,9 +265,6 @@ class KeyedLoadIC: public IC {
static void GeneratePreMonomorphic(MacroAssembler* masm); static void GeneratePreMonomorphic(MacroAssembler* masm);
static void GenerateGeneric(MacroAssembler* masm); static void GenerateGeneric(MacroAssembler* masm);
// Check if this IC corresponds to an inlined version.
static bool HasInlinedVersion(Address address);
// Clear the use of the inlined version. // Clear the use of the inlined version.
static void ClearInlinedVersion(Address address); static void ClearInlinedVersion(Address address);
...@@ -287,7 +295,7 @@ class KeyedLoadIC: public IC { ...@@ -287,7 +295,7 @@ class KeyedLoadIC: public IC {
// Support for patching the map that is checked in an inlined // Support for patching the map that is checked in an inlined
// version of keyed load. // version of keyed load.
static void PatchInlinedMapCheck(Address address, Object* map); static bool PatchInlinedLoad(Address address, Object* map);
friend class IC; friend class IC;
}; };
......
...@@ -116,6 +116,8 @@ namespace v8 { namespace internal { ...@@ -116,6 +116,8 @@ namespace v8 { namespace internal {
SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \ SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \
SC(keyed_load_inline, V8.KeyedLoadInline) \ SC(keyed_load_inline, V8.KeyedLoadInline) \
SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \ SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \
SC(named_load_inline, V8.NamedLoadInline) \
SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \
SC(keyed_store_field, V8.KeyedStoreField) \ SC(keyed_store_field, V8.KeyedStoreField) \
SC(for_in, V8.ForIn) \ SC(for_in, V8.ForIn) \
SC(enum_cache_hits, V8.EnumCacheHits) \ SC(enum_cache_hits, V8.EnumCacheHits) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment