Bug 645416, part 6 - JIT support for symbol values. r=jandem.
authorJason Orendorff <jorendorff@mozilla.com>
Mon, 23 Jun 2014 10:56:18 -0500
changeset 190273 e08a6942e21cbd09bd9a976f76115386e4b48fb9
parent 190272 5d71e73ce8d4187d6d9e9d017895ac03f1a08c3c
child 190274 cdf258b25a12c688a7821f6351dd06ea6d99b93f
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjandem
bugs645416
milestone33.0a1
Bug 645416, part 6 - JIT support for symbol values. r=jandem. Symbols are not yet supported as property keys at this point in the stack. The work here is to pass symbol pointers around in Ion JIT code unboxed. The baseline compiler doesn't need much new code. A few kinds of ICs need to know all the primitive types.
js/src/jit-test/tests/symbol/toString.js
js/src/jit-test/tests/symbol/typeof.js
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonBuilder.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonFrames.cpp
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/IonTypes.h
js/src/jit/LIR.h
js/src/jit/Lowering.cpp
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/Snapshots.cpp
js/src/jit/TypePolicy.cpp
js/src/jit/VMFunctions.cpp
js/src/jit/VMFunctions.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/mips/MacroAssembler-mips.cpp
js/src/jit/mips/MacroAssembler-mips.h
js/src/jit/shared/CodeGenerator-shared.cpp
js/src/jit/shared/Lowering-shared.cpp
js/src/jit/x64/CodeGenerator-x64.cpp
js/src/jit/x64/MacroAssembler-x64.h
js/src/jit/x86/MacroAssembler-x86.h
js/src/jsinfer.h
js/src/vm/GlobalObject.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/symbol/toString.js
@@ -0,0 +1,8 @@
+// ToString(symbol) throws a TypeError.
+
+var obj;
+for (var i = 0; i < 10; i++) {
+    try {
+        obj = new String(Symbol());
+    } catch (exc) {}
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/symbol/typeof.js
@@ -0,0 +1,9 @@
+var a = [0, 0, 0, 0, 0, Symbol(), Symbol()];
+var b = [];
+function f(i, v) {
+    b[i] = typeof v;
+}
+for (var i = 0; i < a.length; i++)
+    f(i, a[i]);
+assertEq(b[b.length - 2], "symbol");
+assertEq(b[b.length - 1], "symbol");
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1688,16 +1688,17 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_Hole:
       case Bailout_NegativeIndex:
       case Bailout_ObjectIdentityOrTypeGuard:
       case Bailout_NonInt32Input:
       case Bailout_NonNumericInput:
       case Bailout_NonBooleanInput:
       case Bailout_NonObjectInput:
       case Bailout_NonStringInput:
+      case Bailout_NonSymbolInput:
       case Bailout_GuardThreadExclusive:
       case Bailout_InitialState:
         // Do nothing.
         break;
 
       // Invalid assumption based on baseline code.
       case Bailout_OverflowInvalidate:
       case Bailout_NonStringInputInvalidate:
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1336,16 +1336,19 @@ ICTypeMonitor_PrimitiveSet::Compiler::ge
         masm.branchTestUndefined(Assembler::Equal, R0, &success);
 
     if (flags_ & TypeToFlag(JSVAL_TYPE_BOOLEAN))
         masm.branchTestBoolean(Assembler::Equal, R0, &success);
 
     if (flags_ & TypeToFlag(JSVAL_TYPE_STRING))
         masm.branchTestString(Assembler::Equal, R0, &success);
 
+    if (flags_ & TypeToFlag(JSVAL_TYPE_SYMBOL))
+        masm.branchTestSymbol(Assembler::Equal, R0, &success);
+
     // Currently, we will never generate primitive stub checks for object.  However,
     // when we do get to the point where we want to collapse our monitor chains of
     // objects and singletons down (when they get too long) to a generic "any object"
     // in coordination with the typeset doing the same thing, this will need to
     // be re-enabled.
     /*
     if (flags_ & TypeToFlag(JSVAL_TYPE_OBJECT))
         masm.branchTestObject(Assembler::Equal, R0, &success);
@@ -1558,16 +1561,19 @@ ICTypeUpdate_PrimitiveSet::Compiler::gen
         masm.branchTestUndefined(Assembler::Equal, R0, &success);
 
     if (flags_ & TypeToFlag(JSVAL_TYPE_BOOLEAN))
         masm.branchTestBoolean(Assembler::Equal, R0, &success);
 
     if (flags_ & TypeToFlag(JSVAL_TYPE_STRING))
         masm.branchTestString(Assembler::Equal, R0, &success);
 
+    if (flags_ & TypeToFlag(JSVAL_TYPE_SYMBOL))
+        masm.branchTestSymbol(Assembler::Equal, R0, &success);
+
     // Currently, we will never generate primitive stub checks for object.  However,
     // when we do get to the point where we want to collapse our monitor chains of
     // objects and singletons down (when they get too long) to a generic "any object"
     // in coordination with the typeset doing the same thing, this will need to
     // be re-enabled.
     /*
     if (flags_ & TypeToFlag(JSVAL_TYPE_OBJECT))
         masm.branchTestObject(Assembler::Equal, R0, &success);
@@ -6282,16 +6288,19 @@ TryAttachPrimitiveGetPropStub(JSContext 
     JS_ASSERT(!*attached);
 
     JSValueType primitiveType;
     RootedObject proto(cx);
     Rooted<GlobalObject*> global(cx, &script->global());
     if (val.isString()) {
         primitiveType = JSVAL_TYPE_STRING;
         proto = GlobalObject::getOrCreateStringPrototype(cx, global);
+    } else if (val.isSymbol()) {
+        primitiveType = JSVAL_TYPE_SYMBOL;
+        proto = GlobalObject::getOrCreateSymbolPrototype(cx, global);
     } else if (val.isNumber()) {
         primitiveType = JSVAL_TYPE_DOUBLE;
         proto = GlobalObject::getOrCreateNumberPrototype(cx, global);
     } else {
         JS_ASSERT(val.isBoolean());
         primitiveType = JSVAL_TYPE_BOOLEAN;
         proto = GlobalObject::getOrCreateBooleanPrototype(cx, global);
     }
@@ -6524,16 +6533,19 @@ ICGetProp_StringLength::Compiler::genera
 bool
 ICGetProp_Primitive::Compiler::generateStubCode(MacroAssembler &masm)
 {
     Label failure;
     switch (primitiveType_) {
       case JSVAL_TYPE_STRING:
         masm.branchTestString(Assembler::NotEqual, R0, &failure);
         break;
+      case JSVAL_TYPE_SYMBOL:
+        masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
+        break;
       case JSVAL_TYPE_DOUBLE: // Also used for int32.
         masm.branchTestNumber(Assembler::NotEqual, R0, &failure);
         break;
       case JSVAL_TYPE_BOOLEAN:
         masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
@@ -9749,16 +9761,20 @@ ICTypeOf_Typed::Compiler::generateStubCo
       case JSTYPE_NUMBER:
         masm.branchTestNumber(Assembler::NotEqual, R0, &failure);
         break;
 
       case JSTYPE_BOOLEAN:
         masm.branchTestBoolean(Assembler::NotEqual, R0, &failure);
         break;
 
+      case JSTYPE_SYMBOL:
+        masm.branchTestSymbol(Assembler::NotEqual, R0, &failure);
+        break;
+
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected type");
     }
 
     masm.movePtr(ImmGCPtr(typeString_), R0.scratchReg());
     masm.tagValue(JSVAL_TYPE_STRING, R0.scratchReg(), R0);
     EmitReturnFromIC(masm);
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -521,20 +521,21 @@ CodeGenerator::testValueTruthyKernel(con
     // all but one of them already so we know exactly what's left based on the
     // mightBe* booleans.
     bool mightBeUndefined = valueMIR->mightBeType(MIRType_Undefined);
     bool mightBeNull = valueMIR->mightBeType(MIRType_Null);
     bool mightBeBoolean = valueMIR->mightBeType(MIRType_Boolean);
     bool mightBeInt32 = valueMIR->mightBeType(MIRType_Int32);
     bool mightBeObject = valueMIR->mightBeType(MIRType_Object);
     bool mightBeString = valueMIR->mightBeType(MIRType_String);
+    bool mightBeSymbol = valueMIR->mightBeType(MIRType_Symbol);
     bool mightBeDouble = valueMIR->mightBeType(MIRType_Double);
     int tagCount = int(mightBeUndefined) + int(mightBeNull) +
         int(mightBeBoolean) + int(mightBeInt32) + int(mightBeObject) +
-        int(mightBeString) + int(mightBeDouble);
+        int(mightBeString) + int(mightBeSymbol) + int(mightBeDouble);
 
     MOZ_ASSERT_IF(!valueMIR->emptyResultTypeSet(), tagCount > 0);
 
     // If we know we're null or undefined, we're definitely falsy, no
     // need to even check the tag.
     if (int(mightBeNull) + int(mightBeUndefined) == tagCount) {
         masm.jump(ifFalsy);
         return;
@@ -614,16 +615,25 @@ CodeGenerator::testValueTruthyKernel(con
         masm.branchTestStringTruthy(false, value, ifFalsy);
         if (tagCount != 1)
             masm.jump(ifTruthy);
         // Else just fall through to truthiness.
         masm.bind(&notString);
         --tagCount;
     }
 
+    if (mightBeSymbol) {
+        // All symbols are truthy.
+        MOZ_ASSERT(tagCount != 0);
+        if (tagCount != 1)
+            masm.branchTestSymbol(Assembler::Equal, tag, ifTruthy);
+        // Else fall through to ifTruthy.
+        --tagCount;
+    }
+
     if (mightBeDouble) {
         MOZ_ASSERT(tagCount == 1);
         // If we reach here the value is a double.
         masm.unboxDouble(value, fr);
         masm.branchTestDoubleTruthy(false, fr, ifFalsy);
         --tagCount;
     }
 
@@ -939,16 +949,20 @@ CodeGenerator::visitValueToString(LValue
         // Bail.
         JS_ASSERT(lir->mir()->fallible());
         Label bail;
         masm.branchTestObject(Assembler::Equal, tag, &bail);
         if (!bailoutFrom(&bail, lir->snapshot()))
             return false;
     }
 
+    // Symbol
+    if (lir->mir()->input()->mightBeType(MIRType_Symbol))
+        masm.branchTestSymbol(Assembler::Equal, tag, ool->entry());
+
 #ifdef DEBUG
     masm.assumeUnreachable("Unexpected type for MValueToString.");
 #endif
 
     masm.bind(&done);
     masm.bind(ool->rejoin());
     return true;
 }
@@ -3204,17 +3218,19 @@ CodeGenerator::emitObjectOrStringResultC
     if (gen->info().executionMode() != ParallelExecution) {
         saveVolatile();
         masm.setupUnalignedABICall(2, temp);
         masm.loadJSContext(temp);
         masm.passABIArg(temp);
         masm.passABIArg(output);
         masm.callWithABINoProfiling(mir->type() == MIRType_Object
                                     ? JS_FUNC_TO_DATA_PTR(void *, AssertValidObjectPtr)
-                                    : JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr));
+                                    : mir->type() == MIRType_String
+                                      ? JS_FUNC_TO_DATA_PTR(void *, AssertValidStringPtr)
+                                      : JS_FUNC_TO_DATA_PTR(void *, AssertValidSymbolPtr));
         restoreVolatile();
     }
 
     masm.bind(&done);
     masm.pop(temp);
     return true;
 }
 
@@ -3285,16 +3301,17 @@ CodeGenerator::emitDebugResultChecks(LIn
 
     MDefinition *mir = ins->mirRaw();
     if (!mir)
         return true;
 
     switch (mir->type()) {
       case MIRType_Object:
       case MIRType_String:
+      case MIRType_Symbol:
         return emitObjectOrStringResultChecks(ins, mir);
       case MIRType_Value:
         return emitValueResultChecks(ins, mir);
       default:
         return true;
     }
 }
 #endif
@@ -7649,17 +7666,29 @@ CodeGenerator::visitTypeOfV(LTypeOfV *li
     masm.bind(&notNull);
 
     Label notBoolean;
     masm.branchTestBoolean(Assembler::NotEqual, tag, &notBoolean);
     masm.movePtr(ImmGCPtr(names.boolean), output);
     masm.jump(&done);
     masm.bind(&notBoolean);
 
+    Label notString;
+    masm.branchTestString(Assembler::NotEqual, tag, &notString);
     masm.movePtr(ImmGCPtr(names.string), output);
+    masm.jump(&done);
+    masm.bind(&notString);
+
+#ifdef DEBUG
+    Label isSymbol;
+    masm.branchTestSymbol(Assembler::Equal, tag, &isSymbol);
+    masm.assumeUnreachable("Unexpected type for TypeOfV");
+    masm.bind(&isSymbol);
+#endif
+    masm.movePtr(ImmGCPtr(names.symbol), output);
 
     masm.bind(&done);
     if (ool)
         masm.bind(ool->rejoin());
     return true;
 }
 
 bool
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1070,16 +1070,17 @@ IonBuilder::addOsrValueTypeBarrier(uint3
         def = barrier;
     }
 
     switch (type) {
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_String:
+      case MIRType_Symbol:
       case MIRType_Object:
         if (type != def->type()) {
             MUnbox *unbox = MUnbox::New(alloc(), def, type, MUnbox::Fallible);
             osrBlock->insertBefore(osrBlock->lastIns(), unbox);
             osrBlock->rewriteSlot(slot, unbox);
             def = unbox;
         }
         break;
@@ -6194,16 +6195,20 @@ IonBuilder::testSingletonPropertyTypes(M
         return testSingletonProperty(objectSingleton, name) == singleton;
 
     JSProtoKey key;
     switch (obj->type()) {
       case MIRType_String:
         key = JSProto_String;
         break;
 
+      case MIRType_Symbol:
+        key = JSProto_Symbol;
+        break;
+
       case MIRType_Int32:
       case MIRType_Double:
         key = JSProto_Number;
         break;
 
       case MIRType_Boolean:
         key = JSProto_Boolean;
         break;
@@ -6496,16 +6501,17 @@ jit::TypeSetIncludes(types::TypeSet *typ
     switch (input) {
       case MIRType_Undefined:
       case MIRType_Null:
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Float32:
       case MIRType_String:
+      case MIRType_Symbol:
       case MIRType_MagicOptimizedArguments:
         return types->hasType(types::Type::PrimitiveType(ValueTypeFromMIRType(input)));
 
       case MIRType_Object:
         return types->unknownObject() || (inputTypes && inputTypes->isSubset(types));
 
       case MIRType_Value:
         return types->unknown() || (inputTypes && inputTypes->isSubset(types));
@@ -7241,35 +7247,39 @@ IonBuilder::getElemTryCache(bool *emitte
     // Make sure we have at least an object.
     if (!obj->mightBeType(MIRType_Object))
         return true;
 
     // Don't cache for strings.
     if (obj->mightBeType(MIRType_String))
         return true;
 
-    // Index should be integer or string
-    if (!index->mightBeType(MIRType_Int32) && !index->mightBeType(MIRType_String))
-        return true;
+    // Index should be integer, string, or symbol
+    if (!index->mightBeType(MIRType_Int32) &&
+        !index->mightBeType(MIRType_String) &&
+        !index->mightBeType(MIRType_Symbol))
+    {
+        return true;
+    }
 
     // Turn off cacheing if the element is int32 and we've seen non-native objects as the target
     // of this getelem.
     bool nonNativeGetElement = inspector->hasSeenNonNativeGetElement(pc);
     if (index->mightBeType(MIRType_Int32) && nonNativeGetElement)
         return true;
 
     // Emit GetElementCache.
 
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
                                                        nullptr, types);
 
-    // Always add a barrier if the index might be a string, so that the cache
-    // can attach stubs for particular properties.
-    if (index->mightBeType(MIRType_String))
+    // Always add a barrier if the index might be a string or symbol, so that
+    // the cache can attach stubs for particular properties.
+    if (index->mightBeType(MIRType_String) || index->mightBeType(MIRType_Symbol))
         barrier = BarrierKind::TypeSet;
 
     // See note about always needing a barrier in jsop_getprop.
     if (needsToMonitorMissingProperties(types))
         barrier = BarrierKind::TypeSet;
 
     MInstruction *ins = MGetElementCache::New(alloc(), obj, index, barrier != BarrierKind::NoBarrier);
 
@@ -7295,17 +7305,18 @@ IonBuilder::getElemTryCache(bool *emitte
     return true;
 }
 
 bool
 IonBuilder::jsop_getelem_dense(MDefinition *obj, MDefinition *index)
 {
     types::TemporaryTypeSet *types = bytecodeTypes(pc);
 
-    if (JSOp(*pc) == JSOP_CALLELEM && !index->mightBeType(MIRType_String)) {
+    MOZ_ASSERT(index->type() == MIRType_Int32 || index->type() == MIRType_Double);
+    if (JSOp(*pc) == JSOP_CALLELEM) {
         // Indexed call on an element of an array. Populate the observed types
         // with any objects that could be in the array, to avoid extraneous
         // type barriers.
         AddObjectsForPropertyRead(obj, nullptr, types);
     }
 
     BarrierKind barrier = PropertyReadNeedsTypeBarrier(analysisContext, constraints(), obj,
                                                        nullptr, types);
@@ -7867,18 +7878,22 @@ bool
 IonBuilder::setElemTryCache(bool *emitted, MDefinition *object,
                             MDefinition *index, MDefinition *value)
 {
     JS_ASSERT(*emitted == false);
 
     if (!object->mightBeType(MIRType_Object))
         return true;
 
-    if (!index->mightBeType(MIRType_Int32) && !index->mightBeType(MIRType_String))
-        return true;
+    if (!index->mightBeType(MIRType_Int32) &&
+        !index->mightBeType(MIRType_String) &&
+        !index->mightBeType(MIRType_Symbol))
+    {
+        return true;
+    }
 
     // TODO: Bug 876650: remove this check:
     // Temporary disable the cache if non dense native,
     // until the cache supports more ics
     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
     if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite())
         return true;
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -3362,16 +3362,17 @@ GetElementIC::attachArgumentsElement(JSC
     // Ensure result is not magic value, and type-check result.
     masm.branchTestMagic(Assembler::Equal, elemIdx, &failureRestoreIndex);
 
     if (output().hasTyped()) {
         JS_ASSERT(!output().typedReg().isFloat());
         JS_ASSERT(index().reg().type() == MIRType_Boolean ||
                   index().reg().type() == MIRType_Int32 ||
                   index().reg().type() == MIRType_String ||
+                  index().reg().type() == MIRType_Symbol ||
                   index().reg().type() == MIRType_Object);
         masm.branchTestMIRType(Assembler::NotEqual, elemIdx, index().reg().type(),
                                &failureRestoreIndex);
     }
 
     masm.loadTypedOrValue(elemIdx, output());
 
     // indexReg may need to be reconstructed if it was originally a value.
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -1447,25 +1447,33 @@ FromObjectPayload(uintptr_t payload)
 
 static Value
 FromStringPayload(uintptr_t payload)
 {
     return StringValue(reinterpret_cast<JSString *>(payload));
 }
 
 static Value
+FromSymbolPayload(uintptr_t payload)
+{
+    return SymbolValue(reinterpret_cast<JS::Symbol *>(payload));
+}
+
+static Value
 FromTypedPayload(JSValueType type, uintptr_t payload)
 {
     switch (type) {
       case JSVAL_TYPE_INT32:
         return Int32Value(payload);
       case JSVAL_TYPE_BOOLEAN:
         return BooleanValue(!!payload);
       case JSVAL_TYPE_STRING:
         return FromStringPayload(payload);
+      case JSVAL_TYPE_SYMBOL:
+        return FromSymbolPayload(payload);
       case JSVAL_TYPE_OBJECT:
         return FromObjectPayload(payload);
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type - needs payload");
     }
 }
 
 bool
@@ -1542,16 +1550,18 @@ SnapshotIterator::allocationValue(const 
           case JSVAL_TYPE_DOUBLE:
             return DoubleValue(ReadFrameDoubleSlot(fp_, alloc.stackOffset2()));
           case JSVAL_TYPE_INT32:
             return Int32Value(ReadFrameInt32Slot(fp_, alloc.stackOffset2()));
           case JSVAL_TYPE_BOOLEAN:
             return BooleanValue(ReadFrameBooleanSlot(fp_, alloc.stackOffset2()));
           case JSVAL_TYPE_STRING:
             return FromStringPayload(fromStack(alloc.stackOffset2()));
+          case JSVAL_TYPE_SYMBOL:
+            return FromSymbolPayload(fromStack(alloc.stackOffset2()));
           case JSVAL_TYPE_OBJECT:
             return FromObjectPayload(fromStack(alloc.stackOffset2()));
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected type");
         }
       }
 
 #if defined(JS_NUNBOX32)
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -71,38 +71,39 @@ class TypeWrapper {
 template <typename Source, typename TypeSet> void
 MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types, BarrierKind kind,
                              Register scratch, Label *miss)
 {
     JS_ASSERT(kind == BarrierKind::TypeTagOnly || kind == BarrierKind::TypeSet);
     JS_ASSERT(!types->unknown());
 
     Label matched;
-    types::Type tests[7] = {
+    types::Type tests[8] = {
         types::Type::Int32Type(),
         types::Type::UndefinedType(),
         types::Type::BooleanType(),
         types::Type::StringType(),
+        types::Type::SymbolType(),
         types::Type::NullType(),
         types::Type::MagicArgType(),
         types::Type::AnyObjectType()
     };
 
     // The double type also implies Int32.
     // So replace the int32 test with the double one.
     if (types->hasType(types::Type::DoubleType())) {
         JS_ASSERT(types->hasType(types::Type::Int32Type()));
         tests[0] = types::Type::DoubleType();
     }
 
     Register tag = extractTag(address, scratch);
 
     // Emit all typed tests.
     BranchType lastBranch;
-    for (size_t i = 0; i < 7; i++) {
+    for (size_t i = 0; i < mozilla::ArrayLength(tests); i++) {
         if (!types->hasType(tests[i]))
             continue;
 
         if (lastBranch.isInitialized())
             lastBranch.emit(*this);
         lastBranch = BranchType(Equal, tag, tests[i], &matched);
     }
 
@@ -1673,16 +1674,17 @@ MacroAssembler::convertTypedOrValueToFlo
             if (src.typedReg().fpu() != output)
                 moveDouble(src.typedReg().fpu(), output);
         } else {
             convertDoubleToFloat32(src.typedReg().fpu(), output);
         }
         break;
       case MIRType_Object:
       case MIRType_String:
+      case MIRType_Symbol:
         jump(fail);
         break;
       case MIRType_Undefined:
         loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Bad MIRType");
     }
@@ -1749,17 +1751,17 @@ MacroAssembler::convertValueToInt(ValueO
             branchEqualTypeIfNeeded(MIRType_Object, maybeInput, tag, fail);
             branchTestUndefined(Assembler::NotEqual, tag, fail);
             break;
         }
     } else {
         jump(fail);
     }
 
-    // The value is null or undefined in truncation contexts - just emit 0.
+    // The value is null, undefined, or a symbol in truncation contexts - just emit 0.
     if (isNull.used())
         bind(&isNull);
     mov(ImmWord(0), output);
     jump(&done);
 
     // Try converting a string into a double, then jump to the double case.
     if (handleStrings) {
         bind(&isString);
@@ -1889,16 +1891,17 @@ MacroAssembler::convertTypedOrValueToInt
         convertDoubleToInt(src.typedReg().fpu(), output, temp, nullptr, fail, behavior);
         break;
       case MIRType_Float32:
         // Conversion to Double simplifies implementation at the expense of performance.
         convertFloat32ToDouble(src.typedReg().fpu(), temp);
         convertDoubleToInt(temp, output, temp, nullptr, fail, behavior);
         break;
       case MIRType_String:
+      case MIRType_Symbol:
       case MIRType_Object:
         jump(fail);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Bad MIRType");
     }
 }
 
@@ -1972,16 +1975,19 @@ MacroAssembler::branchEqualTypeIfNeeded(
             branchTestInt32(Equal, tag, label);
             break;
           case MIRType_Double:
             branchTestDouble(Equal, tag, label);
             break;
           case MIRType_String:
             branchTestString(Equal, tag, label);
             break;
+          case MIRType_Symbol:
+            branchTestSymbol(Equal, tag, label);
+            break;
           case MIRType_Object:
             branchTestObject(Equal, tag, label);
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Unsupported type");
         }
     }
 }
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -329,16 +329,17 @@ class MacroAssembler : public MacroAssem
     template <typename Value>
     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_Symbol:    return branchTestSymbol(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");
         }
@@ -641,16 +642,17 @@ class MacroAssembler : public MacroAssem
         AbsoluteAddress needsBarrierAddr(zone->addressOfNeedsBarrier());
         branchTest32(cond, needsBarrierAddr, Imm32(0x1), label);
     }
 
     template <typename T>
     void callPreBarrier(const T &address, MIRType type) {
         JS_ASSERT(type == MIRType_Value ||
                   type == MIRType_String ||
+                  type == MIRType_Symbol ||
                   type == MIRType_Object ||
                   type == MIRType_Shape);
         Label done;
 
         if (type == MIRType_Value)
             branchTestGCThing(Assembler::NotEqual, address, &done);
 
         Push(PreBarrierReg);
@@ -666,16 +668,17 @@ class MacroAssembler : public MacroAssem
 
         bind(&done);
     }
 
     template <typename T>
     void patchableCallPreBarrier(const T &address, MIRType type) {
         JS_ASSERT(type == MIRType_Value ||
                   type == MIRType_String ||
+                  type == MIRType_Symbol ||
                   type == MIRType_Object ||
                   type == MIRType_Shape);
 
         Label done;
 
         // All barriers are off by default.
         // They are enabled if necessary at the end of CodeGenerator::generate().
         CodeOffsetLabel nopJump = toggledJump(&done);
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -93,16 +93,17 @@ enum BailoutKind
     Bailout_ObjectIdentityOrTypeGuard,
 
     // Unbox expects a given type, bails out if it doesn't get it.
     Bailout_NonInt32Input,
     Bailout_NonNumericInput, // unboxing a double works with int32 too
     Bailout_NonBooleanInput,
     Bailout_NonObjectInput,
     Bailout_NonStringInput,
+    Bailout_NonSymbolInput,
 
     Bailout_GuardThreadExclusive,
 
     // For the initial snapshot when entering a function.
     Bailout_InitialState,
 
     // END Normal bailouts
 
@@ -184,16 +185,18 @@ BailoutKindString(BailoutKind kind)
       case Bailout_NonNumericInput:
         return "Bailout_NonNumericInput";
       case Bailout_NonBooleanInput:
         return "Bailout_NonBooleanInput";
       case Bailout_NonObjectInput:
         return "Bailout_NonObjectInput";
       case Bailout_NonStringInput:
         return "Bailout_NonStringInput";
+      case Bailout_NonSymbolInput:
+        return "Bailout_NonSymbolInput";
       case Bailout_GuardThreadExclusive:
         return "Bailout_GuardThreadExclusive";
       case Bailout_InitialState:
         return "Bailout_InitialState";
 
       // Bailouts caused by invalid assumptions.
       case Bailout_OverflowInvalidate:
         return "Bailout_OverflowInvalidate";
@@ -278,16 +281,18 @@ MIRTypeFromValueType(JSValueType type)
       case JSVAL_TYPE_DOUBLE:
         return MIRType_Double;
       case JSVAL_TYPE_INT32:
         return MIRType_Int32;
       case JSVAL_TYPE_UNDEFINED:
         return MIRType_Undefined;
       case JSVAL_TYPE_STRING:
         return MIRType_String;
+      case JSVAL_TYPE_SYMBOL:
+        return MIRType_Symbol;
       case JSVAL_TYPE_BOOLEAN:
         return MIRType_Boolean;
       case JSVAL_TYPE_NULL:
         return MIRType_Null;
       case JSVAL_TYPE_OBJECT:
         return MIRType_Object;
       case JSVAL_TYPE_UNKNOWN:
         return MIRType_Value;
@@ -308,16 +313,18 @@ ValueTypeFromMIRType(MIRType type)
       return JSVAL_TYPE_BOOLEAN;
     case MIRType_Int32:
       return JSVAL_TYPE_INT32;
     case MIRType_Float32: // Fall through, there's no JSVAL for Float32
     case MIRType_Double:
       return JSVAL_TYPE_DOUBLE;
     case MIRType_String:
       return JSVAL_TYPE_STRING;
+    case MIRType_Symbol:
+      return JSVAL_TYPE_SYMBOL;
     case MIRType_MagicOptimizedArguments:
     case MIRType_MagicOptimizedOut:
     case MIRType_MagicHole:
     case MIRType_MagicIsConstructing:
       return JSVAL_TYPE_MAGIC;
     default:
       JS_ASSERT(type == MIRType_Object);
       return JSVAL_TYPE_OBJECT;
@@ -343,16 +350,18 @@ StringFromMIRType(MIRType type)
     case MIRType_Int32:
       return "Int32";
     case MIRType_Double:
       return "Double";
     case MIRType_Float32:
       return "Float32";
     case MIRType_String:
       return "String";
+    case MIRType_Symbol:
+      return "Symbol";
     case MIRType_Object:
       return "Object";
     case MIRType_MagicOptimizedArguments:
       return "MagicOptimizedArguments";
     case MIRType_MagicOptimizedOut:
       return "MagicOptimizedOut";
     case MIRType_MagicHole:
       return "MagicHole";
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -526,16 +526,17 @@ class LDefinition
         switch (type) {
           case MIRType_Boolean:
           case MIRType_Int32:
             // The stack slot allocator doesn't currently support allocating
             // 1-byte slots, so for now we lower MIRType_Boolean into INT32.
             static_assert(sizeof(bool) <= sizeof(int32_t), "bool doesn't fit in an int32 slot");
             return LDefinition::INT32;
           case MIRType_String:
+          case MIRType_Symbol:
           case MIRType_Object:
             return LDefinition::OBJECT;
           case MIRType_Double:
             return LDefinition::DOUBLE;
           case MIRType_Float32:
             return LDefinition::FLOAT32;
 #if defined(JS_PUNBOX64)
           case MIRType_Value:
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -652,17 +652,17 @@ bool
 LIRGenerator::visitTest(MTest *test)
 {
     MDefinition *opd = test->getOperand(0);
     MBasicBlock *ifTrue = test->ifTrue();
     MBasicBlock *ifFalse = test->ifFalse();
 
     // String is converted to length of string in the type analysis phase (see
     // TestPolicy).
-    JS_ASSERT(opd->type() != MIRType_String);
+    MOZ_ASSERT(opd->type() != MIRType_String);
 
     if (opd->type() == MIRType_Value) {
         LDefinition temp0, temp1;
         if (test->operandMightEmulateUndefined()) {
             temp0 = temp();
             temp1 = temp();
         } else {
             temp0 = LDefinition::BogusTemp();
@@ -683,16 +683,20 @@ LIRGenerator::visitTest(MTest *test)
         return add(new(alloc()) LGoto(ifTrue));
     }
 
     // These must be explicitly sniffed out since they are constants and have
     // no payload.
     if (opd->type() == MIRType_Undefined || opd->type() == MIRType_Null)
         return add(new(alloc()) LGoto(ifFalse));
 
+    // All symbols are truthy.
+    if (opd->type() == MIRType_Symbol)
+        return add(new(alloc()) LGoto(ifTrue));
+
     // Constant Double operand.
     if (opd->type() == MIRType_Double && opd->isConstant()) {
         bool result = opd->toConstant()->valueToBoolean();
         return add(new(alloc()) LGoto(result ? ifTrue : ifFalse));
     }
 
     // Constant Float32 operand.
     if (opd->type() == MIRType_Float32 && opd->isConstant()) {
@@ -1820,19 +1824,20 @@ LIRGenerator::visitToInt32(MToInt32 *con
 
       case MIRType_Double:
       {
         LDoubleToInt32 *lir = new(alloc()) LDoubleToInt32(useRegister(opd));
         return assignSnapshot(lir, Bailout_PrecisionLoss) && define(lir, convert);
       }
 
       case MIRType_String:
+      case MIRType_Symbol:
       case MIRType_Object:
       case MIRType_Undefined:
-        // Objects might be effectful. Undefined coerces to NaN, not int32.
+        // Objects might be effectful. Undefined and symbols coerce to NaN, not int32.
         MOZ_ASSUME_UNREACHABLE("ToInt32 invalid input type");
         return false;
 
       default:
         MOZ_ASSUME_UNREACHABLE("unexpected type");
     }
 }
 
@@ -1850,16 +1855,17 @@ LIRGenerator::visitTruncateToInt32(MTrun
             return false;
         return assignSnapshot(lir, Bailout_NonPrimitiveInput)
                && define(lir, truncate)
                && assignSafepoint(lir, truncate);
       }
 
       case MIRType_Null:
       case MIRType_Undefined:
+      case MIRType_Symbol:
         return define(new(alloc()) LInteger(0), truncate);
 
       case MIRType_Int32:
       case MIRType_Boolean:
         return redefine(truncate, opd);
 
       case MIRType_Double:
         return lowerTruncateDToInt32(truncate);
@@ -2452,16 +2458,18 @@ LIRGenerator::visitNot(MNot *ins)
       }
       case MIRType_Double:
         return define(new(alloc()) LNotD(useRegister(op)), ins);
       case MIRType_Float32:
         return define(new(alloc()) LNotF(useRegister(op)), ins);
       case MIRType_Undefined:
       case MIRType_Null:
         return define(new(alloc()) LInteger(1), ins);
+      case MIRType_Symbol:
+        return define(new(alloc()) LInteger(0), ins);
       case MIRType_Object: {
         // Objects that don't emulate undefined can be constant-folded.
         if (!ins->operandMightEmulateUndefined())
             return define(new(alloc()) LInteger(0), ins);
         // All others require further work.
         return define(new(alloc()) LNotO(useRegister(op)), ins);
       }
       case MIRType_Value: {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -895,16 +895,17 @@ MUnbox::printOpcode(FILE *fp) const
     getOperand(0)->printName(fp);
     fprintf(fp, " ");
 
     switch (type()) {
       case MIRType_Int32: fprintf(fp, "to Int32"); break;
       case MIRType_Double: fprintf(fp, "to Double"); break;
       case MIRType_Boolean: fprintf(fp, "to Boolean"); break;
       case MIRType_String: fprintf(fp, "to String"); break;
+      case MIRType_Symbol: fprintf(fp, "to Symbol"); break;
       case MIRType_Object: fprintf(fp, "to Object"); break;
       default: break;
     }
 
     switch (mode()) {
       case Fallible: fprintf(fp, " (fallible)"); break;
       case Infallible: fprintf(fp, " (infallible)"); break;
       case TypeBarrier: fprintf(fp, " (typebarrier)"); break;
@@ -1680,20 +1681,21 @@ bool
 MUrsh::fallible() const
 {
     if (bailoutsDisabled())
         return false;
     return !range() || !range()->hasInt32Bounds();
 }
 
 static inline bool
-KnownNonStringPrimitive(MDefinition *op)
+SimpleArithOperand(MDefinition *op)
 {
     return !op->mightBeType(MIRType_Object)
         && !op->mightBeType(MIRType_String)
+        && !op->mightBeType(MIRType_Symbol)
         && !op->mightBeType(MIRType_MagicOptimizedArguments)
         && !op->mightBeType(MIRType_MagicHole)
         && !op->mightBeType(MIRType_MagicIsConstructing);
 }
 
 void
 MBinaryArithInstruction::infer(TempAllocator &alloc, BaselineInspector *inspector, jsbytecode *pc)
 {
@@ -1702,19 +1704,19 @@ MBinaryArithInstruction::infer(TempAlloc
     specialization_ = MIRType_None;
 
     // Don't specialize if one operand could be an object. If we specialize
     // as int32 or double based on baseline feedback, we could DCE this
     // instruction and fail to invoke any valueOf methods.
     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object))
         return;
 
-    // Anything complex - strings and objects - are not specialized
+    // Anything complex - strings, symbols, and objects - are not specialized
     // unless baseline type hints suggest it might be profitable
-    if (!KnownNonStringPrimitive(getOperand(0)) || !KnownNonStringPrimitive(getOperand(1)))
+    if (!SimpleArithOperand(getOperand(0)) || !SimpleArithOperand(getOperand(1)))
         return inferFallback(inspector, pc);
 
     // Retrieve type information of lhs and rhs.
     MIRType lhs = getOperand(0)->type();
     MIRType rhs = getOperand(1)->type();
 
     // Guess a result type based on the inputs.
     // Don't specialize for neither-integer-nor-double results.
@@ -1798,26 +1800,27 @@ MBinaryArithInstruction::inferFallback(B
         if (types)
             setResultTypeSet(types);
     }
 }
 
 static bool
 SafelyCoercesToDouble(MDefinition *op)
 {
-    // Strings are unhandled -- visitToDouble() doesn't support them yet.
+    // Strings and symbols are unhandled -- visitToDouble() doesn't support them yet.
     // Null is unhandled -- ToDouble(null) == 0, but (0 == null) is false.
-    return KnownNonStringPrimitive(op) && !op->mightBeType(MIRType_Null);
+    return SimpleArithOperand(op) && !op->mightBeType(MIRType_Null);
 }
 
 static bool
 ObjectOrSimplePrimitive(MDefinition *op)
 {
     // Return true if op is either undefined/null/boolean/int32 or an object.
     return !op->mightBeType(MIRType_String)
+        && !op->mightBeType(MIRType_Symbol)
         && !op->mightBeType(MIRType_Double)
         && !op->mightBeType(MIRType_Float32)
         && !op->mightBeType(MIRType_MagicOptimizedArguments)
         && !op->mightBeType(MIRType_MagicHole)
         && !op->mightBeType(MIRType_MagicIsConstructing);
 }
 
 static bool
@@ -2115,16 +2118,19 @@ MTypeOf::foldsTo(TempAllocator &alloc, b
     switch (inputType()) {
       case MIRType_Double:
       case MIRType_Int32:
         type = JSTYPE_NUMBER;
         break;
       case MIRType_String:
         type = JSTYPE_STRING;
         break;
+      case MIRType_Symbol:
+        type = JSTYPE_SYMBOL;
+        break;
       case MIRType_Null:
         type = JSTYPE_OBJECT;
         break;
       case MIRType_Undefined:
         type = JSTYPE_VOID;
         break;
       case MIRType_Boolean:
         type = JSTYPE_BOOLEAN;
@@ -2453,16 +2459,17 @@ MCompare::tryFold(bool *result)
           case MIRType_Object:
             if ((op == JSOP_EQ || op == JSOP_NE) && operandMightEmulateUndefined())
                 return false;
             /* FALL THROUGH */
           case MIRType_Int32:
           case MIRType_Double:
           case MIRType_Float32:
           case MIRType_String:
+          case MIRType_Symbol:
           case MIRType_Boolean:
             *result = (op == JSOP_NE || op == JSOP_STRICTNE);
             return true;
           default:
             MOZ_ASSUME_UNREACHABLE("Unexpected type");
         }
     }
 
