From 32ae0307bfd6f4cfd691a897d8a1e4eaf2611e59 Mon Sep 17 00:00:00 2001 From: Shu-yu Guo Date: Tue, 22 Apr 2014 18:23:27 -0700 Subject: [PATCH] Bug 996422 - Part 2: Split MIRType_Magic into one type for each magic constant. (r=jandem) --- js/src/jit/CodeGenerator.cpp | 4 +- js/src/jit/IonAnalysis.cpp | 23 ++++++++- js/src/jit/IonBuilder.cpp | 31 ++++++------ js/src/jit/IonMacroAssembler.h | 30 +++++++----- js/src/jit/IonTypes.h | 34 ++++++++----- js/src/jit/MIR.cpp | 18 +++++-- js/src/jit/MIR.h | 14 +++++- js/src/jit/TypePolicy.cpp | 5 +- js/src/jit/shared/CodeGenerator-shared.cpp | 2 +- js/src/jsinfer.cpp | 57 ++++++++++++---------- js/src/jsinfer.h | 6 ++- 11 files changed, 147 insertions(+), 77 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index a2586629661a..f3fa5873e4df 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4047,7 +4047,7 @@ CodeGenerator::visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir) #ifdef DEBUG Label success; masm.branchTestMagic(Assembler::NotEqual, out, &success); - masm.assumeUnreachable("Result from ArgumentObject shouldn't be MIRType_Magic."); + masm.assumeUnreachable("Result from ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); masm.bind(&success); #endif return true; @@ -4066,7 +4066,7 @@ CodeGenerator::visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir) #ifdef DEBUG Label success; masm.branchTestMagic(Assembler::NotEqual, argAddr, &success); - masm.assumeUnreachable("Result in ArgumentObject shouldn't be MIRType_Magic."); + masm.assumeUnreachable("Result in ArgumentObject shouldn't be JSVAL_TYPE_MAGIC."); masm.bind(&success); #endif masm.storeValue(value, argAddr); diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 7a121ad42cab..3688c64d5fd9 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -442,6 +442,22 @@ class TypeAnalyzer static MIRType GuessPhiType(MPhi *phi, bool *hasInputsWithEmptyTypes) { +#ifdef DEBUG + // Check that different magic constants aren't flowing together. + MIRType magicType = MIRType_None; + for (size_t i = 0; i < phi->numOperands(); i++) { + MDefinition *in = phi->getOperand(i); + if (in->type() == MIRType_MagicOptimizedArguments || + in->type() == MIRType_MagicHole || + in->type() == MIRType_MagicIsConstructing) + { + if (magicType == MIRType_None) + magicType = in->type(); + MOZ_ASSERT(magicType == in->type()); + } + } +#endif + *hasInputsWithEmptyTypes = false; MIRType type = MIRType_None; @@ -714,7 +730,7 @@ TypeAnalyzer::replaceRedundantPhi(MPhi *phi) case MIRType_Null: v = NullValue(); break; - case MIRType_Magic: + case MIRType_MagicOptimizedArguments: v = MagicValue(JS_OPTIMIZED_ARGUMENTS); break; default: @@ -737,7 +753,10 @@ TypeAnalyzer::insertConversions() return false; for (MPhiIterator phi(block->phisBegin()); phi != block->phisEnd();) { - if (phi->type() <= MIRType_Null || phi->type() == MIRType_Magic) { + if (phi->type() == MIRType_Undefined || + phi->type() == MIRType_Null || + phi->type() == MIRType_MagicOptimizedArguments) + { replaceRedundantPhi(*phi); phi = block->discardPhiAt(phi); } else { diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 066c05053da7..8180834ba822 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1031,7 +1031,7 @@ IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction **def_, def = barrier; } else if (type == MIRType_Null || type == MIRType_Undefined || - type == MIRType_Magic) + type == MIRType_MagicOptimizedArguments) { // No unbox instruction will be added below, so check the type by // adding a type barrier for a singleton type set. @@ -1077,7 +1077,7 @@ IonBuilder::addOsrValueTypeBarrier(uint32_t slot, MInstruction **def_, break; } - case MIRType_Magic: + case MIRType_MagicOptimizedArguments: JS_ASSERT(lazyArguments_); osrBlock->rewriteSlot(slot, lazyArguments_); def = lazyArguments_; @@ -4943,14 +4943,14 @@ IonBuilder::jsop_funapply(uint32_t argc) // to be either definitely |arguments| or definitely not |arguments|. MDefinition *argument = current->peek(-1); if (script()->argumentsHasVarBinding() && - argument->mightBeType(MIRType_Magic) && - argument->type() != MIRType_Magic) + argument->mightBeType(MIRType_MagicOptimizedArguments) && + argument->type() != MIRType_MagicOptimizedArguments) { return abort("fun.apply with MaybeArguments"); } // Fallback to regular call if arg 2 is not definitely |arguments|. - if (argument->type() != MIRType_Magic) { + if (argument->type() != MIRType_MagicOptimizedArguments) { CallInfo callInfo(alloc(), false); if (!callInfo.init(current, argc)) return false; @@ -5531,7 +5531,7 @@ IonBuilder::jsop_initelem_array() // to them during initialization. bool needStub = false; types::TypeObjectKey *initializer = obj->resultTypeSet()->getObject(0); - if (value->isConstant() && value->toConstant()->value().isMagic(JS_ELEMENTS_HOLE)) { + if (value->type() == MIRType_MagicHole) { if (!initializer->hasFlags(constraints(), types::OBJECT_FLAG_NON_PACKED)) needStub = true; } else if (!initializer->unknownProperties()) { @@ -6462,7 +6462,7 @@ jit::TypeSetIncludes(types::TypeSet *types, MIRType input, types::TypeSet *input case MIRType_Double: case MIRType_Float32: case MIRType_String: - case MIRType_Magic: + case MIRType_MagicOptimizedArguments: return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input))); case MIRType_Object: @@ -6677,7 +6677,7 @@ IonBuilder::jsop_getelem() if (!getElemTryArgumentsInlined(&emitted, obj, index) || emitted) return emitted; - if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_Magic)) + if (script()->argumentsHasVarBinding() && obj->mightBeType(MIRType_MagicOptimizedArguments)) return abort("Type is not definitely lazy arguments."); if (!getElemTryCache(&emitted, obj, index) || emitted) @@ -7113,7 +7113,7 @@ IonBuilder::getElemTryArguments(bool *emitted, MDefinition *obj, MDefinition *in if (inliningDepth_ > 0) return true; - if (obj->type() != MIRType_Magic) + if (obj->type() != MIRType_MagicOptimizedArguments) return true; // Emit GetFrameArgument. @@ -7156,7 +7156,7 @@ IonBuilder::getElemTryArgumentsInlined(bool *emitted, MDefinition *obj, MDefinit if (inliningDepth_ == 0) return true; - if (obj->type() != MIRType_Magic) + if (obj->type() != MIRType_MagicOptimizedArguments) return true; // Emit inlined arguments. @@ -7573,7 +7573,7 @@ IonBuilder::jsop_setelem() if (!setElemTryArguments(&emitted, object, index, value) || emitted) return emitted; - if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_Magic)) + if (script()->argumentsHasVarBinding() && object->mightBeType(MIRType_MagicOptimizedArguments)) return abort("Type is not definitely lazy arguments."); if (!setElemTryCache(&emitted, object, index, value) || emitted) @@ -7784,7 +7784,7 @@ IonBuilder::setElemTryArguments(bool *emitted, MDefinition *object, { JS_ASSERT(*emitted == false); - if (object->type() != MIRType_Magic) + if (object->type() != MIRType_MagicOptimizedArguments) return true; // Arguments are not supported yet. @@ -8588,9 +8588,12 @@ bool IonBuilder::getPropTryArgumentsLength(bool *emitted) { JS_ASSERT(*emitted == false); - if (current->peek(-1)->type() != MIRType_Magic) { - if (script()->argumentsHasVarBinding() && current->peek(-1)->mightBeType(MIRType_Magic)) + if (current->peek(-1)->type() != MIRType_MagicOptimizedArguments) { + if (script()->argumentsHasVarBinding() && + current->peek(-1)->mightBeType(MIRType_MagicOptimizedArguments)) + { return abort("Type is not definitely lazy arguments."); + } return true; } if (JSOp(*pc) != JSOP_LENGTH) diff --git a/js/src/jit/IonMacroAssembler.h b/js/src/jit/IonMacroAssembler.h index a8bcc44a01a3..542f0418a757 100644 --- a/js/src/jit/IonMacroAssembler.h +++ b/js/src/jit/IonMacroAssembler.h @@ -127,12 +127,16 @@ class MacroAssembler : public MacroAssemblerSpecific JS_ASSERT(isInitialized()); MIRType mirType = MIRType_None; - if (type_.isPrimitive()) - mirType = MIRTypeFromValueType(type_.primitive()); - else if (type_.isAnyObject()) + if (type_.isPrimitive()) { + if (type_.isMagicArguments()) + mirType = MIRType_MagicOptimizedArguments; + else + mirType = MIRTypeFromValueType(type_.primitive()); + } else if (type_.isAnyObject()) { mirType = MIRType_Object; - else + } else { MOZ_ASSUME_UNREACHABLE("Unknown conversion to mirtype"); + } if (mirType == MIRType_Double) masm.branchTestNumber(cond(), reg(), jump()); @@ -332,14 +336,16 @@ class MacroAssembler : public MacroAssemblerSpecific template void branchTestMIRType(Condition cond, const Value &val, MIRType type, Label *label) { switch (type) { - case MIRType_Null: return branchTestNull(cond, val, label); - case MIRType_Undefined: return branchTestUndefined(cond, val, label); - case MIRType_Boolean: return branchTestBoolean(cond, val, label); - case MIRType_Int32: return branchTestInt32(cond, val, label); - case MIRType_String: return branchTestString(cond, val, label); - case MIRType_Object: return branchTestObject(cond, val, label); - case MIRType_Double: return branchTestDouble(cond, val, label); - case MIRType_Magic: return branchTestMagic(cond, val, label); + case MIRType_Null: return branchTestNull(cond, val, label); + case MIRType_Undefined: return branchTestUndefined(cond, val, label); + case MIRType_Boolean: return branchTestBoolean(cond, val, label); + case MIRType_Int32: return branchTestInt32(cond, val, label); + case MIRType_String: return branchTestString(cond, val, label); + case MIRType_Object: return branchTestObject(cond, val, label); + case MIRType_Double: return branchTestDouble(cond, val, label); + case MIRType_MagicOptimizedArguments: // Fall through. + case MIRType_MagicIsConstructing: + case MIRType_MagicHole: return branchTestMagic(cond, val, label); default: MOZ_ASSUME_UNREACHABLE("Bad MIRType"); } diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index f5c8fd4daab8..63b395ca0f51 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -70,7 +70,7 @@ BailoutKindString(BailoutKind kind) } } -static const uint32_t ELEMENT_TYPE_BITS = 4; +static const uint32_t ELEMENT_TYPE_BITS = 5; static const uint32_t ELEMENT_TYPE_SHIFT = 0; static const uint32_t ELEMENT_TYPE_MASK = (1 << ELEMENT_TYPE_BITS) - 1; static const uint32_t VECTOR_SCALE_BITS = 2; @@ -90,14 +90,16 @@ enum MIRType MIRType_Float32, MIRType_String, MIRType_Object, - MIRType_Magic, + MIRType_MagicOptimizedArguments, // JS_OPTIMIZED_ARGUMENTS magic value. + MIRType_MagicHole, // JS_ELEMENTS_HOLE magic value. + MIRType_MagicIsConstructing, // JS_IS_CONSTRUCTING magic value. MIRType_Value, - MIRType_None, // Invalid, used as a placeholder. - MIRType_Slots, // A slots vector - MIRType_Elements, // An elements vector - MIRType_Pointer, // An opaque pointer that receives no special treatment - MIRType_Shape, // A Shape pointer. - MIRType_ForkJoinContext, // js::ForkJoinContext* + MIRType_None, // Invalid, used as a placeholder. + MIRType_Slots, // A slots vector + MIRType_Elements, // An elements vector + MIRType_Pointer, // An opaque pointer that receives no special treatment + MIRType_Shape, // A Shape pointer. + MIRType_ForkJoinContext, // js::ForkJoinContext* MIRType_Last = MIRType_ForkJoinContext, MIRType_Float32x4 = MIRType_Float32 | (2 << VECTOR_SCALE_SHIFT), MIRType_Int32x4 = MIRType_Int32 | (2 << VECTOR_SCALE_SHIFT), @@ -120,6 +122,8 @@ VectorSize(MIRType type) static inline MIRType MIRTypeFromValueType(JSValueType type) { + // This function does not deal with magic types. Magic constants should be + // filtered out in MIRTypeFromValue. switch (type) { case JSVAL_TYPE_DOUBLE: return MIRType_Double; @@ -135,8 +139,6 @@ MIRTypeFromValueType(JSValueType type) return MIRType_Null; case JSVAL_TYPE_OBJECT: return MIRType_Object; - case JSVAL_TYPE_MAGIC: - return MIRType_Magic; case JSVAL_TYPE_UNKNOWN: return MIRType_Value; default: @@ -161,7 +163,9 @@ ValueTypeFromMIRType(MIRType type) return JSVAL_TYPE_DOUBLE; case MIRType_String: return JSVAL_TYPE_STRING; - case MIRType_Magic: + case MIRType_MagicOptimizedArguments: + case MIRType_MagicHole: + case MIRType_MagicIsConstructing: return JSVAL_TYPE_MAGIC; default: JS_ASSERT(type == MIRType_Object); @@ -195,8 +199,12 @@ StringFromMIRType(MIRType type) return "String"; case MIRType_Object: return "Object"; - case MIRType_Magic: - return "Magic"; + case MIRType_MagicOptimizedArguments: + return "MagicOptimizedArguments"; + case MIRType_MagicHole: + return "MagicHole"; + case MIRType_MagicIsConstructing: + return "MagicIsConstructing"; case MIRType_Value: return "Value"; case MIRType_None: diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index bdd686a19b97..45ed00204812 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -548,8 +548,14 @@ MConstant::printOpcode(FILE *fp) const case MIRType_String: fprintf(fp, "string %p", (void *)value().toString()); break; - case MIRType_Magic: - fprintf(fp, "magic"); + case MIRType_MagicOptimizedArguments: + fprintf(fp, "magic lazyargs"); + break; + case MIRType_MagicHole: + fprintf(fp, "magic hole"); + break; + case MIRType_MagicIsConstructing: + fprintf(fp, "magic is-constructing"); break; default: MOZ_ASSUME_UNREACHABLE("unexpected type"); @@ -1695,7 +1701,9 @@ KnownNonStringPrimitive(MDefinition *op) { return !op->mightBeType(MIRType_Object) && !op->mightBeType(MIRType_String) - && !op->mightBeType(MIRType_Magic); + && !op->mightBeType(MIRType_MagicOptimizedArguments) + && !op->mightBeType(MIRType_MagicHole) + && !op->mightBeType(MIRType_MagicIsConstructing); } void @@ -1819,7 +1827,9 @@ ObjectOrSimplePrimitive(MDefinition *op) return !op->mightBeType(MIRType_String) && !op->mightBeType(MIRType_Double) && !op->mightBeType(MIRType_Float32) - && !op->mightBeType(MIRType_Magic); + && !op->mightBeType(MIRType_MagicOptimizedArguments) + && !op->mightBeType(MIRType_MagicHole) + && !op->mightBeType(MIRType_MagicIsConstructing); } static bool diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 37247ce763cc..62be31996629 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -42,6 +42,18 @@ MIRType MIRTypeFromValue(const js::Value &vp) { if (vp.isDouble()) return MIRType_Double; + if (vp.isMagic()) { + switch (vp.whyMagic()) { + case JS_OPTIMIZED_ARGUMENTS: + return MIRType_MagicOptimizedArguments; + case JS_ELEMENTS_HOLE: + return MIRType_MagicHole; + case JS_IS_CONSTRUCTING: + return MIRType_MagicIsConstructing; + default: + MOZ_ASSERT(!"Unexpected magic constant"); + } + } return MIRTypeFromValueType(vp.extractNonDoubleType()); } @@ -455,7 +467,7 @@ class MDefinition : public MNode bool emptyResultTypeSet() const; bool mightBeType(MIRType type) const { - JS_ASSERT(type != MIRType_Value); + MOZ_ASSERT(type != MIRType_Value); if (type == this->type()) return true; diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index fe28d2b303a9..85d7f3201286 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -293,8 +293,9 @@ TypeBarrierPolicy::adjustInputs(TempAllocator &alloc, MInstruction *def) if (inputType == MIRType_Value) { JS_ASSERT(outputType != MIRType_Value); - // We can't unbox a value to null/undefined. So keep output also a value. - if (IsNullOrUndefined(outputType) || outputType == MIRType_Magic) { + // We can't unbox a value to null/undefined/lazyargs. So keep output + // also a value. + if (IsNullOrUndefined(outputType) || outputType == MIRType_MagicOptimizedArguments) { JS_ASSERT(ins->defUseCount() == 0); ins->setResultType(MIRType_Value); return true; diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index f0dad9d03a3b..2edf80b7bb32 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -193,7 +193,7 @@ CodeGeneratorShared::encodeAllocations(LSnapshot *snapshot, MResumePoint *resume } break; } - case MIRType_Magic: + case MIRType_MagicOptimizedArguments: { uint32_t index; if (!graph.addConstantToPool(MagicValue(JS_OPTIMIZED_ARGUMENTS), &index)) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 3c1a0dbe066d..518f1ac368b4 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -310,29 +310,6 @@ TemporaryTypeSet::TemporaryTypeSet(Type type) } } -static inline TypeFlags -PrimitiveMIRType(jit::MIRType type) -{ - switch (type) { - case jit::MIRType_Undefined: - return TYPE_FLAG_UNDEFINED; - case jit::MIRType_Null: - return TYPE_FLAG_NULL; - case jit::MIRType_Boolean: - return TYPE_FLAG_BOOLEAN; - case jit::MIRType_Int32: - return TYPE_FLAG_INT32; - case jit::MIRType_Double: - return TYPE_FLAG_DOUBLE; - case jit::MIRType_String: - return TYPE_FLAG_STRING; - case jit::MIRType_Magic: - return TYPE_FLAG_LAZYARGS; - default: - MOZ_ASSUME_UNREACHABLE("Bad MIR type"); - } -} - bool TypeSet::mightBeMIRType(jit::MIRType type) { @@ -342,7 +319,37 @@ TypeSet::mightBeMIRType(jit::MIRType type) if (type == jit::MIRType_Object) return unknownObject() || baseObjectCount() != 0; - return baseFlags() & PrimitiveMIRType(type); + switch (type) { + case jit::MIRType_Undefined: + return baseFlags() & TYPE_FLAG_UNDEFINED; + case jit::MIRType_Null: + return baseFlags() & TYPE_FLAG_NULL; + case jit::MIRType_Boolean: + return baseFlags() & TYPE_FLAG_BOOLEAN; + case jit::MIRType_Int32: + return baseFlags() & TYPE_FLAG_INT32; + case jit::MIRType_Float32: // Fall through, there's no JSVAL for Float32. + case jit::MIRType_Double: + return baseFlags() & TYPE_FLAG_DOUBLE; + case jit::MIRType_String: + return baseFlags() & TYPE_FLAG_STRING; + case jit::MIRType_MagicOptimizedArguments: + return baseFlags() & TYPE_FLAG_LAZYARGS; + case jit::MIRType_MagicHole: + case jit::MIRType_MagicIsConstructing: + // These magic constants do not escape to script and are not observed + // in the type sets. + // + // The reason we can return false here is subtle: if Ion is asking the + // type set if it has seen such a magic constant, then the MIR in + // question is the most generic type, MIRType_Value. A magic constant + // could only be emitted by a MIR of MIRType_Value if that MIR is a + // phi, and we check that different magic constants do not flow to the + // same join point in GuessPhiType. + return false; + default: + MOZ_ASSUME_UNREACHABLE("Bad MIR type"); + } } bool @@ -1182,7 +1189,7 @@ GetMIRTypeFromTypeFlags(TypeFlags flags) case TYPE_FLAG_STRING: return jit::MIRType_String; case TYPE_FLAG_LAZYARGS: - return jit::MIRType_Magic; + return jit::MIRType_MagicOptimizedArguments; case TYPE_FLAG_ANYOBJECT: return jit::MIRType_Object; default: diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 2ca3d5f2eab7..6acb33cb4868 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -224,6 +224,10 @@ class Type return (JSValueType) data; } + bool isMagicArguments() const { + return primitive() == JSVAL_TYPE_MAGIC; + } + bool isSomeObject() const { return data == JSVAL_TYPE_OBJECT || data > JSVAL_TYPE_UNKNOWN; } @@ -657,7 +661,7 @@ class TemporaryTypeSet : public TypeSet /* Get any type tag which all values in this set must have. */ jit::MIRType getKnownMIRType(); - bool isMagicArguments() { return getKnownMIRType() == jit::MIRType_Magic; } + bool isMagicArguments() { return getKnownMIRType() == jit::MIRType_MagicOptimizedArguments; } /* Whether this value may be an object. */ bool maybeObject() { return unknownObject() || baseObjectCount() > 0; }