Skip to content

Commit

Permalink
Bug 1839422 part 5 - Optimize RegExpHasCaptureGroups intrinsic in the…
Browse files Browse the repository at this point in the history
… JITs. r=iain

Differential Revision: https://phabricator.services.mozilla.com/D184816
  • Loading branch information
jandem committed Jul 31, 2023
1 parent f3e6c1a commit c51e1e0
Show file tree
Hide file tree
Showing 19 changed files with 200 additions and 2 deletions.
20 changes: 20 additions & 0 deletions js/src/jit-test/tests/regexp/has-capture-groups-intrinsic.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
function test() {
var RegExpHasCaptureGroups = getSelfHostedValue("RegExpHasCaptureGroups");
var cases = [
[/a.+/, false],
[/abc/, false],
[/\r\n?|\n/, false],
[/(abc)/, true],
[/a(.+)/, true],
[/a(b)(c)(d)/, true],
[/a(?:b)/, false],
[/((?:a))/, true],
[/(?<name>a)/, true],
];
for (var i = 0; i < 10; i++) {
for (var [re, expected] of cases) {
assertEq(RegExpHasCaptureGroups(re, "abcdef"), expected);
}
}
}
test();
49 changes: 49 additions & 0 deletions js/src/jit/BaselineCacheIRCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4194,3 +4194,52 @@ bool BaselineCacheIRCompiler::emitRegExpBuiltinExecTestResult(
stubFrame.leave(masm);
return true;
}

bool BaselineCacheIRCompiler::emitRegExpHasCaptureGroupsResult(
ObjOperandId regexpId, StringOperandId inputId) {
JitSpew(JitSpew_Codegen, "%s", __FUNCTION__);

AutoOutputRegister output(*this);
Register regexp = allocator.useRegister(masm, regexpId);
Register input = allocator.useRegister(masm, inputId);
AutoScratchRegisterMaybeOutput scratch(allocator, masm, output);

allocator.discardStack(masm);

// Load RegExpShared in |scratch|.
Label vmCall;
masm.loadParsedRegExpShared(regexp, scratch, &vmCall);

// Return true iff pairCount > 1.
Label returnTrue, done;
masm.branch32(Assembler::Above,
Address(scratch, RegExpShared::offsetOfPairCount()), Imm32(1),
&returnTrue);
masm.moveValue(BooleanValue(false), output.valueReg());
masm.jump(&done);

masm.bind(&returnTrue);
masm.moveValue(BooleanValue(true), output.valueReg());
masm.jump(&done);

{
masm.bind(&vmCall);

AutoStubFrame stubFrame(*this);
stubFrame.enter(masm, scratch);

masm.Push(input);
masm.Push(regexp);

using Fn =
bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*);
callVM<Fn, RegExpHasCaptureGroups>(masm);

stubFrame.leave(masm);
masm.storeCallBoolResult(scratch);
masm.tagValue(JSVAL_TYPE_BOOLEAN, scratch, output.valueReg());
}

masm.bind(&done);
return true;
}
26 changes: 26 additions & 0 deletions js/src/jit/CacheIR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6825,6 +6825,30 @@ AttachDecision InlinableNativeIRGenerator::tryAttachRegExpSearcherLastLimit() {
return AttachDecision::Attach;
}

AttachDecision InlinableNativeIRGenerator::tryAttachRegExpHasCaptureGroups() {
// Self-hosted code calls this with object and string arguments.
MOZ_ASSERT(argc_ == 2);
MOZ_ASSERT(args_[0].toObject().is<RegExpObject>());
MOZ_ASSERT(args_[1].isString());

// Initialize the input operand.
initializeInputOperand();

// Note: we don't need to call emitNativeCalleeGuard for intrinsics.

ValOperandId arg0Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg0, argc_);
ObjOperandId objId = writer.guardToObject(arg0Id);

ValOperandId arg1Id = writer.loadArgumentFixedSlot(ArgumentKind::Arg1, argc_);
StringOperandId inputId = writer.guardToString(arg1Id);

