// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ jmp(&suspend);
-
__ bind(&continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&resume);
__ bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ push(r0); // result
+
__ jmp(&l_suspend);
__ bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&l_resume);
+
__ bind(&l_suspend);
const int generator_object_depth = kPointerSize + try_block_size;
__ ldr(r0, MemOperand(sp, generator_object_depth));
(rmode <= RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL)) ||
(rmode == RelocInfo::INTERNAL_REFERENCE) ||
(rmode == RelocInfo::CONST_POOL) || (rmode == RelocInfo::VENEER_POOL) ||
- (rmode == RelocInfo::DEOPT_REASON)) {
+ (rmode == RelocInfo::DEOPT_REASON) ||
+ (rmode == RelocInfo::GENERATOR_CONTINUATION)) {
// Adjust code for new modes.
- DCHECK(RelocInfo::IsDebugBreakSlot(rmode)
- || RelocInfo::IsJSReturn(rmode)
- || RelocInfo::IsComment(rmode)
- || RelocInfo::IsDeoptReason(rmode)
- || RelocInfo::IsPosition(rmode)
- || RelocInfo::IsInternalReference(rmode)
- || RelocInfo::IsConstPool(rmode)
- || RelocInfo::IsVeneerPool(rmode));
+ DCHECK(RelocInfo::IsDebugBreakSlot(rmode) || RelocInfo::IsJSReturn(rmode) ||
+ RelocInfo::IsComment(rmode) || RelocInfo::IsDeoptReason(rmode) ||
+ RelocInfo::IsPosition(rmode) ||
+ RelocInfo::IsInternalReference(rmode) ||
+ RelocInfo::IsConstPool(rmode) || RelocInfo::IsVeneerPool(rmode) ||
+ RelocInfo::IsGeneratorContinuation(rmode));
// These modes do not need an entry in the constant pool.
} else {
constpool_.RecordEntry(data, rmode);
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ B(&suspend);
-
// TODO(jbramley): This label is bound here because the following code
// looks at its pos(). Is it possible to do something more efficient here,
// perhaps using Adr?
__ Bind(&continuation);
+ __ RecordGeneratorContinuation();
__ B(&resume);
__ Bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ Push(x0); // result
- __ B(&l_suspend);
+ __ B(&l_suspend);
// TODO(jbramley): This label is bound here because the following code
// looks at its pos(). Is it possible to do something more efficient here,
// perhaps using Adr?
__ Bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ B(&l_resume);
__ Bind(&l_suspend);
case DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL:
return "debug break slot at construct call";
case CODE_AGE_SEQUENCE:
- return "code_age_sequence";
+ return "code age sequence";
+ case GENERATOR_CONTINUATION:
+ return "generator continuation";
case NUMBER_OF_MODES:
case PC_JUMP:
UNREACHABLE();
case DEBUG_BREAK_SLOT_AT_POSITION:
case DEBUG_BREAK_SLOT_AT_CALL:
case DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL:
+ case GENERATOR_CONTINUATION:
case NONE32:
case NONE64:
break;
}
+void Assembler::RecordGeneratorContinuation() {
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::GENERATOR_CONTINUATION);
+}
+
+
void Assembler::RecordDebugBreakSlot() {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION);
// Encoded internal reference, used only on MIPS, MIPS64 and PPC.
INTERNAL_REFERENCE_ENCODED,
+ // Continuation points for a generator yield.
+ GENERATOR_CONTINUATION,
+
// Marks constant and veneer pools. Only used on ARM and ARM64.
// They use a custom noncompact encoding.
CONST_POOL,
static inline bool IsCodeAgeSequence(Mode mode) {
return mode == CODE_AGE_SEQUENCE;
}
+ static inline bool IsGeneratorContinuation(Mode mode) {
+ return mode == GENERATOR_CONTINUATION;
+ }
static inline int ModeMask(Mode mode) { return 1 << mode; }
// Accessors
}
-// Figure out how many bytes of "pc_offset" correspond to actual code by
-// subtracting off the bytes that correspond to constant/veneer pools. See
-// Assembler::CheckConstPool() and Assembler::CheckVeneerPool(). Note that this
-// is only useful for architectures using constant pools or veneer pools.
-static int ComputeCodeOffsetFromPcOffset(Code *code, int pc_offset) {
+// Count the number of calls before the current frame PC to find the
+// corresponding PC in the newly recompiled code.
+static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code,
+ Address old_pc) {
+ DCHECK_EQ(old_code->kind(), Code::FUNCTION);
+ DCHECK_EQ(new_code->kind(), Code::FUNCTION);
+ DCHECK(!old_code->has_debug_break_slots());
+ DCHECK(new_code->has_debug_break_slots());
+ int mask = RelocInfo::kCodeTargetMask;
+ int index = 0;
+ intptr_t delta = 0;
+ for (RelocIterator it(old_code, mask); !it.done(); it.next()) {
+ RelocInfo* rinfo = it.rinfo();
+ Address current_pc = rinfo->pc();
+ // The frame PC is behind the call instruction by the call instruction size.
+ if (current_pc > old_pc) break;
+ index++;
+ delta = old_pc - current_pc;
+ }
+
+ RelocIterator it(new_code, mask);
+ for (int i = 1; i < index; i++) it.next();
+ return it.rinfo()->pc() + delta;
+}
+
+
+// Count the number of continuations at which the current pc offset is at.
+static int ComputeContinuationIndexFromPcOffset(Code* code, int pc_offset) {
DCHECK_EQ(code->kind(), Code::FUNCTION);
DCHECK(!code->has_debug_break_slots());
- DCHECK_LE(0, pc_offset);
- DCHECK_LT(pc_offset, code->instruction_end() - code->instruction_start());
-
- int mask = RelocInfo::ModeMask(RelocInfo::CONST_POOL) |
- RelocInfo::ModeMask(RelocInfo::VENEER_POOL);
- byte *pc = code->instruction_start() + pc_offset;
- int code_offset = pc_offset;
+ Address pc = code->instruction_start() + pc_offset;
+ int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION);
+ int index = 0;
for (RelocIterator it(code, mask); !it.done(); it.next()) {
- RelocInfo* info = it.rinfo();
- if (info->pc() >= pc) break;
- DCHECK(RelocInfo::IsConstPool(info->rmode()));
- code_offset -= static_cast<int>(info->data());
- DCHECK_LE(0, code_offset);
+ index++;
+ RelocInfo* rinfo = it.rinfo();
+ Address current_pc = rinfo->pc();
+ if (current_pc == pc) break;
+ DCHECK(current_pc < pc);
}
-
- return code_offset;
+ return index;
}
-// The inverse of ComputeCodeOffsetFromPcOffset.
-static int ComputePcOffsetFromCodeOffset(Code *code, int code_offset) {
+// Find the pc offset for the given continuation index.
+static int ComputePcOffsetFromContinuationIndex(Code* code, int index) {
DCHECK_EQ(code->kind(), Code::FUNCTION);
-
- int mask = RelocInfo::kDebugBreakSlotMask |
- RelocInfo::ModeMask(RelocInfo::CONST_POOL) |
- RelocInfo::ModeMask(RelocInfo::VENEER_POOL);
- int reloc = 0;
- for (RelocIterator it(code, mask); !it.done(); it.next()) {
- RelocInfo* info = it.rinfo();
- if (info->pc() - code->instruction_start() - reloc >= code_offset) break;
- if (RelocInfo::IsDebugBreakSlot(info->rmode())) {
- reloc += Assembler::kDebugBreakSlotLength;
- } else {
- DCHECK(RelocInfo::IsConstPool(info->rmode()));
- reloc += static_cast<int>(info->data());
- }
- }
-
- int pc_offset = code_offset + reloc;
-
- DCHECK_LT(code->instruction_start() + pc_offset, code->instruction_end());
-
- return pc_offset;
+ DCHECK(code->has_debug_break_slots());
+ int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION);
+ RelocIterator it(code, mask);
+ for (int i = 1; i < index; i++) it.next();
+ return static_cast<int>(it.rinfo()->pc() - code->instruction_start());
}
continue;
}
- int old_pc_offset =
- static_cast<int>(frame->pc() - frame_code->instruction_start());
- int code_offset = ComputeCodeOffsetFromPcOffset(*frame_code, old_pc_offset);
- int new_pc_offset = ComputePcOffsetFromCodeOffset(*new_code, code_offset);
-
- // Compute the equivalent pc in the new code.
- byte* new_pc = new_code->instruction_start() + new_pc_offset;
+ Address new_pc =
+ ComputeNewPcForRedirect(*new_code, *frame_code, frame->pc());
if (FLAG_trace_deopt) {
PrintF("Replacing code %08" V8PRIxPTR " - %08" V8PRIxPTR " (%d) "
EnsureFunctionHasDebugBreakSlots(fun);
- int code_offset = generators[i]->continuation();
- int pc_offset = ComputePcOffsetFromCodeOffset(fun->code(), code_offset);
+ int index = generators[i]->continuation();
+ int pc_offset = ComputePcOffsetFromContinuationIndex(fun->code(), index);
generators[i]->set_continuation(pc_offset);
}
}
int pc_offset = gen->continuation();
DCHECK_LT(0, pc_offset);
- int code_offset =
- ComputeCodeOffsetFromPcOffset(fun->code(), pc_offset);
+ int index =
+ ComputeContinuationIndexFromPcOffset(fun->code(), pc_offset);
// This will be fixed after we recompile the functions.
- gen->set_continuation(code_offset);
+ gen->set_continuation(index);
suspended_generators.Add(Handle<JSGeneratorObject>(gen, isolate_));
} else if (obj->IsSharedFunctionInfo()) {
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ jmp(&suspend);
-
__ bind(&continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&resume);
__ bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ push(eax); // result
+
__ jmp(&l_suspend);
__ bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&l_resume);
+
__ bind(&l_suspend);
const int generator_object_depth = kPointerSize + try_block_size;
__ mov(eax, Operand(esp, generator_object_depth));
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ jmp(&suspend);
-
__ bind(&continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&resume);
__ bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ push(a0); // result
+
__ jmp(&l_suspend);
__ bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ mov(a0, v0);
__ jmp(&l_resume);
+
__ bind(&l_suspend);
const int generator_object_depth = kPointerSize + try_block_size;
__ lw(a0, MemOperand(sp, generator_object_depth));
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ jmp(&suspend);
-
__ bind(&continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&resume);
__ bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ push(a0); // result
+
__ jmp(&l_suspend);
__ bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ mov(a0, v0);
__ jmp(&l_resume);
+
__ bind(&l_suspend);
const int generator_object_depth = kPointerSize + try_block_size;
__ ld(a0, MemOperand(sp, generator_object_depth));
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ b(&suspend);
-
__ bind(&continuation);
+ __ RecordGeneratorContinuation();
__ b(&resume);
__ bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ push(r3); // result
+
__ b(&l_suspend);
__ bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ b(&l_resume);
+
__ bind(&l_suspend);
const int generator_object_depth = kPointerSize + try_block_size;
__ LoadP(r3, MemOperand(sp, generator_object_depth));
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ jmp(&suspend);
-
__ bind(&continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&resume);
__ bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ Push(rax); // result
+
__ jmp(&l_suspend);
__ bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&l_resume);
+
__ bind(&l_suspend);
const int generator_object_depth = kPointerSize + try_block_size;
__ movp(rax, Operand(rsp, generator_object_depth));
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark generator continuation.
+ void RecordGeneratorContinuation();
+
// Mark address of a debug break slot.
void RecordDebugBreakSlot();
void RecordDebugBreakSlotForCall(int argc);
Label suspend, continuation, post_runtime, resume;
__ jmp(&suspend);
-
__ bind(&continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&resume);
__ bind(&suspend);
EnterTryBlock(handler_index, &l_catch);
const int try_block_size = TryCatch::kElementCount * kPointerSize;
__ push(eax); // result
+
__ jmp(&l_suspend);
__ bind(&l_continuation);
+ __ RecordGeneratorContinuation();
__ jmp(&l_resume);
+
__ bind(&l_suspend);
const int generator_object_depth = kPointerSize + try_block_size;
__ mov(eax, Operand(esp, generator_object_depth));
--- /dev/null
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --allow-natives-syntax --cache=code
+
+try { } catch(e) { }
+try { try { } catch (e) { } } catch(e) { }
+try {
+ var Debug = %GetDebugContext().Debug;
+ Debug.setListener(function(){});
+} catch(e) { }
+(function() {
+ Debug.setBreakPoint(function(){}, 0, 0);
+})();
+
+var a = 1;
+a += a;
+Debug.setListener(null);
+assertEquals(2, a);