Bug 877893 - Part 2: Support string concat in parallel in Ion. (r=djvj)

This commit is contained in:
Shu-yu Guo 2013-06-27 14:47:44 -07:00
parent 385e86bbeb
commit 9c45b9352d
17 changed files with 281 additions and 14 deletions

View File

@ -3822,7 +3822,7 @@ CodeGenerator::visitIsNullOrLikeUndefinedAndBranch(LIsNullOrLikeUndefinedAndBran
return true;
}
typedef JSString *(*ConcatStringsFn)(JSContext *, HandleString, HandleString);
typedef JSString *(*ConcatStringsFn)(ThreadSafeContext *, HandleString, HandleString);
static const VMFunction ConcatStringsInfo = FunctionInfo<ConcatStringsFn>(ConcatStrings<CanGC>);
bool
@ -3923,7 +3923,41 @@ CodeGenerator::visitConcat(LConcat *lir)
if (!ool)
return false;
IonCode *stringConcatStub = gen->ionCompartment()->stringConcatStub();
IonCode *stringConcatStub = gen->ionCompartment()->stringConcatStub(SequentialExecution);
masm.call(stringConcatStub);
masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
masm.bind(ool->rejoin());
return true;
}
typedef ParallelResult (*ParallelConcatStringsFn)(ForkJoinSlice *, HandleString, HandleString,
MutableHandleString);
static const VMFunction ParallelConcatStringsInfo =
FunctionInfo<ParallelConcatStringsFn>(ParConcatStrings);
bool
CodeGenerator::visitParConcat(LParConcat *lir)
{
Register slice = ToRegister(lir->parSlice());
Register lhs = ToRegister(lir->lhs());
Register rhs = ToRegister(lir->rhs());
Register output = ToRegister(lir->output());
JS_ASSERT(lhs == CallTempReg0);
JS_ASSERT(rhs == CallTempReg1);
JS_ASSERT(slice == CallTempReg5);
JS_ASSERT(ToRegister(lir->temp1()) == CallTempReg2);
JS_ASSERT(ToRegister(lir->temp2()) == CallTempReg3);
JS_ASSERT(ToRegister(lir->temp3()) == CallTempReg4);
JS_ASSERT(output == CallTempReg6);
OutOfLineCode *ool = oolCallVM(ParallelConcatStringsInfo, lir, (ArgList(), lhs, rhs),
StoreRegisterTo(output));
if (!ool)
return false;
IonCode *stringConcatStub = gen->ionCompartment()->stringConcatStub(ParallelExecution);
masm.call(stringConcatStub);
masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
@ -3957,7 +3991,7 @@ CopyStringChars(MacroAssembler &masm, Register to, Register from, Register len,
}
IonCode *
IonCompartment::generateStringConcatStub(JSContext *cx)
IonCompartment::generateStringConcatStub(JSContext *cx, ExecutionMode mode)
{
MacroAssembler masm(cx);
@ -3969,7 +4003,12 @@ IonCompartment::generateStringConcatStub(JSContext *cx)
Register temp4 = CallTempReg5;
Register output = CallTempReg6;
Label failure;
// In parallel execution, we pass in the ForkJoinSlice in CallTempReg5, as
// by the time we need to use the temp4 we no longer have need of the
// slice.
Register forkJoinSlice = CallTempReg5;
Label failure, failurePopTemps;
// If lhs is empty, return rhs.
Label leftEmpty;
@ -3992,7 +4031,20 @@ IonCompartment::generateStringConcatStub(JSContext *cx)
masm.branch32(Assembler::Above, temp2, Imm32(JSString::MAX_LENGTH), &failure);
// Allocate a new rope.
masm.newGCString(output, &failure);
switch (mode) {
case SequentialExecution:
masm.newGCString(output, &failure);
break;
case ParallelExecution:
masm.push(temp1);
masm.push(temp2);
masm.parNewGCString(output, forkJoinSlice, temp1, temp2, &failurePopTemps);
masm.pop(temp2);
masm.pop(temp1);
break;
default:
JS_NOT_REACHED("No such execution mode");
}
// Store lengthAndFlags.
JS_STATIC_ASSERT(JSString::ROPE_FLAGS == 0);
@ -4024,7 +4076,20 @@ IonCompartment::generateStringConcatStub(JSContext *cx)
Imm32(JSString::FLAGS_MASK), &failure);
// Allocate a JSShortString.
masm.newGCShortString(output, &failure);
switch (mode) {
case SequentialExecution:
masm.newGCShortString(output, &failure);
break;
case ParallelExecution:
masm.push(temp1);
masm.push(temp2);
masm.parNewGCShortString(output, forkJoinSlice, temp1, temp2, &failurePopTemps);
masm.pop(temp2);
masm.pop(temp1);
break;
default:
JS_NOT_REACHED("No such execution mode");
}
// Set lengthAndFlags.
masm.lshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp2);
@ -4049,6 +4114,10 @@ IonCompartment::generateStringConcatStub(JSContext *cx)
masm.store16(Imm32(0), Address(temp2, 0));
masm.ret();
masm.bind(&failurePopTemps);
masm.pop(temp2);
masm.pop(temp1);
masm.bind(&failure);
masm.movePtr(ImmWord((void *)NULL), output);
masm.ret();

