[VM interpreter] Save previous argdesc_ and pp_ on reentry, restore on exit.

Fix previously broken AssertAssignable bytecode failing to throw.
Do not assume interpreter is never called on generic closure.
Propagate error after failed getter invocation in GetFieldForDispatch runtime
call.

Change-Id: I548a49829cc29addbbd310f1dc19ce8073738015
Reviewed-on: https://dart-review.googlesource.com/67922
Commit-Queue: Régis Crelier <regis@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
This commit is contained in:
Régis Crelier 2018-08-02 17:56:50 +00:00 committed by commit-bot@chromium.org
parent 92ae73b6d8
commit b04def964c
3 changed files with 50 additions and 33 deletions

View file

@ -1499,7 +1499,11 @@ DART_FORCE_INLINE void Interpreter::PrepareForTailCall(
FP = reinterpret_cast<RawObject**>(fp_); \
pc = reinterpret_cast<uint32_t*>(pc_); \
if ((reinterpret_cast<uword>(pc) & 2) != 0) { /* Entry frame? */ \
uword exit_fp = reinterpret_cast<uword>(fp_[0]); \
pp_ = reinterpret_cast<RawObjectPool*>(fp_[kKBCSavedPpSlotFromEntryFp]); \
argdesc_ = \
reinterpret_cast<RawArray*>(fp_[kKBCSavedArgDescSlotFromEntryFp]); \
uword exit_fp = \
reinterpret_cast<uword>(fp_[kKBCExitLinkSlotFromEntryFp]); \
thread->set_top_exit_frame_info(exit_fp); \
thread->set_top_resource(top_resource); \
thread->set_vm_tag(vm_tag); \
@ -1524,7 +1528,11 @@ DART_FORCE_INLINE void Interpreter::PrepareForTailCall(
FP = reinterpret_cast<RawObject**>(fp_); \
pc = reinterpret_cast<uint32_t*>(pc_); \
if ((reinterpret_cast<uword>(pc) & 2) != 0) { /* Entry frame? */ \
uword exit_fp = reinterpret_cast<uword>(fp_[0]); \
pp_ = reinterpret_cast<RawObjectPool*>(fp_[kKBCSavedPpSlotFromEntryFp]); \
argdesc_ = \
reinterpret_cast<RawArray*>(fp_[kKBCSavedArgDescSlotFromEntryFp]); \
uword exit_fp = \
reinterpret_cast<uword>(fp_[kKBCExitLinkSlotFromEntryFp]); \
thread->set_top_exit_frame_info(exit_fp); \
thread->set_top_resource(top_resource); \
thread->set_vm_tag(vm_tag); \
@ -1744,14 +1752,17 @@ RawObject* Interpreter::Call(RawFunction* function,
//
// ^
// | previous Dart frames
// ~~~~~~~~~~~~~~~ |
// |
// | ........... | -+
// fp_ > | | saved top_exit_frame_info
// fp_ > | exit fp_ | saved top_exit_frame_info
// | argdesc_ | saved argdesc_ (for reentering interpreter)
// | pp_ | saved pp_ (for reentering interpreter)
// | arg 0 | -+
// ~~~~~~~~~~~~~~~ |
// | arg 1 | |
// ... |
// > incoming arguments
// ~~~~~~~~~~~~~~~ |
// | arg 1 | -+
// |
// | arg argc-1 | -+
// | function | -+
// | code | |
// | caller PC | ---> special fake PC marking an entry frame
@ -1762,16 +1773,19 @@ RawObject* Interpreter::Call(RawFunction* function,
//
// A negative argc indicates reverse memory order of arguments.
const intptr_t arg_count = argc < 0 ? -argc : argc;
FP = fp_ + 1 + arg_count + kKBCDartFrameFixedSize;
FP = fp_ + kKBCEntrySavedSlots + arg_count + kKBCDartFrameFixedSize;
SP = FP - 1;
// Save outer top_exit_frame_info.
fp_[0] = reinterpret_cast<RawObject*>(thread->top_exit_frame_info());
// Save outer top_exit_frame_info, current argdesc, and current pp.
fp_[kKBCExitLinkSlotFromEntryFp] =
reinterpret_cast<RawObject*>(thread->top_exit_frame_info());
thread->set_top_exit_frame_info(0);
fp_[kKBCSavedArgDescSlotFromEntryFp] = reinterpret_cast<RawObject*>(argdesc_);
fp_[kKBCSavedPpSlotFromEntryFp] = reinterpret_cast<RawObject*>(pp_);
// Copy arguments and setup the Dart frame.
for (intptr_t i = 0; i < arg_count; i++) {
fp_[1 + i] = argv[argc < 0 ? -i : i];
fp_[kKBCEntrySavedSlots + i] = argv[argc < 0 ? -i : i];
}
RawCode* bytecode = function->ptr()->bytecode_;
@ -3404,7 +3418,10 @@ RawObject* Interpreter::Call(RawFunction* function,
// Pop entry frame.
fp_ = SavedCallerFP(FP);
// Restore exit frame info saved in entry frame.
uword exit_fp = reinterpret_cast<uword>(fp_[0]);
pp_ = reinterpret_cast<RawObjectPool*>(fp_[kKBCSavedPpSlotFromEntryFp]);
argdesc_ =
reinterpret_cast<RawArray*>(fp_[kKBCSavedArgDescSlotFromEntryFp]);
uword exit_fp = reinterpret_cast<uword>(fp_[kKBCExitLinkSlotFromEntryFp]);
thread->set_top_exit_frame_info(exit_fp);
thread->set_top_resource(top_resource);
thread->set_vm_tag(vm_tag);
@ -3418,7 +3435,7 @@ RawObject* Interpreter::Call(RawFunction* function,
}
ASSERT(reinterpret_cast<uword>(fp_) < stack_limit());
const intptr_t argc = reinterpret_cast<uword>(pc) >> 2;
ASSERT(fp_ == FrameArguments(FP, argc + 1));
ASSERT(fp_ == FrameArguments(FP, argc + kKBCEntrySavedSlots));
// Exception propagation should have been done.
ASSERT(!result->IsHeapObject() ||
result->GetClassId() != kUnhandledExceptionCid);
@ -3812,7 +3829,9 @@ RawObject* Interpreter::Call(RawFunction* function,
RawSubtypeTestCache* cache =
static_cast<RawSubtypeTestCache*>(LOAD_CONSTANT(rD));
AssertAssignable(thread, pc, FP, SP, args, cache);
if (!AssertAssignable(thread, pc, FP, SP, args, cache)) {
HANDLE_EXCEPTION;
}
}
SP -= 4; // Instance remains on stack.
@ -4623,8 +4642,9 @@ RawObject* Interpreter::Call(RawFunction* function,
const bool has_dart_caller = (reinterpret_cast<uword>(pc) & 2) == 0;
const intptr_t argc = has_dart_caller ? KernelBytecode::DecodeArgc(pc[-1])
: (reinterpret_cast<uword>(pc) >> 2);
const bool has_function_type_args =
has_dart_caller && InterpreterHelpers::ArgDescTypeArgsLen(argdesc_) > 0;
const intptr_t type_args_len =
InterpreterHelpers::ArgDescTypeArgsLen(argdesc_);
const intptr_t receiver_idx = type_args_len > 0 ? 1 : 0;
SP = FrameArguments(FP, 0);
RawObject** args = SP - argc;
@ -4634,7 +4654,7 @@ RawObject* Interpreter::Call(RawFunction* function,
}
*++SP = null_value;
*++SP = args[has_function_type_args ? 1 : 0]; // Closure object.
*++SP = args[receiver_idx]; // Closure object.
*++SP = argdesc_;
*++SP = null_value; // Array of arguments (will be filled).

View file

@ -524,6 +524,14 @@ DEFINE_RUNTIME_ENTRY(ExtractMethod, 2) {
#endif // defined(DART_USE_INTERPRETER)
}
// Result of an invoke may be an unhandled exception, in which case we
// rethrow it.
static void CheckResultError(const Object& result) {
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
}
}
// Invoke field getter before dispatch.
// Arg0: instance.
// Arg1: field name.
@ -546,6 +554,7 @@ DEFINE_RUNTIME_ENTRY(GetFieldForDispatch, 2) {
args.SetAt(0, receiver);
const Object& result =
Object::Handle(zone, DartEntry::InvokeFunction(getter, args));
CheckResultError(result);
arguments.SetReturn(result);
#else
UNREACHABLE();
@ -1049,14 +1058,6 @@ DEFINE_RUNTIME_ENTRY(PatchStaticCall, 0) {
#endif
}
// Result of an invoke may be an unhandled exception, in which case we
// rethrow it.
static void CheckResultError(const Object& result) {
if (result.IsError()) {
Exceptions::PropagateError(Error::Cast(result));
}
}
#if defined(PRODUCT) || defined(DART_PRECOMPILED_RUNTIME)
DEFINE_RUNTIME_ENTRY(BreakpointRuntimeHandler, 0) {
UNREACHABLE();

View file

@ -47,15 +47,11 @@ static const int kKBCCallerSpSlotFromFp = -kKBCDartFrameFixedSize - 1;
static const int kKBCPcMarkerSlotFromFp = -3;
static const int kKBCFunctionSlotFromFp = -4;
// Note: These constants don't match actual KBC behavior. This is done because
// setting kKBCFirstLocalSlotFromFp to 0 breaks assumptions spread across the
// code.
// Instead for the purposes of local variable allocation we pretend that KBC
// behaves as other architectures (stack growing downwards) and later fix
// these indices during code generation in the backend.
static const int kKBCParamEndSlotFromFp = 4; // One slot past last parameter.
static const int kKBCFirstLocalSlotFromFp = -1;
// Entry and exit frame layout.
static const int kKBCEntrySavedSlots = 3;
static const int kKBCExitLinkSlotFromEntryFp = 0;
static const int kKBCSavedArgDescSlotFromEntryFp = 1;
static const int kKBCSavedPpSlotFromEntryFp = 2;
// Value for stack limit that is used to cause an interrupt.
// Note that on KBC stack is growing upwards so interrupt limit is 0 unlike