Bug 960367 - OdinMonkey: compress source stored in in cache file (r=sstangl)

This commit is contained in:
Luke Wagner
2014-01-17 17:34:33 -06:00
parent d0b53ae3e5
commit 1a0b0ef75b
2 changed files with 68 additions and 21 deletions

View File

@@ -10,6 +10,7 @@
# include <sys/mman.h> # include <sys/mman.h>
#endif #endif
#include "mozilla/Compression.h"
#include "mozilla/PodOperations.h" #include "mozilla/PodOperations.h"
#include "jslibmath.h" #include "jslibmath.h"
@@ -32,6 +33,7 @@ using namespace js;
using namespace jit; using namespace jit;
using namespace frontend; using namespace frontend;
using mozilla::PodEqual; using mozilla::PodEqual;
using mozilla::Compression::LZ4;
void void
AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx) AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
@@ -814,8 +816,7 @@ struct PropertyNameWrapper
class ModuleChars class ModuleChars
{ {
uint32_t length_; protected:
const jschar *begin_;
uint32_t isFunCtor_; uint32_t isFunCtor_;
js::Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_; js::Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_;
@@ -827,8 +828,34 @@ class ModuleChars
static uint32_t endOffset(AsmJSParser &parser) { static uint32_t endOffset(AsmJSParser &parser) {
return parser.tokenStream.peekTokenPos().end; return parser.tokenStream.peekTokenPos().end;
} }
};
class ModuleCharsForStore : ModuleChars
{
uint32_t uncompressedSize_;
uint32_t compressedSize_;
js::Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
public:
bool init(AsmJSParser &parser, const AsmJSModule &module) {
JS_ASSERT(beginOffset(parser) < endOffset(parser));
uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(jschar);
size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
if (maxCompressedSize < uncompressedSize_)
return false;
if (!compressedBuffer_.resize(maxCompressedSize))
return false;
const jschar *chars = parser.tokenStream.rawBase() + beginOffset(parser);
const char *source = reinterpret_cast<const char*>(chars);
size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
if (!compressedSize || compressedSize > UINT32_MAX)
return false;
compressedSize_ = compressedSize;
bool initFromParsedModule(AsmJSParser &parser, const AsmJSModule &module) {
// For a function statement or named function expression: // For a function statement or named function expression:
// function f(x,y,z) { abc } // function f(x,y,z) { abc }
// the range [beginOffset, endOffset) captures the source: // the range [beginOffset, endOffset) captures the source:
@@ -841,9 +868,6 @@ class ModuleChars
// For functions created with 'new Function', function arguments are // For functions created with 'new Function', function arguments are
// not present in the source so we must manually explicitly serialize // not present in the source so we must manually explicitly serialize
// and match the formals as a Vector of PropertyName. // and match the formals as a Vector of PropertyName.
JS_ASSERT(beginOffset(parser) < endOffset(parser));
begin_ = parser.tokenStream.rawBase() + beginOffset(parser);
length_ = endOffset(parser) - beginOffset(parser);
isFunCtor_ = parser.pc->isFunctionConstructorBody(); isFunCtor_ = parser.pc->isFunctionConstructorBody();
if (isFunCtor_) { if (isFunCtor_) {
unsigned numArgs; unsigned numArgs;
@@ -853,42 +877,65 @@ class ModuleChars
return false; return false;
} }
} }
return true; return true;
} }
size_t serializedSize() const { size_t serializedSize() const {
return sizeof(uint32_t) + return sizeof(uint32_t) +
length_ * sizeof(jschar) + sizeof(uint32_t) +
compressedSize_ +
sizeof(uint32_t) + sizeof(uint32_t) +
(isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0); (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
} }
uint8_t *serialize(uint8_t *cursor) const { uint8_t *serialize(uint8_t *cursor) const {
cursor = WriteScalar<uint32_t>(cursor, length_); cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
cursor = WriteBytes(cursor, begin_, length_ * sizeof(jschar)); cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
cursor = WriteScalar<uint32_t>(cursor, isFunCtor_); cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
if (isFunCtor_) if (isFunCtor_)
cursor = SerializeVector(cursor, funCtorArgs_); cursor = SerializeVector(cursor, funCtorArgs_);
return cursor; return cursor;
} }
};
class ModuleCharsForLookup : ModuleChars
{
js::Vector<jschar, 0, SystemAllocPolicy> chars_;
public:
const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) { const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) {
cursor = ReadScalar<uint32_t>(cursor, &length_); uint32_t uncompressedSize;
begin_ = reinterpret_cast<const jschar *>(cursor); cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
cursor += length_ * sizeof(jschar);
uint32_t compressedSize;
cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
if (!chars_.resize(uncompressedSize / sizeof(jschar)))
return nullptr;
const char *source = reinterpret_cast<const char*>(cursor);
char *dest = reinterpret_cast<char*>(chars_.begin());
if (!LZ4::decompress(source, dest, uncompressedSize))
return nullptr;
cursor += compressedSize;
cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_); cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
if (isFunCtor_) if (isFunCtor_)
cursor = DeserializeVector(cx, cursor, &funCtorArgs_); cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
return cursor; return cursor;
} }
bool matchUnparsedModule(AsmJSParser &parser) const { bool match(AsmJSParser &parser) const {
const jschar *parseBegin = parser.tokenStream.rawBase() + beginOffset(parser); const jschar *parseBegin = parser.tokenStream.rawBase() + beginOffset(parser);
const jschar *parseLimit = parser.tokenStream.rawLimit(); const jschar *parseLimit = parser.tokenStream.rawLimit();
JS_ASSERT(parseLimit >= parseBegin); JS_ASSERT(parseLimit >= parseBegin);
if (uint32_t(parseLimit - parseBegin) < length_) if (uint32_t(parseLimit - parseBegin) < chars_.length())
return false; return false;
if (!PodEqual(begin_, parseBegin, length_)) if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
return false; return false;
if (isFunCtor_ != parser.pc->isFunctionConstructorBody()) if (isFunCtor_ != parser.pc->isFunctionConstructorBody())
return false; return false;
@@ -900,7 +947,7 @@ class ModuleChars
// new Function('"use asm"; function f() {} return f') // new Function('"use asm"; function f() {} return f')
// from incorrectly matching // from incorrectly matching
// new Function('"use asm"; function f() {} return ff') // new Function('"use asm"; function f() {} return ff')
if (parseBegin + length_ != parseLimit) if (parseBegin + chars_.length() != parseLimit)
return false; return false;
unsigned numArgs; unsigned numArgs;
ParseNode *arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs); ParseNode *arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
@@ -942,8 +989,8 @@ js::StoreAsmJSModuleInCache(AsmJSParser &parser,
if (!machineId.extractCurrentState(cx)) if (!machineId.extractCurrentState(cx))
return false; return false;
ModuleChars moduleChars; ModuleCharsForStore moduleChars;
if (!moduleChars.initFromParsedModule(parser, module)) if (!moduleChars.init(parser, module))
return false; return false;
size_t serializedSize = machineId.serializedSize() + size_t serializedSize = machineId.serializedSize() +
@@ -1021,9 +1068,9 @@ js::LookupAsmJSModuleInCache(ExclusiveContext *cx,
if (machineId != cachedMachineId) if (machineId != cachedMachineId)
return true; return true;
ModuleChars moduleChars; ModuleCharsForLookup moduleChars;
cursor = moduleChars.deserialize(cx, cursor); cursor = moduleChars.deserialize(cx, cursor);
if (!moduleChars.matchUnparsedModule(parser)) if (!moduleChars.match(parser))
return true; return true;
ScopedJSDeletePtr<AsmJSModule> module( ScopedJSDeletePtr<AsmJSModule> module(

View File

@@ -34,7 +34,7 @@ public:
* Compresses 'inputSize' bytes from 'source' into 'dest'. * Compresses 'inputSize' bytes from 'source' into 'dest'.
* Destination buffer must be already allocated, * Destination buffer must be already allocated,
* and must be sized to handle worst cases situations (input data not compressible) * and must be sized to handle worst cases situations (input data not compressible)
* Worst case size evaluation is provided by function LZ4_compressBound() * Worst case size evaluation is provided by function maxCompressedSize()
* *
* @param inputSize is the input size. Max supported value is ~1.9GB * @param inputSize is the input size. Max supported value is ~1.9GB
* @param return the number of bytes written in buffer dest * @param return the number of bytes written in buffer dest