Allocate the next page to be used in a lirbuf early to avoid running OOM during a page overflow (471316, r=danderson).
authorAndreas Gal <gal@mozilla.com>
Tue, 30 Dec 2008 17:03:43 -0800
changeset 22909 a65262a34346e3692666440bb0972942a9cec744
parent 22908 348227af256540cd3ff2a87ff2ef0750261aaee4
child 22910 a73c123918ba22f824a6f3efc1c98262f9999557
push id498
push userrsayre@mozilla.com
push dateFri, 23 Jan 2009 02:33:10 +0000
reviewersdanderson
bugs471316
milestone1.9.1b3pre
Allocate the next page to be used in a lirbuf early to avoid running OOM during a page overflow (471316, r=danderson).
js/src/nanojit/LIR.cpp
js/src/nanojit/LIR.h
js/src/trace-test.js
--- a/js/src/nanojit/LIR.cpp
+++ b/js/src/nanojit/LIR.cpp
@@ -107,23 +107,25 @@ namespace nanojit
 		_frago = 0;
 	}
 	
 	void LirBuffer::clear()
 	{
 		// free all the memory and clear the stats
 		_frago->pagesRelease(_pages);
 		NanoAssert(!_pages.size());
-		_thresholdPage = 0;
 		_unused = 0;
 		_stats.lir = 0;
 		_noMem = 0;
 		for (int i = 0; i < NumSavedRegs; ++i)
 			savedRegs[i] = NULL;
 		explicitSavedRegs = false;
+		// pre-allocate the next page we will be using
+		_nextPage = pageAlloc();
+		NanoAssert(_nextPage || _noMem);
 	}
 
 	int32_t LirBuffer::insCount() 
 	{
 		// doesn't include embedded constants nor LIR_skip payload
 		return _stats.lir;
 	}
 
@@ -147,34 +149,26 @@ namespace nanojit
 	{
 		return _unused;
 	}
 
 	void LirBufWriter::ensureRoom(uint32_t count)
 	{
 		LInsp before = _buf->next();
 		LInsp after = before+count+LIR_FAR_SLOTS;
-		if (!samepage(before,after+LirBuffer::LIR_BUF_THRESHOLD))
+		// transition to the next page?
+		if (!samepage(before,after))
 		{
-			if (!_buf->_thresholdPage)
-			{
-				// LIR_BUF_THRESHOLD away from a new page but pre-alloc it, setting noMem for early OOM detection
-				_buf->_thresholdPage = _buf->pageAlloc();
-				NanoAssert(_buf->_thresholdPage || _buf->_noMem);
-			}
-			// transition to the next page?
-			if (!samepage(before,after))
-			{
-				NanoAssert(_buf->_thresholdPage);
-				_buf->_unused = &_buf->_thresholdPage->lir[0];	
-				_buf->_thresholdPage = 0;  // pageAlloc() stored it in _pages already			
-
-				// link LIR stream back to prior instruction (careful insLink relies on _unused...)
-				insLinkTo(LIR_skip, before-1);
-			}
+			// we don't want this to fail, so we always have a page in reserve
+			NanoAssert(_buf->_nextPage);
+			_buf->_unused = &_buf->_nextPage->lir[0];	
+			// link LIR stream back to prior instruction (careful insLink relies on _unused...)
+			insLinkTo(LIR_skip, before-1);
+			_buf->_nextPage = _buf->pageAlloc();
+			NanoAssert(_buf->_nextPage || _buf->_noMem);
 		}
 	}
 
 	LInsp LirBufWriter::insLinkTo(LOpcode op, LInsp to)
 	{
 		LInsp l = _buf->next();
 		NanoAssert(samepage(l,l+LIR_FAR_SLOTS)); // must have called ensureRoom()
         if (can24bReach(l,to))
--- a/js/src/nanojit/LIR.h
+++ b/js/src/nanojit/LIR.h
@@ -684,18 +684,16 @@ namespace nanojit
 		LIns* insLoad(LOpcode v, LInsp b, LInsp d);
 		LIns* insCall(const CallInfo *call, LInsp args[]);
 		LIns* insGuard(LOpcode op, LInsp cond, LIns *x);
 	};
 
 	class LirBuffer : public avmplus::GCFinalizedObject
 	{
 		public:
-			static const uint32_t LIR_BUF_THRESHOLD = 1024/sizeof(LIns);  // 1KB prior to running out of space we'll allocate a new page
-
 			DWB(Fragmento*)		_frago;
 			LirBuffer(Fragmento* frago, const CallInfo* functions);
 			virtual ~LirBuffer();
 			void        clear();
 			LInsp		next();
 			bool		outOMem() { return _noMem != 0; }
 			
 			debug_only (void validate() const;)
@@ -719,17 +717,17 @@ namespace nanojit
 			
 		protected:
 			friend class LirBufWriter;
 
 			LInsp		commit(uint32_t count);
 			Page*		pageAlloc();
 
 			PageList	_pages;
-			Page*		_thresholdPage; // allocated in preperation of a needing to growing the buffer
+			Page*		_nextPage; // allocated in preperation of a needing to growing the buffer
 			LInsp		_unused;	// next unused instruction slot
 			int			_noMem;		// set if ran out of memory when writing to buffer
 	};	
 
 	class LirBufWriter : public LirWriter
 	{
 		DWB(LirBuffer*)	_buf;		// underlying buffer housing the instructions
         LInsp spref, rpref;
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -3840,16 +3840,52 @@ function testDoubleComparison()
   return "finished";
 }
 testDoubleComparison.expected = "finished";
 testDoubleComparison.jitstats = {
   sideExitIntoInterpreter: 1
 };
 test(testDoubleComparison);
 
+function testLirBufOOM()
+{
+    var a = [
+             "12345678901234",
+             "123456789012",
+             "1234567890123456789012345678",
+             "12345678901234567890123456789012345678901234567890123456",
+             "f",
+             "$",
+             "",
+             "f()",
+             "(\\*)",
+             "b()",
+             "()",
+             "(#)",
+             "ABCDEFGHIJK",
+             "ABCDEFGHIJKLM",
+             "ABCDEFGHIJKLMNOPQ",
+             "ABCDEFGH",
+             "(.)",
+             "(|)",
+             "()$",
+             "/()",
+             "(.)$"
+             ];
+    
+    for (var j = 0; j < 200; ++j) {
+        var js = "" + j;
+        for (var i = 0; i < a.length; i++)
+            "".match(a[i] + js)
+    }
+    return "ok";
+}
+testLirBufOOM.expected = "ok";
+test(testLirBufOOM);
+
 /*****************************************************************************
  *                                                                           *
  *  _____ _   _  _____ ______ _____ _______                                  *
  * |_   _| \ | |/ ____|  ____|  __ \__   __|                                 *
  *   | | |  \| | (___ | |__  | |__) | | |                                    *
  *   | | | . ` |\___ \|  __| |  _  /  | |                                    *
  *  _| |_| |\  |____) | |____| | \ \  | |                                    *
  * |_____|_| \_|_____/|______|_|  \_\ |_|                                    *