Bug 1066828 - Fully inline RegExp.exec and .test in jitcode, r=jandem.

This commit is contained in:
Brian Hackett 2014-10-04 18:00:58 -07:00
parent d8efbd5466
commit 2b950d362d
29 changed files with 897 additions and 72 deletions

View File

@ -59,15 +59,12 @@ js::CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs
if (pair.isUndefined()) {
MOZ_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
arr->setDenseInitializedLength(i + 1);
arr->initDenseElementWithType(cx, i, UndefinedValue());
arr->initDenseElement(i, UndefinedValue());
} else {
JSLinearString *str = NewDependentString(cx, input, pair.start, pair.length());
if (!str)
return false;
arr->setDenseInitializedLength(i + 1);
// We don't have to update type information here, since the match
// result template is already known to have string elements.
arr->initDenseElement(i, StringValue(str));
}
}
@ -678,8 +675,13 @@ js::regexp_exec(JSContext *cx, unsigned argc, Value *vp)
/* Separate interface for use by IonMonkey. */
bool
js::regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, MutableHandleValue output)
js::regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input,
MatchPairs *maybeMatches, MutableHandleValue output)
{
// The MatchPairs will always be passed in, but RegExp execution was
// successful only if the pairs have actually been filled in.
if (maybeMatches && maybeMatches->pairsRaw()[0] >= 0)
return CreateRegExpMatchResult(cx, input, *maybeMatches, output);
return regexp_exec_impl(cx, regexp, input, UpdateRegExpStatics, output);
}

View File

@ -45,7 +45,8 @@ CreateRegExpMatchResult(JSContext *cx, HandleString input, const MatchPairs &mat
MutableHandleValue rval);
extern bool
regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, MutableHandleValue output);
regexp_exec_raw(JSContext *cx, HandleObject regexp, HandleString input, MatchPairs *maybeMatches,
MutableHandleValue output);
extern bool
regexp_exec(JSContext *cx, unsigned argc, Value *vp);

View File

@ -903,15 +903,6 @@ jit::FinishDiscardBaselineScript(FreeOp *fop, JSScript *script)
BaselineScript::Destroy(fop, baseline);
}
void
jit::JitCompartment::toggleBaselineStubBarriers(bool enabled)
{
for (ICStubCodeMap::Enum e(*stubCodes_); !e.empty(); e.popFront()) {
JitCode *code = *e.front().value().unsafeGet();
code->togglePreBarriers(enabled);
}
}
void
jit::AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, size_t *data,
size_t *fallbackStubs)

View File

