Bug 1033442 - Remove non-pod calloc from MallocProvider and AllocPolicy; r=jonco
authorTerrence Cole <terrence@mozilla.com>
Tue, 05 Aug 2014 14:06:35 -0700
changeset 198950 a8138c05044d8ab72aaf7361e6c2514017db29fa
parent 198949 ce8b92c9ad6389fb55e108ca992e0bdb312619d6
child 198951 f2091b49fee734c3a83e992894c4e37920d89744
push id47537
push usertcole@mozilla.com
push dateMon, 11 Aug 2014 23:40:35 +0000
treeherdermozilla-inbound@a8138c05044d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1033442
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1033442 - Remove non-pod calloc from MallocProvider and AllocPolicy; r=jonco
js/public/HashTable.h
js/src/ds/LifoAlloc.h
js/src/jit/IonAllocPolicy.h
js/src/jit/RematerializedFrame.cpp
js/src/jsalloc.h
js/src/jscntxt.h
js/src/jsinfer.cpp
js/src/jsiter.cpp
js/src/jsscript.cpp
js/src/vm/MallocProvider.h
js/src/vm/Runtime.h
js/src/vm/Shape.cpp
js/src/vm/Shape.h
layout/style/nsNthIndexCache.h
memory/replace/dmd/DMD.cpp
mfbt/AllocPolicy.h
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -1075,17 +1075,17 @@ class HashTable : private AllocPolicy
     }
 
     static Entry *createTable(AllocPolicy &alloc, uint32_t capacity)
     {
         static_assert(sFreeKey == 0,
                       "newly-calloc'd tables have to be considered empty");
         static_assert(sMaxCapacity <= SIZE_MAX / sizeof(Entry),
                       "would overflow allocating max number of entries");
-        return static_cast<Entry*>(alloc.calloc_(capacity * sizeof(Entry)));
+        return alloc.template pod_calloc<Entry>(capacity);
     }
 
     static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity)
     {
         for (Entry *e = oldTable, *end = e + capacity; e < end; ++e)
             e->destroyIfLive();
         alloc.free_(oldTable);
     }
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -514,21 +514,24 @@ class LifoAllocPolicy
 
   public:
     MOZ_IMPLICIT LifoAllocPolicy(LifoAlloc &alloc)
       : alloc_(alloc)
     {}
     void *malloc_(size_t bytes) {
         return fb == Fallible ? alloc_.alloc(bytes) : alloc_.allocInfallible(bytes);
     }
