From: danielk1977 Date: Tue, 29 Mar 2005 08:26:13 +0000 (+0000) Subject: Reduce the space allocated for the runtime virtual machine stack. (CVS 2428) X-Git-Tag: version-3.6.10~3730 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=bc04f8529d15207a275c49611aca3e46f789cfe4;p=thirdparty%2Fsqlite.git Reduce the space allocated for the runtime virtual machine stack. (CVS 2428) FossilOrigin-Name: 7d6818da33a87076d1faf35ffc15a3aada0533b3 --- diff --git a/manifest b/manifest index 391954cbd2..c84a327933 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\sthe\sSQLITE_OMIT_TEMPDB\scompile\stime\smacro.\s(CVS\s2427) -D 2005-03-29T03:10:59 +C Reduce\sthe\sspace\sallocated\sfor\sthe\sruntime\svirtual\smachine\sstack.\s(CVS\s2428) +D 2005-03-29T08:26:13 F Makefile.in 5c00d0037104de2a50ac7647a5f12769795957a3 F Makefile.linux-gcc 06be33b2a9ad4f005a5f42b22c4a19dab3cbb5c7 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -19,7 +19,7 @@ F ltmain.sh f6b283068efa69f06eb8aa1fe4bddfdbdeb35826 F main.mk 3acc5ae52addd43fd1858fe99fca54a49bca6a56 F mkdll.sh 5ec23622515d5bf8969404e80cfb5e220ddf0512 F mkopcodec.awk bd46ad001c98dfbab07b1713cb8e692fa0e5415d -F mkopcodeh.awk e4d010870a3e98fd4323635c31565a3ca8584741 +F mkopcodeh.awk d3d6dfcef5a7b69398627aec581d1ec52561517d F mkso.sh 125868def279650a07d3f0f5e8476fecf99613fd F publish.sh ed0aba4ffdfadb36597d03ce8efdae96efc038cb F spec.template b2f6c4e488cbc3b993a57deba22cbc36203c4da3 @@ -75,11 +75,11 @@ F src/update.c 42823d00865c9fe4f01b3c62647858726345a28e F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c F src/util.c 02bc2750336b021b3f10e61538f665c4b0033b5d F src/vacuum.c 5cf598003191bd91c17a64742bad8e46241698a8 -F src/vdbe.c e93a28aaa2a59c398ae243c2dc3b2da10cf1c5d2 +F src/vdbe.c 68c8f34cbf7c05418c93e900d46f81b09500353a F src/vdbe.h 7f586cb6d6b57764e5aac1f87107d6a95ddce24c -F src/vdbeInt.h e80721cd8ff611789e20743eec43363a9fb5a48e +F src/vdbeInt.h 39beecdfb017d3e042fe60974691abd573a5c5a2 F src/vdbeapi.c 467caa6e6fb9247528b1c7ab9132ae1b4748e8ac -F src/vdbeaux.c 0932f570d276992c7b3ee989589b6ff9056f97e7 +F src/vdbeaux.c b28883d9efc9d4c1ec26c0d2473d5d8febc8178e F src/vdbemem.c 4e853ce3151eaf7906150da85a1b3ce1fe5e8da8 F src/where.c c4b227458e8993decb515ed9a2fe2d4f5f8e3125 F tclinstaller.tcl 046e3624671962dc50f0481d7c25b38ef803eb42 @@ -173,7 +173,7 @@ F test/pagesize.test 1b826d1608fd86d2303aa895b5586052ad07eba1 F test/pragma.test effcf2f0dea35223cbaa6bf40c47d0796ba39e40 F test/printf.test 92ba4c510b4fc61120ffa4a01820446ed917ae57 F test/progress.test 16496001da445e6534afb94562c286708316d82f x -F test/quick.test e9fdedf06bd06ff22fcbf6895c4f62fcbeb9e6b4 +F test/quick.test a94d12658a2b590c1a5be580bef09bbb04c1266b F test/quote.test 6d75cf635d93ba2484dc9cb378d88cbae9dc2c62 F test/reindex.test 38b138abe36bf9a08c791ed44d9f76cd6b97b78b F test/rollback.test 94cd981ee3a627d9f6466f69dcf1f7dbfe695d7a @@ -221,7 +221,7 @@ F tool/lemon.c c88936c67f6411608db8fa4254d254f509fa40f6 F tool/lempar.c e8b0eb00a6b905ce2ebd55965ed243574482cd5f F tool/memleak.awk 4e7690a51bf3ed757e611273d43fe3f65b510133 F tool/memleak2.awk 9cc20c8e8f3c675efac71ea0721ee6874a1566e8 -F tool/memleak3.tcl 2b1ab290badf3b26f9ba433baf7fad8def14aea8 +F tool/memleak3.tcl 009da0ea82dc5893edca76cf1a21fb7260e9412e F tool/mkkeywordhash.c 02ac5c523fd6d55364cd70aded5c36ba6651a6bf F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e x F tool/opcodeDoc.awk b3a2a3d5d3075b8bd90b7afe24283efdd586659c @@ -278,7 +278,7 @@ F www/tclsqlite.tcl e73f8f8e5f20e8277619433f7970060ab01088fc F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl 528299b8316726dbcc5548e9aa0648c8b1bd055b -P 78df3d040443414ae5b440eeabc0c8c205ab311d -R ab70e88fa1e907bd768a36809abb74cd +P c41d55443c2dd532147962b87f542fb7d37075fd +R d1e86050613dbac2624d4c64006d314d U danielk1977 -Z a20882d0dd8ba8be64666597fc85c766 +Z d987ab454820e190643b62f4a4339773 diff --git a/manifest.uuid b/manifest.uuid index e120abc78a..feb0e5397c 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c41d55443c2dd532147962b87f542fb7d37075fd \ No newline at end of file +7d6818da33a87076d1faf35ffc15a3aada0533b3 \ No newline at end of file diff --git a/mkopcodeh.awk b/mkopcodeh.awk index 641b987a81..d2c51dfa6b 100644 --- a/mkopcodeh.awk +++ b/mkopcodeh.awk @@ -24,6 +24,7 @@ # the total library smaller. # + # Remember the TK_ values from the parse.h file /^#define TK_/ { tk[$2] = $3 @@ -35,11 +36,16 @@ gsub(/:/,"",name) gsub("\r","",name) op[name] = -1 - for(i=3; imagic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE; assert( db->magic==SQLITE_MAGIC_BUSY ); @@ -533,6 +536,23 @@ int sqlite3VdbeExec( } #endif +#ifndef NDEBUG + /* This is to check that the return value of static function + ** opcodeUsesStack() (see vdbeaux.c) returns values that match the + ** implementation of the virtual machine in this file. If + ** opcodeUsesStack() returns non-zero, then the stack is guarenteed + ** not to grow when the opcode is executed. If it returns zero, then + ** the stack may grow by at most 1. + ** + ** The global wrapper function sqlite3VdbeOpcodeUsesStack() is not + ** available if NDEBUG is defined at build time. + */ + pStackLimit = pTos; + if( !sqlite3VdbeOpcodeUsesStack(pOp->opcode) ){ + pStackLimit++; + } +#endif + switch( pOp->opcode ){ /***************************************************************************** @@ -553,6 +573,11 @@ int sqlite3VdbeExec( ** case statement is followed by a comment of the form "/# same as ... #/" ** that comment is used to determine the particular value of the opcode. ** +** If a comment on the same line as the "case OP_" construction contains +** the word "stack", then the opcode is guarenteed not to grow the +** vdbe stack when it is executed. See function opcodeUsesStack() in +** vdbeaux.c for details. +** ** Documentation about VDBE opcodes is generated by scanning this file ** for lines of that contain "Opcode:". That line and all subsequent ** comment lines are used in the generation of the opcode.html documentation @@ -572,7 +597,7 @@ int sqlite3VdbeExec( ** the one at index P2 from the beginning of ** the program. */ -case OP_Goto: { +case OP_Goto: { /* no stack growth */ CHECK_FOR_INTERRUPT; pc = pOp->p2 - 1; break; @@ -588,7 +613,7 @@ case OP_Goto: { ** the return address stack will fill up and processing will abort ** with a fatal error. */ -case OP_Gosub: { +case OP_Gosub: { /* no stack growth */ assert( p->returnDepthreturnStack)/sizeof(p->returnStack[0]) ); p->returnStack[p->returnDepth++] = pc+1; pc = pOp->p2 - 1; @@ -601,7 +626,7 @@ case OP_Gosub: { ** OP_Gosub. If an OP_Return has occurred for all OP_Gosubs, then ** processing aborts with a fatal error. */ -case OP_Return: { +case OP_Return: { /* no stack growth */ assert( p->returnDepth>0 ); p->returnDepth--; pc = p->returnStack[p->returnDepth] - 1; @@ -625,7 +650,7 @@ case OP_Return: { ** every program. So a jump past the last instruction of the program ** is the same as executing Halt. */ -case OP_Halt: { +case OP_Halt: { /* no stack growth */ p->pTos = pTos; p->rc = pOp->p1; p->pc = pc; @@ -671,7 +696,7 @@ case OP_Integer: { ** ** The string value P3 is converted to a real and pushed on to the stack. */ -case OP_Real: { /* same as TK_FLOAT */ +case OP_Real: { /* same as TK_FLOAT, */ pTos++; pTos->flags = MEM_Str|MEM_Static|MEM_Term; pTos->z = pOp->p3; @@ -807,7 +832,7 @@ case OP_Variable: { ** ** P1 elements are popped off of the top of stack and discarded. */ -case OP_Pop: { +case OP_Pop: { /* no stack growth */ assert( pOp->p1>=0 ); popStack(&pTos, pOp->p1); assert( pTos>=&p->aStack[-1] ); @@ -850,7 +875,7 @@ case OP_Dup: { ** ** See also the Dup instruction. */ -case OP_Pull: { +case OP_Pull: { /* no stack growth */ Mem *pFrom = &pTos[-pOp->p1]; int i; Mem ts; @@ -882,7 +907,7 @@ case OP_Pull: { ** stack (P1==0 is the top of the stack) with the value ** of the top of the stack. Then pop the top of the stack. */ -case OP_Push: { +case OP_Push: { /* no stack growth */ Mem *pTo = &pTos[-pOp->p1]; assert( pTo>=p->aStack ); @@ -897,7 +922,7 @@ case OP_Push: { ** invoke the callback function using the newly formed array as the ** 3rd parameter. */ -case OP_Callback: { +case OP_Callback: { /* no stack growth */ int i; assert( p->nResColumn==pOp->p1 ); @@ -1033,11 +1058,11 @@ case OP_Concat: { /* same as TK_CONCAT */ ** function before the division. Division by zero returns NULL. ** If either operand is NULL, the result is NULL. */ -case OP_Add: /* same as TK_PLUS */ -case OP_Subtract: /* same as TK_MINUS */ -case OP_Multiply: /* same as TK_STAR */ -case OP_Divide: /* same as TK_SLASH */ -case OP_Remainder: { /* same as TK_REM */ +case OP_Add: /* same as TK_PLUS, no stack growth */ +case OP_Subtract: /* same as TK_MINUS, no stack growth */ +case OP_Multiply: /* same as TK_STAR, no stack growth */ +case OP_Divide: /* same as TK_SLASH, no stack growth */ +case OP_Remainder: { /* same as TK_REM, no stack growth */ Mem *pNos = &pTos[-1]; assert( pNos>=p->aStack ); if( ((pTos->flags | pNos->flags) & MEM_Null)!=0 ){ @@ -1117,7 +1142,7 @@ divide_by_zero: ** to retrieve the collation sequence set by this opcode is not available ** publicly, only to user functions defined in func.c. */ -case OP_CollSeq: { +case OP_CollSeq: { /* no stack growth */ assert( pOp->p3type==P3_COLLSEQ ); break; } @@ -1235,10 +1260,10 @@ case OP_Function: { ** right by N bits where N is the top element on the stack. ** If either operand is NULL, the result is NULL. */ -case OP_BitAnd: /* same as TK_BITAND */ -case OP_BitOr: /* same as TK_BITOR */ -case OP_ShiftLeft: /* same as TK_LSHIFT */ -case OP_ShiftRight: { /* same as TK_RSHIFT */ +case OP_BitAnd: /* same as TK_BITAND, no stack growth */ +case OP_BitOr: /* same as TK_BITOR, no stack growth */ +case OP_ShiftLeft: /* same as TK_LSHIFT, no stack growth */ +case OP_ShiftRight: { /* same as TK_RSHIFT, no stack growth */ Mem *pNos = &pTos[-1]; int a, b; @@ -1273,7 +1298,7 @@ case OP_ShiftRight: { /* same as TK_RSHIFT */ ** ** To force the top of the stack to be an integer, just add 0. */ -case OP_AddImm: { +case OP_AddImm: { /* no stack growth */ assert( pTos>=p->aStack ); Integerify(pTos); pTos->i += pOp->p1; @@ -1290,7 +1315,7 @@ case OP_AddImm: { ** current value if P1==0, or to the least integer that is strictly ** greater than its current value if P1==1. */ -case OP_ForceInt: { +case OP_ForceInt: { /* no stack growth */ int v; assert( pTos>=p->aStack ); applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc); @@ -1325,7 +1350,7 @@ case OP_ForceInt: { ** P1 is 1, then the stack is popped. In all other cases, the depth ** of the stack is unchanged. */ -case OP_MustBeInt: { +case OP_MustBeInt: { /* no stack growth */ assert( pTos>=p->aStack ); applyAffinity(pTos, SQLITE_AFF_INTEGER, db->enc); if( (pTos->flags & MEM_Int)==0 ){ @@ -1400,12 +1425,12 @@ case OP_MustBeInt: { ** the 2nd element down on the stack is greater than or equal to the ** top of the stack. See the Eq opcode for additional information. */ -case OP_Eq: /* same as TK_EQ */ -case OP_Ne: /* same as TK_NE */ -case OP_Lt: /* same as TK_LT */ -case OP_Le: /* same as TK_LE */ -case OP_Gt: /* same as TK_GT */ -case OP_Ge: { /* same as TK_GE */ +case OP_Eq: /* same as TK_EQ, no stack growth */ +case OP_Ne: /* same as TK_NE, no stack growth */ +case OP_Lt: /* same as TK_LT, no stack growth */ +case OP_Le: /* same as TK_LE, no stack growth */ +case OP_Gt: /* same as TK_GT, no stack growth */ +case OP_Ge: { /* same as TK_GE, no stack growth */ Mem *pNos; int flags; int res; @@ -1471,8 +1496,8 @@ case OP_Ge: { /* same as TK_GE */ ** two values and push the resulting boolean value back onto the ** stack. */ -case OP_And: /* same as TK_AND */ -case OP_Or: { /* same as TK_OR */ +case OP_And: /* same as TK_AND, no stack growth */ +case OP_Or: { /* same as TK_OR, no stack growth */ Mem *pNos = &pTos[-1]; int v1, v2; /* 0==TRUE, 1==FALSE, 2==UNKNOWN or NULL */ @@ -1519,7 +1544,7 @@ case OP_Or: { /* same as TK_OR */ ** with its absolute value. If the top of the stack is NULL ** its value is unchanged. */ -case OP_Negative: /* same as TK_UMINUS */ +case OP_Negative: /* same as TK_UMINUS, no stack growth */ case OP_AbsValue: { assert( pTos>=p->aStack ); if( pTos->flags & MEM_Real ){ @@ -1552,7 +1577,7 @@ case OP_AbsValue: { ** with its complement. If the top of the stack is NULL its value ** is unchanged. */ -case OP_Not: { /* same as TK_NOT */ +case OP_Not: { /* same as TK_NOT, no stack growth */ assert( pTos>=p->aStack ); if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */ Integerify(pTos); @@ -1568,7 +1593,7 @@ case OP_Not: { /* same as TK_NOT */ ** with its ones-complement. If the top of the stack is NULL its ** value is unchanged. */ -case OP_BitNot: { /* same as TK_BITNOT */ +case OP_BitNot: { /* same as TK_BITNOT, no stack growth */ assert( pTos>=p->aStack ); if( pTos->flags & MEM_Null ) break; /* Do nothing to NULLs */ Integerify(pTos); @@ -1583,7 +1608,7 @@ case OP_BitNot: { /* same as TK_BITNOT */ ** Do nothing. This instruction is often useful as a jump ** destination. */ -case OP_Noop: { +case OP_Noop: { /* no stack growth */ break; } @@ -1607,8 +1632,8 @@ case OP_Noop: { ** If the value popped of the stack is NULL, then take the jump if P1 ** is true and fall through if P1 is false. */ -case OP_If: -case OP_IfNot: { +case OP_If: /* no stack growth */ +case OP_IfNot: { /* no stack growth */ int c; assert( pTos>=p->aStack ); if( pTos->flags & MEM_Null ){ @@ -1629,7 +1654,7 @@ case OP_IfNot: { ** to P2. Pop the stack P1 times if P1>0. If P1<0 leave the stack ** unchanged. */ -case OP_IsNull: { /* same as TK_ISNULL */ +case OP_IsNull: { /* same as TK_ISNULL, no stack growth */ int i, cnt; Mem *pTerm; cnt = pOp->p1; @@ -1652,7 +1677,7 @@ case OP_IsNull: { /* same as TK_ISNULL */ ** stack if P1 times if P1 is greater than zero. If P1 is less than ** zero then leave the stack unchanged. */ -case OP_NotNull: { /* same as TK_NOTNULL */ +case OP_NotNull: { /* same as TK_NOTNULL, no stack growth */ int i, cnt; cnt = pOp->p1; if( cnt<0 ) cnt = -cnt; @@ -1673,7 +1698,7 @@ case OP_NotNull: { /* same as TK_NOTNULL */ ** If OP_KeyAsData is to be applied to cursor P1, it must be executed ** before this op-code. */ -case OP_SetNumColumns: { +case OP_SetNumColumns: { /* no stack growth */ Cursor *pC; assert( (pOp->p1)nCursor ); assert( p->apCsr[pOp->p1]!=0 ); @@ -2131,7 +2156,7 @@ case OP_MakeRecord: { ** database file has an index of 0 and the file used for temporary tables ** has an index of 1. */ -case OP_Statement: { +case OP_Statement: { /* no stack growth */ int i = pOp->p1; Btree *pBt; if( i>=0 && inDb && (pBt = db->aDb[i].pBt) && !(db->autoCommit) ){ @@ -2151,7 +2176,7 @@ case OP_Statement: { ** ** This instruction causes the VM to halt. */ -case OP_AutoCommit: { +case OP_AutoCommit: { /* no stack growth */ u8 i = pOp->p1; u8 rollback = pOp->p2; @@ -2212,7 +2237,7 @@ case OP_AutoCommit: { ** ** If P2 is zero, then a read-lock is obtained on the database file. */ -case OP_Transaction: { +case OP_Transaction: { /* no stack growth */ int i = pOp->p1; Btree *pBt; @@ -2275,7 +2300,7 @@ case OP_ReadCookie: { ** ** A transaction must be started before executing this opcode. */ -case OP_SetCookie: { +case OP_SetCookie: { /* no stack growth */ Db *pDb; assert( pOp->p2p1>=0 && pOp->p1nDb ); @@ -2311,7 +2336,7 @@ case OP_SetCookie: { ** to be executed (to establish a read lock) before this opcode is ** invoked. */ -case OP_VerifyCookie: { +case OP_VerifyCookie: { /* no stack growth */ int iMeta; Btree *pBt; assert( pOp->p1>=0 && pOp->p1nDb ); @@ -2371,8 +2396,8 @@ case OP_VerifyCookie: { ** ** See also OpenRead. */ -case OP_OpenRead: -case OP_OpenWrite: { +case OP_OpenRead: /* no stack growth */ +case OP_OpenWrite: { /* no stack growth */ int i = pOp->p1; int p2 = pOp->p2; int wrFlag; @@ -2460,7 +2485,7 @@ case OP_OpenWrite: { ** whereas "Temporary" in the context of CREATE TABLE means for the duration ** of the connection to the database. Same word; different meanings. */ -case OP_OpenTemp: { +case OP_OpenTemp: { /* no stack growth */ int i = pOp->p1; Cursor *pCx; assert( i>=0 ); @@ -2509,7 +2534,7 @@ case OP_OpenTemp: { ** A pseudo-table created by this opcode is useful for holding the ** NEW or OLD tables in a trigger. */ -case OP_OpenPseudo: { +case OP_OpenPseudo: { /* no stack growth */ int i = pOp->p1; Cursor *pCx; assert( i>=0 ); @@ -2527,7 +2552,7 @@ case OP_OpenPseudo: { ** Close a cursor previously opened as P1. If P1 is not ** currently open, this instruction is a no-op. */ -case OP_Close: { +case OP_Close: { /* no stack growth */ int i = pOp->p1; if( i>=0 && inCursor ){ sqlite3VdbeFreeCursor(p->apCsr[i]); @@ -2576,10 +2601,10 @@ case OP_Close: { ** ** See also: Found, NotFound, Distinct, MoveGt, MoveGe, MoveLt */ -case OP_MoveLt: -case OP_MoveLe: -case OP_MoveGe: -case OP_MoveGt: { +case OP_MoveLt: /* no stack growth */ +case OP_MoveLe: /* no stack growth */ +case OP_MoveGe: /* no stack growth */ +case OP_MoveGt: { /* no stack growth */ int i = pOp->p1; Cursor *pC; @@ -2688,9 +2713,9 @@ case OP_MoveGt: { ** ** See also: Distinct, Found, MoveTo, NotExists, IsUnique */ -case OP_Distinct: -case OP_NotFound: -case OP_Found: { +case OP_Distinct: /* no stack growth */ +case OP_NotFound: /* no stack growth */ +case OP_Found: { /* no stack growth */ int i = pOp->p1; int alreadyExists = 0; Cursor *pC; @@ -2739,7 +2764,7 @@ case OP_Found: { ** ** See also: Distinct, NotFound, NotExists, Found */ -case OP_IsUnique: { +case OP_IsUnique: { /* no stack growth */ int i = pOp->p1; Mem *pNos = &pTos[-1]; Cursor *pCx; @@ -2833,7 +2858,7 @@ case OP_IsUnique: { ** ** See also: Distinct, Found, MoveTo, NotFound, IsUnique */ -case OP_NotExists: { +case OP_NotExists: { /* no stack growth */ int i = pOp->p1; Cursor *pC; BtCursor *pCrsr; @@ -3036,8 +3061,8 @@ case OP_NewRecno: { ** ** P1 may not be a pseudo-table opened using the OpenPseudo opcode. */ -case OP_PutIntKey: -case OP_PutStrKey: { +case OP_PutIntKey: /* no stack growth */ +case OP_PutStrKey: { /* no stack growth */ Mem *pNos = &pTos[-1]; int i = pOp->p1; Cursor *pC; @@ -3131,7 +3156,7 @@ case OP_PutStrKey: { ** ** If P1 is a pseudo-table, then this instruction is a no-op. */ -case OP_Delete: { +case OP_Delete: { /* no stack growth */ int i = pOp->p1; Cursor *pC; assert( i>=0 && inCursor ); @@ -3155,7 +3180,7 @@ case OP_Delete: { ** change counter (returned by subsequent calls to sqlite3_changes()) ** before it is reset. This is used by trigger programs. */ -case OP_ResetCount: { +case OP_ResetCount: { /* no stack growth */ if( pOp->p1 ){ sqlite3VdbeSetChanges(db, p->nChange); } @@ -3170,7 +3195,7 @@ case OP_ResetCount: { ** data off of the key rather than the data. This is used for ** processing compound selects. */ -case OP_KeyAsData: { +case OP_KeyAsData: { /* no stack growth */ int i = pOp->p1; Cursor *pC; assert( i>=0 && inCursor ); @@ -3346,7 +3371,7 @@ case OP_FullKey: { ** that occur while the cursor is on the null row will always push ** a NULL onto the stack. */ -case OP_NullRow: { +case OP_NullRow: { /* no stack growth */ int i = pOp->p1; Cursor *pC; @@ -3366,7 +3391,7 @@ case OP_NullRow: { ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ -case OP_Last: { +case OP_Last: { /* no stack growth */ int i = pOp->p1; Cursor *pC; BtCursor *pCrsr; @@ -3397,7 +3422,7 @@ case OP_Last: { ** If P2 is 0 or if the table or index is not empty, fall through ** to the following instruction. */ -case OP_Rewind: { +case OP_Rewind: { /* no stack growth */ int i = pOp->p1; Cursor *pC; BtCursor *pCrsr; @@ -3437,8 +3462,8 @@ case OP_Rewind: { ** to the following instruction. But if the cursor backup was successful, ** jump immediately to P2. */ -case OP_Prev: -case OP_Next: { +case OP_Prev: /* no stack growth */ +case OP_Next: { /* no stack growth */ Cursor *pC; BtCursor *pCrsr; @@ -3479,7 +3504,7 @@ case OP_Next: { ** is rolled back. If P3 is not null, then it becomes part of the ** error message returned with the SQLITE_CONSTRAINT. */ -case OP_IdxPut: { +case OP_IdxPut: { /* no stack growth */ int i = pOp->p1; Cursor *pC; BtCursor *pCrsr; @@ -3531,7 +3556,7 @@ case OP_IdxPut: { ** The top of the stack is an index key built using the MakeIdxKey opcode. ** This opcode removes that entry from the index. */ -case OP_IdxDelete: { +case OP_IdxDelete: { /* no stack growth */ int i = pOp->p1; Cursor *pC; BtCursor *pCrsr; @@ -3633,9 +3658,9 @@ case OP_IdxRecno: { ** an epsilon prior to the comparison. This makes the opcode work ** like IdxLE. */ -case OP_IdxLT: -case OP_IdxGT: -case OP_IdxGE: { +case OP_IdxLT: /* no stack growth */ +case OP_IdxGT: /* no stack growth */ +case OP_IdxGE: { /* no stack growth */ int i= pOp->p1; BtCursor *pCrsr; Cursor *pC; @@ -3679,7 +3704,7 @@ case OP_IdxGE: { ** ** The index entry is always popped from the stack. */ -case OP_IdxIsNull: { +case OP_IdxIsNull: { /* no stack growth */ int i = pOp->p1; int k, n; const char *z; @@ -3753,7 +3778,7 @@ case OP_Destroy: { ** ** See also: Destroy */ -case OP_Clear: { +case OP_Clear: { /* no stack growth */ rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1); break; } @@ -3811,7 +3836,7 @@ case OP_CreateTable: { ** This opcode invokes the parser to create a new virtual machine, ** then runs the new virtual machine. It is thus a reentrant opcode. */ -case OP_ParseSchema: { +case OP_ParseSchema: { /* no stack growth */ char *zSql; int iDb = pOp->p1; const char *zMaster; @@ -3843,7 +3868,7 @@ case OP_ParseSchema: { ** is dropped in order to keep the internal representation of the ** schema consistent with what is on disk. */ -case OP_DropTable: { +case OP_DropTable: { /* no stack growth */ sqlite3UnlinkAndDeleteTable(db, pOp->p1, pOp->p3); break; } @@ -3855,7 +3880,7 @@ case OP_DropTable: { ** is dropped in order to keep the internal representation of the ** schema consistent with what is on disk. */ -case OP_DropIndex: { +case OP_DropIndex: { /* no stack growth */ sqlite3UnlinkAndDeleteIndex(db, pOp->p1, pOp->p3); break; } @@ -3867,7 +3892,7 @@ case OP_DropIndex: { ** is dropped in order to keep the internal representation of the ** schema consistent with what is on disk. */ -case OP_DropTrigger: { +case OP_DropTrigger: { /* no stack growth */ sqlite3UnlinkAndDeleteTrigger(db, pOp->p1, pOp->p3); break; } @@ -3932,7 +3957,7 @@ case OP_IntegrityCk: { ** Write the integer on the top of the stack ** into the temporary storage list. */ -case OP_ListWrite: { +case OP_ListWrite: { /* no stack growth */ Keylist *pKeylist; assert( pTos>=p->aStack ); pKeylist = p->pList; @@ -3956,7 +3981,7 @@ case OP_ListWrite: { ** ** Rewind the temporary buffer back to the beginning. */ -case OP_ListRewind: { +case OP_ListRewind: { /* no stack growth */ /* What this opcode codes, really, is reverse the order of the ** linked list of Keylist structures so that they are read out ** in the same order that they were read in. */ @@ -4003,7 +4028,7 @@ case OP_ListRead: { ** ** Reset the temporary storage buffer so that it holds nothing. */ -case OP_ListReset: { +case OP_ListReset: { /* no stack growth */ if( p->pList ){ sqlite3VdbeKeylistFree(p->pList); p->pList = 0; @@ -4018,7 +4043,7 @@ case OP_ListReset: { ** AggContextPop opcode. ** */ -case OP_AggContextPush: { +case OP_AggContextPush: { /* no stack growth */ p->pAgg++; assert( p->pAgg<&p->apAgg[p->nAgg] ); break; @@ -4029,7 +4054,7 @@ case OP_AggContextPush: { ** Restore the aggregator to the state it was in when AggContextPush ** was last called. Any data in the current aggregator is deleted. */ -case OP_AggContextPop: { +case OP_AggContextPop: { /* no stack growth */ p->pAgg--; assert( p->pAgg>=p->apAgg ); break; @@ -4043,7 +4068,7 @@ case OP_AggContextPop: { ** opcode. The context stores the last insert row id, the last statement change ** count, and the current statement change count. */ -case OP_ContextPush: { +case OP_ContextPush: { /* no stack growth */ int i = p->contextStackTop++; Context *pContext; @@ -4068,7 +4093,7 @@ case OP_ContextPush: { ** executed. The context stores the last insert row id, the last statement ** change count, and the current statement change count. */ -case OP_ContextPop: { +case OP_ContextPop: { /* no stack growth */ Context *pContext = &p->contextStack[--p->contextStackTop]; assert( p->contextStackTop>=0 ); db->lastRowid = pContext->lastRowid; @@ -4085,7 +4110,7 @@ case OP_ContextPop: { ** and put them on the sorter. The key and data should have been ** made using the MakeRecord opcode. */ -case OP_SortPut: { +case OP_SortPut: { /* no stack growth */ Mem *pNos = &pTos[-1]; Sorter *pSorter; assert( pNos>=p->aStack ); @@ -4109,7 +4134,7 @@ case OP_SortPut: { ** mergesort. The P3 argument is a pointer to a KeyInfo structure ** that describes the keys to be sorted. */ -case OP_Sort: { +case OP_Sort: { /* no stack growth */ int i; KeyInfo *pKeyInfo = (KeyInfo*)pOp->p3; Sorter *pElem; @@ -4171,7 +4196,7 @@ case OP_SortNext: { ** ** Remove any elements that remain on the sorter. */ -case OP_SortReset: { +case OP_SortReset: { /* no stack growth */ sqlite3VdbeSorterReset(p); break; } @@ -4186,7 +4211,7 @@ case OP_SortReset: { ** stack is popped once if P2 is 1. If P2 is zero, then ** the original data remains on the stack. */ -case OP_MemStore: { +case OP_MemStore: { /* no stack growth */ assert( pTos>=p->aStack ); assert( pOp->p1>=0 && pOp->p1nMem ); rc = sqlite3VdbeMemMove(&p->aMem[pOp->p1], pTos); @@ -4225,7 +4250,7 @@ case OP_MemLoad: { ** This instruction throws an error if the memory cell is not initially ** an integer. */ -case OP_MemMax: { +case OP_MemMax: { /* no stack growth */ int i = pOp->p1; Mem *pMem; assert( pTos>=p->aStack ); @@ -4249,7 +4274,7 @@ case OP_MemMax: { ** This instruction throws an error if the memory cell is not initially ** an integer. */ -case OP_MemIncr: { +case OP_MemIncr: { /* no stack growth */ int i = pOp->p1; Mem *pMem; assert( i>=0 && inMem ); @@ -4267,7 +4292,7 @@ case OP_MemIncr: { ** If the value of memory cell P1 is 1 or greater, jump to P2. This ** opcode assumes that memory cell P1 holds an integer value. */ -case OP_IfMemPos: { +case OP_IfMemPos: { /* no stack growth */ int i = pOp->p1; Mem *pMem; assert( i>=0 && inMem ); @@ -4289,7 +4314,7 @@ case OP_IfMemPos: { ** there is no GROUP BY expression). In this case it is illegal to invoke ** OP_AggFocus. */ -case OP_AggReset: { +case OP_AggReset: { /* no stack growth */ assert( !pOp->p3 || pOp->p3type==P3_KEYINFO ); if( pOp->p1 ){ rc = sqlite3VdbeAggReset(0, p->pAgg, (KeyInfo *)pOp->p3); @@ -4313,7 +4338,7 @@ case OP_AggReset: { ** The aggregate will operate out of aggregate column P2. ** P3 is a pointer to the FuncDef structure for the function. */ -case OP_AggInit: { +case OP_AggInit: { /* no stack growth */ int i = pOp->p2; assert( i>=0 && ipAgg->nMem ); p->pAgg->apFunc[i] = (FuncDef*)pOp->p3; @@ -4331,7 +4356,7 @@ case OP_AggInit: { ** Ideally, this index would be another parameter, but there are ** no free parameters left. The integer is popped from the stack. */ -case OP_AggFunc: { +case OP_AggFunc: { /* no stack growth */ int n = pOp->p2; int i; Mem *pMem, *pRec; @@ -4389,7 +4414,7 @@ case OP_AggFunc: { ** zero or more AggNext operations. You must not execute an AggFocus ** in between an AggNext and an AggReset. */ -case OP_AggFocus: { +case OP_AggFocus: { /* no stack growth */ char *zKey; int nKey; int res; @@ -4423,7 +4448,7 @@ case OP_AggFocus: { ** Move the top of the stack into the P2-th field of the current ** aggregate. String values are duplicated into new memory. */ -case OP_AggSet: { +case OP_AggSet: { /* no stack growth */ AggElem *pFocus; int i = pOp->p2; pFocus = p->pAgg->pCurrent; @@ -4488,7 +4513,7 @@ case OP_AggGet: { ** zero or more AggNext operations. You must not execute an AggFocus ** in between an AggNext and an AggReset. */ -case OP_AggNext: { +case OP_AggNext: { /* no stack growth */ int res; assert( rc==SQLITE_OK ); CHECK_FOR_INTERRUPT; @@ -4549,7 +4574,7 @@ case OP_AggNext: { ** machines to be created and run. It may not be called from within ** a transaction. */ -case OP_Vacuum: { +case OP_Vacuum: { /* no stack growth */ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; rc = sqlite3RunVacuum(&p->zErrMsg, db); if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; @@ -4565,7 +4590,7 @@ case OP_Vacuum: { ** If P1 is 0, then all SQL statements become expired. If P1 is non-zero, ** then only the currently executing statement is affected. */ -case OP_Expire: { +case OP_Expire: { /* no stack growth */ if( !pOp->p1 ){ sqlite3ExpirePreparedStatements(db); }else{ @@ -4592,6 +4617,9 @@ default: { *****************************************************************************/ } + /* Make sure the stack limit was not exceeded */ + assert( pTos<=pStackLimit ); + #ifdef VDBE_PROFILE { long long elapse = hwtime() - start; diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 42682d1e25..44bdb5ef08 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -406,6 +406,7 @@ int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*); void sqlite3VdbeMemRelease(Mem *p); #ifndef NDEBUG void sqlite3VdbeMemSanity(Mem*, u8); +int sqlite3VdbeOpcodeUsesStack(u8); #endif int sqlite3VdbeMemTranslate(Mem*, u8); void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf, int nBuf); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index d2c934063a..86a9144cf6 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -166,6 +166,47 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){ } } +/* +** Return non-zero if opcode 'op' is guarenteed not to push more values +** onto the VDBE stack than it pops off. +*/ +static int opcodeUsesStack(u8 op){ + /* The 10 STACK_MASK_n constants are defined in the automatically + ** generated header file opcodes.h. Each is a 16-bit bitmask, one + ** bit corresponding to each opcode implemented by the virtual + ** machine in vdbe.c. The bit is true if the word "stack" appears + ** in a comment on the same line as the "case OP_XXX:" in + ** sqlite3VdbeExec() in vdbe.c. + ** + ** If the bit is true, then the corresponding opcode is guarenteed not + ** to grow the stack when it is executed. Otherwise, it may grow the + ** stack by at most one entry. + ** + ** STACK_MASK_0 corresponds to opcodes 0 to 15. STACK_MASK_1 contains + ** one bit for opcodes 16 to 31, and so on. + ** + ** 16-bit bitmasks (rather than 32-bit) are specified in opcodes.h + ** because the file is generated by an awk program. Awk manipulates + ** all numbers as floating-point and we don't want to risk a rounding + ** error if someone builds with an awk that uses (for example) 32-bit + ** IEEE floats. + */ + static u32 masks[5] = { + STACK_MASK_0 + (STACK_MASK_1<<16), + STACK_MASK_2 + (STACK_MASK_3<<16), + STACK_MASK_4 + (STACK_MASK_5<<16), + STACK_MASK_6 + (STACK_MASK_7<<16), + STACK_MASK_8 + (STACK_MASK_9<<16) + }; + return (masks[op>>5] & (1<<(op&0x1F))); +} + +#ifndef NDEBUG +int sqlite3VdbeOpcodeUsesStack(u8 op){ + return opcodeUsesStack(op); +} +#endif + /* ** Loop through the program looking for P2 values that are negative. ** Each such value is a label. Resolve the label by setting the P2 @@ -176,22 +217,30 @@ void sqlite3VdbeResolveLabel(Vdbe *p, int x){ ** Variable *pMaxFuncArgs is set to the maximum value of any P1 argument ** to an OP_Function or P2 to an OP_AggFunc opcode. This is used by ** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array. +** +** The integer *pMaxStack is set to the maximum number of vdbe stack +** entries that static analysis reveals this program might need. */ -static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ +static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs, int *pMaxStack){ int i; - int nMax = 0; + int nMaxArgs = 0; + int nMaxStack = p->nOp; Op *pOp; int *aLabel = p->aLabel; - if( aLabel==0 ) return; for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ u8 opcode = pOp->opcode; /* Todo: Maybe OP_AggFunc should change to use P1 in the same - * way as OP_Function. */ + * way as OP_Function. + */ if( opcode==OP_Function ){ - if( pOp->p1>nMax ) nMax = pOp->p1; + if( pOp->p1>nMaxArgs ) nMaxArgs = pOp->p1; }else if( opcode==OP_AggFunc ){ - if( pOp->p2>nMax ) nMax = pOp->p2; + if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; + } + + if( opcodeUsesStack(opcode) ){ + nMaxStack--; } if( pOp->p2>=0 ) continue; @@ -200,7 +249,9 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ } sqliteFree(p->aLabel); p->aLabel = 0; - *pMaxFuncArgs = nMax; + + *pMaxFuncArgs = nMaxArgs; + *pMaxStack = nMaxStack; } /* @@ -641,12 +692,14 @@ void sqlite3VdbeMakeReady( */ if( p->aStack==0 ){ int nArg; /* Maximum number of args passed to a user function. */ - resolveP2Values(p, &nArg); + int nStack; /* Maximum number of stack entries required */ + resolveP2Values(p, &nArg, &nStack); resizeOpArray(p, p->nOp); assert( nVar>=0 ); - n = isExplain ? 10 : p->nOp; + assert( nStacknOp ); + nStack = isExplain ? 10 : nStack; p->aStack = sqliteMalloc( - n*sizeof(p->aStack[0]) /* aStack */ + nStack*sizeof(p->aStack[0]) /* aStack */ + nArg*sizeof(Mem*) /* apArg */ + nVar*sizeof(Mem) /* aVar */ + nVar*sizeof(char*) /* azVar */ @@ -655,7 +708,7 @@ void sqlite3VdbeMakeReady( + nAgg*sizeof(Agg) /* Aggregate contexts */ ); if( !sqlite3_malloc_failed ){ - p->aMem = &p->aStack[n]; + p->aMem = &p->aStack[nStack]; p->nMem = nMem; p->aVar = &p->aMem[nMem]; p->nVar = nVar; diff --git a/test/quick.test b/test/quick.test index 06ba9e540a..93db66bdbf 100644 --- a/test/quick.test +++ b/test/quick.test @@ -10,7 +10,7 @@ #*********************************************************************** # This file runs all tests. # -# $Id: quick.test,v 1.35 2005/03/29 03:11:00 danielk1977 Exp $ +# $Id: quick.test,v 1.36 2005/03/29 08:26:13 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -19,7 +19,6 @@ proc finish_test {} {} set ISQUICK 1 set EXCLUDE { - alter.test all.test btree2.test btree3.test diff --git a/tool/memleak3.tcl b/tool/memleak3.tcl index d3fde1ee06..2e3f43bc13 100644 --- a/tool/memleak3.tcl +++ b/tool/memleak3.tcl @@ -34,14 +34,46 @@ Example: $ ./testfixture ../sqlite/test/select1.test 2> memtrace.out $ tclsh $argv0 ?-r ? ./testfixture memtrace.out " -if { [llength $argv]!=2 && [llength $argv]!=4 } { - set prg [file tail $argv0] + + +proc usage {} { + set prg [file tail $::argv0] puts "Usage: $prg ?-r ? " puts "" - puts [string trim $doco] + puts [string trim $::doco] exit -1 } +proc shift {listvar} { + upvar $listvar l + set ret [lindex $l 0] + set l [lrange $l 1 end] + return $ret +} + +# Argument handling. The following vars are set: +# +# $exe - the name of the executable (i.e. "testfixture" or "./sqlite3") +# $memfile - the name of the file containing the trace output. +# $report_at - The malloc number to stop and report at. Or -1 to read +# all of $memfile. +# +set report_at -1 +while {[llength $argv]>2} { + set arg [shift argv] + switch -- $arg { + "-r" { + set report_at [shift argv] + } + default { + usage + } + } +} +if {[llength $argv]!=2} usage +set exe [lindex $argv 0] +set memfile [lindex $argv 1] + # If stack traces are enabled, the 'addr2line' program is called to # translate a binary stack address into a human-readable form. set addr2line addr2line @@ -69,17 +101,6 @@ set iPeak 0 ;# nMalloc when nPeak was set. # array unset memmap -# The executable program being analyzed. -if {[llength $argv]==2} { - set exe [lindex $argv 0] - set memfile [lindex $argv 1] - set report_at -1 -} else { - set exe [lindex $argv 2] - set memfile [lindex $argv 3] - set report_at [lindex $argv 1] -} - proc process_input {input_file array_name} { upvar $array_name mem set input [open $input_file]