@ -22,6 +22,7 @@
#ifdef JSGC_GENERATIONAL
# include "gc/Nursery.h"
#endif
#include "irregexp/NativeRegExpMacroAssembler.h"
#include "jit/BaselineCompiler.h"
#include "jit/IonBuilder.h"
#include "jit/IonCaches.h"
@ -36,6 +37,8 @@
#include "jit/ParallelSafetyAnalysis.h"
#include "jit/RangeAnalysis.h"
#include "vm/ForkJoin.h"
#include "vm/MatchPairs.h"
#include "vm/RegExpStatics.h"
#include "vm/TraceLogging.h"
#include "jsboolinlines.h"
@ -1012,28 +1015,633 @@ CodeGenerator::visitRegExp(LRegExp *lir)
return callVM(CloneRegExpObjectInfo, lir);
}
// The maximum number of pairs we can handle when executing RegExps inline.
static const size_t RegExpMaxPairCount = 6;
// Amount of space to reserve on the stack when executing RegExps inline.
static const size_t RegExpReservedStack = sizeof(irregexp::InputOutputData)
+ sizeof(MatchPairs)
+ RegExpMaxPairCount * sizeof(MatchPair);
static size_t
RegExpPairsVectorStartOffset(size_t inputOutputDataStartOffset)
{
return inputOutputDataStartOffset + sizeof(irregexp::InputOutputData) + sizeof(MatchPairs);
}
static Address
RegExpPairCountAddress(size_t inputOutputDataStartOffset)
{
return Address(StackPointer, inputOutputDataStartOffset
+ sizeof(irregexp::InputOutputData)
+ MatchPairs::offsetOfPairCount());
}
// Prepare an InputOutputData and optional MatchPairs which space has been
// allocated for on the stack, and try to execute a RegExp on a string input.
// If the RegExp was successfully executed and matched the input, fallthrough,
// otherwise jump to notFound or failure.
static bool
PrepareAndExecuteRegExp(JSContext *cx, MacroAssembler &masm, Register regexp, Register input,
Register temp1, Register temp2, Register temp3,
size_t inputOutputDataStartOffset,
RegExpShared::CompilationMode mode,
Label *notFound, Label *failure)
{
size_t matchPairsStartOffset = inputOutputDataStartOffset + sizeof(irregexp::InputOutputData);
size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
Address inputStartAddress(StackPointer,
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputStart));
Address inputEndAddress(StackPointer,
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, inputEnd));
Address matchesPointerAddress(StackPointer,
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, matches));
Address startIndexAddress(StackPointer,
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, startIndex));
Address matchResultAddress(StackPointer,
inputOutputDataStartOffset + offsetof(irregexp::InputOutputData, result));
Address pairCountAddress = RegExpPairCountAddress(inputOutputDataStartOffset);
Address pairsPointerAddress(StackPointer, matchPairsStartOffset + MatchPairs::offsetOfPairs());
Address pairsVectorAddress(StackPointer, pairsVectorStartOffset);
RegExpStatics *res = cx->global()->getRegExpStatics(cx);
if (!res)
return false;
if (mode == RegExpShared::Normal) {
// First, fill in a skeletal MatchPairs instance on the stack. This will be
// passed to the OOL stub in the caller if we aren't able to execute the
// RegExp inline, and that stub needs to be able to determine whether the
// execution finished successfully.
masm.store32(Imm32(1), pairCountAddress);
masm.store32(Imm32(-1), pairsVectorAddress);
masm.computeEffectiveAddress(pairsVectorAddress, temp1);
masm.storePtr(temp1, pairsPointerAddress);
}
// Check for a linear input string.
masm.branchIfRope(input, failure);
// Get the RegExpShared for the RegExp.
masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp1);
masm.branchPtr(Assembler::Equal, temp1, ImmWord(0), failure);
// Don't handle RegExps which read and write to lastIndex.
masm.branchTest32(Assembler::NonZero, Address(temp1, RegExpShared::offsetOfFlags()),
Imm32(StickyFlag | GlobalFlag), failure);
if (mode == RegExpShared::Normal) {
// Don't handle RegExps with excessive parens.
masm.load32(Address(temp1, RegExpShared::offsetOfParenCount()), temp2);
masm.branch32(Assembler::AboveOrEqual, temp2, Imm32(RegExpMaxPairCount), failure);
// Fill in the paren count in the MatchPairs on the stack.
masm.add32(Imm32(1), temp2);
masm.store32(temp2, pairCountAddress);
}
// Load the code pointer for the type of input string we have, and compute
// the input start/end pointers in the InputOutputData.
Register codePointer = temp1;
{
masm.loadStringChars(input, temp2);
masm.storePtr(temp2, inputStartAddress);
masm.loadStringLength(input, temp3);
Label isLatin1, done;
masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
{
masm.lshiftPtr(Imm32(1), temp3);
masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, false)), codePointer);
}
masm.jump(&done);
{
masm.bind(&isLatin1);
masm.loadPtr(Address(temp1, RegExpShared::offsetOfJitCode(mode, true)), codePointer);
}
masm.bind(&done);
masm.addPtr(temp3, temp2);
masm.storePtr(temp2, inputEndAddress);
}
// Check the RegExpShared has been compiled for this type of input.
masm.branchPtr(Assembler::Equal, codePointer, ImmWord(0), failure);
masm.loadPtr(Address(codePointer, JitCode::offsetOfCode()), codePointer);
// Don't handle execution inside a PreserveRegExpStatics instance.
masm.branchPtr(Assembler::NotEqual, AbsoluteAddress(res->addressOfBufferLink()), ImmWord(0), failure);
// Finish filling in the InputOutputData instance on the stack.
if (mode == RegExpShared::Normal) {
masm.computeEffectiveAddress(Address(StackPointer, matchPairsStartOffset), temp2);
masm.storePtr(temp2, matchesPointerAddress);
}
masm.storePtr(ImmWord(0), startIndexAddress);
masm.store32(Imm32(0), matchResultAddress);
// Save any volatile inputs.
GeneralRegisterSet volatileRegs;
if (input.volatile_())
volatileRegs.add(input);
if (regexp.volatile_())
volatileRegs.add(regexp);
// Execute the RegExp.
masm.computeEffectiveAddress(Address(StackPointer, inputOutputDataStartOffset), temp2);
masm.PushRegsInMask(volatileRegs);
masm.setupUnalignedABICall(1, temp3);
masm.passABIArg(temp2);
masm.callWithABI(codePointer);
masm.PopRegsInMask(volatileRegs);
Label success;
masm.branch32(Assembler::Equal, matchResultAddress,
Imm32(RegExpRunStatus_Success_NotFound), notFound);
masm.branch32(Assembler::Equal, matchResultAddress,
Imm32(RegExpRunStatus_Error), failure);
// Lazily update the RegExpStatics.
masm.movePtr(ImmPtr(res), temp1);
Address pendingInputAddress(temp1, RegExpStatics::offsetOfPendingInput());
Address matchesInputAddress(temp1, RegExpStatics::offsetOfMatchesInput());
Address lazySourceAddress(temp1, RegExpStatics::offsetOfLazySource());
masm.patchableCallPreBarrier(pendingInputAddress, MIRType_String);
masm.patchableCallPreBarrier(matchesInputAddress, MIRType_String);
masm.patchableCallPreBarrier(lazySourceAddress, MIRType_String);
masm.storePtr(input, pendingInputAddress);
masm.storePtr(input, matchesInputAddress);
masm.storePtr(ImmWord(0), Address(temp1, RegExpStatics::offsetOfLazyIndex()));
masm.store32(Imm32(1), Address(temp1, RegExpStatics::offsetOfPendingLazyEvaluation()));
masm.loadPtr(Address(regexp, NativeObject::getFixedSlotOffset(RegExpObject::PRIVATE_SLOT)), temp2);
masm.loadPtr(Address(temp2, RegExpShared::offsetOfSource()), temp3);
masm.storePtr(temp3, lazySourceAddress);
masm.load32(Address(temp2, RegExpShared::offsetOfFlags()), temp3);
masm.store32(temp3, Address(temp1, RegExpStatics::offsetOfLazyFlags()));
return true;
}
static void
CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len, Register scratch,
size_t fromWidth, size_t toWidth);
static void
CreateDependentString(MacroAssembler &masm, const JSAtomState &names,
bool latin1, Register string,
Register base, Register temp1, Register temp2,
BaseIndex startIndexAddress, BaseIndex limitIndexAddress,
Label *failure)
{
// Compute the string length.
masm.load32(startIndexAddress, temp2);
masm.load32(limitIndexAddress, temp1);
masm.sub32(temp2, temp1);
Label done, nonEmpty;
// Zero length matches use the empty string.
masm.branchTest32(Assembler::NonZero, temp1, temp1, &nonEmpty);
masm.movePtr(ImmGCPtr(names.empty), string);
masm.jump(&done);
masm.bind(&nonEmpty);
Label notInline;
int32_t maxInlineLength =
latin1 ? JSFatInlineString::MAX_LENGTH_LATIN1 : JSFatInlineString::MAX_LENGTH_TWO_BYTE;
masm.branch32(Assembler::Above, temp1, Imm32(maxInlineLength), &notInline);
{
// Make a normal or fat inline string.
Label stringAllocated, fatInline;
int32_t maxNormalInlineLength =
latin1 ? JSInlineString::MAX_LENGTH_LATIN1 : JSInlineString::MAX_LENGTH_TWO_BYTE;
masm.branch32(Assembler::Above, temp1, Imm32(maxNormalInlineLength), &fatInline);
int32_t normalFlags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::INIT_INLINE_FLAGS;
masm.newGCString(string, temp2, failure);
masm.store32(Imm32(normalFlags), Address(string, JSString::offsetOfFlags()));
masm.jump(&stringAllocated);
masm.bind(&fatInline);
int32_t fatFlags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::INIT_FAT_INLINE_FLAGS;
masm.newGCFatInlineString(string, temp2, failure);
masm.store32(Imm32(fatFlags), Address(string, JSString::offsetOfFlags()));
masm.bind(&stringAllocated);
masm.store32(temp1, Address(string, JSString::offsetOfLength()));
masm.push(string);
masm.push(base);
// Adjust the start index address for the above pushes.
MOZ_ASSERT(startIndexAddress.base == StackPointer);
BaseIndex newStartIndexAddress = startIndexAddress;
newStartIndexAddress.offset += 2 * sizeof(void *);
// Load chars pointer for the new string.
masm.addPtr(ImmWord(JSInlineString::offsetOfInlineStorage()), string);
// Load the source characters pointer.
masm.loadStringChars(base, base);
masm.load32(newStartIndexAddress, temp2);
if (latin1)
masm.addPtr(temp2, base);
else
masm.computeEffectiveAddress(BaseIndex(base, temp2, TimesTwo), base);
CopyStringChars(masm, string, base, temp1, temp2, latin1 ? 1 : 2, latin1 ? 1 : 2);
// Null-terminate.
if (latin1)
masm.store8(Imm32(0), Address(string, 0));
else
masm.store16(Imm32(0), Address(string, 0));
masm.pop(base);
masm.pop(string);
}
masm.jump(&done);
masm.bind(&notInline);
{
// Make a dependent string.
int32_t flags = (latin1 ? JSString::LATIN1_CHARS_BIT : 0) | JSString::DEPENDENT_FLAGS;
masm.newGCString(string, temp2, failure);
masm.store32(Imm32(flags), Address(string, JSString::offsetOfFlags()));
masm.store32(temp1, Address(string, JSString::offsetOfLength()));
masm.loadPtr(Address(base, JSString::offsetOfNonInlineChars()), temp1);
masm.load32(startIndexAddress, temp2);
if (latin1)
masm.addPtr(temp2, temp1);
else
masm.computeEffectiveAddress(BaseIndex(temp1, temp2, TimesTwo), temp1);
masm.storePtr(temp1, Address(string, JSString::offsetOfNonInlineChars()));
masm.storePtr(base, Address(string, JSDependentString::offsetOfBase()));
// Follow any base pointer if the input is itself a dependent string.
// Watch for undepended strings, which have a base pointer but don't
// actually share their characters with it.
Label noBase;
masm.branchTest32(Assembler::Zero, Address(base, JSString::offsetOfFlags()),
Imm32(JSString::HAS_BASE_BIT), &noBase);
masm.branchTest32(Assembler::Zero, Address(base, JSString::offsetOfFlags()),
Imm32(JSString::FLAT_BIT), &noBase);
masm.loadPtr(Address(base, JSDependentString::offsetOfBase()), temp1);
masm.storePtr(temp1, Address(string, JSDependentString::offsetOfBase()));
masm.bind(&noBase);
}
masm.bind(&done);
}
JitCode *
JitCompartment::generateRegExpExecStub(JSContext *cx)
{
Register regexp = CallTempReg0;
Register input = CallTempReg1;
ValueOperand result = JSReturnOperand;
// We are free to clobber all registers, as LRegExpExec is a call instruction.
GeneralRegisterSet regs = GeneralRegisterSet::All();
regs.take(input);
regs.take(regexp);
// temp5 is used in single byte instructions when creating dependent
// strings, and has restrictions on which register it can be on some
// platforms.
Register temp5;
{
GeneralRegisterSet oregs = regs;
do {
temp5 = oregs.takeAny();
} while (!MacroAssembler::canUseInSingleByteInstruction(temp5));
regs.take(temp5);
}
Register temp1 = regs.takeAny();
Register temp2 = regs.takeAny();
Register temp3 = regs.takeAny();
Register temp4 = regs.takeAny();
ArrayObject *templateObject = cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);
if (!templateObject)
return nullptr;
// The template object should have enough space for the maximum number of
// pairs this stub can handle.
MOZ_ASSERT(ObjectElements::VALUES_PER_HEADER + RegExpMaxPairCount ==
gc::GetGCKindSlots(templateObject->asTenured().getAllocKind()));
MacroAssembler masm(cx);
// The InputOutputData is placed above the return address on the stack.
size_t inputOutputDataStartOffset = sizeof(void *);
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3,
inputOutputDataStartOffset, RegExpShared::Normal,
&notFound, &oolEntry))
{
return nullptr;
}
// Construct the result.
Register object = temp1;
masm.createGCObject(object, temp2, templateObject, gc::DefaultHeap, &oolEntry);
Register matchIndex = temp2;
masm.move32(Imm32(0), matchIndex);
size_t pairsVectorStartOffset = RegExpPairsVectorStartOffset(inputOutputDataStartOffset);
Address pairsVectorAddress(StackPointer, pairsVectorStartOffset);
Address pairCountAddress = RegExpPairCountAddress(inputOutputDataStartOffset);
size_t elementsOffset = NativeObject::offsetOfFixedElements();
BaseIndex stringAddress(object, matchIndex, TimesEight, elementsOffset);
JS_STATIC_ASSERT(sizeof(MatchPair) == 8);
BaseIndex stringIndexAddress(StackPointer, matchIndex, TimesEight,
pairsVectorStartOffset + offsetof(MatchPair, start));
BaseIndex stringLimitAddress(StackPointer, matchIndex, TimesEight,
pairsVectorStartOffset + offsetof(MatchPair, limit));
// Loop to construct the match strings. There are two different loops,
// depending on whether the input is latin1.
{
Label isLatin1, done;
masm.branchTest32(Assembler::NonZero, Address(input, JSString::offsetOfFlags()),
Imm32(JSString::LATIN1_CHARS_BIT), &isLatin1);
for (int isLatin = 0; isLatin <= 1; isLatin++) {
if (isLatin)
masm.bind(&isLatin1);
Label matchLoop;
masm.bind(&matchLoop);
Label isUndefined, storeDone;
masm.branch32(Assembler::LessThan, stringIndexAddress, Imm32(0), &isUndefined);
CreateDependentString(masm, cx->names(), isLatin, temp3, input, temp4, temp5,
stringIndexAddress, stringLimitAddress, &oolEntry);
masm.storeValue(JSVAL_TYPE_STRING, temp3, stringAddress);
masm.jump(&storeDone);
masm.bind(&isUndefined);
masm.storeValue(UndefinedValue(), stringAddress);
masm.bind(&storeDone);
masm.add32(Imm32(1), matchIndex);
masm.branch32(Assembler::LessThanOrEqual, pairCountAddress, matchIndex, &done);
masm.jump(&matchLoop);
}
masm.bind(&done);
}
// Fill in the rest of the output object.
masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfInitializedLength()));
masm.store32(matchIndex, Address(object, elementsOffset + ObjectElements::offsetOfLength()));
masm.loadPtr(Address(object, NativeObject::offsetOfSlots()), temp2);
MOZ_ASSERT(templateObject->numFixedSlots() == 0);
MOZ_ASSERT(templateObject->lookupPure(cx->names().index)->slot() == 0);
MOZ_ASSERT(templateObject->lookupPure(cx->names().input)->slot() == 1);
masm.load32(pairsVectorAddress, temp3);
masm.storeValue(JSVAL_TYPE_INT32, temp3, Address(temp2, 0));
masm.storeValue(JSVAL_TYPE_STRING, input, Address(temp2, sizeof(Value)));
// All done!
masm.tagValue(JSVAL_TYPE_OBJECT, object, result);
masm.ret();
masm.bind(&notFound);
masm.moveValue(NullValue(), result);
masm.ret();
// Use an undefined value to signal to the caller that the OOL stub needs to be called.
masm.bind(&oolEntry);
masm.moveValue(UndefinedValue(), result);
masm.ret();
Linker linker(masm);
AutoFlushICache afc("RegExpExecStub");
JitCode *code = linker.newCode<CanGC>(cx, OTHER_CODE);
#ifdef JS_ION_PERF
writePerfSpewerJitCodeProfile(code, "RegExpExecStub");
#endif
if (cx->zone()->needsIncrementalBarrier())
code->togglePreBarriers(true);
return code;
}
class OutOfLineRegExpExec : public OutOfLineCodeBase<CodeGenerator>
{
LRegExpExec *lir_;
public:
explicit OutOfLineRegExpExec(LRegExpExec *lir)
: lir_(lir)
{ }
bool accept(CodeGenerator *codegen) {
return codegen->visitOutOfLineRegExpExec(this);
}
LRegExpExec *lir() const {
return lir_;
}
};
typedef bool (*RegExpExecRawFn)(JSContext *cx, HandleObject regexp,
HandleString input, MutableHandleValue output);
HandleString input, MatchPairs *pairs, MutableHandleValue output);
static const VMFunction RegExpExecRawInfo = FunctionInfo<RegExpExecRawFn>(regexp_exec_raw);
bool
CodeGenerator::visitOutOfLineRegExpExec(OutOfLineRegExpExec *ool)
{
LRegExpExec *lir = ool->lir();
Register input = ToRegister(lir->string());
Register regexp = ToRegister(lir->regexp());
GeneralRegisterSet regs = GeneralRegisterSet::All();
regs.take(input);
regs.take(regexp);
Register temp = regs.takeAny();
masm.computeEffectiveAddress(Address(StackPointer, sizeof(irregexp::InputOutputData)), temp);
pushArg(temp);
pushArg(input);
pushArg(regexp);
if (!callVM(RegExpExecRawInfo, lir))
return false;
masm.jump(ool->rejoin());
return true;
}
bool
CodeGenerator::visitRegExpExec(LRegExpExec *lir)
{
pushArg(ToRegister(lir->string()));
pushArg(ToRegister(lir->regexp()));
return callVM(RegExpExecRawInfo, lir);
MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg0);
MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg1);
MOZ_ASSERT(GetValueOutput(lir) == JSReturnOperand);
masm.reserveStack(RegExpReservedStack);
OutOfLineRegExpExec *ool = new(alloc()) OutOfLineRegExpExec(lir);
if (!addOutOfLineCode(ool, lir->mir()))
return false;
JitCode *regExpExecStub = gen->compartment->jitCompartment()->regExpExecStubNoBarrier();
masm.call(regExpExecStub);
masm.branchTestUndefined(Assembler::Equal, JSReturnOperand, ool->entry());
masm.bind(ool->rejoin());
masm.freeStack(RegExpReservedStack);
return true;
}
// The value returned by the RegExp test stub if inline execution failed.
static const int32_t RegExpTestFailedValue = 2;
JitCode *
JitCompartment::generateRegExpTestStub(JSContext *cx)
{
Register regexp = CallTempReg2;
Register input = CallTempReg3;
Register result = ReturnReg;
MOZ_ASSERT(regexp != result && input != result);
// We are free to clobber all registers, as LRegExpTest is a call instruction.
GeneralRegisterSet regs = GeneralRegisterSet::All();
regs.take(input);
regs.take(regexp);
Register temp1 = regs.takeAny();
Register temp2 = regs.takeAny();
Register temp3 = regs.takeAny();
MacroAssembler masm(cx);
masm.reserveStack(sizeof(irregexp::InputOutputData));
Label notFound, oolEntry;
if (!PrepareAndExecuteRegExp(cx, masm, regexp, input, temp1, temp2, temp3, 0,
RegExpShared::MatchOnly, &notFound, &oolEntry))
{
return nullptr;
}
Label done;
masm.move32(Imm32(1), result);
masm.jump(&done);
masm.bind(&notFound);
masm.move32(Imm32(0), result);
masm.jump(&done);
masm.bind(&oolEntry);
masm.move32(Imm32(RegExpTestFailedValue), result);
masm.bind(&done);
masm.freeStack(sizeof(irregexp::InputOutputData));
masm.ret();
Linker linker(masm);
AutoFlushICache afc("RegExpTestStub");
JitCode *code = linker.newCode<CanGC>(cx, OTHER_CODE);
#ifdef JS_ION_PERF
writePerfSpewerJitCodeProfile(code, "RegExpTestStub");
#endif
if (cx->zone()->needsIncrementalBarrier())
code->togglePreBarriers(true);
return code;
}
class OutOfLineRegExpTest : public OutOfLineCodeBase<CodeGenerator>
{
LRegExpTest *lir_;
public:
explicit OutOfLineRegExpTest(LRegExpTest *lir)
: lir_(lir)
{ }
bool accept(CodeGenerator *codegen) {
return codegen->visitOutOfLineRegExpTest(this);
}
LRegExpTest *lir() const {
return lir_;
}
};
typedef bool (*RegExpTestRawFn)(JSContext *cx, HandleObject regexp,
HandleString input, bool *result);
static const VMFunction RegExpTestRawInfo = FunctionInfo<RegExpTestRawFn>(regexp_test_raw);
bool
CodeGenerator::visitOutOfLineRegExpTest(OutOfLineRegExpTest *ool)
{
LRegExpTest *lir = ool->lir();
Register input = ToRegister(lir->string());
Register regexp = ToRegister(lir->regexp());
pushArg(input);
pushArg(regexp);
if (!callVM(RegExpTestRawInfo, lir))
return false;
masm.jump(ool->rejoin());
return true;
}
bool
CodeGenerator::visitRegExpTest(LRegExpTest *lir)
{
pushArg(ToRegister(lir->string()));
pushArg(ToRegister(lir->regexp()));
return callVM(RegExpTestRawInfo, lir);
MOZ_ASSERT(ToRegister(lir->regexp()) == CallTempReg2);
MOZ_ASSERT(ToRegister(lir->string()) == CallTempReg3);
MOZ_ASSERT(ToRegister(lir->output()) == ReturnReg);
OutOfLineRegExpTest *ool = new(alloc()) OutOfLineRegExpTest(lir);
if (!addOutOfLineCode(ool, lir->mir()))
return false;
JitCode *regExpTestStub = gen->compartment->jitCompartment()->regExpTestStubNoBarrier();
masm.call(regExpTestStub);
masm.branch32(Assembler::Equal, ReturnReg, Imm32(RegExpTestFailedValue), ool->entry());
masm.bind(ool->rejoin());
return true;
}
typedef JSString *(*RegExpReplaceFn)(JSContext *, HandleString, HandleObject, HandleString);

