Bug 903519 - Force non-atom strings to have their low flag bit set in order to distinguish them from JSObjects in the nursery, r=jandem

Atoms will not be nursery-allocated, so if a cell is in the nursery and its low bit is set, we can be sure it's a string.

--HG--
extra : rebase_source : 22a9b99c4053532dbe33597ef33335574ef4c98b
This commit is contained in:
Steve Fink 2018-02-06 07:40:54 -08:00
parent 055f32d6b8
commit a5d00c04c8
10 changed files with 85 additions and 53 deletions

View File

@ -16,7 +16,7 @@ mozilla.prettyprinters.clear_module_printers(__name__)
class JSStringTypeCache(object):
def __init__(self, cache):
dummy = gdb.Value(0).cast(cache.JSString_ptr_t)
self.ATOM_BIT = dummy['ATOM_BIT']
self.NON_ATOM_BIT = dummy['NON_ATOM_BIT']
self.LINEAR_BIT = dummy['LINEAR_BIT']
self.INLINE_CHARS_BIT = dummy['INLINE_CHARS_BIT']
self.TYPE_FLAGS_MASK = dummy['TYPE_FLAGS_MASK']

View File

@ -356,8 +356,8 @@ BaselineCacheIRCompiler::emitGuardSpecificAtom()
// The pointers are not equal, so if the input string is also an atom it
// must be a different string.
masm.branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
Imm32(JSString::ATOM_BIT), failure->label());
masm.branchTest32(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
Imm32(JSString::NON_ATOM_BIT), failure->label());
// Check the length.
masm.loadPtr(atomAddr, scratch);

View File

@ -8049,10 +8049,13 @@ JitCompartment::generateStringConcatStub(JSContext* cx)
masm.newGCString(output, temp3, &failure);
// Store rope length and flags. temp1 still holds the result of AND'ing the
// lhs and rhs flags, so we just have to clear the other flags to get our
// rope flags (Latin1 if both lhs and rhs are Latin1).
static_assert(JSString::INIT_ROPE_FLAGS == 0, "Rope type flags must be 0");
// lhs and rhs flags, so we just have to clear the other flags and set
// NON_ATOM_BIT to get our rope flags (Latin1 if both lhs and rhs are
// Latin1).
static_assert(JSString::INIT_ROPE_FLAGS == JSString::NON_ATOM_BIT,
"Rope type flags must be NON_ATOM_BIT only");
masm.and32(Imm32(JSString::LATIN1_CHARS_BIT), temp1);
masm.or32(Imm32(JSString::NON_ATOM_BIT), temp1);
masm.store32(temp1, Address(output, JSString::offsetOfFlags()));
masm.store32(temp2, Address(output, JSString::offsetOfLength()));

View File

