From 1a0b0ef75bd809785853a5f9ba2f67b51ce7832c Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 17 Jan 2014 17:34:33 -0600 Subject: [PATCH] Bug 960367 - OdinMonkey: compress source stored in in cache file (r=sstangl) --- js/src/jit/AsmJSModule.cpp | 87 +++++++++++++++++++++++++++++--------- mfbt/Compression.h | 2 +- 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/js/src/jit/AsmJSModule.cpp b/js/src/jit/AsmJSModule.cpp index 0d2dee0619a5..ba00bfc91148 100644 --- a/js/src/jit/AsmJSModule.cpp +++ b/js/src/jit/AsmJSModule.cpp @@ -10,6 +10,7 @@ # include #endif +#include "mozilla/Compression.h" #include "mozilla/PodOperations.h" #include "jslibmath.h" @@ -32,6 +33,7 @@ using namespace js; using namespace jit; using namespace frontend; using mozilla::PodEqual; +using mozilla::Compression::LZ4; void AsmJSModule::initHeap(Handle heap, JSContext *cx) @@ -814,8 +816,7 @@ struct PropertyNameWrapper class ModuleChars { - uint32_t length_; - const jschar *begin_; + protected: uint32_t isFunCtor_; js::Vector funCtorArgs_; @@ -827,8 +828,34 @@ class ModuleChars static uint32_t endOffset(AsmJSParser &parser) { return parser.tokenStream.peekTokenPos().end; } +}; + +class ModuleCharsForStore : ModuleChars +{ + uint32_t uncompressedSize_; + uint32_t compressedSize_; + js::Vector 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(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: // function f(x,y,z) { abc } // the range [beginOffset, endOffset) captures the source: @@ -841,9 +868,6 @@ class ModuleChars // For functions created with 'new Function', function arguments are // not present in the source so we must manually explicitly serialize // 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(); if (isFunCtor_) { unsigned numArgs; @@ -853,42 +877,65 @@ class ModuleChars return false; } } + return true; } size_t serializedSize() const { return sizeof(uint32_t) + - length_ * sizeof(jschar) + + sizeof(uint32_t) + + compressedSize_ + sizeof(uint32_t) + (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0); } uint8_t *serialize(uint8_t *cursor) const { - cursor = WriteScalar(cursor, length_); - cursor = WriteBytes(cursor, begin_, length_ * sizeof(jschar)); + cursor = WriteScalar(cursor, uncompressedSize_); + cursor = WriteScalar(cursor, compressedSize_); + cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_); cursor = WriteScalar(cursor, isFunCtor_); if (isFunCtor_) cursor = SerializeVector(cursor, funCtorArgs_); return cursor; } +}; +class ModuleCharsForLookup : ModuleChars +{ + js::Vector chars_; + + public: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) { - cursor = ReadScalar(cursor, &length_); - begin_ = reinterpret_cast(cursor); - cursor += length_ * sizeof(jschar); + uint32_t uncompressedSize; + cursor = ReadScalar(cursor, &uncompressedSize); + + uint32_t compressedSize; + cursor = ReadScalar(cursor, &compressedSize); + + if (!chars_.resize(uncompressedSize / sizeof(jschar))) + return nullptr; + + const char *source = reinterpret_cast(cursor); + char *dest = reinterpret_cast(chars_.begin()); + if (!LZ4::decompress(source, dest, uncompressedSize)) + return nullptr; + + cursor += compressedSize; + cursor = ReadScalar(cursor, &isFunCtor_); if (isFunCtor_) cursor = DeserializeVector(cx, cursor, &funCtorArgs_); + return cursor; } - bool matchUnparsedModule(AsmJSParser &parser) const { + bool match(AsmJSParser &parser) const { const jschar *parseBegin = parser.tokenStream.rawBase() + beginOffset(parser); const jschar *parseLimit = parser.tokenStream.rawLimit(); JS_ASSERT(parseLimit >= parseBegin); - if (uint32_t(parseLimit - parseBegin) < length_) + if (uint32_t(parseLimit - parseBegin) < chars_.length()) return false; - if (!PodEqual(begin_, parseBegin, length_)) + if (!PodEqual(chars_.begin(), parseBegin, chars_.length())) return false; if (isFunCtor_ != parser.pc->isFunctionConstructorBody()) return false; @@ -900,7 +947,7 @@ class ModuleChars // new Function('"use asm"; function f() {} return f') // from incorrectly matching // new Function('"use asm"; function f() {} return ff') - if (parseBegin + length_ != parseLimit) + if (parseBegin + chars_.length() != parseLimit) return false; unsigned numArgs; ParseNode *arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs); @@ -942,8 +989,8 @@ js::StoreAsmJSModuleInCache(AsmJSParser &parser, if (!machineId.extractCurrentState(cx)) return false; - ModuleChars moduleChars; - if (!moduleChars.initFromParsedModule(parser, module)) + ModuleCharsForStore moduleChars; + if (!moduleChars.init(parser, module)) return false; size_t serializedSize = machineId.serializedSize() + @@ -1021,9 +1068,9 @@ js::LookupAsmJSModuleInCache(ExclusiveContext *cx, if (machineId != cachedMachineId) return true; - ModuleChars moduleChars; + ModuleCharsForLookup moduleChars; cursor = moduleChars.deserialize(cx, cursor); - if (!moduleChars.matchUnparsedModule(parser)) + if (!moduleChars.match(parser)) return true; ScopedJSDeletePtr module( diff --git a/mfbt/Compression.h b/mfbt/Compression.h index 2b8a7a7d8ee2..592f6f457d89 100644 --- a/mfbt/Compression.h +++ b/mfbt/Compression.h @@ -34,7 +34,7 @@ public: * Compresses 'inputSize' bytes from 'source' into 'dest'. * Destination buffer must be already allocated, * 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 return the number of bytes written in buffer dest