View File

@ -44,6 +44,8 @@ class OutOfLineNewGCThingPar;
class OutOfLineUpdateCache;
class OutOfLineCallPostWriteBarrier;
class OutOfLineIsCallable;
class OutOfLineRegExpExec;
class OutOfLineRegExpTest;
class CodeGenerator : public CodeGeneratorSpecific
{
@ -101,7 +103,9 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitInteger(LInteger *lir);
bool visitRegExp(LRegExp *lir);
bool visitRegExpExec(LRegExpExec *lir);
bool visitOutOfLineRegExpExec(OutOfLineRegExpExec *ool);
bool visitRegExpTest(LRegExpTest *lir);
bool visitOutOfLineRegExpTest(OutOfLineRegExpTest *ool);
bool visitRegExpReplace(LRegExpReplace *lir);
bool visitStringReplace(LStringReplace *lir);
bool visitLambda(LLambda *lir);

View File

@ -276,6 +276,11 @@ JitRuntime::initialize(JSContext *cx)
if (!valuePreBarrier_)
return false;
JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for String");
stringPreBarrier_ = generatePreBarrier(cx, MIRType_String);
if (!stringPreBarrier_)
return false;
JitSpew(JitSpew_Codegen, "# Emitting Pre Barrier for Shape");
shapePreBarrier_ = generatePreBarrier(cx, MIRType_Shape);
if (!shapePreBarrier_)
@ -498,6 +503,8 @@ JitCompartment::JitCompartment()
baselineSetPropReturnAddr_(nullptr),
stringConcatStub_(nullptr),
parallelStringConcatStub_(nullptr),
regExpExecStub_(nullptr),
regExpTestStub_(nullptr),
activeParallelEntryScripts_(nullptr)
{
}
@ -738,6 +745,12 @@ JitCompartment::sweep(FreeOp *fop, JSCompartment *compartment)
if (parallelStringConcatStub_ && !IsJitCodeMarked(&parallelStringConcatStub_))
parallelStringConcatStub_ = nullptr;
if (regExpExecStub_ && !IsJitCodeMarked(&regExpExecStub_))
regExpExecStub_ = nullptr;
if (regExpTestStub_ && !IsJitCodeMarked(&regExpTestStub_))
regExpTestStub_ = nullptr;
if (activeParallelEntryScripts_) {
for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
JSScript *script = e.front();
@ -749,6 +762,22 @@ JitCompartment::sweep(FreeOp *fop, JSCompartment *compartment)
}
}
void
JitCompartment::toggleBarriers(bool enabled)
{
// Toggle barriers in compartment wide stubs that have patchable pre barriers.
if (regExpExecStub_)
regExpExecStub_->togglePreBarriers(enabled);
if (regExpTestStub_)
regExpTestStub_->togglePreBarriers(enabled);
// Toggle barriers in baseline IC stubs.
for (ICStubCodeMap::Enum e(*stubCodes_); !e.empty(); e.popFront()) {
JitCode *code = *e.front().value().unsafeGet();
code->togglePreBarriers(enabled);
}
}
JitCode *
JitRuntime::getBailoutTable(const FrameSizeClass &frameClass) const
{
@ -1353,7 +1382,7 @@ jit::ToggleBarriers(JS::Zone *zone, bool needs)
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
if (comp->jitCompartment())
comp->jitCompartment()->toggleBaselineStubBarriers(needs);
comp->jitCompartment()->toggleBarriers(needs);
}
}