View File

@ -172,6 +172,7 @@ class CodeGenerator : public CodeGeneratorSpecific
bool visitEmulatesUndefined(LEmulatesUndefined *lir);
bool visitEmulatesUndefinedAndBranch(LEmulatesUndefinedAndBranch *lir);
bool visitConcat(LConcat *lir);
bool visitParConcat(LParConcat *lir);
bool visitCharCodeAt(LCharCodeAt *lir);
bool visitFromCharCode(LFromCharCode *lir);
bool visitFunctionEnvironment(LFunctionEnvironment *lir);

View File

@ -298,7 +298,8 @@ IonCompartment::IonCompartment(IonRuntime *rt)
: rt(rt),
stubCodes_(NULL),
baselineCallReturnAddr_(NULL),
stringConcatStub_(NULL)
stringConcatStub_(NULL),
parallelStringConcatStub_(NULL)
{
}
@ -322,11 +323,19 @@ bool
IonCompartment::ensureIonStubsExist(JSContext *cx)
{
if (!stringConcatStub_) {
stringConcatStub_ = generateStringConcatStub(cx);
stringConcatStub_ = generateStringConcatStub(cx, SequentialExecution);
if (!stringConcatStub_)
return false;
}
#ifdef JS_THREADSAFE
if (!parallelStringConcatStub_) {
parallelStringConcatStub_ = generateStringConcatStub(cx, ParallelExecution);
if (!parallelStringConcatStub_)
return false;
}
#endif
return true;
}

View File