-    void *calloc_(size_t bytes) {
-        void *p = malloc_(bytes);
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+        if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+            return nullptr;
+        T *p = (T *)malloc_(numElems * sizeof(T));
         if (fb == Fallible && !p)
             return nullptr;
-        memset(p, 0, bytes);
+        memset(p, 0, numElems * sizeof(T));
         return p;
     }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) {
         void *n = malloc_(bytes);
         if (fb == Fallible && !n)
             return nullptr;
         memcpy(n, p, Min(oldBytes, bytes));
         return n;
--- a/js/src/jit/IonAllocPolicy.h
+++ b/js/src/jit/IonAllocPolicy.h
@@ -98,20 +98,23 @@ class IonAllocPolicy
 
   public:
     MOZ_IMPLICIT IonAllocPolicy(TempAllocator &alloc)
       : alloc_(alloc)
     {}
     void *malloc_(size_t bytes) {
         return alloc_.allocate(bytes);
     }
-    void *calloc_(size_t bytes) {
-        void *p = alloc_.allocate(bytes);
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+        if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value)
+            return nullptr;
+        T *p = (T *)alloc_.allocate(numElems * sizeof(T));
         if (p)
-            memset(p, 0, bytes);
+            memset(p, 0, numElems * sizeof(T));
         return p;
     }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) {
         void *n = malloc_(bytes);
         if (!n)
             return n;
         memcpy(n, p, Min(oldBytes, bytes));
         return n;
@@ -127,22 +130,16 @@ class IonAllocPolicy
 class OldIonAllocPolicy
 {
   public:
     OldIonAllocPolicy()
     {}
     void *malloc_(size_t bytes) {
         return GetIonContext()->temp->allocate(bytes);
     }
-    void *calloc_(size_t bytes) {
-        void *p = GetIonContext()->temp->allocate(bytes);
-        if (p)
-            memset(p, 0, bytes);
-        return p;
-    }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) {
         void *n = malloc_(bytes);
         if (!n)
             return n;
         memcpy(n, p, Min(oldBytes, bytes));
         return n;
     }
     void free_(void *p) {
--- a/js/src/jit/RematerializedFrame.cpp
+++ b/js/src/jit/RematerializedFrame.cpp
@@ -47,17 +47,17 @@ RematerializedFrame::RematerializedFrame
 RematerializedFrame::New(ThreadSafeContext *cx, uint8_t *top, InlineFrameIterator &iter)
 {
     unsigned numFormals = iter.isFunctionFrame() ? iter.callee()->nargs() : 0;
     unsigned numActualArgs = Max(numFormals, iter.numActualArgs());
     size_t numBytes = sizeof(RematerializedFrame) +
         (numActualArgs + iter.script()->nfixed()) * sizeof(Value) -
         sizeof(Value); // 1 Value included in sizeof(RematerializedFrame)
 
-    void *buf = cx->calloc_(numBytes);
+    void *buf = cx->pod_calloc<uint8_t>(numBytes);
     if (!buf)
         return nullptr;
 
     return new (buf) RematerializedFrame(cx, top, numActualArgs, iter);
 }
 
 /* static */ bool
 RematerializedFrame::RematerializeInlineFrames(ThreadSafeContext *cx, uint8_t *top,
--- a/js/src/jsalloc.h
+++ b/js/src/jsalloc.h
@@ -21,17 +21,17 @@ namespace js {
 
 struct ContextFriendFields;
 
 /* Policy for using system memory functions and doing no error reporting. */
 class SystemAllocPolicy
 {
   public:
     void *malloc_(size_t bytes) { return js_malloc(bytes); }
-    void *calloc_(size_t bytes) { return js_calloc(bytes); }
+    template <typename T> T *pod_calloc(size_t numElems) { return js_pod_calloc<T>(numElems); }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) { return js_realloc(p, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 /*
  * Allocation policy that calls the system memory functions and reports errors
  * to the context. Since the JSContext given on construction is stored for
@@ -57,20 +57,21 @@ class TempAllocPolicy
 
     void *malloc_(size_t bytes) {
         void *p = js_malloc(bytes);
         if (MOZ_UNLIKELY(!p))
             p = onOutOfMemory(nullptr, bytes);
         return p;
     }
 
-    void *calloc_(size_t bytes) {
-        void *p = js_calloc(bytes);
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+        T *p = js_pod_calloc<T>(numElems);
         if (MOZ_UNLIKELY(!p))
-            p = onOutOfMemory(nullptr, bytes);
+            p = (T *)onOutOfMemory(reinterpret_cast<void *>(1), numElems * sizeof(T));
         return p;
     }
 
     void *realloc_(void *p, size_t oldBytes, size_t bytes) {
         void *p2 = js_realloc(p, bytes);
         if (MOZ_UNLIKELY(!p2))
             p2 = onOutOfMemory(p2, bytes);
         return p2;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -977,17 +977,16 @@ class AutoAssertNoException
 class ContextAllocPolicy
 {
     ThreadSafeContext *const cx_;
 
   public:
     MOZ_IMPLICIT ContextAllocPolicy(ThreadSafeContext *cx) : cx_(cx) {}
     ThreadSafeContext *context() const { return cx_; }
     void *malloc_(size_t bytes) { return cx_->malloc_(bytes); }
-    void *calloc_(size_t bytes) { return cx_->calloc_(bytes); }
     void *realloc_(void *p, size_t oldBytes, size_t bytes) { return cx_->realloc_(p, oldBytes, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const { js_ReportAllocationOverflow(cx_); }
 };
 
 /* Exposed intrinsics so that Ion may inline them. */
 bool intrinsic_ToObject(JSContext *cx, unsigned argc, Value *vp);
 bool intrinsic_IsObject(JSContext *cx, unsigned argc, Value *vp);
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3384,17 +3384,17 @@ CheckNewScriptProperties(JSContext *cx, 
         !type->addDefiniteProperties(cx, baseobj) ||
         !initializerList.append(done))
     {
         return;
     }
 
     size_t numBytes = sizeof(TypeNewScript)
                     + (initializerList.length() * sizeof(TypeNewScript::Initializer));
-    TypeNewScript *newScript = (TypeNewScript *) cx->calloc_(numBytes);
+    TypeNewScript *newScript = (TypeNewScript *) type->zone()->pod_calloc<uint8_t>(numBytes);
     if (!newScript)
         return;
 
     new (newScript) TypeNewScript();
 
     type->setNewScript(newScript);
 
     newScript->fun = fun;
@@ -3547,17 +3547,17 @@ JSScript::makeTypes(JSContext *cx)
 {
     JS_ASSERT(!types);
 
     AutoEnterAnalysis enter(cx);
 
     unsigned count = TypeScript::NumTypeSets(this);
 
     TypeScript *typeScript = (TypeScript *)
-        cx->calloc_(TypeScript::SizeIncludingTypeArray(count));
+        zone()->pod_calloc<uint8_t>(TypeScript::SizeIncludingTypeArray(count));
     if (!typeScript)
         return false;
 
     types = typeScript;
 
 #ifdef DEBUG
     StackTypeSet *typeArray = typeScript->typeArray();
     for (unsigned i = 0; i < nTypeSets(); i++) {
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1779,17 +1779,17 @@ js_NewGenerator(JSContext *cx, const Int
                    (-1 + /* one Value included in JSGenerator */
                     vplen +
                     VALUES_PER_STACK_FRAME +
                     stackfp->script()->nslots()) * sizeof(HeapValue);
 
     JS_ASSERT(nbytes % sizeof(Value) == 0);
     JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0);
 
-    JSGenerator *gen = (JSGenerator *) cx->calloc_(nbytes);
+    JSGenerator *gen = (JSGenerator *) obj->zone()->pod_calloc<uint8_t>(nbytes);
     if (!gen)
         return nullptr;
 
     /* Cut up floatingStack space. */
     HeapValue *genvp = gen->stackSnapshot;
     SetValueRangeToUndefined((Value *)genvp, vplen);
 
     InterpreterFrame *genfp = reinterpret_cast<InterpreterFrame *>(genvp + vplen);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1133,34 +1133,34 @@ JSScript::initScriptCounts(JSContext *cx
 {
     JS_ASSERT(!hasScriptCounts());
 
     size_t n = 0;
 
     for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc))
         n += PCCounts::numCounts(JSOp(*pc));
 
-    size_t bytes = (length() * sizeof(PCCounts)) + (n * sizeof(double));
-    char *base = (char *) cx->calloc_(bytes);
+    size_t nbytes = (length() * sizeof(PCCounts)) + (n * sizeof(double));
+    uint8_t *base = zone()->pod_calloc<uint8_t>(nbytes);
     if (!base)
         return false;
 
     /* Create compartment's scriptCountsMap if necessary. */
     ScriptCountsMap *map = compartment()->scriptCountsMap;
     if (!map) {
         map = cx->new_<ScriptCountsMap>();
         if (!map || !map->init()) {
             js_free(base);
             js_delete(map);
             return false;
         }
         compartment()->scriptCountsMap = map;
     }
 
-    char *cursor = base;
+    uint8_t *cursor = base;
 
     ScriptCounts scriptCounts;
     scriptCounts.pcCountsVector = (PCCounts *) cursor;
     cursor += length() * sizeof(PCCounts);
 
     for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc)) {
         JS_ASSERT(uintptr_t(cursor) % sizeof(double) == 0);
         scriptCounts.pcCountsVector[pcToOffset(pc)].counts = (double *) cursor;
@@ -1172,17 +1172,17 @@ JSScript::initScriptCounts(JSContext *cx
     }
 
     if (!map->putNew(this, scriptCounts)) {
         js_free(base);
         return false;
     }
     hasScriptCounts_ = true; // safe to set this;  we can't fail after this point
 
-    JS_ASSERT(size_t(cursor - base) == bytes);
+    JS_ASSERT(size_t(cursor - base) == nbytes);
 
     /* Enable interrupts in any interpreter frames running on this script. */
     for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
         if (iter->isInterpreter())
             iter->asInterpreter()->enableInterruptsIfRunning(this);
     }
 
     return true;
@@ -2353,41 +2353,38 @@ JSScript::Create(ExclusiveContext *cx, H
     script->setSourceObject(sourceObject);
     script->sourceStart_ = bufStart;
     script->sourceEnd_ = bufEnd;
 
     return script;
 }
 
 static inline uint8_t *
-AllocScriptData(ExclusiveContext *cx, size_t size)
+AllocScriptData(JS::Zone *zone, size_t size)
 {
-    uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
+    if (!size)
+        return nullptr;
+
+    uint8_t *data = zone->pod_calloc<uint8_t>(JS_ROUNDUP(size, sizeof(Value)));
     if (!data)
         return nullptr;
-
-    // All script data is optional, so size might be 0. In that case, we don't care about alignment.
-    JS_ASSERT(size == 0 || size_t(data) % sizeof(Value) == 0);
+    JS_ASSERT(size_t(data) % sizeof(Value) == 0);
     return data;
 }
 
 /* static */ bool
 JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nconsts,
                         uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes,
                         uint32_t nblockscopes, uint32_t nTypeSets)
 {
     size_t size = ScriptDataSize(script->bindings.count(), nconsts, nobjects, nregexps, ntrynotes,
                                  nblockscopes);
-    if (size > 0) {
-        script->data = AllocScriptData(cx, size);
-        if (!script->data)
-            return false;
-    } else {
-        script->data = nullptr;
-    }
+    script->data = AllocScriptData(script->zone(), size);
+    if (size && !script->data)
+        return false;
     script->dataSize_ = size;
 
     JS_ASSERT(nTypeSets <= UINT16_MAX);
     script->nTypeSets_ = uint16_t(nTypeSets);
 
     uint8_t *cursor = script->data;
     if (nconsts != 0) {
         script->setHasArray(CONSTS);
@@ -2885,18 +2882,18 @@ js::CloneScript(JSContext *cx, HandleObj
     uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
     uint32_t nregexps  = src->hasRegexps()  ? src->regexps()->length  : 0;
     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
     uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0;
 
     /* Script data */
 
     size_t size = src->dataSize();
-    uint8_t *data = AllocScriptData(cx, size);
-    if (!data)
+    uint8_t *data = AllocScriptData(cx->zone(), size);
+    if (size && !data)
         return nullptr;
 
     /* Bindings */
 
     Rooted<Bindings> bindings(cx);
     InternalHandle<Bindings*> bindingsHandle =
         InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
     if (!Bindings::clone(cx, bindingsHandle, data, src))
@@ -3147,17 +3144,17 @@ JSScript::destroyDebugScript(FreeOp *fop
 
 bool
 JSScript::ensureHasDebugScript(JSContext *cx)
 {
     if (hasDebugScript_)
         return true;
 
     size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
-    DebugScript *debug = (DebugScript *) cx->calloc_(nbytes);
+    DebugScript *debug = (DebugScript *) zone()->pod_calloc<uint8_t>(nbytes);
     if (!debug)
         return false;
 
     /* Create compartment's debugScriptMap if necessary. */
     DebugScriptMap *map = compartment()->debugScriptMap;
     if (!map) {
         map = cx->new_<DebugScriptMap>();
         if (!map || !map->init()) {
--- a/js/src/vm/MallocProvider.h
+++ b/js/src/vm/MallocProvider.h
@@ -18,16 +18,20 @@
  *     These allocators are for system memory whose lifetime is not associated
  *     with a GC thing. See js/src/jsalloc.h.
  *
  *       - SystemAllocPolicy: No extra functionality over bare allocators.
  *
  *       - TempAllocPolicy: Adds automatic error reporting to the provided
  *         Context when allocations fail.
  *
+ *       - ContextAllocPolicy: forwards to the JSContext MallocProvider.
+ *
+ *       - RuntimeAllocPolicy: forwards to the JSRuntime MallocProvider.
+ *
  *   - MallocProvider. A mixin base class that handles automatically updating
  *     the GC's state in response to allocations that are tied to a GC lifetime
  *     or are for a particular GC purpose. These allocators must only be used
  *     for memory that will be freed when a GC thing is swept.
  *
  *       - gc::Zone:  Automatically triggers zone GC.
  *       - JSRuntime: Automatically triggers full GC.
  *       - ThreadsafeContext > ExclusiveContext > JSContext:
@@ -50,23 +54,16 @@ struct MallocProvider
 {
     void *malloc_(size_t bytes) {
         Client *client = static_cast<Client *>(this);
         client->updateMallocCounter(bytes);
         void *p = js_malloc(bytes);
         return MOZ_LIKELY(!!p) ? p : client->onOutOfMemory(nullptr, bytes);
     }
 
-    void *calloc_(size_t bytes) {
-        Client *client = static_cast<Client *>(this);
-        client->updateMallocCounter(bytes);
-        void *p = js_calloc(bytes);
-        return MOZ_LIKELY(!!p) ? p : client->onOutOfMemory(reinterpret_cast<void *>(1), bytes);
-    }
-
     void *realloc_(void *p, size_t oldBytes, size_t newBytes) {
         Client *client = static_cast<Client *>(this);
         /*
          * For compatibility we do not account for realloc that decreases
          * previously allocated memory.
          */
         if (newBytes > oldBytes)
             client->updateMallocCounter(newBytes - oldBytes);
@@ -88,17 +85,24 @@ struct MallocProvider
 
     template <class T>
     T *pod_malloc() {
         return (T *)malloc_(sizeof(T));
     }
 
     template <class T>
     T *pod_calloc() {
-        return (T *)calloc_(sizeof(T));
+        Client *client = static_cast<Client *>(this);
+        client->updateMallocCounter(sizeof(T));
+        T *p = js_pod_calloc<T>();
+        if (MOZ_UNLIKELY(!p)) {
+            client->onOutOfMemory(reinterpret_cast<void *>(1), sizeof(T));
+            return nullptr;
+        }
+        return p;
     }
 
     template <class T>
     T *pod_malloc(size_t numElems) {
         if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
             Client *client = static_cast<Client *>(this);
             client->reportAllocationOverflow();
             return nullptr;
@@ -115,33 +119,39 @@ struct MallocProvider
     template <class T>
     T *
     pod_calloc(size_t numElems, JSCompartment *comp = nullptr, JSContext *cx = nullptr) {
         if (numElems & mozilla::tl::MulOverflowMask<sizeof(T)>::value) {
             Client *client = static_cast<Client *>(this);
             client->reportAllocationOverflow();
             return nullptr;
         }
-        return (T *)calloc_(numElems * sizeof(T));
+        Client *client = static_cast<Client *>(this);
+        client->updateMallocCounter(numElems * sizeof(T));
+        T *p = js_pod_calloc<T>(numElems);
+        if (MOZ_UNLIKELY(!p)) {
+            client->onOutOfMemory(reinterpret_cast<void *>(1), sizeof(T));
+            return nullptr;
+        }
+        return p;
     }
 
     template <class T>
     mozilla::UniquePtr<T[], JS::FreePolicy>
     make_zeroed_pod_array(size_t numElems,
                           JSCompartment *comp = nullptr,
                           JSContext *cx = nullptr)
     {
         return mozilla::UniquePtr<T[], JS::FreePolicy>(pod_calloc<T>(numElems, comp, cx));
     }
 
     template <class T>
     T *pod_realloc(T *prior, size_t oldSize, size_t newSize) {
         return (T *)realloc_(prior, oldSize * sizeof(T), newSize * sizeof(T));
     }
 
-
     JS_DECLARE_NEW_METHODS(new_, malloc_, MOZ_ALWAYS_INLINE)
     JS_DECLARE_MAKE_METHODS(make_unique, new_, MOZ_ALWAYS_INLINE)
 };
 
 } /* namespace js */
 
 #endif /* vm_MallocProvider_h */
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1365,17 +1365,17 @@ struct JSRuntime : public JS::shadow::Ru
     /*
      * These variations of malloc/calloc/realloc will call the
      * large-allocation-failure callback on OOM and retry the allocation.
      */
 
     static const unsigned LARGE_ALLOCATION = 25 * 1024 * 1024;
 
     void *callocCanGC(size_t bytes) {
-        void *p = calloc_(bytes);
+        void *p = (void *)pod_calloc<uint8_t>(bytes);
         if (MOZ_LIKELY(!!p))
             return p;
         return onOutOfMemoryCanGC(reinterpret_cast<void *>(1), bytes);
     }
 
     void *reallocCanGC(void *p, size_t bytes) {
         void *p2 = realloc_(p, bytes);
         if (MOZ_LIKELY(!!p2))
@@ -1645,17 +1645,22 @@ SetValueRangeToNull(Value *vec, size_t l
  */
 class RuntimeAllocPolicy
 {
     JSRuntime *const runtime;
 
   public:
     MOZ_IMPLICIT RuntimeAllocPolicy(JSRuntime *rt) : runtime(rt) {}
     void *malloc_(size_t bytes) { return runtime->malloc_(bytes); }
-    void *calloc_(size_t bytes) { return runtime->calloc_(bytes); }
+
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+        return runtime->pod_calloc<T>(numElems);
+    }
+
     void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 extern const JSSecurityCallbacks NullSecurityCallbacks;
 
 // Debugging RAII class which marks the current thread as performing an Ion
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -40,20 +40,20 @@ ShapeTable::init(ThreadSafeContext *cx, 
     uint32_t sizeLog2 = CeilingLog2Size(entryCount);
     uint32_t size = JS_BIT(sizeLog2);
     if (entryCount >= size - (size >> 2))
         sizeLog2++;
     if (sizeLog2 < MIN_SIZE_LOG2)
         sizeLog2 = MIN_SIZE_LOG2;
 
     /*
-     * Use rt->calloc_ for memory accounting and overpressure handling
+     * Use rt->calloc for memory accounting and overpressure handling
      * without OOM reporting. See ShapeTable::change.
      */
-    entries = (Shape **) cx->calloc_(sizeOfEntries(JS_BIT(sizeLog2)));
+    entries = cx->pod_calloc<Shape *>(JS_BIT(sizeLog2));
     if (!entries)
         return false;
 
     hashShift = HASH_BITS - sizeLog2;
     for (Shape::Range<NoGC> r(lastProp); !r.empty(); r.popFront()) {
         Shape &shape = r.front();
         JS_ASSERT(cx->isThreadLocal(&shape));
         Shape **spp = search(shape.propid(), true);
@@ -255,17 +255,17 @@ ShapeTable::change(int log2Delta, Thread
 
     /*
      * Grow, shrink, or compress by changing this->entries.
      */
     int oldlog2 = HASH_BITS - hashShift;
     int newlog2 = oldlog2 + log2Delta;
     uint32_t oldsize = JS_BIT(oldlog2);
     uint32_t newsize = JS_BIT(newlog2);
-    Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize));
+    Shape **newTable = cx->pod_calloc<Shape *>(newsize);
     if (!newTable)
         return false;
 
     /* Now that we have newTable allocated, update members. */
     hashShift = HASH_BITS - newlog2;
     removedCount = 0;
     Shape **oldTable = entries;
     entries = newTable;
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -156,19 +156,16 @@ struct ShapeTable {
 
     ~ShapeTable() {
         js_free(entries);
     }
 
     /* By definition, hashShift = HASH_BITS - log2(capacity). */
     uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift); }
 
-    /* Computes the size of the entries array for a given capacity. */
-    static size_t sizeOfEntries(size_t cap) { return cap * sizeof(Shape *); }
-
     /*
      * This counts the ShapeTable object itself (which must be
      * heap-allocated) and its |entries| array.
      */
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
         return mallocSizeOf(this) + mallocSizeOf(entries);
     }
 
@@ -182,17 +179,17 @@ struct ShapeTable {
      * Try to grow the table.  On failure, reports out of memory on cx
      * and returns false.  This will make any extant pointers into the
      * table invalid.  Don't call this unless needsToGrow() is true.
      */
     bool grow(ThreadSafeContext *cx);
 
     /*
      * NB: init and change are fallible but do not report OOM, so callers can
-     * cope or ignore. They do however use the context's calloc_ method in
+     * cope or ignore. They do however use the context's calloc method in
      * order to update the malloc counter on success.
      */
     bool            init(ThreadSafeContext *cx, Shape *lastProp);
     bool            change(int log2Delta, ThreadSafeContext *cx);
     Shape           **search(jsid id, bool adding);
 };
 
 /*
--- a/layout/style/nsNthIndexCache.h
+++ b/layout/style/nsNthIndexCache.h
@@ -56,17 +56,22 @@ private:
   // If -2, needs to be computed.
   // If -1, needs to be computed but known not to be 1.
   // If 0, the node is not at any index in its parent.
   typedef int32_t CacheEntry;
 
   class SystemAllocPolicy {
   public:
     void *malloc_(size_t bytes) { return ::malloc(bytes); }
-    void *calloc_(size_t bytes) { return ::calloc(bytes, 1); }
+
+    template <typename T>
+    T *pod_calloc(size_t numElems) {
+      return static_cast<T *>(::calloc(numElems, sizeof(T)));
+    }
+
     void *realloc_(void *p, size_t bytes) { return ::realloc(p, bytes); }
     void free_(void *p) { ::free(p); }
     void reportAllocOverflow() const {}
   };
 
   typedef js::HashMap<nsIContent*, CacheEntry, js::DefaultHasher<nsIContent*>,
                       SystemAllocPolicy> Cache;
 
--- a/memory/replace/dmd/DMD.cpp
+++ b/memory/replace/dmd/DMD.cpp
@@ -104,16 +104,24 @@ public:
 
   static void* calloc_(size_t aSize)
   {
     void* p = gMallocTable->calloc(1, aSize);
     ExitOnFailure(p);
     return p;
   }
 
+  template <typename T>
+  static T* pod_calloc(size_t aNumElems)
+  {
+    void* p = gMallocTable->calloc(aNumElems, sizeof(T));
+    ExitOnFailure(p);
+    return (T*)p;
+  }
+
   // This realloc_ is the one we use for direct reallocs within DMD.
   static void* realloc_(void* aPtr, size_t aNewSize)
   {
     void* p = gMallocTable->realloc(aPtr, aNewSize);
     ExitOnFailure(p);
     return p;
   }
 
--- a/mfbt/AllocPolicy.h
+++ b/mfbt/AllocPolicy.h
@@ -21,17 +21,17 @@ namespace mozilla {
  * Allocation policies are used to implement the standard allocation behaviors
  * in a customizable way.  Additionally, custom behaviors may be added to these
  * behaviors, such as additionally reporting an error through an out-of-band
  * mechanism when OOM occurs.  The concept modeled here is as follows:
  *
  *  - public copy constructor, assignment, destructor
  *  - void* malloc_(size_t)
  *      Responsible for OOM reporting when null is returned.
- *  - void* calloc_(size_t)
+ *  - template <typename T> T* pod_calloc(size_t)
  *      Responsible for OOM reporting when null is returned.
  *  - void* realloc_(void*, size_t, size_t)
  *      Responsible for OOM reporting when null is returned.  The *used* bytes
  *      of the previous buffer is passed in (rather than the old allocation
  *      size), in addition to the *new* allocation size requested.
  *  - void free_(void*)
  *  - void reportAllocOverflow() const
  *      Called on allocation overflow (that is, an allocation implicitly tried
@@ -50,19 +50,20 @@ namespace mozilla {
 class MallocAllocPolicy
 {
 public:
   void* malloc_(size_t aBytes)
   {
     return malloc(aBytes);
   }
 
-  void* calloc_(size_t aBytes)
+  template <typename T>
+  T* pod_calloc(size_t aNumElems)
   {
-    return calloc(aBytes, 1);
+    return static_cast<T*>(calloc(aNumElems, sizeof(T)));
   }
 
   void* realloc_(void* aPtr, size_t aOldBytes, size_t aBytes)
   {
     return realloc(aPtr, aBytes);
   }
 
   void free_(void* aPtr)