View File

@ -186,6 +186,7 @@ class JitRuntime
// Thunk that calls the GC pre barrier.
JitCode *valuePreBarrier_;
JitCode *stringPreBarrier_;
JitCode *shapePreBarrier_;
JitCode *typeObjectPreBarrier_;
@ -359,6 +360,7 @@ class JitRuntime
JitCode *preBarrier(MIRType type) const {
switch (type) {
case MIRType_Value: return valuePreBarrier_;
case MIRType_String: return stringPreBarrier_;
case MIRType_Shape: return shapePreBarrier_;
case MIRType_TypeObject: return typeObjectPreBarrier_;
default: MOZ_CRASH();
@ -439,13 +441,16 @@ class JitCompartment
void *baselineGetPropReturnAddr_;
void *baselineSetPropReturnAddr_;
// Stub to concatenate two strings inline. Note that it can't be
// stored in JitRuntime because masm.newGCString bakes in zone-specific
// pointers. These are weak pointers, but are not declared as ReadBarriered
// since they are only read from during Ion compilation, which may occur
// off thread and whose barriers are captured during CodeGenerator::link.
// Stubs to concatenate two strings inline, or perform RegExp calls inline.
// These bake in zone and compartment specific pointers and can't be stored
// in JitRuntime. These are weak pointers, but are not declared as
// ReadBarriered since they are only read from during Ion compilation,
// which may occur off thread and whose barriers are captured during
// CodeGenerator::link.
JitCode *stringConcatStub_;
JitCode *parallelStringConcatStub_;
JitCode *regExpExecStub_;
JitCode *regExpTestStub_;
// Set of JSScripts invoked by ForkJoin (i.e. the entry script). These
// scripts are marked if their respective parallel IonScripts' age is less
@ -454,6 +459,8 @@ class JitCompartment
ScriptSet *activeParallelEntryScripts_;
JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
JitCode *generateRegExpExecStub(JSContext *cx);
JitCode *generateRegExpTestStub(JSContext *cx);
public:
JitCode *getStubCode(uint32_t key) {
@ -498,7 +505,7 @@ class JitCompartment
bool notifyOfActiveParallelEntryScript(JSContext *cx, HandleScript script);
bool hasRecentParallelActivity() const;
void toggleBaselineStubBarriers(bool enabled);
void toggleBarriers(bool enabled);
ExecutableAllocator *createIonAlloc();
@ -521,6 +528,28 @@ class JitCompartment
default: MOZ_CRASH("No such execution mode");
}
}
JitCode *regExpExecStubNoBarrier() const {
return regExpExecStub_;
}
bool ensureRegExpExecStubExists(JSContext *cx) {
if (regExpExecStub_)
return true;
regExpExecStub_ = generateRegExpExecStub(cx);
return regExpExecStub_ != nullptr;
}
JitCode *regExpTestStubNoBarrier() const {
return regExpTestStub_;
}
bool ensureRegExpTestStubExists(JSContext *cx) {
if (regExpTestStub_)
return true;
regExpTestStub_ = generateRegExpTestStub(cx);
return regExpTestStub_ != nullptr;
}
};
// Called from JSCompartment::discardJitCode().