@ -767,8 +767,8 @@ IonCacheIRCompiler::emitGuardSpecificAtom()
// The pointers are not equal, so if the input string is also an atom it
// must be a different string.
masm.branchTest32(Assembler::NonZero, Address(str, JSString::offsetOfFlags()),
Imm32(JSString::ATOM_BIT), failure->label());
masm.branchTest32(Assembler::Zero, Address(str, JSString::offsetOfFlags()),
Imm32(JSString::NON_ATOM_BIT), failure->label());
// Check the length.
masm.branch32(Assembler::NotEqual, Address(str, JSString::offsetOfLength()),

View File

@ -408,7 +408,6 @@ MacroAssembler::branchIfRopeOrExternal(Register str, Register temp, Label* label
and32(flags, temp);
branchTest32(Assembler::Zero, temp, Imm32(JSString::LINEAR_BIT), label);
branch32(Assembler::Equal, temp, Imm32(JSString::EXTERNAL_FLAGS), label);
}

View File

@ -1349,9 +1349,9 @@ MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register
Label notAtom;
// Optimize the equality operation to a pointer compare for two atoms.
Imm32 atomBit(JSString::ATOM_BIT);
branchTest32(Assembler::Zero, Address(left, JSString::offsetOfFlags()), atomBit, &notAtom);
branchTest32(Assembler::Zero, Address(right, JSString::offsetOfFlags()), atomBit, &notAtom);
Imm32 nonAtomBit(JSString::NON_ATOM_BIT);
branchTest32(Assembler::NonZero, Address(left, JSString::offsetOfFlags()), nonAtomBit, &notAtom);
branchTest32(Assembler::NonZero, Address(right, JSString::offsetOfFlags()), nonAtomBit, &notAtom);
cmpPtrSet(JSOpToCondition(MCompare::Compare_String, op), left, right, result);
jump(&done);

View File

@ -616,10 +616,11 @@ struct Function {
struct String
{
static const uint32_t LINEAR_BIT = JS_BIT(0);
static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
static const uint32_t NON_ATOM_BIT = JS_BIT(0);
static const uint32_t LINEAR_BIT = JS_BIT(1);
static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
static const uint32_t LATIN1_CHARS_BIT = JS_BIT(6);
static const uint32_t EXTERNAL_FLAGS = JS_BIT(0) | JS_BIT(5);
static const uint32_t EXTERNAL_FLAGS = LINEAR_BIT | NON_ATOM_BIT | JS_BIT(5);
static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
uint32_t flags;
uint32_t length;
@ -630,6 +631,11 @@ struct String
char16_t inlineStorageTwoByte[1];
};
const JSStringFinalizer* externalFinalizer;
static bool nurseryCellIsString(const js::gc::Cell* cell) {
MOZ_ASSERT(IsInsideNursery(cell));
return reinterpret_cast<const String*>(cell)->flags & NON_ATOM_BIT;
}
};
} /* namespace shadow */

View File

@ -205,7 +205,7 @@ MOZ_ALWAYS_INLINE void
JSFlatString::init(const char16_t* chars, size_t length)
{
d.u1.length = length;
d.u1.flags = LINEAR_BIT;
d.u1.flags = INIT_FLAT_FLAGS;
d.s.u2.nonInlineCharsTwoByte = chars;
}
@ -213,7 +213,7 @@ MOZ_ALWAYS_INLINE void
JSFlatString::init(const JS::Latin1Char* chars, size_t length)
{
d.u1.length = length;
d.u1.flags = LINEAR_BIT | LATIN1_CHARS_BIT;
d.u1.flags = INIT_FLAT_FLAGS | LATIN1_CHARS_BIT;
d.s.u2.nonInlineCharsLatin1 = chars;
}

View File

@ -207,7 +207,7 @@ JSString::dumpRepresentationHeader(js::GenericPrinter& out, int indent, const ch
if (flags & LINEAR_BIT) out.put(" LINEAR");
if (flags & HAS_BASE_BIT) out.put(" HAS_BASE");
if (flags & INLINE_CHARS_BIT) out.put(" INLINE_CHARS");
if (flags & ATOM_BIT) out.put(" ATOM");
if (flags & NON_ATOM_BIT) out.put(" NON_ATOM");
if (isPermanentAtom()) out.put(" PERMANENT");
if (flags & LATIN1_CHARS_BIT) out.put(" LATIN1");
if (flags & INDEX_VALUE_BIT) out.printf(" INDEX_VALUE(%u)", getIndexValue());
@ -1103,7 +1103,7 @@ JSExternalString::ensureFlat(JSContext* cx)
// Transform the string into a non-external, flat string.
setNonInlineChars<char16_t>(s);
d.u1.flags = LINEAR_BIT;
d.u1.flags = INIT_FLAT_FLAGS;
return &this->asFlat();
}

View File

