Bug 844196 - Hoist allocation of executable memory (r=jandem)
authorLuke Wagner <luke@mozilla.com>
Wed, 08 Oct 2014 14:24:13 -0500
changeset 209483 541dbaa3fec2170c525424ac8a7dc2faafbbf04d
parent 209482 b756cab77bac8fd4b328c86f43f7b1d24ff62438
child 209484 f920a1e7d15a1b43b3c2d08e28e454fc2d3c0f04
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjandem
bugs844196
milestone35.0a1
Bug 844196 - Hoist allocation of executable memory (r=jandem)
js/src/asmjs/AsmJSModule.cpp
js/src/jit/ExecutableAllocator.h
js/src/jit/ExecutableAllocatorPosix.cpp
js/src/jit/ExecutableAllocatorWin.cpp
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -13,18 +13,16 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "asmjs/AsmJSModule.h"
 
-#include <errno.h>
-
 #ifndef XP_WIN
 # include <sys/mman.h>
 #endif
 
 #include "mozilla/BinarySearch.h"
 #include "mozilla/Compression.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TaggedAnonymousMemory.h"
@@ -52,47 +50,27 @@ using namespace jit;
 using namespace frontend;
 using mozilla::BinarySearch;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::Compression::LZ4;
 using mozilla::Swap;
 
 static uint8_t *
-AllocateExecutableMemory(ExclusiveContext *cx, size_t totalBytes)
-{
-    MOZ_ASSERT(totalBytes % AsmJSPageSize == 0);
-
-#ifdef XP_WIN
-    void *p = VirtualAlloc(nullptr, totalBytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
-    if (!p) {
-        js_ReportOutOfMemory(cx);
-        return nullptr;
-    }
-#else  // assume Unix
-    void *p = MozTaggedAnonymousMmap(nullptr, totalBytes,
-                                     PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON,
-                                     -1, 0, "asm-js-code");
-    if (p == MAP_FAILED) {
-        js_ReportOutOfMemory(cx);
-        return nullptr;
-    }
-#endif
-
-    return (uint8_t *)p;
-}
-
-static void
-DeallocateExecutableMemory(uint8_t *code, size_t totalBytes)
+AllocateExecutableMemory(ExclusiveContext *cx, size_t bytes)
 {
 #ifdef XP_WIN
-    JS_ALWAYS_TRUE(VirtualFree(code, 0, MEM_RELEASE));
+    unsigned permissions = PAGE_EXECUTE_READWRITE;
 #else
-    JS_ALWAYS_TRUE(munmap(code, totalBytes) == 0 || errno == ENOMEM);
+    unsigned permissions = PROT_READ | PROT_WRITE | PROT_EXEC;
 #endif
+    void *p = AllocateExecutableMemory(nullptr, bytes, permissions, "asm-js-code", AsmJSPageSize);
+    if (!p)
+        js_ReportOutOfMemory(cx);
+    return (uint8_t *)p;
 }
 
 AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
                          bool strict, bool canUseSignalHandlers)
   : srcStart_(srcStart),
     srcBodyStart_(srcBodyStart),
     scriptSource_(scriptSource),
     globalArgumentName_(nullptr),
@@ -124,17 +102,17 @@ AsmJSModule::~AsmJSModule()
             AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
             if (!exitDatum.ionScript)
                 continue;
 
             jit::DependentAsmJSModuleExit exit(this, i);
             exitDatum.ionScript->removeDependentAsmJSModule(exit);
         }
 
-        DeallocateExecutableMemory(code_, pod.totalBytes_);
+        DeallocateExecutableMemory(code_, pod.totalBytes_, AsmJSPageSize);
     }
 
     for (size_t i = 0; i < numFunctionCounts(); i++)
         js_delete(functionCounts(i));
 }
 
 void
 AsmJSModule::trace(JSTracer *trc)
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -467,12 +467,19 @@ private:
     // its reference is removed from m_pools.
     typedef js::HashSet<ExecutablePool *, js::DefaultHasher<ExecutablePool *>, js::SystemAllocPolicy>
             ExecPoolHashSet;
     ExecPoolHashSet m_pools;    // All pools, just for stats purposes.
 
     static size_t determinePageSize();
 };
 
+extern void *
+AllocateExecutableMemory(void *addr, size_t bytes, unsigned permissions, const char *tag,
+                         size_t pageSize);
+
+extern void
+DeallocateExecutableMemory(void *addr, size_t bytes, size_t pageSize);
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_ExecutableAllocator_h */
--- a/js/src/jit/ExecutableAllocatorPosix.cpp
+++ b/js/src/jit/ExecutableAllocatorPosix.cpp
@@ -21,42 +21,58 @@
  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/TaggedAnonymousMemory.h"
 