@@ -2472,16 +2479,17 @@ MCompare::tryFold(bool *result)
 
         switch (lhs()->type()) {
           case MIRType_Value:
             return false;
           case MIRType_Int32:
           case MIRType_Double:
           case MIRType_Float32:
           case MIRType_String:
+          case MIRType_Symbol:
           case MIRType_Object:
           case MIRType_Null:
           case MIRType_Undefined:
             *result = (op == JSOP_STRICTNE);
             return true;
           case MIRType_Boolean:
             // Int32 specialization should handle this.
             MOZ_ASSUME_UNREACHABLE("Wrong specialization");
@@ -2496,16 +2504,17 @@ MCompare::tryFold(bool *result)
 
         switch (lhs()->type()) {
           case MIRType_Value:
             return false;
           case MIRType_Boolean:
           case MIRType_Int32:
           case MIRType_Double:
           case MIRType_Float32:
+          case MIRType_Symbol:
           case MIRType_Object:
           case MIRType_Null:
           case MIRType_Undefined:
             *result = (op == JSOP_STRICTNE);
             return true;
           case MIRType_String:
             // Compare_String specialization should handle this.
             MOZ_ASSUME_UNREACHABLE("Wrong specialization");
@@ -3435,17 +3444,18 @@ TryAddTypeBarrierForWrite(TempAllocator 
 
     JS_ASSERT(!aggregateProperty.empty());
 
     MIRType propertyType = aggregateProperty.ref().knownMIRType(constraints);
     switch (propertyType) {
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
-      case MIRType_String: {
+      case MIRType_String:
+      case MIRType_Symbol: {
         // The property is a particular primitive type, guard by unboxing the
         // value before the write.
         if (!(*pvalue)->mightBeType(propertyType)) {
             // The value's type does not match the property type. Just do a VM
             // call as it will always trigger invalidation of the compiled code.
             JS_ASSERT_IF((*pvalue)->type() != MIRType_Value, (*pvalue)->type() != propertyType);
             return false;
         }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2475,16 +2475,17 @@ class MCompare
 
         // Anything compared to Null
         Compare_Null,
 
         // Undefined compared to Boolean
         // Null      compared to Boolean
         // Double    compared to Boolean
         // String    compared to Boolean
+        // Symbol    compared to Boolean
         // Object    compared to Boolean
         // Value     compared to Boolean
         Compare_Boolean,
 
         // Int32   compared to Int32
         // Boolean compared to Boolean
         Compare_Int32,
         Compare_Int32MaybeCoerceBoth,
@@ -2698,16 +2699,17 @@ class MUnbox : public MUnaryInstruction,
       : MUnaryInstruction(ins),
         mode_(mode)
     {
         JS_ASSERT(ins->type() == MIRType_Value);
         JS_ASSERT(type == MIRType_Boolean ||
                   type == MIRType_Int32   ||
                   type == MIRType_Double  ||
                   type == MIRType_String  ||
+                  type == MIRType_Symbol  ||
                   type == MIRType_Object);
 
         setResultType(type);
         setResultTypeSet(ins->resultTypeSet());
         setMovable();
 
         if (mode_ == TypeBarrier || mode_ == Fallible)
             setGuard();
@@ -2729,16 +2731,19 @@ class MUnbox : public MUnaryInstruction,
             kind = Bailout_NonInt32Input;
             break;
           case MIRType_Double:
             kind = Bailout_NonNumericInput; // Int32s are fine too
             break;
           case MIRType_String:
             kind = Bailout_NonStringInput;
             break;
+          case MIRType_Symbol:
+            kind = Bailout_NonSymbolInput;
+            break;
           case MIRType_Object:
             kind = Bailout_NonObjectInput;
             break;
           default:
             MOZ_ASSUME_UNREACHABLE("Given MIRType cannot be unboxed.");
         }
 
         return new(alloc) MUnbox(ins, type, mode, kind);
--- a/js/src/jit/Snapshots.cpp
+++ b/js/src/jit/Snapshots.cpp
@@ -387,16 +387,18 @@ ValTypeToString(JSValueType type)
 {
     switch (type) {
       case JSVAL_TYPE_INT32:
         return "int32_t";
       case JSVAL_TYPE_DOUBLE:
         return "double";
       case JSVAL_TYPE_STRING:
         return "string";
+      case JSVAL_TYPE_SYMBOL:
+        return "symbol";
       case JSVAL_TYPE_BOOLEAN:
         return "boolean";
       case JSVAL_TYPE_OBJECT:
         return "object";
       case JSVAL_TYPE_MAGIC:
         return "magic";
       default:
         MOZ_ASSUME_UNREACHABLE("no payload");
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -71,19 +71,21 @@ ArithPolicy::adjustInputs(TempAllocator 
 
     for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
         MDefinition *in = ins->getOperand(i);
         if (in->type() == ins->type())
             continue;
 
         MInstruction *replace;
 
-        // If the input is a string or an object, the conversion is not
-        // possible, at least, we can't specialize. So box the input.
-        if (in->type() == MIRType_Object || in->type() == MIRType_String ||
+        // If the input is a string, symbol, or object, the conversion is not
+        // possible--at least, we can't specialize. So box the input.
+        if (in->type() == MIRType_Object ||
+            in->type() == MIRType_String ||
+            in->type() == MIRType_Symbol ||
             (in->type() == MIRType_Undefined && specialization_ == MIRType_Int32))
         {
             in = boxAt(alloc, ins, in);
         }
 
         if (ins->type() == MIRType_Double)
             replace = MToDouble::New(alloc, in);
         else if (ins->type() == MIRType_Float32)
@@ -322,16 +324,17 @@ TestPolicy::adjustInputs(TempAllocator &
     switch (op->type()) {
       case MIRType_Value:
       case MIRType_Null:
       case MIRType_Undefined:
       case MIRType_Boolean:
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Float32:
+      case MIRType_Symbol:
       case MIRType_Object:
         break;
 
       case MIRType_String:
       {
         MStringLength *length = MStringLength::New(alloc, op);
         ins->block()->insertBefore(ins, length);
         ins->replaceOperand(0, length);
@@ -469,18 +472,22 @@ template bool ConvertToInt32Policy<0>::s
 template <unsigned Op>
 bool
 DoublePolicy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Double)
         return true;
 
-    // Force a bailout. Objects may be effectful; strings are currently unhandled.
-    if (in->type() == MIRType_Object || in->type() == MIRType_String) {
+    // Force a bailout. Objects may be effectful; strings and symbols are
+    // currently unhandled.
+    if (in->type() == MIRType_Object ||
+        in->type() == MIRType_String ||
+        in->type() == MIRType_Symbol)
+    {
         MBox *box = MBox::New(alloc, in);
         def->block()->insertBefore(def, box);
 
         MUnbox *unbox = MUnbox::New(alloc, box, MIRType_Double, MUnbox::Fallible);
         def->block()->insertBefore(def, unbox);
         def->replaceOperand(Op, unbox);
         return true;
     }
@@ -497,32 +504,35 @@ template bool DoublePolicy<1>::staticAdj
 template <unsigned Op>
 bool
 Float32Policy<Op>::staticAdjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MDefinition *in = def->getOperand(Op);
     if (in->type() == MIRType_Float32)
         return true;
 
-    // Force a bailout. Objects may be effectful; strings are currently unhandled.
-    if (in->type() == MIRType_Object || in->type() == MIRType_String) {
+    // Force a bailout. Objects may be effectful; strings and symbols are
+    // currently unhandled.
+    if (in->type() == MIRType_Object ||
+        in->type() == MIRType_String ||
+        in->type() == MIRType_Symbol)
+    {
         MToDouble *toDouble = MToDouble::New(alloc, in);
         def->block()->insertBefore(def, toDouble);
 
         MBox *box = MBox::New(alloc, toDouble);
         def->block()->insertBefore(def, box);
 
         MUnbox *unbox = MUnbox::New(alloc, box, MIRType_Double, MUnbox::Fallible);
         def->block()->insertBefore(def, unbox);
 
         MToFloat32 *toFloat32 = MToFloat32::New(alloc, unbox);
         def->block()->insertBefore(def, toFloat32);
 
         def->replaceOperand(Op, unbox);
-
         return true;
     }
 
     MToFloat32 *replace = MToFloat32::New(alloc, in);
     def->block()->insertBefore(def, replace);
     def->replaceOperand(Op, replace);
     return true;
 }
@@ -576,51 +586,57 @@ template bool BoxExceptPolicy<1, MIRType
                                                                      MInstruction *ins);
 template bool BoxExceptPolicy<2, MIRType_String>::staticAdjustInputs(TempAllocator &alloc,
                                                                      MInstruction *ins);
 
 bool
 ToDoublePolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     MDefinition *in = ins->getOperand(0);
-    if (in->type() != MIRType_Object && in->type() != MIRType_String)
+    if (in->type() != MIRType_Object &&
+        in->type() != MIRType_String &&
+        in->type() != MIRType_Symbol)
+    {
         return true;
+    }
 
     in = boxAt(alloc, ins, in);
     ins->replaceOperand(0, in);
     return true;
 }
 
 bool
 ToInt32Policy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     JS_ASSERT(ins->isToInt32());
 
     MDefinition *in = ins->getOperand(0);
     switch (in->type()) {
       case MIRType_Object:
       case MIRType_String:
+      case MIRType_Symbol:
       case MIRType_Undefined:
-        // Objects might be effectful. Undefined coerces to NaN, not int32.
+        // Objects might be effectful. Undefined and symbols coerce to NaN, not int32.
         in = boxAt(alloc, ins, in);
         ins->replaceOperand(0, in);
         break;
       default:
         break;
     }
 
     return true;
 }
 
 bool
 ToStringPolicy::staticAdjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
     JS_ASSERT(ins->isToString());
 
-    if (ins->getOperand(0)->type() == MIRType_Object) {
+    MIRType type = ins->getOperand(0)->type();
+    if (type == MIRType_Object || type == MIRType_Symbol) {
         ins->replaceOperand(0, boxAt(alloc, ins, ins->getOperand(0)));
         return true;
     }
 
     // TODO remove the following line once 966957 has landed
     EnsureOperandNotFloat32(alloc, ins, 0);
 
     return true;
@@ -721,16 +737,17 @@ StoreTypedArrayPolicy::adjustValueInput(
         break;
       case MIRType_Undefined:
         value->setImplicitlyUsedUnchecked();
         value = MConstant::New(alloc, DoubleNaNValue());
         ins->block()->insertBefore(ins, value->toInstruction());
         break;
       case MIRType_Object:
       case MIRType_String:
+      case MIRType_Symbol:
         value = boxAt(alloc, ins, value);
         break;
       default:
         MOZ_ASSUME_UNREACHABLE("Unexpected type");
     }
 
     if (value != curValue) {
         ins->replaceOperand(valueOperand, value);
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -1154,26 +1154,43 @@ AssertValidStringPtr(JSContext *cx, JSSt
         JS_ASSERT(kind == gc::FINALIZE_EXTERNAL_STRING);
     else if (str->isAtom() || str->isFlat())
         JS_ASSERT(kind == gc::FINALIZE_STRING || kind == gc::FINALIZE_FAT_INLINE_STRING);
     else
         JS_ASSERT(kind == gc::FINALIZE_STRING);
 }
 
 void
+AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym)
+{
+    // We can't closely inspect symbols from another runtime.
+    if (sym->runtimeFromAnyThread() != cx->runtime())
+        return;
+
+    JS_ASSERT(cx->runtime()->isAtomsZone(sym->tenuredZone()));
+
+    JS_ASSERT(sym->runtimeFromMainThread() == cx->runtime());
+    JS_ASSERT(sym->isAligned());
+    if (JSString *desc = sym->description()) {
+        JS_ASSERT(desc->isAtom());
+        AssertValidStringPtr(cx, desc);
+    }
+
+    JS_ASSERT(sym->tenuredGetAllocKind() == gc::FINALIZE_SYMBOL);
+}
+
+void
 AssertValidValue(JSContext *cx, Value *v)
 {
-    if (v->isObject()) {
+    if (v->isObject())
         AssertValidObjectPtr(cx, &v->toObject());
-        return;
-    }
-    if (v->isString()) {
+    else if (v->isString())
         AssertValidStringPtr(cx, v->toString());
-        return;
-    }
+    else if (v->isSymbol())
+        AssertValidSymbolPtr(cx, v->toSymbol());
 }
 #endif
 
 // Definition of the MTypedObjectProto MIR.
 JSObject *
 TypedObjectProto(JSObject *obj)
 {
     JS_ASSERT(obj->is<TypedObject>());
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -683,16 +683,17 @@ JSString *StringReplace(JSContext *cx, H
                         HandleString repl);
 
 bool SetDenseElement(JSContext *cx, HandleObject obj, int32_t index, HandleValue value,
                      bool strict);
 
 #ifdef DEBUG
 void AssertValidObjectPtr(JSContext *cx, JSObject *obj);
 void AssertValidStringPtr(JSContext *cx, JSString *str);
+void AssertValidSymbolPtr(JSContext *cx, JS::Symbol *sym);
 void AssertValidValue(JSContext *cx, Value *v);
 #endif
 
 JSObject *TypedObjectProto(JSObject *obj);
 
 } // namespace jit
 } // namespace js
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2724,16 +2724,22 @@ MacroAssemblerARMCompat::testUndefined(A
 
 Assembler::Condition
 MacroAssemblerARMCompat::testString(Assembler::Condition cond, const ValueOperand &value)
 {
     return testString(cond, value.typeReg());
 }
 
 Assembler::Condition
+MacroAssemblerARMCompat::testSymbol(Assembler::Condition cond, const ValueOperand &value)
+{
+    return testSymbol(cond, value.typeReg());
+}
+
+Assembler::Condition
 MacroAssemblerARMCompat::testObject(Assembler::Condition cond, const ValueOperand &value)
 {
     return testObject(cond, value.typeReg());
 }
 
 Assembler::Condition
 MacroAssemblerARMCompat::testNumber(Assembler::Condition cond, const ValueOperand &value)
 {
@@ -2765,37 +2771,48 @@ Assembler::Condition
 MacroAssemblerARMCompat::testBoolean(Assembler::Condition cond, Register tag)
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     ma_cmp(tag, ImmTag(JSVAL_TAG_BOOLEAN));
     return cond;
 }
 
 Assembler::Condition
-MacroAssemblerARMCompat::testNull(Assembler::Condition cond, Register tag) {
+MacroAssemblerARMCompat::testNull(Assembler::Condition cond, Register tag)
+{
     JS_ASSERT(cond == Equal || cond == NotEqual);
     ma_cmp(tag, ImmTag(JSVAL_TAG_NULL));
     return cond;
 }
 
 Assembler::Condition
-MacroAssemblerARMCompat::testUndefined(Assembler::Condition cond, Register tag) {
+MacroAssemblerARMCompat::testUndefined(Assembler::Condition cond, Register tag)
+{
     JS_ASSERT(cond == Equal || cond == NotEqual);
     ma_cmp(tag, ImmTag(JSVAL_TAG_UNDEFINED));
     return cond;
 }
 
 Assembler::Condition
-MacroAssemblerARMCompat::testString(Assembler::Condition cond, Register tag) {
+MacroAssemblerARMCompat::testString(Assembler::Condition cond, Register tag)
+{
     JS_ASSERT(cond == Equal || cond == NotEqual);
     ma_cmp(tag, ImmTag(JSVAL_TAG_STRING));
     return cond;
 }
 
 Assembler::Condition
+MacroAssemblerARMCompat::testSymbol(Assembler::Condition cond, Register tag)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    ma_cmp(tag, ImmTag(JSVAL_TAG_SYMBOL));
+    return cond;
+}
+
+Assembler::Condition
 MacroAssemblerARMCompat::testObject(Assembler::Condition cond, Register tag)
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     ma_cmp(tag, ImmTag(JSVAL_TAG_OBJECT));
     return cond;
 }
 
 Assembler::Condition
@@ -2877,16 +2894,24 @@ Assembler::Condition
 MacroAssemblerARMCompat::testString(Condition cond, const Address &address)
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     extractTag(address, ScratchRegister);
     return testString(cond, ScratchRegister);
 }
 
 Assembler::Condition
+MacroAssemblerARMCompat::testSymbol(Condition cond, const Address &address)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(address, ScratchRegister);
+    return testSymbol(cond, ScratchRegister);
+}
+
+Assembler::Condition
 MacroAssemblerARMCompat::testObject(Condition cond, const Address &address)
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     extractTag(address, ScratchRegister);
     return testObject(cond, ScratchRegister);
 }
 
 Assembler::Condition
@@ -2946,16 +2971,25 @@ MacroAssemblerARMCompat::testString(Cond
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     extractTag(src, ScratchRegister);
     ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_STRING));
     return cond;
 }
 
 Assembler::Condition
+MacroAssemblerARMCompat::testSymbol(Condition cond, const BaseIndex &src)
+{
+    JS_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(src, ScratchRegister);
+    ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_SYMBOL));
+    return cond;
+}
+
+Assembler::Condition
 MacroAssemblerARMCompat::testInt32(Condition cond, const BaseIndex &src)
 {
     JS_ASSERT(cond == Equal || cond == NotEqual);
     extractTag(src, ScratchRegister);
     ma_cmp(ScratchRegister, ImmTag(JSVAL_TAG_INT32));
     return cond;
 }
 
@@ -3040,35 +3074,24 @@ MacroAssemblerARMCompat::branchTestValue
         branchPtr(Equal, ScratchRegister, value.typeReg(), label);
 
         bind(&fallthrough);
     }
 }
 
 // unboxing code
 void
-MacroAssemblerARMCompat::unboxInt32(const ValueOperand &operand, Register dest)
-{
-    ma_mov(operand.payloadReg(), dest);
-}
-
-void
-MacroAssemblerARMCompat::unboxInt32(const Address &src, Register dest)
-{
-    ma_ldr(payloadOf(src), dest);
-}
-
-void
-MacroAssemblerARMCompat::unboxBoolean(const ValueOperand &operand, Register dest)
-{
-    ma_mov(operand.payloadReg(), dest);
-}
-
-void
-MacroAssemblerARMCompat::unboxBoolean(const Address &src, Register dest)
+MacroAssemblerARMCompat::unboxNonDouble(const ValueOperand &operand, Register dest)
+{
+    if (operand.payloadReg() != dest)
+        ma_mov(operand.payloadReg(), dest);
+}
+
+void
+MacroAssemblerARMCompat::unboxNonDouble(const Address &src, Register dest)
 {
     ma_ldr(payloadOf(src), dest);
 }
 
 void
 MacroAssemblerARMCompat::unboxDouble(const ValueOperand &operand, FloatRegister dest)
 {
     JS_ASSERT(dest != ScratchFloatReg);
@@ -3078,40 +3101,16 @@ MacroAssemblerARMCompat::unboxDouble(con
 
 void
 MacroAssemblerARMCompat::unboxDouble(const Address &src, FloatRegister dest)
 {
     ma_vldr(Operand(src), dest);
 }
 
 void
-MacroAssemblerARMCompat::unboxString(const ValueOperand &operand, Register dest)
-{
-    ma_mov(operand.payloadReg(), dest);
-}
-
-void
-MacroAssemblerARMCompat::unboxString(const Address &src, Register dest)
-{
-    ma_ldr(payloadOf(src), dest);
-}
-
-void
-MacroAssemblerARMCompat::unboxObject(const ValueOperand &src, Register dest)
-{
-    ma_mov(src.payloadReg(), dest);
-}
-
-void
-MacroAssemblerARMCompat::unboxObject(const Address &src, Register dest)
-{
-    ma_ldr(payloadOf(src), dest);
-}
-
-void
 MacroAssemblerARMCompat::unboxValue(const ValueOperand &src, AnyRegister dest)
 {
     if (dest.isFloat()) {
         Label notInt32, end;
         branchTestInt32(Assembler::NotEqual, src, &notInt32);
         convertInt32ToDouble(src.payloadReg(), dest.fpu());
         ma_b(&end);
         bind(&notInt32);
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -741,49 +741,53 @@ class MacroAssemblerARMCompat : public M
 
     // higher level tag testing code
     Condition testInt32(Condition cond, const ValueOperand &value);
     Condition testBoolean(Condition cond, const ValueOperand &value);
     Condition testDouble(Condition cond, const ValueOperand &value);
     Condition testNull(Condition cond, const ValueOperand &value);
     Condition testUndefined(Condition cond, const ValueOperand &value);
     Condition testString(Condition cond, const ValueOperand &value);
+    Condition testSymbol(Condition cond, const ValueOperand &value);
     Condition testObject(Condition cond, const ValueOperand &value);
     Condition testNumber(Condition cond, const ValueOperand &value);
     Condition testMagic(Condition cond, const ValueOperand &value);
 
     Condition testPrimitive(Condition cond, const ValueOperand &value);
 
     // register-based tests
     Condition testInt32(Condition cond, Register tag);
     Condition testBoolean(Condition cond, Register tag);
     Condition testNull(Condition cond, Register tag);
     Condition testUndefined(Condition cond, Register tag);
     Condition testString(Condition cond, Register tag);
+    Condition testSymbol(Condition cond, Register tag);
     Condition testObject(Condition cond, Register tag);
     Condition testDouble(Condition cond, Register tag);
     Condition testNumber(Condition cond, Register tag);
     Condition testMagic(Condition cond, Register tag);
     Condition testPrimitive(Condition cond, Register tag);
 
     Condition testGCThing(Condition cond, const Address &address);
     Condition testMagic(Condition cond, const Address &address);
     Condition testInt32(Condition cond, const Address &address);
     Condition testDouble(Condition cond, const Address &address);
     Condition testBoolean(Condition cond, const Address &address);
     Condition testNull(Condition cond, const Address &address);
     Condition testUndefined(Condition cond, const Address &address);
     Condition testString(Condition cond, const Address &address);
+    Condition testSymbol(Condition cond, const Address &address);
     Condition testObject(Condition cond, const Address &address);
     Condition testNumber(Condition cond, const Address &address);
 
     Condition testUndefined(Condition cond, const BaseIndex &src);
     Condition testNull(Condition cond, const BaseIndex &src);
     Condition testBoolean(Condition cond, const BaseIndex &src);
     Condition testString(Condition cond, const BaseIndex &src);
+    Condition testSymbol(Condition cond, const BaseIndex &src);
     Condition testInt32(Condition cond, const BaseIndex &src);
     Condition testObject(Condition cond, const BaseIndex &src);
     Condition testDouble(Condition cond, const BaseIndex &src);
     Condition testMagic(Condition cond, const BaseIndex &src);
     Condition testGCThing(Condition cond, const BaseIndex &src);
 
     template <typename T>
     void branchTestGCThing(Condition cond, const T &t, Label *label) {
@@ -796,26 +800,30 @@ class MacroAssemblerARMCompat : public M
         ma_b(label, c);
     }
 
     void branchTestValue(Condition cond, const ValueOperand &value, const Value &v, Label *label);
     void branchTestValue(Condition cond, const Address &valaddr, const ValueOperand &value,
                          Label *label);
 
     // unboxing code
-    void unboxInt32(const ValueOperand &operand, Register dest);
-    void unboxInt32(const Address &src, Register dest);
-    void unboxBoolean(const ValueOperand &operand, Register dest);
-    void unboxBoolean(const Address &src, Register dest);
-    void unboxDouble(const ValueOperand &operand, FloatRegister dest);
+    void unboxNonDouble(const ValueOperand &operand, Register dest);
+    void unboxNonDouble(const Address &src, Register dest);
+    void unboxInt32(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxInt32(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxBoolean(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxBoolean(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxString(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxString(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxSymbol(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxSymbol(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxObject(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxObject(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxDouble(const ValueOperand &src, FloatRegister dest);
     void unboxDouble(const Address &src, FloatRegister dest);
-    void unboxString(const ValueOperand &operand, Register dest);
-    void unboxString(const Address &src, Register dest);
-    void unboxObject(const ValueOperand &src, Register dest);
-    void unboxObject(const Address &src, Register dest);
     void unboxValue(const ValueOperand &src, AnyRegister dest);
     void unboxPrivate(const ValueOperand &src, Register dest);
 
     void notBoolean(const ValueOperand &val) {
         ma_eor(Imm32(1), val.payloadReg());
     }
 
     // boxing code
@@ -931,16 +939,21 @@ class MacroAssemblerARMCompat : public M
         ma_b(label, c);
     }
     template<typename T>
     void branchTestString(Condition cond, const T & t, Label *label) {
         Condition c = testString(cond, t);
         ma_b(label, c);
     }
     template<typename T>
+    void branchTestSymbol(Condition cond, const T & t, Label *label) {
+        Condition c = testSymbol(cond, t);
+        ma_b(label, c);
+    }
+    template<typename T>
     void branchTestUndefined(Condition cond, const T & t, Label *label) {
         Condition c = testUndefined(cond, t);
         ma_b(label, c);
     }
     template <typename T>
     void branchTestNumber(Condition cond, const T &t, Label *label) {
         cond = testNumber(cond, t);
         ma_b(label, cond);
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -2316,16 +2316,37 @@ void
 MacroAssemblerMIPSCompat::branchTestString(Condition cond, const BaseIndex &src, Label *label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     extractTag(src, SecondScratchReg);
     ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_STRING), label, cond);
 }
 
 void
+MacroAssemblerMIPSCompat::branchTestSymbol(Condition cond, const ValueOperand &value, Label *label)
+{
+    branchTestSymbol(cond, value.typeReg(), label);
+}
+
+void
+MacroAssemblerMIPSCompat::branchTestSymbol(Condition cond, const Register &tag, Label *label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    ma_b(tag, ImmTag(JSVAL_TAG_SYMBOL), label, cond);
+}
+
+void
+MacroAssemblerMIPSCompat::branchTestSymbol(Condition cond, const BaseIndex &src, Label *label)
+{
+    MOZ_ASSERT(cond == Equal || cond == NotEqual);
+    extractTag(src, SecondScratchReg);
+    ma_b(SecondScratchReg, ImmTag(JSVAL_TAG_SYMBOL), label, cond);
+}
+
+void
 MacroAssemblerMIPSCompat::branchTestUndefined(Condition cond, const ValueOperand &value,
                                               Label *label)
 {
     MOZ_ASSERT(cond == Equal || cond == NotEqual);
     ma_b(value.typeReg(), ImmType(JSVAL_TYPE_UNDEFINED), label, cond);
 }
 
 void
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -686,16 +686,20 @@ class MacroAssemblerMIPSCompat : public 
     void branchTestObject(Condition cond, const ValueOperand &value, Label *label);
     void branchTestObject(Condition cond, Register tag, Label *label);
     void branchTestObject(Condition cond, const BaseIndex &src, Label *label);
 
     void branchTestString(Condition cond, const ValueOperand &value, Label *label);
     void branchTestString(Condition cond, Register tag, Label *label);
     void branchTestString(Condition cond, const BaseIndex &src, Label *label);
 
+    void branchTestSymbol(Condition cond, const ValueOperand &value, Label *label);
+    void branchTestSymbol(Condition cond, const Register &tag, Label *label);
+    void branchTestSymbol(Condition cond, const BaseIndex &src, Label *label);
+
     void branchTestUndefined(Condition cond, const ValueOperand &value, Label *label);
     void branchTestUndefined(Condition cond, Register tag, Label *label);
     void branchTestUndefined(Condition cond, const BaseIndex &src, Label *label);
     void branchTestUndefined(Condition cond, const Address &address, Label *label);
     void testUndefinedSet(Condition cond, const ValueOperand &value, Register dest);
 
     void branchTestNumber(Condition cond, const ValueOperand &value, Label *label);
     void branchTestNumber(Condition cond, Register tag, Label *label);
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -172,16 +172,17 @@ CodeGeneratorShared::encodeAllocation(LS
       case MIRType_Undefined:
         alloc = RValueAllocation::Undefined();
         break;
       case MIRType_Null:
         alloc = RValueAllocation::Null();
         break;
       case MIRType_Int32:
       case MIRType_String:
+      case MIRType_Symbol:
       case MIRType_Object:
       case MIRType_Boolean:
       case MIRType_Double:
       case MIRType_Float32:
       {
         LAllocation *payload = snapshot->payloadOfSlot(*allocIndex);
         JSValueType valueType = ValueTypeFromMIRType(type);
         if (payload->isMemory()) {
--- a/js/src/jit/shared/Lowering-shared.cpp
+++ b/js/src/jit/shared/Lowering-shared.cpp
@@ -4,30 +4,34 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/shared/Lowering-shared-inl.h"
 
 #include "jit/LIR.h"
 #include "jit/MIR.h"
 
+#include "vm/Symbol.h"
+
 using namespace js;
 using namespace jit;
 
 bool
 LIRGeneratorShared::visitConstant(MConstant *ins)
 {
     const Value &v = ins->value();
     switch (ins->type()) {
       case MIRType_Boolean:
         return define(new(alloc()) LInteger(v.toBoolean()), ins);
       case MIRType_Int32:
         return define(new(alloc()) LInteger(v.toInt32()), ins);
       case MIRType_String:
         return define(new(alloc()) LPointer(v.toString()), ins);
+      case MIRType_Symbol:
+        return define(new(alloc()) LPointer(v.toSymbol()), ins);
       case MIRType_Object:
         return define(new(alloc()) LPointer(&v.toObject()), ins);
       default:
         // Constants of special types (undefined, null) should never flow into
         // here directly. Operations blindly consuming them require a Box.
         JS_ASSERT(!"unexpected constant type");
         return false;
     }
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -101,16 +101,19 @@ CodeGeneratorX64::visitUnbox(LUnbox *unb
             cond = masm.testBoolean(Assembler::NotEqual, value);
             break;
           case MIRType_Object:
             cond = masm.testObject(Assembler::NotEqual, value);
             break;
           case MIRType_String:
             cond = masm.testString(Assembler::NotEqual, value);
             break;
+          case MIRType_Symbol:
+            cond = masm.testSymbol(Assembler::NotEqual, value);
+            break;
           default:
             MOZ_ASSUME_UNREACHABLE("Given MIRType cannot be unboxed.");
         }
         if (!bailoutIf(cond, unbox->snapshot()))
             return false;
     }
 
     switch (mir->type()) {
@@ -121,16 +124,19 @@ CodeGeneratorX64::visitUnbox(LUnbox *unb
         masm.unboxBoolean(value, ToRegister(result));
         break;
       case MIRType_Object:
         masm.unboxObject(value, ToRegister(result));
         break;
       case MIRType_String:
         masm.unboxString(value, ToRegister(result));
         break;
+      case MIRType_Symbol:
+        masm.unboxSymbol(value, ToRegister(result));
+        break;
       default:
         MOZ_ASSUME_UNREACHABLE("Given MIRType cannot be unboxed.");
     }
 
     return true;
 }
 
 bool
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -276,16 +276,21 @@ class MacroAssemblerX64 : public MacroAs
         cmpl(tag, ImmTag(JSVAL_TAG_NULL));
         return cond;
     }
     Condition testString(Condition cond, Register tag) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tag, ImmTag(JSVAL_TAG_STRING));
         return cond;
     }
+    Condition testSymbol(Condition cond, Register tag) {
+        JS_ASSERT(cond == Equal || cond == NotEqual);
+        cmpl(tag, ImmTag(JSVAL_TAG_SYMBOL));
+        return cond;
+    }
     Condition testObject(Condition cond, Register tag) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tag, ImmTag(JSVAL_TAG_OBJECT));
         return cond;
     }
     Condition testDouble(Condition cond, Register tag) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tag, Imm32(JSVAL_TAG_MAX_DOUBLE));
@@ -339,16 +344,20 @@ class MacroAssemblerX64 : public MacroAs
     Condition testNull(Condition cond, const ValueOperand &src) {
         splitTag(src, ScratchReg);
         return testNull(cond, ScratchReg);
     }
     Condition testString(Condition cond, const ValueOperand &src) {
         splitTag(src, ScratchReg);
         return testString(cond, ScratchReg);
     }
+    Condition testSymbol(Condition cond, const ValueOperand &src) {
+        splitTag(src, ScratchReg);
+        return testSymbol(cond, ScratchReg);
+    }
     Condition testObject(Condition cond, const ValueOperand &src) {
         splitTag(src, ScratchReg);
         return testObject(cond, ScratchReg);
     }
     Condition testGCThing(Condition cond, const ValueOperand &src) {
         splitTag(src, ScratchReg);
         return testGCThing(cond, ScratchReg);
     }
@@ -381,16 +390,20 @@ class MacroAssemblerX64 : public MacroAs
     Condition testNull(Condition cond, const Address &src) {
         splitTag(src, ScratchReg);
         return testNull(cond, ScratchReg);
     }
     Condition testString(Condition cond, const Address &src) {
         splitTag(src, ScratchReg);
         return testString(cond, ScratchReg);
     }
+    Condition testSymbol(Condition cond, const Address &src) {
+        splitTag(src, ScratchReg);
+        return testSymbol(cond, ScratchReg);
+    }
     Condition testObject(Condition cond, const Address &src) {
         splitTag(src, ScratchReg);
         return testObject(cond, ScratchReg);
     }
     Condition testPrimitive(Condition cond, const Address &src) {
         splitTag(src, ScratchReg);
         return testPrimitive(cond, ScratchReg);
     }
@@ -415,16 +428,20 @@ class MacroAssemblerX64 : public MacroAs
     Condition testBoolean(Condition cond, const BaseIndex &src) {
         splitTag(src, ScratchReg);
         return testBoolean(cond, ScratchReg);
     }
     Condition testString(Condition cond, const BaseIndex &src) {
         splitTag(src, ScratchReg);
         return testString(cond, ScratchReg);
     }
+    Condition testSymbol(Condition cond, const BaseIndex &src) {
+        splitTag(src, ScratchReg);
+        return testSymbol(cond, ScratchReg);
+    }
     Condition testInt32(Condition cond, const BaseIndex &src) {
         splitTag(src, ScratchReg);
         return testInt32(cond, ScratchReg);
     }
     Condition testObject(Condition cond, const BaseIndex &src) {
         splitTag(src, ScratchReg);
         return testObject(cond, ScratchReg);
     }
@@ -834,16 +851,20 @@ class MacroAssemblerX64 : public MacroAs
     void branchTestNull(Condition cond, Register tag, Label *label) {
         cond = testNull(cond, tag);
         j(cond, label);
     }
     void branchTestString(Condition cond, Register tag, Label *label) {
         cond = testString(cond, tag);
         j(cond, label);
     }
+    void branchTestSymbol(Condition cond, Register tag, Label *label) {
+        cond = testSymbol(cond, tag);
+        j(cond, label);
+    }
     void branchTestObject(Condition cond, Register tag, Label *label) {
         cond = testObject(cond, tag);
         j(cond, label);
     }
     void branchTestNumber(Condition cond, Register tag, Label *label) {
         cond = testNumber(cond, tag);
         j(cond, label);
     }
@@ -910,16 +931,20 @@ class MacroAssemblerX64 : public MacroAs
     void branchTestNull(Condition cond, const ValueOperand &src, Label *label) {
         cond = testNull(cond, src);
         j(cond, label);
     }
     void branchTestString(Condition cond, const ValueOperand &src, Label *label) {
         cond = testString(cond, src);
         j(cond, label);
     }
+    void branchTestSymbol(Condition cond, const ValueOperand &src, Label *label) {
+        cond = testSymbol(cond, src);
+        j(cond, label);
+    }
     void branchTestObject(Condition cond, const ValueOperand &src, Label *label) {
         cond = testObject(cond, src);
         j(cond, label);
     }
     void branchTestNumber(Condition cond, const ValueOperand &src, Label *label) {
         cond = testNumber(cond, src);
         j(cond, label);
     }
@@ -945,16 +970,20 @@ class MacroAssemblerX64 : public MacroAs
     void branchTestNull(Condition cond, const BaseIndex &address, Label *label) {
         cond = testNull(cond, address);
         j(cond, label);
     }
     void branchTestString(Condition cond, const BaseIndex &address, Label *label) {
         cond = testString(cond, address);
         j(cond, label);
     }
+    void branchTestSymbol(Condition cond, const BaseIndex &address, Label *label) {
+        cond = testSymbol(cond, address);
+        j(cond, label);
+    }
     void branchTestObject(Condition cond, const BaseIndex &address, Label *label) {
         cond = testObject(cond, address);
         j(cond, label);
     }
 
     template <typename T>
     void branchTestGCThing(Condition cond, const T &src, Label *label) {
         cond = testGCThing(cond, src);
@@ -1089,16 +1118,19 @@ class MacroAssemblerX64 : public MacroAs
             mov(ImmWord(JSVAL_PAYLOAD_MASK), dest);
             andq(src, dest);
         }
     }
 
     void unboxString(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
     void unboxString(const Operand &src, Register dest) { unboxNonDouble(src, dest); }
 
+    void unboxSymbol(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxSymbol(const Operand &src, Register dest) { unboxNonDouble(src, dest); }
+
     void unboxObject(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
     void unboxObject(const Operand &src, Register dest) { unboxNonDouble(src, dest); }
     void unboxObject(const Address &src, Register dest) { unboxNonDouble(Operand(src), dest); }
 
     // Extended unboxing API. If the payload is already in a register, returns
     // that register. Otherwise, provides a move to the given scratch register,
     // and returns that.
     Register extractObject(const Address &address, Register scratch) {
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -285,16 +285,21 @@ class MacroAssemblerX86 : public MacroAs
         cmpl(tag, ImmTag(JSVAL_TAG_NULL));
         return cond;
     }
     Condition testString(Condition cond, Register tag) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tag, ImmTag(JSVAL_TAG_STRING));
         return cond;
     }
+    Condition testSymbol(Condition cond, Register tag) {
+        JS_ASSERT(cond == Equal || cond == NotEqual);
+        cmpl(tag, ImmTag(JSVAL_TAG_SYMBOL));
+        return cond;
+    }
     Condition testObject(Condition cond, Register tag) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tag, ImmTag(JSVAL_TAG_OBJECT));
         return cond;
     }
     Condition testNumber(Condition cond, Register tag) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tag, ImmTag(JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET));
@@ -377,16 +382,19 @@ class MacroAssemblerX86 : public MacroAs
         return testDouble(cond, value.typeReg());
     }
     Condition testNull(Condition cond, const ValueOperand &value) {
         return testNull(cond, value.typeReg());
     }
     Condition testString(Condition cond, const ValueOperand &value) {
         return testString(cond, value.typeReg());
     }
+    Condition testSymbol(Condition cond, const ValueOperand &value) {
+        return testSymbol(cond, value.typeReg());
+    }
     Condition testObject(Condition cond, const ValueOperand &value) {
         return testObject(cond, value.typeReg());
     }
     Condition testMagic(Condition cond, const ValueOperand &value) {
         return testMagic(cond, value.typeReg());
     }
     Condition testError(Condition cond, const ValueOperand &value) {
         return testMagic(cond, value);
@@ -417,16 +425,21 @@ class MacroAssemblerX86 : public MacroAs
         cmpl(tagOf(address), ImmTag(JSVAL_TAG_BOOLEAN));
         return cond;
     }
     Condition testString(Condition cond, const BaseIndex &address) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tagOf(address), ImmTag(JSVAL_TAG_STRING));
         return cond;
     }
+    Condition testSymbol(Condition cond, const BaseIndex &address) {
+        JS_ASSERT(cond == Equal || cond == NotEqual);
+        cmpl(tagOf(address), ImmTag(JSVAL_TAG_SYMBOL));
+        return cond;
+    }
     Condition testInt32(Condition cond, const BaseIndex &address) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tagOf(address), ImmTag(JSVAL_TAG_INT32));
         return cond;
     }
     Condition testObject(Condition cond, const BaseIndex &address) {
         JS_ASSERT(cond == Equal || cond == NotEqual);
         cmpl(tagOf(address), ImmTag(JSVAL_TAG_OBJECT));
@@ -746,16 +759,21 @@ class MacroAssemblerX86 : public MacroAs
         j(cond, label);
     }
     template <typename T>
     void branchTestString(Condition cond, const T &t, Label *label) {
         cond = testString(cond, t);
         j(cond, label);
     }
     template <typename T>
+    void branchTestSymbol(Condition cond, const T &t, Label *label) {
+        cond = testSymbol(cond, t);
+        j(cond, label);
+    }
+    template <typename T>
     void branchTestObject(Condition cond, const T &t, Label *label) {
         cond = testObject(cond, t);
         j(cond, label);
     }
     template <typename T>
     void branchTestNumber(Condition cond, const T &t, Label *label) {
         cond = testNumber(cond, t);
         j(cond, label);
@@ -788,38 +806,37 @@ class MacroAssemblerX86 : public MacroAs
         psrldq(Imm32(4), src);
         movd(src, dest.typeReg());
     }
     void boxNonDouble(JSValueType type, Register src, const ValueOperand &dest) {
         if (src != dest.payloadReg())
             movl(src, dest.payloadReg());
         movl(ImmType(type), dest.typeReg());
     }
-    void unboxInt32(const ValueOperand &src, Register dest) {
-        movl(src.payloadReg(), dest);
-    }
-    void unboxInt32(const Address &src, Register dest) {
-        movl(payloadOf(src), dest);
-    }
-    void unboxDouble(const Address &src, FloatRegister dest) {
-        loadDouble(Operand(src), dest);
-    }
-    void unboxBoolean(const ValueOperand &src, Register dest) {
-        movl(src.payloadReg(), dest);
-    }
-    void unboxBoolean(const Address &src, Register dest) {
-        movl(payloadOf(src), dest);
-    }
-    void unboxObject(const ValueOperand &src, Register dest) {
+
+    void unboxNonDouble(const ValueOperand &src, Register dest) {
         if (src.payloadReg() != dest)
             movl(src.payloadReg(), dest);
     }
-    void unboxObject(const Address &src, Register dest) {
+    void unboxNonDouble(const Address &src, Register dest) {
         movl(payloadOf(src), dest);
     }
+    void unboxInt32(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxInt32(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxBoolean(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxBoolean(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxString(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxString(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxSymbol(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxSymbol(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxObject(const ValueOperand &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxObject(const Address &src, Register dest) { unboxNonDouble(src, dest); }
+    void unboxDouble(const Address &src, FloatRegister dest) {
+        loadDouble(Operand(src), dest);
+    }
     void unboxDouble(const ValueOperand &src, FloatRegister dest) {
         JS_ASSERT(dest != ScratchFloatReg);
         if (Assembler::HasSSE41()) {
             movd(src.payloadReg(), dest);
             pinsrd(src.typeReg(), dest);
         } else {
             movd(src.payloadReg(), dest);
             movd(src.typeReg(), ScratchFloatReg);
@@ -837,22 +854,16 @@ class MacroAssemblerX86 : public MacroAs
         } else {
             movl(payload, scratch);
             movd(scratch, dest);
             movl(type, scratch);
             movd(scratch, ScratchFloatReg);
             unpcklps(ScratchFloatReg, dest);
         }
     }
-    void unboxString(const ValueOperand &src, Register dest) {
-        movl(src.payloadReg(), dest);
-    }
-    void unboxString(const Address &src, Register dest) {
-        movl(payloadOf(src), dest);
-    }
     void unboxValue(const ValueOperand &src, AnyRegister dest) {
         if (dest.isFloat()) {
             Label notInt32, end;
             branchTestInt32(Assembler::NotEqual, src, &notInt32);
             convertInt32ToDouble(src.payloadReg(), dest.fpu());
             jump(&end);
             bind(&notInt32);
             unboxDouble(src, dest.fpu());
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -292,16 +292,17 @@ class Type
     bool operator != (Type o) const { return data != o.data; }
 
     static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
     static inline Type NullType()      { return Type(JSVAL_TYPE_NULL); }
     static inline Type BooleanType()   { return Type(JSVAL_TYPE_BOOLEAN); }
     static inline Type Int32Type()     { return Type(JSVAL_TYPE_INT32); }
     static inline Type DoubleType()    { return Type(JSVAL_TYPE_DOUBLE); }
     static inline Type StringType()    { return Type(JSVAL_TYPE_STRING); }
+    static inline Type SymbolType()    { return Type(JSVAL_TYPE_SYMBOL); }
     static inline Type MagicArgType()  { return Type(JSVAL_TYPE_MAGIC); }
     static inline Type AnyObjectType() { return Type(JSVAL_TYPE_OBJECT); }
     static inline Type UnknownType()   { return Type(JSVAL_TYPE_UNKNOWN); }
 
     static inline Type PrimitiveType(JSValueType type) {
         JS_ASSERT(type < JSVAL_TYPE_UNKNOWN);
         return Type(type);
     }
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -334,16 +334,22 @@ class GlobalObject : public JSObject
     }
 
     static JSObject *getOrCreateStringPrototype(JSContext *cx, Handle<GlobalObject*> global) {
         if (!ensureConstructor(cx, global, JSProto_String))
             return nullptr;
         return &global->getPrototype(JSProto_String).toObject();
     }
 
+    static JSObject *getOrCreateSymbolPrototype(JSContext *cx, Handle<GlobalObject*> global) {
+        if (!ensureConstructor(cx, global, JSProto_Symbol))
+            return nullptr;
+        return &global->getPrototype(JSProto_Symbol).toObject();
+    }
+
     static JSObject *getOrCreateRegExpPrototype(JSContext *cx, Handle<GlobalObject*> global) {
         if (!ensureConstructor(cx, global, JSProto_RegExp))
             return nullptr;
         return &global->getPrototype(JSProto_RegExp).toObject();
     }
 
     JSObject *maybeGetRegExpPrototype() {
         if (regexpClassInitialized())