Bug 1561925 - Implement remaining interpreter-specific BaselineCodeGen methods. r=tcampbell

After the recent script data changes we can now read objects/scopes/bigints and resume offsets
from JIT code.

Differential Revision: https://phabricator.services.mozilla.com/D36201

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jan de Mooij 2019-07-06 10:34:06 +00:00
parent e4638db56f
commit 676b258268
3 changed files with 209 additions and 41 deletions

View File

@ -41,6 +41,8 @@
using namespace js;
using namespace js::jit;
using JS::TraceKind;
using mozilla::AssertedCast;
using mozilla::Maybe;
@ -926,33 +928,89 @@ void BaselineInterpreterCodeGen::pushScriptNameArg(Register scratch1,
pushArg(scratch2);
}
template <>
void BaselineCompilerCodeGen::pushScriptObjectArg(ScriptObjectType type) {
JSScript* script = handler.script();
static gc::Cell* GetScriptGCThing(JSScript* script, jsbytecode* pc,
ScriptGCThingType type) {
switch (type) {
case ScriptObjectType::RegExp:
pushArg(ImmGCPtr(script->getRegExp(handler.pc())));
return;
case ScriptObjectType::Function:
pushArg(ImmGCPtr(script->getFunction(handler.pc())));
return;
case ScriptGCThingType::RegExp:
return script->getRegExp(pc);
case ScriptGCThingType::Function:
return script->getFunction(pc);
case ScriptGCThingType::Scope:
return script->getScope(pc);
case ScriptGCThingType::BigInt:
return script->getBigInt(pc);
}
MOZ_CRASH("Unexpected object type");
MOZ_CRASH("Unexpected GCThing type");
}
template <>
void BaselineInterpreterCodeGen::pushScriptObjectArg(ScriptObjectType type) {
MOZ_CRASH("NYI: interpreter pushScriptObjectArg");
void BaselineCompilerCodeGen::loadScriptGCThing(ScriptGCThingType type,
Register dest,
Register scratch) {
gc::Cell* thing = GetScriptGCThing(handler.script(), handler.pc(), type);
masm.movePtr(ImmGCPtr(thing), dest);
}
template <>
void BaselineCompilerCodeGen::pushScriptScopeArg() {
pushArg(ImmGCPtr(handler.script()->getScope(handler.pc())));
void BaselineInterpreterCodeGen::loadScriptGCThing(ScriptGCThingType type,
Register dest,
Register scratch) {
MOZ_ASSERT(dest != scratch);
// Load the index in |scratch|.
masm.loadPtr(frame.addressOfInterpreterPC(), scratch);
LoadInt32Operand(masm, scratch, scratch);
// Load the GCCellPtr.
loadScript(dest);
masm.loadPtr(Address(dest, JSScript::offsetOfPrivateScriptData()), dest);
masm.loadPtr(BaseIndex(dest, scratch, ScalePointer,
PrivateScriptData::offsetOfGCThings()),
dest);
// Clear the tag bits.
switch (type) {
case ScriptGCThingType::RegExp:
case ScriptGCThingType::Function:
// No-op because GCCellPtr tag bits are zero for objects.
static_assert(uintptr_t(TraceKind::Object) == 0,
"Unexpected tag bits for object GCCellPtr");
break;
case ScriptGCThingType::Scope:
case ScriptGCThingType::BigInt:
// Use xorPtr with a 32-bit immediate because it's more efficient than
// andPtr on 64-bit.
static_assert(uintptr_t(TraceKind::Scope) >= JS::OutOfLineTraceKindMask,
"Expected Scopes to have OutOfLineTraceKindMask tag");
static_assert(uintptr_t(TraceKind::BigInt) >= JS::OutOfLineTraceKindMask,
"Expected BigInts to have OutOfLineTraceKindMask tag");
masm.xorPtr(Imm32(JS::OutOfLineTraceKindMask), dest);
break;
}
#ifdef DEBUG
// Assert low bits are not set.
Label ok;
masm.branchTestPtr(Assembler::Zero, dest, Imm32(0b111), &ok);
masm.assumeUnreachable("GC pointer with tag bits set");
masm.bind(&ok);
#endif
}
template <>
void BaselineInterpreterCodeGen::pushScriptScopeArg() {
MOZ_CRASH("NYI: interpreter pushScriptScopeArg");
void BaselineCompilerCodeGen::pushScriptGCThingArg(ScriptGCThingType type,
Register scratch1,
Register scratch2) {
gc::Cell* thing = GetScriptGCThing(handler.script(), handler.pc(), type);
pushArg(ImmGCPtr(thing));
}
template <>
void BaselineInterpreterCodeGen::pushScriptGCThingArg(ScriptGCThingType type,
Register scratch1,
Register scratch2) {
loadScriptGCThing(type, scratch1, scratch2);
pushArg(scratch1);
}
template <>
@ -1530,7 +1588,10 @@ bool BaselineCompilerCodeGen::emitTraceLoggerEnter() {
template <>
bool BaselineInterpreterCodeGen::emitTraceLoggerEnter() {
MOZ_CRASH("NYI: interpreter emitTraceLoggerEnter");
if (JS::TraceLoggerSupported()) {
MOZ_CRASH("NYI: interpreter emitTraceLoggerEnter");
}
return true;
}
template <typename Handler>
@ -2466,7 +2527,12 @@ bool BaselineCompilerCodeGen::emit_JSOP_BIGINT() {
template <>
bool BaselineInterpreterCodeGen::emit_JSOP_BIGINT() {
MOZ_CRASH("NYI: interpreter JSOP_BIGINT");
Register scratch1 = R0.scratchReg();
Register scratch2 = R1.scratchReg();
loadScriptGCThing(ScriptGCThingType::BigInt, scratch1, scratch2);
masm.tagValue(JSVAL_TYPE_BIGINT, scratch1, R0);
frame.push(R0);
return true;
}
template <>
@ -2579,7 +2645,8 @@ bool BaselineInterpreterCodeGen::emit_JSOP_CALLSITEOBJ() {
template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_REGEXP() {
prepareVMCall();
pushScriptObjectArg(ScriptObjectType::RegExp);
pushScriptGCThingArg(ScriptGCThingType::RegExp, R0.scratchReg(),
R1.scratchReg());
using Fn = JSObject* (*)(JSContext*, Handle<RegExpObject*>);
if (!callVM<Fn, CloneRegExpObject>()) {
@ -2598,7 +2665,8 @@ bool BaselineCodeGen<Handler>::emit_JSOP_LAMBDA() {
masm.loadPtr(frame.addressOfEnvironmentChain(), R0.scratchReg());
pushArg(R0.scratchReg());
pushScriptObjectArg(ScriptObjectType::Function);
pushScriptGCThingArg(ScriptGCThingType::Function, R0.scratchReg(),
R1.scratchReg());
using Fn = JSObject* (*)(JSContext*, HandleFunction, HandleObject);
if (!callVM<Fn, js::Lambda>()) {
@ -2621,7 +2689,8 @@ bool BaselineCodeGen<Handler>::emit_JSOP_LAMBDA_ARROW() {
pushArg(R0);
pushArg(R2.scratchReg());
pushScriptObjectArg(ScriptObjectType::Function);
pushScriptGCThingArg(ScriptGCThingType::Function, R0.scratchReg(),
R1.scratchReg());
using Fn =
JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleValue);
@ -4738,6 +4807,32 @@ static void LoadBaselineScriptResumeEntries(MacroAssembler& masm,
masm.addPtr(scratch, dest);
}
template <typename Handler>
void BaselineCodeGen<Handler>::emitInterpJumpToResumeEntry(Register script,
Register resumeIndex,
Register scratch) {
// Load JSScript::scriptData() into |script|.
masm.loadPtr(Address(script, JSScript::offsetOfScriptData()), script);
// Load the resume pcOffset in |resumeIndex|.
masm.load32(Address(script, SharedScriptData::offsetOfResumeOffsetsOffset()),
scratch);
masm.computeEffectiveAddress(BaseIndex(scratch, resumeIndex, TimesFour),
scratch);
masm.load32(BaseIndex(script, scratch, TimesOne), resumeIndex);
// Load pc* in |script|.
masm.load32(Address(script, SharedScriptData::offsetOfCodeOffset()), scratch);
masm.addPtr(scratch, script);
// Add resume offset to PC, jump to it.
masm.addPtr(resumeIndex, script);
Address pcAddr(BaselineFrameReg,
BaselineFrame::reverseOffsetOfInterpreterPC());
masm.storePtr(script, pcAddr);
emitJumpToInterpretOpLabel();
}
template <>
void BaselineCompilerCodeGen::jumpToResumeEntry(Register resumeIndex,
Register scratch1,
@ -4753,7 +4848,8 @@ template <>
void BaselineInterpreterCodeGen::jumpToResumeEntry(Register resumeIndex,
Register scratch1,
Register scratch2) {
MOZ_CRASH("NYI: interpreter jumpToResumeEntry");
loadScript(scratch1);
emitInterpJumpToResumeEntry(scratch1, resumeIndex, scratch2);
}
template <typename Handler>
@ -4837,7 +4933,8 @@ bool BaselineCodeGen<Handler>::emit_JSOP_PUSHLEXICALENV() {
prepareVMCall();
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
pushScriptScopeArg();
pushScriptGCThingArg(ScriptGCThingType::Scope, R1.scratchReg(),
R2.scratchReg());
pushArg(R0.scratchReg());
using Fn = bool (*)(JSContext*, BaselineFrame*, Handle<LexicalScope*>);
@ -4934,7 +5031,8 @@ template <typename Handler>
bool BaselineCodeGen<Handler>::emit_JSOP_PUSHVARENV() {
prepareVMCall();
masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
pushScriptScopeArg();
pushScriptGCThingArg(ScriptGCThingType::Scope, R1.scratchReg(),
R2.scratchReg());
pushArg(R0.scratchReg());
using Fn = bool (*)(JSContext*, BaselineFrame*, HandleScope);
@ -4958,10 +5056,11 @@ bool BaselineCodeGen<Handler>::emit_JSOP_ENTERWITH() {
// Call a stub to push the object onto the environment chain.
prepareVMCall();
masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
pushScriptScopeArg();
pushScriptGCThingArg(ScriptGCThingType::Scope, R1.scratchReg(),
R2.scratchReg());
pushArg(R0);
masm.loadBaselineFramePtr(BaselineFrameReg, R1.scratchReg());
pushArg(R1.scratchReg());
using Fn =
@ -5274,15 +5373,22 @@ bool BaselineCodeGen<Handler>::emit_JSOP_TOSTRING() {
return true;
}
static constexpr uint32_t TableSwitchOpLowOffset = 1 * JUMP_OFFSET_LEN;
static constexpr uint32_t TableSwitchOpHighOffset = 2 * JUMP_OFFSET_LEN;
static constexpr uint32_t TableSwitchOpFirstResumeIndexOffset =
3 * JUMP_OFFSET_LEN;
template <>
void BaselineCompilerCodeGen::emitGetTableSwitchIndex(ValueOperand val,
Register dest) {
Register dest,
Register scratch1,
Register scratch2) {
jsbytecode* pc = handler.pc();
jsbytecode* defaultpc = pc + GET_JUMP_OFFSET(pc);
Label* defaultLabel = handler.labelOf(defaultpc);
int32_t low = GET_JUMP_OFFSET(pc + 1 * JUMP_OFFSET_LEN);
int32_t high = GET_JUMP_OFFSET(pc + 2 * JUMP_OFFSET_LEN);
int32_t low = GET_JUMP_OFFSET(pc + TableSwitchOpLowOffset);
int32_t high = GET_JUMP_OFFSET(pc + TableSwitchOpHighOffset);
int32_t length = high - low + 1;
// Jump to the 'default' pc if not int32 (tableswitch is only used when
@ -5299,8 +5405,35 @@ void BaselineCompilerCodeGen::emitGetTableSwitchIndex(ValueOperand val,
template <>
void BaselineInterpreterCodeGen::emitGetTableSwitchIndex(ValueOperand val,
Register dest) {
MOZ_CRASH("NYI: interpreter emitTableSwitchJumpTableIndex");
Register dest,
Register scratch1,
Register scratch2) {
// Jump to the 'default' pc if not int32 (tableswitch is only used when
// all cases are int32).
Label done, jumpToDefault;
masm.branchTestInt32(Assembler::NotEqual, val, &jumpToDefault);
masm.unboxInt32(val, dest);
masm.loadPtr(frame.addressOfInterpreterPC(), scratch1);
Address lowAddr(scratch1, sizeof(jsbytecode) + TableSwitchOpLowOffset);
Address highAddr(scratch1, sizeof(jsbytecode) + TableSwitchOpHighOffset);
// Jump to default if val > high.
masm.branch32(Assembler::LessThan, highAddr, dest, &jumpToDefault);
// Jump to default if val < low.
masm.load32(lowAddr, scratch2);
masm.branch32(Assembler::GreaterThan, scratch2, dest, &jumpToDefault);
// index := val - low.
masm.sub32(scratch2, dest);
masm.jump(&done);
masm.bind(&jumpToDefault);
emitJump();
masm.bind(&done);
}
template <>
@ -5312,7 +5445,7 @@ void BaselineCompilerCodeGen::emitTableSwitchJump(Register key,
// Note: BytecodeEmitter::allocateResumeIndex static_asserts
// |firstResumeIndex * sizeof(uintptr_t)| fits in int32_t.
uint32_t firstResumeIndex =
GET_RESUMEINDEX(handler.pc() + 3 * JUMP_OFFSET_LEN);
GET_RESUMEINDEX(handler.pc() + TableSwitchOpFirstResumeIndexOffset);
LoadBaselineScriptResumeEntries(masm, handler.script(), scratch1, scratch2);
masm.loadPtr(BaseIndex(scratch1, key, ScaleFromElemWidth(sizeof(uintptr_t)),
firstResumeIndex * sizeof(uintptr_t)),
@ -5324,7 +5457,13 @@ template <>
void BaselineInterpreterCodeGen::emitTableSwitchJump(Register key,
Register scratch1,
Register scratch2) {
MOZ_CRASH("NYI: interpreter emitTableSwitchJump");
// Load the op's firstResumeIndex in scratch1.
masm.loadPtr(frame.addressOfInterpreterPC(), scratch1);
LoadUint24Operand(masm, scratch1, TableSwitchOpFirstResumeIndexOffset,
scratch1);
masm.add32(key, scratch1);
jumpToResumeEntry(scratch1, key, scratch2);
}
template <typename Handler>
@ -5341,7 +5480,7 @@ bool BaselineCodeGen<Handler>::emit_JSOP_TABLESWITCH() {
// Load the index in the jump table in |key|, or branch to default pc if not
// int32 or out-of-range.
emitGetTableSwitchIndex(R0, key);
emitGetTableSwitchIndex(R0, key, scratch1, scratch2);
// Jump to the target pc.
emitTableSwitchJump(key, scratch1, scratch2);
@ -5872,7 +6011,17 @@ bool BaselineCodeGen<Handler>::emitEnterGeneratorCode(Register script,
emitEnterBaseline();
masm.bind(&noBaselineScript);
MOZ_CRASH("NYI: enter interpreted generator");
// Initialize interpreter frame fields.
Address flagsAddr(BaselineFrameReg, BaselineFrame::reverseOffsetOfFlags());
Address scriptAddr(BaselineFrameReg,
BaselineFrame::reverseOffsetOfInterpreterScript());
masm.or32(Imm32(BaselineFrame::RUNNING_IN_INTERPRETER), flagsAddr);
masm.storePtr(script, scriptAddr);
// Initialize pc and jump to it.
emitInterpJumpToResumeEntry(script, resumeIndex, scratch);
return true;
}
template <typename Handler>
@ -6369,7 +6518,8 @@ bool BaselineCodeGen<Handler>::emit_JSOP_FUNWITHPROTO() {
prepareVMCall();
pushArg(R0.scratchReg());
pushArg(R1.scratchReg());
pushScriptObjectArg(ScriptObjectType::Function);
pushScriptGCThingArg(ScriptGCThingType::Function, R0.scratchReg(),
R1.scratchReg());
using Fn =
JSObject* (*)(JSContext*, HandleFunction, HandleObject, HandleObject);

View File

@ -256,6 +256,8 @@ namespace jit {
_(JSOP_INC) \
_(JSOP_DEC)
enum class ScriptGCThingType { RegExp, Function, Scope, BigInt };
// Base class for BaselineCompiler and BaselineInterpreterGenerator. The Handler
// template is a class storing fields/methods that are interpreter or compiler
// specific. This can be combined with template specialization of methods in
@ -318,10 +320,11 @@ class BaselineCodeGen {
// Pushes a name/object/scope associated with the current bytecode op (and
// stored in the script) as argument for a VM function.
enum class ScriptObjectType { RegExp, Function };
void pushScriptObjectArg(ScriptObjectType type);
void loadScriptGCThing(ScriptGCThingType type, Register dest,
Register scratch);
void pushScriptGCThingArg(ScriptGCThingType type, Register scratch1,
Register scratch2);
void pushScriptNameArg(Register scratch1, Register scratch2);
void pushScriptScopeArg();
// Pushes a bytecode operand as argument for a VM function.
void pushUint8BytecodeOperandArg(Register scratch);
@ -407,6 +410,8 @@ class BaselineCodeGen {
MOZ_MUST_USE bool emitEnterGeneratorCode(Register script,
Register resumeIndex,
Register scratch);
void emitInterpJumpToResumeEntry(Register script, Register resumeIndex,
Register scratch);
void emitJumpToInterpretOpLabel();
MOZ_MUST_USE bool emitIncExecutionProgressCounter(Register scratch);
@ -444,7 +449,8 @@ class BaselineCodeGen {
// Converts |val| to an index in the jump table and stores this in |dest|
// or branches to the default pc if not int32 or out-of-range.
void emitGetTableSwitchIndex(ValueOperand val, Register dest);
void emitGetTableSwitchIndex(ValueOperand val, Register dest,
Register scratch1, Register scratch2);
// Jumps to the target of a table switch based on |key| and the
// firstResumeIndex stored in JSOP_TABLESWITCH.

View File

@ -1474,9 +1474,13 @@ class alignas(uintptr_t) PrivateScriptData final {
explicit PrivateScriptData(uint32_t ngcthings);
public:
static constexpr size_t offsetOfGCThings() {
return sizeof(PrivateScriptData);
}
// Accessors for typed array spans.
mozilla::Span<JS::GCCellPtr> gcthings() {
size_t offset = sizeof(PrivateScriptData);
size_t offset = offsetOfGCThings();
return mozilla::MakeSpan(offsetToPointer<JS::GCCellPtr>(offset), ngcthings);
}
@ -1778,6 +1782,11 @@ class alignas(uintptr_t) SharedScriptData final {
static constexpr size_t offsetOfCodeOffset() {
return offsetof(SharedScriptData, codeOffset_);
}
static constexpr size_t offsetOfResumeOffsetsOffset() {
// Resume-offsets are the first optional array if they exist. Locate the
// array with the 'optArrayOffset_' field.
return offsetof(SharedScriptData, optArrayOffset_);
}
static constexpr size_t offsetOfNfixed() {
return offsetof(SharedScriptData, nfixed);
}
@ -2552,6 +2561,9 @@ class JSScript : public js::gc::TenuredCell {
static constexpr size_t offsetOfScriptData() {
return offsetof(JSScript, scriptData_);
}
static constexpr size_t offsetOfPrivateScriptData() {
return offsetof(JSScript, data_);
}
static constexpr size_t offsetOfJitScript() {
return offsetof(JSScript, jitScript_);
}