View File

@ -2095,8 +2095,8 @@ LIRGenerator::visitRegExpExec(MRegExpExec *ins)
MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
MOZ_ASSERT(ins->string()->type() == MIRType_String);
LRegExpExec *lir = new(alloc()) LRegExpExec(useRegisterAtStart(ins->regexp()),
useRegisterAtStart(ins->string()));
LRegExpExec *lir = new(alloc()) LRegExpExec(useFixedAtStart(ins->regexp(), CallTempReg0),
useFixedAtStart(ins->string(), CallTempReg1));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}
@ -2106,8 +2106,8 @@ LIRGenerator::visitRegExpTest(MRegExpTest *ins)
MOZ_ASSERT(ins->regexp()->type() == MIRType_Object);
MOZ_ASSERT(ins->string()->type() == MIRType_String);
LRegExpTest *lir = new(alloc()) LRegExpTest(useRegisterAtStart(ins->regexp()),
useRegisterAtStart(ins->string()));
LRegExpTest *lir = new(alloc()) LRegExpTest(useFixedAtStart(ins->regexp(), CallTempReg2),
useFixedAtStart(ins->string(), CallTempReg3));
return defineReturn(lir, ins) && assignSafepoint(lir, ins);
}

View File

@ -1389,6 +1389,10 @@ IonBuilder::inlineRegExpExec(CallInfo &callInfo)
if (callInfo.getArg(0)->mightBeType(MIRType_Object))
return InliningStatus_NotInlined;
JSContext *cx = GetIonContext()->cx;
if (!cx->compartment()->jitCompartment()->ensureRegExpExecStubExists(cx))
return InliningStatus_Error;
callInfo.setImplicitlyUsedUnchecked();
MInstruction *exec = MRegExpExec::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));
@ -1423,6 +1427,10 @@ IonBuilder::inlineRegExpTest(CallInfo &callInfo)
if (callInfo.getArg(0)->mightBeType(MIRType_Object))
return InliningStatus_NotInlined;
JSContext *cx = GetIonContext()->cx;
if (!cx->compartment()->jitCompartment()->ensureRegExpTestStubExists(cx))
return InliningStatus_Error;
callInfo.setImplicitlyUsedUnchecked();
MInstruction *match = MRegExpTest::New(alloc(), callInfo.thisArg(), callInfo.getArg(0));

View File