writer.regExpHasCaptureGroupsResult(objId, inputId);
writer.returnFromIC();

trackAttached("RegExpHasCaptureGroups");
return AttachDecision::Attach;
}

AttachDecision
InlinableNativeIRGenerator::tryAttachRegExpPrototypeOptimizable() {
// Self-hosted code calls this with a single object argument.
Expand Down Expand Up @@ -10677,6 +10701,8 @@ AttachDecision InlinableNativeIRGenerator::tryAttachStub() {
return tryAttachRegExpMatcherSearcher(native);
case InlinableNative::RegExpSearcherLastLimit:
return tryAttachRegExpSearcherLastLimit();
case InlinableNative::RegExpHasCaptureGroups:
return tryAttachRegExpHasCaptureGroups();
case InlinableNative::RegExpPrototypeOptimizable:
return tryAttachRegExpPrototypeOptimizable();
case InlinableNative::RegExpInstanceOptimizable:
Expand Down
1 change: 1 addition & 0 deletions js/src/jit/CacheIRGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ class MOZ_RAII InlinableNativeIRGenerator {
bool isPossiblyWrapped);
AttachDecision tryAttachRegExpMatcherSearcher(InlinableNative native);
AttachDecision tryAttachRegExpSearcherLastLimit();
AttachDecision tryAttachRegExpHasCaptureGroups();
AttachDecision tryAttachRegExpPrototypeOptimizable();
AttachDecision tryAttachRegExpInstanceOptimizable();
AttachDecision tryAttachIntrinsicRegExpBuiltinExec(InlinableNative native);
Expand Down
8 changes: 8 additions & 0 deletions js/src/jit/CacheIROps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,14 @@
cost_estimate: 1
args:

- name: RegExpHasCaptureGroupsResult
shared: false
transpile: true
cost_estimate: 1
args:
regexp: ObjId
input: StringId

- name: RegExpBuiltinExecMatchResult
shared: false
transpile: true
Expand Down
28 changes: 28 additions & 0 deletions js/src/jit/CodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3282,6 +3282,34 @@ void CodeGenerator::visitRegExpExecTest(LRegExpExecTest* lir) {
masm.bind(ool->rejoin());
}

void CodeGenerator::visitRegExpHasCaptureGroups(LRegExpHasCaptureGroups* ins) {
Register regexp = ToRegister(ins->regexp());
Register input = ToRegister(ins->input());
Register output = ToRegister(ins->output());

using Fn =
bool (*)(JSContext*, Handle<RegExpObject*>, Handle<JSString*>, bool*);
auto* ool = oolCallVM<Fn, js::RegExpHasCaptureGroups>(
ins, ArgList(regexp, input), StoreRegisterTo(output));

// Load RegExpShared in |output|.
Label vmCall;
masm.loadParsedRegExpShared(regexp, output, ool->entry());

// Return true iff pairCount > 1.
Label returnTrue;
masm.branch32(Assembler::Above,
Address(output, RegExpShared::offsetOfPairCount()), Imm32(1),
&returnTrue);
masm.move32(Imm32(0), output);
masm.jump(ool->rejoin());

masm.bind(&returnTrue);
masm.move32(Imm32(1), output);

masm.bind(ool->rejoin());
}

class OutOfLineRegExpPrototypeOptimizable
: public OutOfLineCodeBase<CodeGenerator> {
LRegExpPrototypeOptimizable* ins_;
Expand Down
1 change: 1 addition & 0 deletions js/src/jit/InlinableNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ bool js::jit::CanInlineNativeCrossRealm(InlinableNative native) {
case InlinableNative::RegExpMatcher:
case InlinableNative::RegExpSearcher:
case InlinableNative::RegExpSearcherLastLimit:
case InlinableNative::RegExpHasCaptureGroups:
case InlinableNative::RegExpPrototypeOptimizable:
case InlinableNative::RegExpInstanceOptimizable:
case InlinableNative::GetFirstDollarIndex:
Expand Down
1 change: 1 addition & 0 deletions js/src/jit/InlinableNatives.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
_(RegExpMatcher) \
_(RegExpSearcher) \
_(RegExpSearcherLastLimit) \
_(RegExpHasCaptureGroups) \
_(IsRegExpObject) \
_(IsPossiblyWrappedRegExpObject) \
_(RegExpPrototypeOptimizable) \
Expand Down
5 changes: 5 additions & 0 deletions js/src/jit/IonCacheIRCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2138,3 +2138,8 @@ bool IonCacheIRCompiler::emitRegExpBuiltinExecTestResult(
ObjOperandId regexpId, StringOperandId inputId, uint32_t stubOffset) {
MOZ_CRASH("Call ICs not used in ion");
}

bool IonCacheIRCompiler::emitRegExpHasCaptureGroupsResult(
ObjOperandId regexpId, StringOperandId inputId) {
MOZ_CRASH("Call ICs not used in ion");
}
7 changes: 7 additions & 0 deletions js/src/jit/LIROps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,13 @@
num_temps: 0
mir_op: true

- name: RegExpHasCaptureGroups
result_type: WordSized
operands:
regexp: WordSized
input: WordSized
mir_op: true

- name: RegExpPrototypeOptimizable
result_type: WordSized
operands:
Expand Down
11 changes: 11 additions & 0 deletions js/src/jit/Lowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3206,6 +3206,17 @@ void LIRGenerator::visitRegExpExecTest(MRegExpExecTest* ins) {
assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpHasCaptureGroups(MRegExpHasCaptureGroups* ins) {
MOZ_ASSERT(ins->regexp()->type() == MIRType::Object);
MOZ_ASSERT(ins->input()->type() == MIRType::String);
MOZ_ASSERT(ins->type() == MIRType::Boolean);

auto* lir = new (alloc()) LRegExpHasCaptureGroups(useRegister(ins->regexp()),
useRegister(ins->input()));
define(lir, ins);
assignSafepoint(lir, ins);
}

void LIRGenerator::visitRegExpPrototypeOptimizable(
MRegExpPrototypeOptimizable* ins) {
MOZ_ASSERT(ins->object()->type() == MIRType::Object);
Expand Down
7 changes: 7 additions & 0 deletions js/src/jit/MIROps.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,13 @@
possibly_calls: true
can_recover: false

- name: RegExpHasCaptureGroups
operands:
regexp: Object
input: String
result_type: Boolean
possibly_calls: true

- name: RegExpPrototypeOptimizable
operands:
object: Object
Expand Down
11 changes: 11 additions & 0 deletions js/src/jit/MacroAssembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4338,6 +4338,17 @@ void MacroAssembler::loadAndClearRegExpSearcherLastLimit(Register result,
#endif
}

void MacroAssembler::loadParsedRegExpShared(Register regexp, Register result,
Label* unparsed) {
Address sharedSlot(regexp, RegExpObject::offsetOfShared());
branchTestUndefined(Assembler::Equal, sharedSlot, unparsed);
unboxNonDouble(sharedSlot, result, JSVAL_TYPE_PRIVATE_GCTHING);

static_assert(sizeof(RegExpShared::Kind) == sizeof(uint32_t));
branch32(Assembler::Equal, Address(result, RegExpShared::offsetOfKind()),
Imm32(int32_t(RegExpShared::Kind::Unparsed)), unparsed);
}

// ===============================================================
// Branch functions

Expand Down
3 changes: 3 additions & 0 deletions js/src/jit/MacroAssembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -1306,6 +1306,9 @@ class MacroAssembler : public MacroAssemblerSpecific {

void loadAndClearRegExpSearcherLastLimit(Register result, Register scratch);

void loadParsedRegExpShared(Register regexp, Register result,
Label* unparsed);

// ===============================================================
// Shift functions

Expand Down
1 change: 1 addition & 0 deletions js/src/jit/VMFunctionList-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ namespace jit {
_(RecreateLexicalEnv, js::jit::RecreateLexicalEnv) \
_(RegExpBuiltinExecMatchFromJit, js::RegExpBuiltinExecMatchFromJit) \
_(RegExpBuiltinExecTestFromJit, js::RegExpBuiltinExecTestFromJit) \
_(RegExpHasCaptureGroups, js::RegExpHasCaptureGroups) \
_(RegExpMatcherRaw, js::RegExpMatcherRaw) \
_(RegExpSearcherRaw, js::RegExpSearcherRaw) \
_(SameValue, js::SameValue) \
Expand Down
12 changes: 12 additions & 0 deletions js/src/jit/WarpCacheIRTranspiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3542,6 +3542,18 @@ bool WarpCacheIRTranspiler::emitRegExpBuiltinExecTestResult(
return resumeAfter(ins);
}

bool WarpCacheIRTranspiler::emitRegExpHasCaptureGroupsResult(
ObjOperandId regexpId, StringOperandId inputId) {
MDefinition* regexp = getOperand(regexpId);
MDefinition* input = getOperand(inputId);

auto* result = MRegExpHasCaptureGroups::New(alloc(), regexp, input);
addEffectful(result);
pushResult(result);

return resumeAfter(result);
}

MInstruction* WarpCacheIRTranspiler::convertToBoolean(MDefinition* input) {
// Convert to bool with the '!!' idiom.
//
Expand Down
4 changes: 4 additions & 0 deletions js/src/vm/RegExpObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,10 @@ class RegExpObject : public NativeObject {
return getFixedSlotOffset(flagsSlot());
}

static constexpr size_t offsetOfShared() {
return getFixedSlotOffset(SHARED_SLOT);
}

JS::RegExpFlags getFlags() const {
return JS::RegExpFlags(getFixedSlot(FLAGS_SLOT).toInt32());
}
Expand Down
4 changes: 3 additions & 1 deletion js/src/vm/RegExpShared.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class RegExpShared
friend class js::gc::CellAllocator;

public:
enum class Kind { Unparsed, Atom, RegExp };
enum class Kind : uint32_t { Unparsed, Atom, RegExp };
enum class CodeKind { Bytecode, Jitcode, Any };

using ByteCode = js::irregexp::ByteArrayData;
Expand Down Expand Up @@ -241,6 +241,8 @@ class RegExpShared
return offsetof(RegExpShared, pairCount_);
}

static size_t offsetOfKind() { return offsetof(RegExpShared, kind_); }

static size_t offsetOfJitCode(bool latin1) {
return offsetof(RegExpShared, compilationArray) +
(CompilationIndex(latin1) * sizeof(RegExpCompilation)) +
Expand Down
3 changes: 2 additions & 1 deletion js/src/vm/SelfHosting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2083,7 +2083,8 @@ static const JSFunctionSpec intrinsic_functions[] = {
JS_INLINABLE_FN("RegExpExecForTest", intrinsic_RegExpExec<true>, 2, 0,
IntrinsicRegExpExecForTest),
JS_FN("RegExpGetSubstitution", intrinsic_RegExpGetSubstitution, 5, 0),
JS_FN("RegExpHasCaptureGroups", intrinsic_RegExpHasCaptureGroups, 2, 0),
JS_INLINABLE_FN("RegExpHasCaptureGroups", intrinsic_RegExpHasCaptureGroups,
2, 0, RegExpHasCaptureGroups),
JS_INLINABLE_FN("RegExpInstanceOptimizable", RegExpInstanceOptimizable, 1,
0, RegExpInstanceOptimizable),
JS_INLINABLE_FN("RegExpMatcher", RegExpMatcher, 3, 0, RegExpMatcher),
Expand Down

0 comments on commit c51e1e0

Please sign in to comment.