@ -221,28 +221,28 @@ class JSString : public js::gc::Cell
* String Instance Subtype
* type encoding predicate
* ------------------------------------
* Rope 000000 xxxxx0
* Linear - xxxxx1
* HasBase - xxxx1x
* Dependent 000011 000011
* External 100001 100001
* Rope 000001 xxxx0x
* Linear - xxxx1x
* HasBase - xxx1xx
* Dependent 000111 000111
* External 100011 100011
* Flat - Linear && !Dependent && !External
* Undepended 010011 010011
* Extensible 010001 010001
* Inline 000101 xxx1xx
* FatInline 010101 x1x1xx
* Atom 001001 xx1xxx
* PermanentAtom 101001 1x1xxx
* InlineAtom - xx11xx
* FatInlineAtom - x111xx
* Undepended 010111 010111
* Extensible 010011 010011
* Inline 001011 xx1xxx
* FatInline 011011 x11xxx
* NormalAtom 000010 xxxxx0
* PermanentAtom 100010 1xxxx0
* InlineAtom - xx1xx0
* FatInlineAtom - x11xx0
*
* Note that the first 4 flag bits (from right to left in the previous table)
* have the following meaning and can be used for some hot queries:
*
* Bit 0: IsLinear
* Bit 1: HasBase (Dependent, Undepended)
* Bit 2: IsInline (Inline, FatInline)
* Bit 3: IsAtom (Atom, PermanentAtom)
* Bit 0: !IsAtom (Atom, PermanentAtom)
* Bit 1: IsLinear
* Bit 2: HasBase (Dependent, Undepended)
* Bit 3: IsInline (Inline, FatInline)
*
* "HasBase" here refers to the two string types that have a 'base' field:
* JSDependentString and JSUndependedString.
@ -250,27 +250,37 @@ class JSString : public js::gc::Cell
* to be null-terminated. In such cases, the string must keep marking its base since
* there may be any number of *other* JSDependentStrings transitively depending on it.
*
* The atom bit (NON_ATOM_BIT) is inverted so that objects and strings can
* be differentiated in the nursery: atoms are never in the nursery, so
* this bit is always 1 for a nursery string. For an object on a
* little-endian architecture, this is the low-order bit of the ObjectGroup
* pointer in a JSObject, which will always be zero. A 64-bit big-endian
* architecture will need to do something else (the ObjectGroup* is in the
* same place as a string's struct { uint32_t flags; uint32_t length; }).
*
* If the INDEX_VALUE_BIT is set the upper 16 bits of the flag word hold the integer
* index.
*/
static const uint32_t LINEAR_BIT = JS_BIT(0);
static const uint32_t HAS_BASE_BIT = JS_BIT(1);
static const uint32_t INLINE_CHARS_BIT = JS_BIT(2);
static const uint32_t ATOM_BIT = JS_BIT(3);
static const uint32_t NON_ATOM_BIT = JS_BIT(0);
static const uint32_t LINEAR_BIT = JS_BIT(1);
static const uint32_t HAS_BASE_BIT = JS_BIT(2);
static const uint32_t INLINE_CHARS_BIT = JS_BIT(3);
static const uint32_t DEPENDENT_FLAGS = LINEAR_BIT | HAS_BASE_BIT;
static const uint32_t UNDEPENDED_FLAGS = LINEAR_BIT | HAS_BASE_BIT | JS_BIT(4);
static const uint32_t EXTENSIBLE_FLAGS = LINEAR_BIT | JS_BIT(4);
static const uint32_t EXTERNAL_FLAGS = LINEAR_BIT | JS_BIT(5);
static const uint32_t DEPENDENT_FLAGS = NON_ATOM_BIT | LINEAR_BIT | HAS_BASE_BIT;
static const uint32_t UNDEPENDED_FLAGS = NON_ATOM_BIT | LINEAR_BIT | HAS_BASE_BIT | JS_BIT(4);
static const uint32_t EXTENSIBLE_FLAGS = NON_ATOM_BIT | LINEAR_BIT | JS_BIT(4);
static const uint32_t EXTERNAL_FLAGS = NON_ATOM_BIT | LINEAR_BIT | JS_BIT(5);
static const uint32_t FAT_INLINE_MASK = INLINE_CHARS_BIT | JS_BIT(4);
static const uint32_t PERMANENT_ATOM_MASK = ATOM_BIT | JS_BIT(5);
static const uint32_t PERMANENT_ATOM_MASK = NON_ATOM_BIT | JS_BIT(5);
static const uint32_t PERMANENT_ATOM_FLAGS = JS_BIT(5);
/* Initial flags for thin inline and fat inline strings. */
static const uint32_t INIT_THIN_INLINE_FLAGS = LINEAR_BIT | INLINE_CHARS_BIT;
static const uint32_t INIT_FAT_INLINE_FLAGS = LINEAR_BIT | FAT_INLINE_MASK;
static const uint32_t INIT_ROPE_FLAGS = 0;
static const uint32_t INIT_THIN_INLINE_FLAGS = NON_ATOM_BIT | LINEAR_BIT | INLINE_CHARS_BIT;
static const uint32_t INIT_FAT_INLINE_FLAGS = NON_ATOM_BIT | LINEAR_BIT | FAT_INLINE_MASK;
static const uint32_t INIT_ROPE_FLAGS = NON_ATOM_BIT;
static const uint32_t INIT_FLAT_FLAGS = NON_ATOM_BIT | LINEAR_BIT;
static const uint32_t TYPE_FLAGS_MASK = JS_BIT(6) - 1;
@ -317,6 +327,8 @@ class JSString : public js::gc::Cell
"shadow::String inlineStorage offset must match JSString");
static_assert(offsetof(JSString, d.inlineStorageTwoByte) == offsetof(String, inlineStorageTwoByte),
"shadow::String inlineStorage offset must match JSString");
static_assert(NON_ATOM_BIT == String::NON_ATOM_BIT,
"shadow::String::NON_ATOM_BIT must match JSString::NON_ATOM_BIT");
static_assert(LINEAR_BIT == String::LINEAR_BIT,
"shadow::String::LINEAR_BIT must match JSString::LINEAR_BIT");
static_assert(INLINE_CHARS_BIT == String::INLINE_CHARS_BIT,
@ -472,12 +484,12 @@ class JSString : public js::gc::Cell
MOZ_ALWAYS_INLINE
bool isAtom() const {
return d.u1.flags & ATOM_BIT;
return !(d.u1.flags & NON_ATOM_BIT);
}
MOZ_ALWAYS_INLINE
bool isPermanentAtom() const {
return (d.u1.flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_MASK;
return (d.u1.flags & PERMANENT_ATOM_MASK) == PERMANENT_ATOM_FLAGS;
}
MOZ_ALWAYS_INLINE
@ -486,6 +498,14 @@ class JSString : public js::gc::Cell
return *(JSAtom*)this;
}
// Used for distinguishing strings from objects in the nursery. 'cell' must
// be in the nursery.
MOZ_ALWAYS_INLINE
static bool nurseryCellIsString(js::gc::Cell* cell) {
MOZ_ASSERT(!cell->isTenured());
return !static_cast<JSString*>(cell)->isAtom();
}
// Fills |array| with various strings that represent the different string
// kinds and character encodings.
static bool fillWithRepresentatives(JSContext* cx, js::HandleArrayObject array);
@ -1091,7 +1111,8 @@ class JSAtom : public JSFlatString
// Transform this atom into a permanent atom. This is only done during
// initialization of the runtime.
MOZ_ALWAYS_INLINE void morphIntoPermanentAtom() {
d.u1.flags |= PERMANENT_ATOM_MASK;
MOZ_ASSERT(static_cast<JSString*>(this)->isAtom());
d.u1.flags |= PERMANENT_ATOM_FLAGS;
}
inline js::HashNumber hash() const;
@ -1170,7 +1191,8 @@ JSAtom::initHash(js::HashNumber hash)
MOZ_ALWAYS_INLINE JSAtom*
JSFlatString::morphAtomizedStringIntoAtom(js::HashNumber hash)
{
d.u1.flags |= ATOM_BIT;
MOZ_ASSERT(!isAtom());
d.u1.flags &= ~NON_ATOM_BIT;
JSAtom* atom = &asAtom();
atom->initHash(hash);
return atom;
@ -1179,7 +1201,9 @@ JSFlatString::morphAtomizedStringIntoAtom(js::HashNumber hash)
MOZ_ALWAYS_INLINE JSAtom*
JSFlatString::morphAtomizedStringIntoPermanentAtom(js::HashNumber hash)
{
d.u1.flags |= PERMANENT_ATOM_MASK;
MOZ_ASSERT(!isAtom());
d.u1.flags |= PERMANENT_ATOM_FLAGS;
d.u1.flags &= ~NON_ATOM_BIT;
JSAtom* atom = &asAtom();
atom->initHash(hash);
return atom;