@ -906,7 +906,7 @@ bool RRegExpExec::recover(JSContext *cx, SnapshotIterator &iter) const{
RootedValue result(cx);
if(!regexp_exec_raw(cx, regexp, input, &result))
if (!regexp_exec_raw(cx, regexp, input, nullptr, &result))
return false;
iter.storeInstructionResult(result);

View File

@ -1216,6 +1216,13 @@ MarkValueFromIon(JSRuntime *rt, Value *vp)
gc::MarkValueUnbarriered(&rt->gc.marker, vp, "write barrier");
}
void
MarkStringFromIon(JSRuntime *rt, JSString **stringp)
{
if (*stringp)
gc::MarkStringUnbarriered(&rt->gc.marker, stringp, "write barrier");
}
void
MarkShapeFromIon(JSRuntime *rt, Shape **shapep)
{

View File

@ -742,6 +742,7 @@ void AssertValidValue(JSContext *cx, Value *v);
JSObject *TypedObjectProto(JSObject *obj);
void MarkValueFromIon(JSRuntime *rt, Value *vp);
void MarkStringFromIon(JSRuntime *rt, JSString **stringp);
void MarkShapeFromIon(JSRuntime *rt, Shape **shapep);
void MarkTypeObjectFromIon(JSRuntime *rt, types::TypeObject **typep);
@ -752,6 +753,8 @@ IonMarkFunction(MIRType type)
switch (type) {
case MIRType_Value:
return JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon);
case MIRType_String:
return JS_FUNC_TO_DATA_PTR(void *, MarkStringFromIon);
case MIRType_Shape:
return JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon);
case MIRType_TypeObject:

View File

