Commit 5b394bf4 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Handle breaks on keyed IC loads which can have an inlined version.

For keyed IC loads setting a break point now ensures that the inlined code is not used. When the break point is set the inlined map check is changed to fail causing the inlined code not to be used but the IC to be called. As long at the break point is set the map check will stay invalid.
Review URL: http://codereview.chromium.org/87025

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1756 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 7fc551ec
...@@ -35,6 +35,8 @@ ...@@ -35,6 +35,8 @@
#include "debug.h" #include "debug.h"
#include "execution.h" #include "execution.h"
#include "global-handles.h" #include "global-handles.h"
#include "ic.h"
#include "ic-inl.h"
#include "natives.h" #include "natives.h"
#include "stub-cache.h" #include "stub-cache.h"
#include "log.h" #include "log.h"
...@@ -289,14 +291,8 @@ void BreakLocationIterator::SetDebugBreak() { ...@@ -289,14 +291,8 @@ void BreakLocationIterator::SetDebugBreak() {
// Patch the frame exit code with a break point. // Patch the frame exit code with a break point.
SetDebugBreakAtReturn(); SetDebugBreakAtReturn();
} else { } else {
// Patch the original code with the current address as the current address // Patch the IC call.
// might have changed by the inline caching since the code was copied. SetDebugBreakAtIC();
original_rinfo()->set_target_address(rinfo()->target_address());
// Patch the code to invoke the builtin debug break function matching the
// calling convention used by the call site.
Handle<Code> dbgbrk_code(Debug::FindDebugBreak(rinfo()));
rinfo()->set_target_address(dbgbrk_code->entry());
} }
ASSERT(IsDebugBreak()); ASSERT(IsDebugBreak());
} }
...@@ -307,8 +303,8 @@ void BreakLocationIterator::ClearDebugBreak() { ...@@ -307,8 +303,8 @@ void BreakLocationIterator::ClearDebugBreak() {
// Restore the frame exit code. // Restore the frame exit code.
ClearDebugBreakAtReturn(); ClearDebugBreakAtReturn();
} else { } else {
// Patch the code to the original invoke. // Patch the IC call.
rinfo()->set_target_address(original_rinfo()->target_address()); ClearDebugBreakAtIC();
} }
ASSERT(!IsDebugBreak()); ASSERT(!IsDebugBreak());
} }
...@@ -361,6 +357,39 @@ bool BreakLocationIterator::IsDebugBreak() { ...@@ -361,6 +357,39 @@ bool BreakLocationIterator::IsDebugBreak() {
} }
void BreakLocationIterator::SetDebugBreakAtIC() {
// Patch the original code with the current address as the current address
// might have changed by the inline caching since the code was copied.
original_rinfo()->set_target_address(rinfo()->target_address());
RelocInfo::Mode mode = rmode();
if (RelocInfo::IsCodeTarget(mode)) {
Address target = rinfo()->target_address();
Handle<Code> code(Code::GetCodeFromTargetAddress(target));
// Patch the code to invoke the builtin debug break function matching the
// calling convention used by the call site.
Handle<Code> dbgbrk_code(Debug::FindDebugBreak(code, mode));
rinfo()->set_target_address(dbgbrk_code->entry());
// For stubs that refer back to an inlined version clear the cached map for
// the inlined case to always go through the IC. As long as the break point
// 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
// keeping it from using the inlined code.
if (code->is_keyed_load_stub() && KeyedLoadIC::HasInlinedVersion(pc())) {
KeyedLoadIC::ClearInlinedVersion(pc());
}
}
}
void BreakLocationIterator::ClearDebugBreakAtIC() {
// Patch the code to the original invoke.
rinfo()->set_target_address(original_rinfo()->target_address());
}
Object* BreakLocationIterator::BreakPointObjects() { Object* BreakLocationIterator::BreakPointObjects() {
return debug_info_->GetBreakPointObjects(code_position()); return debug_info_->GetBreakPointObjects(code_position());
} }
...@@ -1056,14 +1085,9 @@ bool Debug::IsBreakStub(Code* code) { ...@@ -1056,14 +1085,9 @@ bool Debug::IsBreakStub(Code* code) {
// Find the builtin to use for invoking the debug break // Find the builtin to use for invoking the debug break
Handle<Code> Debug::FindDebugBreak(RelocInfo* rinfo) { Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
// Find the builtin debug break function matching the calling convention // Find the builtin debug break function matching the calling convention
// used by the call site. // used by the call site.
RelocInfo::Mode mode = rinfo->rmode();
if (RelocInfo::IsCodeTarget(mode)) {
Address target = rinfo->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
if (code->is_inline_cache_stub()) { if (code->is_inline_cache_stub()) {
if (code->is_call_stub()) { if (code->is_call_stub()) {
return ComputeCallDebugBreak(code->arguments_count()); return ComputeCallDebugBreak(code->arguments_count());
...@@ -1097,7 +1121,6 @@ Handle<Code> Debug::FindDebugBreak(RelocInfo* rinfo) { ...@@ -1097,7 +1121,6 @@ Handle<Code> Debug::FindDebugBreak(RelocInfo* rinfo) {
Handle<Code>(Builtins::builtin(Builtins::StubNoRegisters_DebugBreak)); Handle<Code>(Builtins::builtin(Builtins::StubNoRegisters_DebugBreak));
return result; return result;
} }
}
UNREACHABLE(); UNREACHABLE();
return Handle<Code>::null(); return Handle<Code>::null();
...@@ -2208,7 +2231,7 @@ MessageQueue::MessageQueue(int size) : start_(0), end_(0), size_(size) { ...@@ -2208,7 +2231,7 @@ MessageQueue::MessageQueue(int size) : start_(0), end_(0), size_(size) {
MessageQueue::~MessageQueue() { MessageQueue::~MessageQueue() {
while(!IsEmpty()) { while (!IsEmpty()) {
Message m = Get(); Message m = Get();
m.Dispose(); m.Dispose();
} }
......
...@@ -132,6 +132,10 @@ class BreakLocationIterator { ...@@ -132,6 +132,10 @@ class BreakLocationIterator {
private: private:
void SetDebugBreak(); void SetDebugBreak();
void ClearDebugBreak(); void ClearDebugBreak();
void SetDebugBreakAtIC();
void ClearDebugBreakAtIC();
bool IsDebugBreakAtReturn(); bool IsDebugBreakAtReturn();
void SetDebugBreakAtReturn(); void SetDebugBreakAtReturn();
void ClearDebugBreakAtReturn(); void ClearDebugBreakAtReturn();
...@@ -205,7 +209,7 @@ class Debug { ...@@ -205,7 +209,7 @@ class Debug {
static bool IsBreakStub(Code* code); static bool IsBreakStub(Code* code);
// Find the builtin to use for invoking the debug break // Find the builtin to use for invoking the debug break
static Handle<Code> FindDebugBreak(RelocInfo* rinfo); static Handle<Code> FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode);
static Handle<Object> GetSourceBreakLocations( static Handle<Object> GetSourceBreakLocations(
Handle<SharedFunctionInfo> shared); Handle<SharedFunctionInfo> shared);
......
...@@ -507,6 +507,8 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { ...@@ -507,6 +507,8 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
// TODO(181): Implement map patching once loop nesting is tracked on // TODO(181): Implement map patching once loop nesting is tracked on
// the ARM platform so we can generate inlined fast-case code for // the ARM platform so we can generate inlined fast-case code for
// array indexing in loops. // array indexing in loops.
bool KeyedLoadIC::HasInlinedVersion(Address address) { return false; }
void KeyedLoadIC::ClearInlinedVersion(Address address) { }
void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) { } void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) { }
......
...@@ -729,8 +729,24 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) { ...@@ -729,8 +729,24 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
} }
// One byte opcode for test eax,0xXXXXXXXX.
static const byte kTestEaxByte = 0xA9;
bool KeyedLoadIC::HasInlinedVersion(Address address) {
Address test_instruction_address = address + 4; // 4 = stub address
return *test_instruction_address == kTestEaxByte;
}
void KeyedLoadIC::ClearInlinedVersion(Address address) {
// 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.
PatchInlinedMapCheck(address, Heap::null_value());
}
void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) { void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) {
static const byte kTestEaxByte = 0xA9;
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.
...@@ -744,7 +760,7 @@ void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) { ...@@ -744,7 +760,7 @@ void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) {
// bytes of the 7-byte operand-immediate compare instruction, so // bytes of the 7-byte operand-immediate compare instruction, so
// we add 3 to the offset to get the map address. // we add 3 to the offset to get the map address.
Address map_address = test_instruction_address + offset_value + 3; Address map_address = test_instruction_address + offset_value + 3;
// patch the map check. // Patch the map check.
(*(reinterpret_cast<Object**>(map_address))) = value; (*(reinterpret_cast<Object**>(map_address))) = value;
} }
} }
......
...@@ -237,7 +237,7 @@ void KeyedLoadIC::Clear(Address address, Code* target) { ...@@ -237,7 +237,7 @@ void KeyedLoadIC::Clear(Address address, Code* target) {
// Make sure to also clear the map used in inline fast cases. If we // Make sure to also clear the map used in inline fast cases. If we
// do not clear these maps, cached code can keep objects alive // do not clear these maps, cached code can keep objects alive
// through the embedded maps. // through the embedded maps.
PatchInlinedMapCheck(address, Heap::null_value()); ClearInlinedVersion(address);
SetTargetAtAddress(address, initialize_stub()); SetTargetAtAddress(address, initialize_stub());
} }
......
...@@ -254,6 +254,12 @@ class KeyedLoadIC: public IC { ...@@ -254,6 +254,12 @@ 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.
static void ClearInlinedVersion(Address address);
private: private:
static void Generate(MacroAssembler* masm, const ExternalReference& f); static void Generate(MacroAssembler* masm, const ExternalReference& f);
......
...@@ -2177,6 +2177,53 @@ TEST(DebugStepLinear) { ...@@ -2177,6 +2177,53 @@ TEST(DebugStepLinear) {
} }
// Test of the stepping mechanism for keyed load in a loop.
TEST(DebugStepKeyedLoadLoop) {
v8::HandleScope scope;
DebugLocalContext env;
// Create a function for testing stepping of keyed load. The statement 'y=1'
// is there to have more than one breakable statement in the loop, TODO(315).
v8::Local<v8::Function> foo = CompileFunction(
&env,
"function foo(a) {\n"
" var x;\n"
" var len = a.length;\n"
" for (var i = 0; i < len; i++) {\n"
" y = 1;\n"
" x = a[i];\n"
" }\n"
"}\n",
"foo");
// Create array [0,1,2,3,4,5,6,7,8,9]
v8::Local<v8::Array> a = v8::Array::New(10);
for (int i = 0; i < 10; i++) {
a->Set(v8::Number::New(i), v8::Number::New(i));
}
// Call function without any break points to ensure inlining is in place.
const int kArgc = 1;
v8::Handle<v8::Value> args[kArgc] = { a };
foo->Call(env->Global(), kArgc, args);
// Register a debug event listener which steps and counts.
v8::Debug::SetDebugEventListener(DebugEventStep);
// Setup break point and step through the function.
SetBreakPoint(foo, 3);
step_action = StepNext;
break_point_hit_count = 0;
foo->Call(env->Global(), kArgc, args);
// With stepping all break locations are hit.
CHECK_EQ(22, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
}
// Test the stepping mechanism with different ICs. // Test the stepping mechanism with different ICs.
TEST(DebugStepLinearMixedICs) { TEST(DebugStepLinearMixedICs) {
v8::HandleScope scope; v8::HandleScope scope;
......
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