Bug 1644472 part 2 - Optimize RegExpMatcher, RegExpSearcher, RegExpTester intrinsics in CacheIR and Warp. r=iain

The CacheIR code is just a VM call that's slightly faster than the CallNative path.
The main benefit is that this lets Warp use the optimized regexp stubs.

At some point it would be nice to call the same code stubs from CacheIR.

Differential Revision: https://phabricator.services.mozilla.com/D79041
This commit is contained in:
Jan de Mooij 2020-06-11 16:51:22 +00:00
parent 9f2f76ec99
commit 8d8c0b77eb
7 changed files with 239 additions and 13 deletions

View File

@ -1115,14 +1115,14 @@ bool js::RegExpMatcher(JSContext* cx, unsigned argc, Value* vp) {
}
/*
* Separate interface for use by IonMonkey.
* This code cannot re-enter Ion code.
* Separate interface for use by the JITs.
* This code cannot re-enter JIT code.
*/
bool js::RegExpMatcherRaw(JSContext* cx, HandleObject regexp,
HandleString input, int32_t maybeLastIndex,
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.
// RegExp execution was successful only if the pairs have actually been
// filled in. Note that IC code always passes a nullptr maybeMatches.
if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch) {
Handle<RegExpObject*> reobj = regexp.as<RegExpObject>();
RootedRegExpShared shared(cx, RegExpObject::getShared(cx, reobj));
@ -1193,16 +1193,16 @@ bool js::RegExpSearcher(JSContext* cx, unsigned argc, Value* vp) {
}
/*
* Separate interface for use by IonMonkey.
* This code cannot re-enter Ion code.
* Separate interface for use by the JITs.
* This code cannot re-enter JIT code.
*/
bool js::RegExpSearcherRaw(JSContext* cx, HandleObject regexp,
HandleString input, int32_t lastIndex,
MatchPairs* maybeMatches, int32_t* result) {
MOZ_ASSERT(lastIndex >= 0);
// The MatchPairs will always be passed in, but RegExp execution was
// successful only if the pairs have actually been filled in.
// RegExp execution was successful only if the pairs have actually been
// filled in. Note that IC code always passes a nullptr maybeMatches.
if (maybeMatches && maybeMatches->pairsRaw()[0] > MatchPair::NoMatch) {
*result = CreateRegExpSearchResult(*maybeMatches);
return true;
@ -1246,8 +1246,8 @@ bool js::RegExpTester(JSContext* cx, unsigned argc, Value* vp) {
}
/*
* Separate interface for use by IonMonkey.
* This code cannot re-enter Ion code.
* Separate interface for use by the JITs.
* This code cannot re-enter JIT code.
*/
bool js::RegExpTesterRaw(JSContext* cx, HandleObject regexp, HandleString input,
int32_t lastIndex, int32_t* endIndex) {

View File

@ -5340,6 +5340,62 @@ AttachDecision CallIRGenerator::tryAttachHasClass(HandleFunction callee,
return AttachDecision::Attach;
}
AttachDecision CallIRGenerator::tryAttachRegExpMatcherSearcherTester(
HandleFunction callee, InlinableNative native) {
// Self-hosted code calls this with (object, string, int32) arguments.
if (argc_ != 3) {
return AttachDecision::NoAction;
}
if (!args_[0].isObject() || !args_[1].isString() || !args_[2].isInt32()) {
return AttachDecision::NoAction;
}
// Initialize the input operand.
Int32OperandId argcId(writer.setInputOperandId(0));
// Note: we don't need to call emitNativeCalleeGuard for intrinsics.
// Guard argument types.
ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
ObjOperandId reId = writer.guardToObject(arg0Id);
ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
StringOperandId inputId = writer.guardToString(arg1Id);
ValOperandId arg2Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg2, argc_);
Int32OperandId lastIndexId = writer.guardToInt32(arg2Id);
switch (native) {
case InlinableNative::RegExpMatcher:
writer.callRegExpMatcherResult(reId, inputId, lastIndexId);
writer.typeMonitorResult();
cacheIRStubKind_ = BaselineCacheIRStubKind::Monitored;
trackAttached("RegExpMatcher");
break;
case InlinableNative::RegExpSearcher:
// No type monitoring because this always returns an int32.
writer.callRegExpSearcherResult(reId, inputId, lastIndexId);
writer.returnFromIC();
cacheIRStubKind_ = BaselineCacheIRStubKind::Regular;
trackAttached("RegExpSearcher");
break;
case InlinableNative::RegExpTester:
// No type monitoring because this always returns an int32.
writer.callRegExpTesterResult(reId, inputId, lastIndexId);
writer.returnFromIC();
cacheIRStubKind_ = BaselineCacheIRStubKind::Regular;
trackAttached("RegExpTester");
break;
default:
MOZ_CRASH("Unexpected native");
}
return AttachDecision::Attach;
}
AttachDecision CallIRGenerator::tryAttachStringChar(HandleFunction callee,
StringChar kind) {
// Need one argument.
@ -6010,6 +6066,10 @@ AttachDecision CallIRGenerator::tryAttachInlinableNative(
// RegExp natives.
case InlinableNative::IsRegExpObject:
return tryAttachHasClass(callee, &RegExpObject::class_);
case InlinableNative::RegExpMatcher:
case InlinableNative::RegExpSearcher:
case InlinableNative::RegExpTester:
return tryAttachRegExpMatcherSearcherTester(callee, native);
// String natives.
case InlinableNative::String:

View File

@ -352,8 +352,9 @@ enum class AttachDecision {
} while (0)
// Set of arguments supported by GetIndexOfArgument.
// Support for Arg2 and up can be added easily, but is currently unneeded.
enum class ArgumentKind : uint8_t { Callee, This, NewTarget, Arg0, Arg1 };
// Support for higher argument indices can be added easily, but is currently
// unneeded.
enum class ArgumentKind : uint8_t { Callee, This, NewTarget, Arg0, Arg1, Arg2 };
// This function calculates the index of an argument based on the call flags.
// addArgc is an out-parameter, indicating whether the value of argc should
@ -380,7 +381,7 @@ inline int32_t GetIndexOfArgument(ArgumentKind kind, CallFlags flags,
break;
case CallFlags::Spread:
// Spread calls do not have Arg1 or higher.
MOZ_ASSERT(kind != ArgumentKind::Arg1);
MOZ_ASSERT(kind <= ArgumentKind::Arg0);
*addArgc = false;
break;
case CallFlags::FunCall:
@ -401,6 +402,8 @@ inline int32_t GetIndexOfArgument(ArgumentKind kind, CallFlags flags,
return flags.isConstructing() + hasArgumentArray - 1;
case ArgumentKind::Arg1:
return flags.isConstructing() + hasArgumentArray - 2;
case ArgumentKind::Arg2:
return flags.isConstructing() + hasArgumentArray - 3;
case ArgumentKind::NewTarget:
MOZ_ASSERT(flags.isConstructing());
*addArgc = false;
@ -1546,6 +1549,8 @@ class MOZ_RAII CallIRGenerator : public IRGenerator {
AttachDecision tryAttachGuardToClass(HandleFunction callee,
InlinableNative native);
AttachDecision tryAttachHasClass(HandleFunction callee, const JSClass* clasp);
AttachDecision tryAttachRegExpMatcherSearcherTester(HandleFunction callee,
InlinableNative native);
AttachDecision tryAttachStringChar(HandleFunction callee, StringChar kind);
AttachDecision tryAttachStringCharCodeAt(HandleFunction callee);
AttachDecision tryAttachStringCharAt(HandleFunction callee);

View File

@ -6182,6 +6182,75 @@ bool CacheIRCompiler::emitCallGetSparseElementResult(ObjOperandId objId,
return true;
}
bool CacheIRCompiler::emitCallRegExpMatcherResult(ObjOperandId regexpId,
StringOperandId inputId,
Int32OperandId lastIndexId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoCallVM callvm(masm, this, allocator);
Register regexp = allocator.useRegister(masm, regexpId);
Register input = allocator.useRegister(masm, inputId);
Register lastIndex = allocator.useRegister(masm, lastIndexId);
callvm.prepare();
masm.Push(ImmWord(0)); // nullptr MatchPairs.
masm.Push(lastIndex);
masm.Push(input);
masm.Push(regexp);
using Fn = bool (*)(JSContext*, HandleObject regexp, HandleString input,
int32_t lastIndex, MatchPairs * pairs,
MutableHandleValue output);
callvm.call<Fn, RegExpMatcherRaw>();
return true;
}
bool CacheIRCompiler::emitCallRegExpSearcherResult(ObjOperandId regexpId,
StringOperandId inputId,
Int32OperandId lastIndexId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoCallVM callvm(masm, this, allocator);
Register regexp = allocator.useRegister(masm, regexpId);
Register input = allocator.useRegister(masm, inputId);
Register lastIndex = allocator.useRegister(masm, lastIndexId);
callvm.prepare();
masm.Push(ImmWord(0)); // nullptr MatchPairs.
masm.Push(lastIndex);
masm.Push(input);
masm.Push(regexp);
using Fn = bool (*)(JSContext*, HandleObject regexp, HandleString input,
int32_t lastIndex, MatchPairs * pairs, int32_t * result);
callvm.call<Fn, RegExpSearcherRaw>();
return true;
}
bool CacheIRCompiler::emitCallRegExpTesterResult(ObjOperandId regexpId,
StringOperandId inputId,
Int32OperandId lastIndexId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);
AutoCallVM callvm(masm, this, allocator);
Register regexp = allocator.useRegister(masm, regexpId);
Register input = allocator.useRegister(masm, inputId);
Register lastIndex = allocator.useRegister(masm, lastIndexId);
callvm.prepare();
masm.Push(lastIndex);
masm.Push(input);
masm.Push(regexp);
using Fn = bool (*)(JSContext*, HandleObject regexp, HandleString input,
int32_t lastIndex, int32_t * result);
callvm.call<Fn, RegExpTesterRaw>();
return true;
}
template <typename Fn, Fn fn>
void CacheIRCompiler::callVM(MacroAssembler& masm) {
VMFunctionId id = VMFunctionToId<Fn, fn>::id;
@ -6320,6 +6389,10 @@ struct ReturnTypeToJSValueType<bool*> {
static constexpr JSValueType result = JSVAL_TYPE_BOOLEAN;
};
template <>
struct ReturnTypeToJSValueType<int32_t*> {
static constexpr JSValueType result = JSVAL_TYPE_INT32;
};
template <>
struct ReturnTypeToJSValueType<JSString*> {
static constexpr JSValueType result = JSVAL_TYPE_STRING;
};

View File

@ -203,6 +203,30 @@
obj: ObjId
clasp: RawPointerField
- name: CallRegExpMatcherResult
shared: true
transpile: true
args:
regexp: ObjId
input: StringId
lastIndex: Int32Id
- name: CallRegExpSearcherResult
shared: true
transpile: true
args:
regexp: ObjId
input: StringId
lastIndex: Int32Id
- name: CallRegExpTesterResult
shared: true
transpile: true
args:
regexp: ObjId
input: StringId
lastIndex: Int32Id
# Add a reference to a global in the compartment to keep it alive.
- name: GuardCompartment
shared: false

View File

@ -1297,6 +1297,48 @@ bool WarpCacheIRTranspiler::emitHasClassResult(ObjOperandId objId,
return true;
}
bool WarpCacheIRTranspiler::emitCallRegExpMatcherResult(
ObjOperandId regexpId, StringOperandId inputId,
Int32OperandId lastIndexId) {
MDefinition* regexp = getOperand(regexpId);
MDefinition* input = getOperand(inputId);
MDefinition* lastIndex = getOperand(lastIndexId);
auto* matcher = MRegExpMatcher::New(alloc(), regexp, input, lastIndex);
addEffectful(matcher);
pushResult(matcher);
return resumeAfter(matcher);
}
bool WarpCacheIRTranspiler::emitCallRegExpSearcherResult(
ObjOperandId regexpId, StringOperandId inputId,
Int32OperandId lastIndexId) {
MDefinition* regexp = getOperand(regexpId);
MDefinition* input = getOperand(inputId);
MDefinition* lastIndex = getOperand(lastIndexId);
auto* searcher = MRegExpSearcher::New(alloc(), regexp, input, lastIndex);
addEffectful(searcher);
pushResult(searcher);
return resumeAfter(searcher);
}
bool WarpCacheIRTranspiler::emitCallRegExpTesterResult(
ObjOperandId regexpId, StringOperandId inputId,
Int32OperandId lastIndexId) {
MDefinition* regexp = getOperand(regexpId);
MDefinition* input = getOperand(inputId);
MDefinition* lastIndex = getOperand(lastIndexId);
auto* tester = MRegExpTester::New(alloc(), regexp, input, lastIndex);
addEffectful(tester);
pushResult(tester);
return resumeAfter(tester);
}
bool WarpCacheIRTranspiler::emitIsArrayResult(ValOperandId inputId) {
MDefinition* value = getOperand(inputId);

View File

@ -748,6 +748,28 @@ AbortReasonOr<Ok> WarpOracle::maybeInlineIC(WarpOpSnapshotList& snapshots,
return Ok();
}
}
// While on the main thread, ensure code stubs exist for ops that require
// them.
switch (op) {
case CacheOp::CallRegExpMatcherResult:
if (!cx_->realm()->jitRealm()->ensureRegExpMatcherStubExists(cx_)) {
return abort(AbortReason::Error);
}
break;
case CacheOp::CallRegExpSearcherResult:
if (!cx_->realm()->jitRealm()->ensureRegExpSearcherStubExists(cx_)) {
return abort(AbortReason::Error);
}
break;
case CacheOp::CallRegExpTesterResult:
if (!cx_->realm()->jitRealm()->ensureRegExpTesterStubExists(cx_)) {
return abort(AbortReason::Error);
}
break;
default:
break;
}
}
// Copy the ICStub data to protect against the stub being unlinked or mutated.