@ -4158,6 +4158,17 @@ MacroAssemblerARMCompat::callWithABI(const Address &fun, MoveOp::Type result)
callWithABIPost(stackAdjust, result);
}
void
MacroAssemblerARMCompat::callWithABI(Register fun, MoveOp::Type result)
{
// Load the callee in r12, as above.
ma_mov(fun, r12);
uint32_t stackAdjust;
callWithABIPre(&stackAdjust);
call(r12);
callWithABIPost(stackAdjust, result);
}
void
MacroAssemblerARMCompat::handleFailureWithHandler(void *handler)
{

View File

@ -886,6 +886,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
load32(lhs, secondScratchReg_);
branch32(cond, secondScratchReg_, rhs, label);
}
void branch32(Condition cond, const BaseIndex &lhs, Imm32 rhs, Label *label) {
// branch32 will use ScratchRegister.
load32(lhs, secondScratchReg_);
branch32(cond, secondScratchReg_, rhs, label);
}
void branchPtr(Condition cond, const Address &lhs, Register rhs, Label *label) {
branch32(cond, lhs, rhs, label);
}
@ -1056,6 +1061,11 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
ma_cmp(ScratchRegister, ptr);
ma_b(label, cond);
}
void branchPtr(Condition cond, AbsoluteAddress addr, ImmWord ptr, Label *label) {
loadPtr(addr, ScratchRegister);
ma_cmp(ScratchRegister, ptr);
ma_b(label, cond);
}
void branchPtr(Condition cond, AsmJSAbsoluteAddress addr, Register ptr, Label *label) {
loadPtr(addr, ScratchRegister);
ma_cmp(ScratchRegister, ptr);
@ -1122,10 +1132,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void storeValue(ValueOperand val, Operand dst);
void storeValue(ValueOperand val, const BaseIndex &dest);
void storeValue(JSValueType type, Register reg, BaseIndex dest) {
// Harder cases not handled yet.
MOZ_ASSERT(dest.offset == 0);
ma_alu(dest.base, lsl(dest.index, dest.scale), ScratchRegister, OpAdd);
storeValue(type, reg, Address(ScratchRegister, 0));
storeValue(type, reg, Address(ScratchRegister, dest.offset));
}
void storeValue(ValueOperand val, const Address &dest) {
storeValue(val, Operand(dest));
@ -1146,10 +1154,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
ma_str(secondScratchReg_, dest);
}
void storeValue(const Value &val, BaseIndex dest) {
// Harder cases not handled yet.
MOZ_ASSERT(dest.offset == 0);
ma_alu(dest.base, lsl(dest.index, dest.scale), ScratchRegister, OpAdd);
storeValue(val, Address(ScratchRegister, 0));
storeValue(val, Address(ScratchRegister, dest.offset));
}
void loadValue(Address src, ValueOperand val);
@ -1558,6 +1564,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
void callWithABI(void *fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(const Address &fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
CodeOffsetLabel labelForPatch() {
return CodeOffsetLabel(nextOffset().getOffset());

View File

@ -3440,6 +3440,17 @@ MacroAssemblerMIPSCompat::callWithABI(const Address &fun, MoveOp::Type result)
}
void
MacroAssemblerMIPSCompat::callWithABI(Register fun, MoveOp::Type result)
{
// Load the callee in t9, as above.
ma_move(t9, fun);
uint32_t stackAdjust;
callWithABIPre(&stackAdjust);
call(t9);
callWithABIPost(stackAdjust, result);
}
void
MacroAssemblerMIPSCompat::handleFailureWithHandler(void *handler)
{

View File

@ -629,6 +629,10 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
ma_lw(SecondScratchReg, lhs);
ma_b(SecondScratchReg, rhs, label, cond);
}
void branch32(Condition cond, const BaseIndex &lhs, Imm32 rhs, Label *label) {
load32(lhs, SecondScratchReg);
ma_b(SecondScratchReg, rhs, label, cond);
}
void branchPtr(Condition cond, const Address &lhs, Register rhs, Label *label) {
branch32(cond, lhs, rhs, label);
}
@ -796,6 +800,10 @@ public:
loadPtr(addr, ScratchRegister);
ma_b(ScratchRegister, ptr, label, cond);
}
void branchPtr(Condition cond, AbsoluteAddress addr, ImmWord ptr, Label *label) {
loadPtr(addr, ScratchRegister);
ma_b(ScratchRegister, Imm32(ptr.value), label, cond);
}
void branchPtr(Condition cond, AsmJSAbsoluteAddress addr, Register ptr,
Label *label) {
loadPtr(addr, ScratchRegister);
@ -1253,6 +1261,7 @@ public:
void callWithABI(void *fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(const Address &fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
CodeOffsetLabel labelForPatch() {
return CodeOffsetLabel(nextOffset().getOffset());

View File

@ -865,6 +865,8 @@ class AssemblerShared
void append(AsmJSAbsoluteLink link) { enoughMemory_ &= asmJSAbsoluteLinks_.append(link); }
size_t numAsmJSAbsoluteLinks() const { return asmJSAbsoluteLinks_.length(); }
AsmJSAbsoluteLink asmJSAbsoluteLink(size_t i) const { return asmJSAbsoluteLinks_[i]; }
static bool canUseInSingleByteInstruction(Register reg) { return true; }
};
} // namespace jit

View File

@ -161,6 +161,13 @@ namespace X86Registers {
} /* namespace X86Registers */
// Byte operand register spl & above require a REX prefix (to prevent
// the 'H' registers be accessed).
static inline bool
ByteRegRequiresRex(int reg)
{
return (reg >= X86Registers::esp);
}
class X86Assembler : public GenericAssembler {
public:
@ -4407,16 +4414,16 @@ private:
// byte of the second four registers (spl..dil).
//
// Address operands should still be checked using regRequiresRex(),
// while byteRegRequiresRex() is provided to check byte register
// while ByteRegRequiresRex() is provided to check byte register
// operands.
void oneByteOp8(OneByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm)
{
#ifdef JS_CODEGEN_X86
MOZ_ASSERT(!byteRegRequiresRex(rm));
MOZ_ASSERT(!ByteRegRequiresRex(rm));
#endif
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(byteRegRequiresRex(rm), 0, 0, rm);
emitRexIf(ByteRegRequiresRex(rm), 0, 0, rm);
m_buffer.putByteUnchecked(opcode);
registerModRM(groupOp, rm);
}
@ -4433,10 +4440,10 @@ private:
void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, int offset)
{
#ifdef JS_CODEGEN_X86
MOZ_ASSERT(!byteRegRequiresRex(reg));
MOZ_ASSERT(!ByteRegRequiresRex(reg));
#endif
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(byteRegRequiresRex(reg), reg, 0, base);
emitRexIf(ByteRegRequiresRex(reg), reg, 0, base);
m_buffer.putByteUnchecked(opcode);
memoryModRM(reg, base, offset);
}
@ -4444,10 +4451,10 @@ private:
void oneByteOp8_disp32(OneByteOpcodeID opcode, int reg, RegisterID base, int offset)
{
#ifdef JS_CODEGEN_X86
MOZ_ASSERT(!byteRegRequiresRex(reg));
MOZ_ASSERT(!ByteRegRequiresRex(reg));
#endif
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(byteRegRequiresRex(reg), reg, 0, base);
emitRexIf(ByteRegRequiresRex(reg), reg, 0, base);
m_buffer.putByteUnchecked(opcode);
memoryModRM_disp32(reg, base, offset);
}
@ -4455,10 +4462,10 @@ private:
void oneByteOp8(OneByteOpcodeID opcode, int reg, RegisterID base, RegisterID index, int scale, int offset)
{
#ifdef JS_CODEGEN_X86
MOZ_ASSERT(!byteRegRequiresRex(reg));
MOZ_ASSERT(!ByteRegRequiresRex(reg));
#endif
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(byteRegRequiresRex(reg), reg, index, base);
emitRexIf(ByteRegRequiresRex(reg), reg, index, base);
m_buffer.putByteUnchecked(opcode);
memoryModRM(reg, base, index, scale, offset);
}
@ -4466,10 +4473,10 @@ private:
void oneByteOp8(OneByteOpcodeID opcode, int reg, const void* address)
{
#ifdef JS_CODEGEN_X86
MOZ_ASSERT(!byteRegRequiresRex(reg));
MOZ_ASSERT(!ByteRegRequiresRex(reg));
#endif
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(byteRegRequiresRex(reg), reg, 0, 0);
emitRexIf(ByteRegRequiresRex(reg), reg, 0, 0);
m_buffer.putByteUnchecked(opcode);
memoryModRM_disp32(reg, address);
}
@ -4477,7 +4484,7 @@ private:
void twoByteOp8(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm)
{
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(byteRegRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm);
emitRexIf(ByteRegRequiresRex(reg)|ByteRegRequiresRex(rm), reg, 0, rm);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode);
registerModRM(reg, rm);
@ -4490,7 +4497,7 @@ private:
void twoByteOp8_movx(TwoByteOpcodeID opcode, RegisterID reg, RegisterID rm)
{
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(regRequiresRex(reg)|byteRegRequiresRex(rm), reg, 0, rm);
emitRexIf(regRequiresRex(reg)|ByteRegRequiresRex(rm), reg, 0, rm);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode);
registerModRM(reg, rm);
@ -4499,7 +4506,7 @@ private:
void twoByteOp8(TwoByteOpcodeID opcode, GroupOpcodeID groupOp, RegisterID rm)
{
m_buffer.ensureSpace(maxInstructionSize);
emitRexIf(byteRegRequiresRex(rm), 0, 0, rm);
emitRexIf(ByteRegRequiresRex(rm), 0, 0, rm);
m_buffer.putByteUnchecked(OP_2BYTE_ESCAPE);
m_buffer.putByteUnchecked(opcode);
registerModRM(groupOp, rm);
@ -4612,13 +4619,6 @@ private:
// Internals; ModRm and REX formatters.
// Byte operand register spl & above require a REX prefix (to prevent
// the 'H' registers be accessed).
inline bool byteRegRequiresRex(int reg)
{
return (reg >= X86Registers::esp);
}
static const RegisterID noBase = X86Registers::ebp;
static const RegisterID hasSib = X86Registers::esp;
static const RegisterID noIndex = X86Registers::esp;
@ -4644,7 +4644,7 @@ private:
emitRex(true, r, x, b);
}
// Used for operations with byte operands - use byteRegRequiresRex() to
// Used for operations with byte operands - use ByteRegRequiresRex() to
// check register operands, regRequiresRex() to check other registers
// (i.e. address base & index).
//

View File

@ -219,6 +219,14 @@ class MacroAssemblerX86Shared : public Assembler
cmpl(Operand(lhs), imm);
j(cond, label);
}
void branch32(Condition cond, const BaseIndex &lhs, Register rhs, Label *label) {
cmpl(Operand(lhs), rhs);
j(cond, label);
}
void branch32(Condition cond, const BaseIndex &lhs, Imm32 imm, Label *label) {
cmpl(Operand(lhs), imm);
j(cond, label);
}
void branch32(Condition cond, Register lhs, Imm32 imm, Label *label) {
cmpl(lhs, imm);
j(cond, label);

View File

@ -359,6 +359,24 @@ MacroAssemblerX64::callWithABI(Address fun, MoveOp::Type result)
callWithABIPost(stackAdjust, result);
}
void
MacroAssemblerX64::callWithABI(Register fun, MoveOp::Type result)
{
if (IsIntArgReg(fun)) {
// Callee register may be clobbered for an argument. Move the callee to
// r10, a volatile, non-argument register.
moveResolver_.addMove(MoveOperand(fun), MoveOperand(r10), MoveOp::GENERAL);
fun = r10;
}
MOZ_ASSERT(!IsIntArgReg(fun));
uint32_t stackAdjust;
callWithABIPre(&stackAdjust);
call(Operand(fun));
callWithABIPost(stackAdjust, result);
}
void
MacroAssemblerX64::handleFailureWithHandler(void *handler)
{

View File

@ -634,6 +634,14 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
branchPtr(cond, Operand(ScratchReg, 0x0), ptr, label);
}
}
void branchPtr(Condition cond, AbsoluteAddress addr, ImmWord ptr, Label *label) {
if (X86Assembler::isAddressImmediate(addr.addr)) {
branchPtr(cond, Operand(addr), ptr, label);
} else {
mov(ImmPtr(addr.addr), ScratchReg);
branchPtr(cond, Operand(ScratchReg, 0x0), ptr, label);
}
}
void branchPtr(Condition cond, AsmJSAbsoluteAddress addr, Register ptr, Label *label) {
MOZ_ASSERT(ptr != ScratchReg);
mov(AsmJSImmPtr(addr.kind()), ScratchReg);
@ -1361,6 +1369,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void callWithABI(void *fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(AsmJSImmPtr imm, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(Address fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
void handleFailureWithHandler(void *handler);
void handleFailureWithHandlerTail();

View File

@ -572,6 +572,10 @@ class Assembler : public AssemblerX86Shared
CodeOffsetLabel label = movlWithPatch(PatchedAbsoluteAddress(), dest);
append(AsmJSGlobalAccess(label, AsmJSActivationGlobalDataOffset));
}
static bool canUseInSingleByteInstruction(Register reg) {
return !ByteRegRequiresRex(reg.code());
}
};
// Get a register in which we plan to put a quantity that will be used as an

View File

@ -348,6 +348,15 @@ MacroAssemblerX86::callWithABI(const Address &fun, MoveOp::Type result)
callWithABIPost(stackAdjust, result);
}
void
MacroAssemblerX86::callWithABI(Register fun, MoveOp::Type result)
{
uint32_t stackAdjust;
callWithABIPre(&stackAdjust);
call(Operand(fun));
callWithABIPost(stackAdjust, result);
}
void
MacroAssemblerX86::handleFailureWithHandler(void *handler)
{

View File

@ -1120,6 +1120,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
void callWithABI(void *fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(AsmJSImmPtr fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(const Address &fun, MoveOp::Type result = MoveOp::GENERAL);
void callWithABI(Register fun, MoveOp::Type result = MoveOp::GENERAL);
// Used from within an Exit frame to handle a pending exception.
void handleFailureWithHandler(void *handler);

View File

@ -699,7 +699,7 @@ RegExpCompartment::~RegExpCompartment()
}
}
JSObject *
ArrayObject *
RegExpCompartment::createMatchResultTemplateObject(JSContext *cx)
{
MOZ_ASSERT(!matchResultTemplateObject_);
@ -737,6 +737,7 @@ RegExpCompartment::createMatchResultTemplateObject(JSContext *cx)
// Make sure type information reflects the indexed properties which might
// be added.
types::AddTypePropertyId(cx, templateObject, JSID_VOID, types::Type::StringType());
types::AddTypePropertyId(cx, templateObject, JSID_VOID, types::Type::UndefinedType());
matchResultTemplateObject_.set(templateObject);

View File

@ -15,6 +15,7 @@
#include "gc/Marking.h"
#include "gc/Zone.h"
#include "proxy/Proxy.h"
#include "vm/ArrayObject.h"
#include "vm/Shape.h"
/*
@ -201,6 +202,24 @@ class RegExpShared
bool marked() const { return marked_; }
void clearMarked() { marked_ = false; }
static size_t offsetOfSource() {
return offsetof(RegExpShared, source);
}
static size_t offsetOfFlags() {
return offsetof(RegExpShared, flags);
}
static size_t offsetOfParenCount() {
return offsetof(RegExpShared, parenCount);
}
static size_t offsetOfJitCode(CompilationMode mode, bool latin1) {
return offsetof(RegExpShared, compilationArray)
+ (CompilationIndex(mode, latin1) * sizeof(RegExpCompilation))
+ offsetof(RegExpCompilation, jitCode);
}
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
};
@ -286,9 +305,9 @@ class RegExpCompartment
* if there is a result. This is used in CreateRegExpMatchResult to set
* the input/index properties faster.
*/
ReadBarrieredObject matchResultTemplateObject_;
ReadBarriered<ArrayObject *> matchResultTemplateObject_;
JSObject *createMatchResultTemplateObject(JSContext *cx);
ArrayObject *createMatchResultTemplateObject(JSContext *cx);
public:
explicit RegExpCompartment(JSRuntime *rt);
@ -305,7 +324,7 @@ class RegExpCompartment
bool get(JSContext *cx, HandleAtom source, JSString *maybeOpt, RegExpGuard *g);
/* Get or create template object used to base the result of .exec() on. */
JSObject *getOrCreateMatchResultTemplateObject(JSContext *cx) {
ArrayObject *getOrCreateMatchResultTemplateObject(JSContext *cx) {
if (matchResultTemplateObject_)
return matchResultTemplateObject_;
return createMatchResultTemplateObject(cx);
@ -325,6 +344,7 @@ class RegExpObject : public NativeObject
public:
static const unsigned RESERVED_SLOTS = 6;
static const unsigned PRIVATE_SLOT = 7;
static const Class class_;
@ -436,7 +456,7 @@ class RegExpObject : public NativeObject
*/
bool createShared(JSContext *cx, RegExpGuard *g);
RegExpShared *maybeShared() const {
return static_cast<RegExpShared *>(NativeObject::getPrivate());
return static_cast<RegExpShared *>(NativeObject::getPrivate(PRIVATE_SLOT));
}
/* Call setShared in preference to setPrivate. */

View File

@ -37,10 +37,10 @@ class RegExpStatics
RegExpFlag flags;
/*
* If true, |matchesInput| and the |lazy*| fields may be used
* If non-zero, |matchesInput| and the |lazy*| fields may be used
* to replay the last executed RegExp, and |matches| is invalid.
*/
bool pendingLazyEvaluation;
int32_t pendingLazyEvaluation;
/* Linkage for preserving RegExpStatics during nested RegExp execution. */
RegExpStatics *bufferLink;
@ -156,6 +156,34 @@ class RegExpStatics
void getLastParen(JSSubString *out) const;
void getLeftContext(JSSubString *out) const;
void getRightContext(JSSubString *out) const;
const void *addressOfBufferLink() {
return &bufferLink;
}
static size_t offsetOfPendingInput() {
return offsetof(RegExpStatics, pendingInput);
}
static size_t offsetOfMatchesInput() {
return offsetof(RegExpStatics, matchesInput);
}
static size_t offsetOfLazySource() {
return offsetof(RegExpStatics, lazySource);
}
static size_t offsetOfLazyFlags() {
return offsetof(RegExpStatics, lazyFlags);
}
static size_t offsetOfLazyIndex() {
return offsetof(RegExpStatics, lazyIndex);
}
static size_t offsetOfPendingLazyEvaluation() {
return offsetof(RegExpStatics, pendingLazyEvaluation);
}
};
class AutoRegExpStaticsBuffer : private JS::CustomAutoRooter
@ -439,7 +467,7 @@ RegExpStatics::updateLazily(JSContext *cx, JSLinearString *input,
lazySource = shared->source;
lazyFlags = shared->flags;
lazyIndex = lastIndex;
pendingLazyEvaluation = true;
pendingLazyEvaluation = 1;
}
inline bool

View File

@ -161,10 +161,11 @@ JSDependentString::new_(js::ExclusiveContext *cx, JSLinearString *baseArg, size_
size_t length)
{
/* Try to avoid long chains of dependent strings. */
while (baseArg->isDependent()) {
if (baseArg->isDependent()) {
start += baseArg->asDependent().baseOffset();
baseArg = baseArg->asDependent().base();
}
MOZ_ASSERT(!baseArg->isDependent());
MOZ_ASSERT(start + length <= baseArg->length());
MOZ_ASSERT(baseArg->isFlat());

View File

@ -682,6 +682,10 @@ class JSDependentString : public JSLinearString
public:
static inline JSLinearString *new_(js::ExclusiveContext *cx, JSLinearString *base,
size_t start, size_t length);
inline static size_t offsetOfBase() {
return offsetof(JSDependentString, d.s.u3.base);
}
};
JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString));
@ -769,10 +773,10 @@ JS_STATIC_ASSERT(sizeof(JSExtensibleString) == sizeof(JSString));
*/
class JSInlineString : public JSFlatString
{
public:
static const size_t MAX_LENGTH_LATIN1 = NUM_INLINE_CHARS_LATIN1 - 1;
static const size_t MAX_LENGTH_TWO_BYTE = NUM_INLINE_CHARS_TWO_BYTE - 1;
public:
template <js::AllowGC allowGC>
static inline JSInlineString *new_(js::ThreadSafeContext *cx);