@ -228,8 +228,9 @@ class IonCompartment
// pointers. This has to be a weak pointer to avoid keeping the whole
// compartment alive.
ReadBarriered<IonCode> stringConcatStub_;
ReadBarriered<IonCode> parallelStringConcatStub_;
IonCode *generateStringConcatStub(JSContext *cx);
IonCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
public:
IonCode *getVMWrapper(const VMFunction &f);
@ -321,8 +322,12 @@ class IonCompartment
return rt->debugTrapHandler(cx);
}
IonCode *stringConcatStub() {
return stringConcatStub_;
IonCode *stringConcatStub(ExecutionMode mode) {
switch (mode) {
case SequentialExecution: return stringConcatStub_;
case ParallelExecution: return parallelStringConcatStub_;
default: JS_NOT_REACHED("No such execution mode");
}
}
AutoFlushCache *flusher() {

View File

@ -523,7 +523,7 @@ MacroAssembler::parNewGCThing(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
JSObject *templateObject,
gc::AllocKind allocKind,
Label *fail)
{
// Similar to ::newGCThing(), except that it allocates from a
@ -536,7 +536,6 @@ MacroAssembler::parNewGCThing(const Register &result,
// register as `threadContextReg`. Then we overwrite that
// register which messed up the OOL code.
gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
uint32_t thingSize = (uint32_t)gc::Arena::thingSize(allocKind);
// Load the allocator:
@ -572,6 +571,41 @@ MacroAssembler::parNewGCThing(const Register &result,
storePtr(tempReg2, Address(tempReg1, offsetof(gc::FreeSpan, first)));
}
void
MacroAssembler::parNewGCThing(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
JSObject *templateObject,
Label *fail)
{
gc::AllocKind allocKind = templateObject->tenuredGetAllocKind();
JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST);
JS_ASSERT(!templateObject->hasDynamicElements());
parNewGCThing(result, threadContextReg, tempReg1, tempReg2, allocKind, fail);
}
void
MacroAssembler::parNewGCString(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
Label *fail)
{
parNewGCThing(result, threadContextReg, tempReg1, tempReg2, js::gc::FINALIZE_STRING, fail);
}
void
MacroAssembler::parNewGCShortString(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
Label *fail)
{
parNewGCThing(result, threadContextReg, tempReg1, tempReg2, js::gc::FINALIZE_SHORT_STRING, fail);
}
void
MacroAssembler::initGCThing(const Register &obj, JSObject *templateObject)
{

View File

@ -600,12 +600,28 @@ class MacroAssembler : public MacroAssemblerSpecific
void newGCString(const Register &result, Label *fail);
void newGCShortString(const Register &result, Label *fail);
void parNewGCThing(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
gc::AllocKind allocKind,
Label *fail);
void parNewGCThing(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
JSObject *templateObject,
Label *fail);
void parNewGCString(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
Label *fail);
void parNewGCShortString(const Register &result,
const Register &threadContextReg,
const Register &tempReg1,
const Register &tempReg2,
Label *fail);
void initGCThing(const Register &obj, JSObject *templateObject);
// Compares two strings for equality based on the JSOP.

View File

@ -2304,6 +2304,41 @@ class LConcat : public LInstructionHelper<1, 2, 4>
}
};
class LParConcat : public LInstructionHelper<1, 3, 3>
{
public:
LIR_HEADER(ParConcat)
LParConcat(const LAllocation &parSlice, const LAllocation &lhs, const LAllocation &rhs,
const LDefinition &temp1, const LDefinition &temp2, const LDefinition &temp3) {
setOperand(0, parSlice);
setOperand(1, lhs);
setOperand(2, rhs);
setTemp(0, temp1);
setTemp(1, temp2);
setTemp(2, temp3);
}
const LAllocation *parSlice() {
return this->getOperand(0);
}
const LAllocation *lhs() {
return this->getOperand(1);
}
const LAllocation *rhs() {
return this->getOperand(2);
}
const LDefinition *temp1() {
return this->getTemp(0);
}
const LDefinition *temp2() {
return this->getTemp(1);
}
const LDefinition *temp3() {
return this->getTemp(2);
}
};
// Get uint16 character code from a string.
class LCharCodeAt : public LInstructionHelper<1, 2, 0>
{

View File

@ -108,6 +108,7 @@
_(ModD) \
_(BinaryV) \
_(Concat) \
_(ParConcat) \
_(CharCodeAt) \
_(FromCharCode) \
_(Int32ToDouble) \

View File

@ -1323,6 +1323,28 @@ LIRGenerator::visitConcat(MConcat *ins)
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitParConcat(MParConcat *ins)
{
MDefinition *parSlice = ins->parSlice();
MDefinition *lhs = ins->lhs();
MDefinition *rhs = ins->rhs();
JS_ASSERT(lhs->type() == MIRType_String);
JS_ASSERT(rhs->type() == MIRType_String);
JS_ASSERT(ins->type() == MIRType_String);
LParConcat *lir = new LParConcat(useFixed(parSlice, CallTempReg5),
useFixed(lhs, CallTempReg0),
useFixed(rhs, CallTempReg1),
tempFixed(CallTempReg2),
tempFixed(CallTempReg3),
tempFixed(CallTempReg4));
if (!defineFixed(lir, ins, LAllocation(AnyRegister(CallTempReg6))))
return false;
return assignSafepoint(lir, ins);
}
bool
LIRGenerator::visitCharCodeAt(MCharCodeAt *ins)
{

View File

@ -143,6 +143,7 @@ class LIRGenerator : public LIRGeneratorSpecific
bool visitDiv(MDiv *ins);
bool visitMod(MMod *ins);
bool visitConcat(MConcat *ins);
bool visitParConcat(MParConcat *ins);
bool visitCharCodeAt(MCharCodeAt *ins);
bool visitFromCharCode(MFromCharCode *ins);
bool visitStart(MStart *start);

View File

@ -3545,6 +3545,49 @@ class MConcat
}
};
class MParConcat
: public MTernaryInstruction,
public MixPolicy<StringPolicy<1>, StringPolicy<2> >
{
MParConcat(MDefinition *parSlice, MDefinition *left, MDefinition *right)
: MTernaryInstruction(parSlice, left, right)
{
setMovable();
setResultType(MIRType_String);
}
public:
INSTRUCTION_HEADER(ParConcat)
static MParConcat *New(MDefinition *parSlice, MDefinition *left, MDefinition *right) {
return new MParConcat(parSlice, left, right);
}
static MParConcat *New(MDefinition *parSlice, MConcat *concat) {
return New(parSlice, concat->lhs(), concat->rhs());
}
MDefinition *parSlice() const {
return getOperand(0);
}
MDefinition *lhs() const {
return getOperand(1);
}
MDefinition *rhs() const {
return getOperand(2);
}
TypePolicy *typePolicy() {
return this;
}
bool congruentTo(MDefinition *const &ins) const {
return congruentIfOperandsEqual(ins);
}
AliasSet getAliasSet() const {
return AliasSet::None();
}
};
class MCharCodeAt
: public MBinaryInstruction,
public MixPolicy<StringPolicy<0>, IntPolicy<1> >

View File

@ -65,6 +65,7 @@ namespace ion {
_(Div) \
_(Mod) \
_(Concat) \
_(ParConcat) \
_(CharCodeAt) \
_(FromCharCode) \
_(Return) \

View File

@ -194,6 +194,17 @@ ion::ParExtendArray(ForkJoinSlice *slice, JSObject *array, uint32_t length)
return array;
}
ParallelResult
ion::ParConcatStrings(ForkJoinSlice *slice, HandleString left, HandleString right,
MutableHandleString out)
{
JSString *str = ConcatStrings<NoGC>(slice, left, right);
if (!str)
return TP_RETRY_SEQUENTIALLY;
out.set(str);
return TP_SUCCESS;
}
#define PAR_RELATIONAL_OP(OP, EXPECTED) \
do { \
/* Optimize for two int-tagged operands (typical loop control). */ \

View File

@ -41,6 +41,10 @@ JSObject* ParPush(ParPushArgs *args);
// generation.
JSObject *ParExtendArray(ForkJoinSlice *slice, JSObject *array, uint32_t length);
// Concatenate two strings.
ParallelResult ParConcatStrings(ForkJoinSlice *slice, HandleString left, HandleString right,
MutableHandleString out);
// These parallel operations fail if they would be required to convert
// to a string etc etc.
ParallelResult ParStrictlyEqual(ForkJoinSlice *slice, MutableHandleValue v1, MutableHandleValue v2, JSBool *);

View File

@ -156,7 +156,8 @@ class ParallelSafetyVisitor : public MInstructionVisitor
SPECIALIZED_OP(Mul, PERMIT_NUMERIC)
SPECIALIZED_OP(Div, PERMIT_NUMERIC)
SPECIALIZED_OP(Mod, PERMIT_NUMERIC)
UNSAFE_OP(Concat)
CUSTOM_OP(Concat)
SAFE_OP(ParConcat)
UNSAFE_OP(CharCodeAt)
UNSAFE_OP(FromCharCode)
SAFE_OP(Return)
@ -555,6 +556,12 @@ ParallelSafetyVisitor::visitRest(MRest *ins)
return replace(ins, MParRest::New(parSlice(), ins));
}
bool
ParallelSafetyVisitor::visitConcat(MConcat *ins)
{
return replace(ins, MParConcat::New(parSlice(), ins));
}
bool
ParallelSafetyVisitor::replaceWithParNew(MInstruction *newInstruction,
JSObject *templateObject)

View File

@ -315,6 +315,7 @@ StringPolicy<Op>::staticAdjustInputs(MInstruction *def)
template bool StringPolicy<0>::staticAdjustInputs(MInstruction *ins);
template bool StringPolicy<1>::staticAdjustInputs(MInstruction *ins);
template bool StringPolicy<2>::staticAdjustInputs(MInstruction *ins);
template <unsigned Op>
bool

View File

@ -332,6 +332,7 @@ template <> struct OutParamToDataType<uint32_t *> { static const DataType result
template <> struct OutParamToDataType<uint8_t **> { static const DataType result = Type_Pointer; };
template <> struct OutParamToDataType<MutableHandleValue> { static const DataType result = Type_Handle; };
template <> struct OutParamToDataType<MutableHandleObject> { static const DataType result = Type_Handle; };
template <> struct OutParamToDataType<MutableHandleString> { static const DataType result = Type_Handle; };
template <class> struct OutParamToRootType {
static const VMFunction::RootType result = VMFunction::RootNone;
@ -353,6 +354,12 @@ template <> struct MatchContext<JSContext *> {
template <> struct MatchContext<ForkJoinSlice *> {
static const ExecutionMode execMode = ParallelExecution;
};
template <> struct MatchContext<ThreadSafeContext *> {
// ThreadSafeContext functions can be called from either mode, but for
// calling from parallel they need to be wrapped first to return a
// ParallelResult, so we default to SequentialExecution here.
static const ExecutionMode execMode = SequentialExecution;
};
#define FOR_EACH_ARGS_1(Macro, Sep, Last) Macro(1) Last(1)
#define FOR_EACH_ARGS_2(Macro, Sep, Last) FOR_EACH_ARGS_1(Macro, Sep, Sep) Macro(2) Last(2)