+#include <errno.h>
 #include <sys/mman.h>
 #include <unistd.h>
 
 #include "jit/ExecutableAllocator.h"
 #include "js/Utility.h"
 
 using namespace js::jit;
 
 size_t ExecutableAllocator::determinePageSize()
 {
     return getpagesize();
 }
 
+void *
+js::jit::AllocateExecutableMemory(void *addr, size_t bytes, unsigned permissions, const char *tag,
+                                  size_t pageSize)
+{
+    MOZ_ASSERT(bytes % pageSize == 0);
+    void *p = MozTaggedAnonymousMmap(addr, bytes, permissions, MAP_PRIVATE | MAP_ANON, -1, 0, tag);
+    return p == MAP_FAILED ? nullptr : p;
+}
+
+void
+js::jit::DeallocateExecutableMemory(void *addr, size_t bytes, size_t pageSize)
+{
+    MOZ_ASSERT(bytes % pageSize == 0);
+    mozilla::DebugOnly<int> result = munmap(addr, bytes);
+    MOZ_ASSERT(!result || errno == ENOMEM);
+}
+
 ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
 {
-    void *allocation = MozTaggedAnonymousMmap(NULL, n, INITIAL_PROTECTION_FLAGS, MAP_PRIVATE | MAP_ANON, -1, 0, "js-jit-code");
-    if (allocation == MAP_FAILED)
-        allocation = NULL;
+    void *allocation = AllocateExecutableMemory(nullptr, n, INITIAL_PROTECTION_FLAGS,
+                                                "js-jit-code", pageSize);
     ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n };
     return alloc;
 }
 
 void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
 {
-    mozilla::DebugOnly<int> result = munmap(alloc.pages, alloc.size);
-    MOZ_ASSERT(!result);
+    DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize);
 }
 
 #if WTF_ENABLE_ASSEMBLER_WX_EXCLUSIVE
 void ExecutableAllocator::reprotectRegion(void* start, size_t size, ProtectionSetting setting)
 {
     if (!pageSize)
         intializePageSize();
 
--- a/js/src/jit/ExecutableAllocatorWin.cpp
+++ b/js/src/jit/ExecutableAllocatorWin.cpp
@@ -80,37 +80,54 @@ static bool
 RandomizeIsBroken()
 {
     // Use the compiler's intrinsic guards for |static type value = expr| to avoid some potential
     // races if runtimes are created from multiple threads.
     static int result = RandomizeIsBrokenImpl();
     return !!result;
 }
 
+void *
+js::jit::AllocateExecutableMemory(void *addr, size_t bytes, unsigned permissions, const char *tag,
+                                  size_t pageSize)
+{
+    MOZ_ASSERT(bytes % pageSize == 0);
+    return VirtualAlloc(addr, bytes, MEM_COMMIT | MEM_RESERVE, permissions);
+}
+
+void
+js::jit::DeallocateExecutableMemory(void *addr, size_t bytes, size_t pageSize)
+{
+    MOZ_ASSERT(bytes % pageSize == 0);
+    VirtualFree(addr, 0, MEM_RELEASE);
+}
+
 ExecutablePool::Allocation ExecutableAllocator::systemAlloc(size_t n)
 {
     void *allocation = NULL;
     // Randomization disabled to avoid a performance fault on x64 builds.
     // See bug 728623.
 #ifndef JS_CPU_X64
     if (!RandomizeIsBroken()) {
         void *randomAddress = computeRandomAllocationAddress();
-        allocation = VirtualAlloc(randomAddress, n, MEM_COMMIT | MEM_RESERVE,
-                                  PAGE_EXECUTE_READWRITE);
+        allocation = AllocateExecutableMemory(randomAddress, n, PAGE_EXECUTE_READWRITE,
+                                              "js-jit-code", pageSize);
     }
 #endif
-    if (!allocation)
-        allocation = VirtualAlloc(0, n, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
+    if (!allocation) {
+        allocation = AllocateExecutableMemory(nullptr, n, PAGE_EXECUTE_READWRITE,
+                                              "js-jit-code", pageSize);
+    }
     ExecutablePool::Allocation alloc = { reinterpret_cast<char*>(allocation), n };
     return alloc;
 }
 
 void ExecutableAllocator::systemRelease(const ExecutablePool::Allocation& alloc)
 {
-    VirtualFree(alloc.pages, 0, MEM_RELEASE);
+    DeallocateExecutableMemory(alloc.pages, alloc.size, pageSize);
 }
 
 void
 ExecutablePool::toggleAllCodeAsAccessible(bool accessible)
 {
     char* begin = m_allocation.pages;
     size_t size = m_freePtr - begin;