michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef jit_AsmJS_h michael@0: #define jit_AsmJS_h michael@0: michael@0: #include michael@0: michael@0: #include "js/TypeDecls.h" michael@0: #include "vm/ObjectImpl.h" michael@0: michael@0: namespace js { michael@0: michael@0: class ExclusiveContext; michael@0: namespace frontend { michael@0: template struct Parser; michael@0: template struct ParseContext; michael@0: class FullParseHandler; michael@0: struct ParseNode; michael@0: } michael@0: michael@0: typedef frontend::Parser AsmJSParser; michael@0: typedef frontend::ParseContext AsmJSParseContext; michael@0: michael@0: // Takes over parsing of a function starting with "use asm". The return value michael@0: // indicates whether an error was reported which the caller should propagate. michael@0: // If no error was reported, the function may still fail to validate as asm.js. michael@0: // In this case, the parser.tokenStream has been advanced an indeterminate michael@0: // amount and the entire function should be reparsed from the beginning. michael@0: extern bool michael@0: CompileAsmJS(ExclusiveContext *cx, AsmJSParser &parser, frontend::ParseNode *stmtList, michael@0: bool *validated); michael@0: michael@0: // The assumed page size; dynamically checked in CompileAsmJS. michael@0: const size_t AsmJSPageSize = 4096; michael@0: michael@0: // The asm.js spec requires that the ArrayBuffer's byteLength be a multiple of 4096. michael@0: static const size_t AsmJSAllocationGranularity = 4096; michael@0: michael@0: #ifdef JS_CODEGEN_X64 michael@0: // On x64, the internal ArrayBuffer data array is inflated to 4GiB (only the michael@0: // byteLength portion of which is accessible) so that out-of-bounds accesses michael@0: // (made using a uint32 index) are guaranteed to raise a SIGSEGV. michael@0: static const size_t AsmJSBufferProtectedSize = 4 * 1024ULL * 1024ULL * 1024ULL; michael@0: michael@0: // To avoid dynamically checking bounds on each load/store, asm.js code relies michael@0: // on the SIGSEGV handler in AsmJSSignalHandlers.cpp. However, this only works michael@0: // if we can guarantee that *any* out-of-bounds access generates a fault. This michael@0: // isn't generally true since an out-of-bounds access could land on other michael@0: // Mozilla data. To overcome this on x64, we reserve an entire 4GB space, michael@0: // making only the range [0, byteLength) accessible, and use a 32-bit unsigned michael@0: // index into this space. (x86 and ARM require different tricks.) michael@0: // michael@0: // One complication is that we need to put an ObjectElements struct immediately michael@0: // before the data array (as required by the general JSObject data structure). michael@0: // Thus, we must stick a page before the elements to hold ObjectElements. michael@0: // michael@0: // |<------------------------------ 4GB + 1 pages --------------------->| michael@0: // |<--- sizeof --->|<------------------- 4GB ----------------->| michael@0: // michael@0: // | waste | ObjectElements | data array | inaccessible reserved memory | michael@0: // ^ ^ ^ michael@0: // | \ / michael@0: // obj->elements required to be page boundaries michael@0: // michael@0: static const size_t AsmJSMappedSize = AsmJSPageSize + AsmJSBufferProtectedSize; michael@0: #endif // JS_CODEGEN_X64 michael@0: michael@0: #ifdef JS_ION michael@0: michael@0: // Return whether asm.js optimization is inhibitted by the platform or michael@0: // dynamically disabled: michael@0: extern bool michael@0: IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, JS::Value *vp); michael@0: michael@0: #else // JS_ION michael@0: michael@0: inline bool michael@0: IsAsmJSCompilationAvailable(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: args.rval().set(BooleanValue(false)); michael@0: return true; michael@0: } michael@0: michael@0: #endif // JS_ION michael@0: michael@0: // The Asm.js heap length is constrained by the x64 backend heap access scheme michael@0: // to be a multiple of the page size which is 4096 bytes, and also constrained michael@0: // by the limits of ARM backends 'cmp immediate' instruction which supports a michael@0: // complex range for the immediate argument. michael@0: // michael@0: // ARMv7 mode supports the following immediate constants, and the Thumb T2 michael@0: // instruction encoding also supports the subset of immediate constants used. michael@0: // abcdefgh 00000000 00000000 00000000 michael@0: // 00abcdef gh000000 00000000 00000000 michael@0: // 0000abcd efgh0000 00000000 00000000 michael@0: // 000000ab cdefgh00 00000000 00000000 michael@0: // 00000000 abcdefgh 00000000 00000000 michael@0: // 00000000 00abcdef gh000000 00000000 michael@0: // 00000000 0000abcd efgh0000 00000000 michael@0: // ... michael@0: // michael@0: // The 4096 page size constraint restricts the length to: michael@0: // xxxxxxxx xxxxxxxx xxxx0000 00000000 michael@0: // michael@0: // Intersecting all the above constraints gives: michael@0: // Heap length 0x40000000 to 0xff000000 quanta 0x01000000 michael@0: // Heap length 0x10000000 to 0x3fc00000 quanta 0x00400000 michael@0: // Heap length 0x04000000 to 0x0ff00000 quanta 0x00100000 michael@0: // Heap length 0x01000000 to 0x03fc0000 quanta 0x00040000 michael@0: // Heap length 0x00400000 to 0x00ff0000 quanta 0x00010000 michael@0: // Heap length 0x00100000 to 0x003fc000 quanta 0x00004000 michael@0: // Heap length 0x00001000 to 0x000ff000 quanta 0x00001000 michael@0: // michael@0: inline uint32_t michael@0: RoundUpToNextValidAsmJSHeapLength(uint32_t length) michael@0: { michael@0: if (length < 0x00001000u) // Minimum length is the pages size of 4096. michael@0: return 0x1000u; michael@0: if (length < 0x00100000u) // < 1M quanta 4K michael@0: return (length + 0x00000fff) & ~0x00000fff; michael@0: if (length < 0x00400000u) // < 4M quanta 16K michael@0: return (length + 0x00003fff) & ~0x00003fff; michael@0: if (length < 0x01000000u) // < 16M quanta 64K michael@0: return (length + 0x0000ffff) & ~0x0000ffff; michael@0: if (length < 0x04000000u) // < 64M quanta 256K michael@0: return (length + 0x0003ffff) & ~0x0003ffff; michael@0: if (length < 0x10000000u) // < 256M quanta 1M michael@0: return (length + 0x000fffff) & ~0x000fffff; michael@0: if (length < 0x40000000u) // < 1024M quanta 4M michael@0: return (length + 0x003fffff) & ~0x003fffff; michael@0: // < 4096M quanta 16M. Note zero is returned if over 0xff000000 but such michael@0: // lengths are not currently valid. michael@0: JS_ASSERT(length <= 0xff000000); michael@0: return (length + 0x00ffffff) & ~0x00ffffff; michael@0: } michael@0: michael@0: inline bool michael@0: IsValidAsmJSHeapLength(uint32_t length) michael@0: { michael@0: if (length < AsmJSAllocationGranularity) michael@0: return false; michael@0: if (length <= 0x00100000u) michael@0: return (length & 0x00000fff) == 0; michael@0: if (length <= 0x00400000u) michael@0: return (length & 0x00003fff) == 0; michael@0: if (length <= 0x01000000u) michael@0: return (length & 0x0000ffff) == 0; michael@0: if (length <= 0x04000000u) michael@0: return (length & 0x0003ffff) == 0; michael@0: if (length <= 0x10000000u) michael@0: return (length & 0x000fffff) == 0; michael@0: if (length <= 0x40000000u) michael@0: return (length & 0x003fffff) == 0; michael@0: if (length <= 0xff000000u) michael@0: return (length & 0x00ffffff) == 0; michael@0: return false; michael@0: } michael@0: michael@0: } // namespace js michael@0: michael@0: #endif // jit_AsmJS_h