Omit the SQLITE_STOREP2 and SQLITE_KEEPNULL options from the comparison
opcodes, allowing them to run faster. This required refactoring the vector comparison logic, which in turn required changing OP_ElseNotEq into OP_ElseEq. FossilOrigin-Name: 380b46054b6a9b67e57357815e8e94057253fa3cce838ae76e5d5031c6bd26b2
This commit is contained in:
parent
871e7ff43d
commit
4bc20452b5
18
manifest
18
manifest
@ -1,5 +1,5 @@
|
||||
C Add\sthe\sOP_ZeroOrNull\sopcode\sand\suse\sit\sto\scompute\sboolean\svalues\sfor\nscalar\scomparisons,\srather\sthan\sthe\sSQLITE_STOREP2\sparameter\sto\sthe\scomparison\nopcode.
|
||||
D 2021-03-29T14:40:48.732
|
||||
C Omit\sthe\sSQLITE_STOREP2\sand\sSQLITE_KEEPNULL\soptions\sfrom\sthe\scomparison\nopcodes,\sallowing\sthem\sto\srun\sfaster.\s\sThis\srequired\srefactoring\sthe\nvector\scomparison\slogic,\swhich\sin\sturn\srequired\schanging\sOP_ElseNotEq\sinto\nOP_ElseEq.
|
||||
D 2021-03-29T18:53:47.017
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
|
||||
@ -494,7 +494,7 @@ F src/date.c e0632f335952b32401482d099321bbf12716b29d6e72836b53ae49683ebae4bf
|
||||
F src/dbpage.c 8a01e865bf8bc6d7b1844b4314443a6436c07c3efe1d488ed89e81719047833a
|
||||
F src/dbstat.c 3aa79fc3aed7ce906e4ea6c10e85d657299e304f6049861fe300053ac57de36c
|
||||
F src/delete.c 73f57a9a183532c344a3135cf8f2a5589376e39183e0b5f562d6b61b2af0f4d8
|
||||
F src/expr.c 39f37546a861477c7272a4d2c84ce343fd2b105bbd5ef7890c66d01274d4a68f
|
||||
F src/expr.c a1403e4cf24866c72d106efec4964dab7e091ba41e74b45391de8feb9559e193
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c e9063648396c58778f77583a678342fe4a9bc82436bf23c5f9f444f2df0fdaa4
|
||||
F src/func.c 479f6929be027eb0210cbdde9d3529c012facf082d64a6b854a9415940761e5e
|
||||
@ -547,7 +547,7 @@ F src/shell.c.in dcce260883836c9b58847505fbccce8d5546af925046f7dacd9443e922ece03
|
||||
F src/sqlite.h.in 3426a080ea1f222a73e3bd91e7eacbd30570a0117c03d42c6dde606f33e5e318
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 61b38c073d5e1e96a3d45271b257aef27d0d13da2bea5347692ae579475cd95e
|
||||
F src/sqliteInt.h 1330dbc07b9d411a7502aba076f7fe17719457cd4dd24c164d956a06b4a4acb9
|
||||
F src/sqliteInt.h 109a1489293798c2aa84b4ee3cbe311994e6ecb9f1cd15e8ec3eec92655c8f67
|
||||
F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
|
||||
F src/status.c 4b8bc2a6905163a38b739854a35b826c737333fab5b1f8e03fa7eb9a4799c4c1
|
||||
F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
|
||||
@ -614,11 +614,11 @@ F src/upsert.c df8f1727d62b5987c4fd302cd4d7c0c84ae57cd65683c5a34a740dfe24039235
|
||||
F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
|
||||
F src/util.c 41c7a72da1df47864faa378a1c720b38adb288c6838cb6be5594511b6287a048
|
||||
F src/vacuum.c 492422c1463c076473bae1858799c7a0a5fe87a133d1223239447c422cd26286
|
||||
F src/vdbe.c 0defb9346e5fe5f275fef6dfd1ba9e012756469cb67ed22a6cdc2464ae0fd95a
|
||||
F src/vdbe.c c2990a6f877a15b2fd7cc8cb624033699a4a602740f5b9f576762861cc86d3ce
|
||||
F src/vdbe.h 25dabb25c7e157b84e59260cfb5b466c3ac103ede9f36f4db371332c47601abe
|
||||
F src/vdbeInt.h 000d9ab1ea4cb55a80de15e28f3f595645b4fddef34bca4347fb3db8031d9041
|
||||
F src/vdbeapi.c 4a43e303ec3354c785f453e881521969378e85628278ab74ba4a9df790c0d93b
|
||||
F src/vdbeaux.c 1b3eaa3a70d9d1877266e8ade0d0c3b2b4c6cf77d393d94dbcbd522b9bfefc15
|
||||
F src/vdbeaux.c 1ffaf15d48b1678dfd403c02ee60061c10a5dd447051860e793cd3393ab019fe
|
||||
F src/vdbeblob.c 253ed82894924c362a7fa3079551d3554cd1cdace39aa833da77d3bc67e7c1b1
|
||||
F src/vdbemem.c 947f2a65910edb4014dc981d33e414a68c51f169f9df8c4c493a0ba840b6eb1f
|
||||
F src/vdbesort.c f5b5e473a7cee44e47a94817b042fd7172cf3aa2c0a7928a8339d612bcfdec5a
|
||||
@ -1911,7 +1911,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P bd00df8f07b7163b0712590d2bb517e838a36c994dc47d7b39b5a07d14e6e6af
|
||||
R 724563e963bf6a8015327aa3621da692
|
||||
P 93781b6f10a94fb273204b95156a8b90e07071f28c89e7966c659a0f44f60e98
|
||||
R ed7fa1a1687fff75bb9781a6223d037a
|
||||
U drh
|
||||
Z c14837012fd78d1d74d6f076ec1e06a4
|
||||
Z 76d5f50926fa29a440f23aff574c758c
|
||||
|
@ -1 +1 @@
|
||||
93781b6f10a94fb273204b95156a8b90e07071f28c89e7966c659a0f44f60e98
|
||||
380b46054b6a9b67e57357815e8e94057253fa3cce838ae76e5d5031c6bd26b2
|
38
src/expr.c
38
src/expr.c
@ -611,6 +611,7 @@ static void codeVectorCompare(
|
||||
int regLeft = 0;
|
||||
int regRight = 0;
|
||||
u8 opx = op;
|
||||
int addrCmp = 0;
|
||||
int addrDone = sqlite3VdbeMakeLabel(pParse);
|
||||
int isCommuted = ExprHasProperty(pExpr,EP_Commuted);
|
||||
|
||||
@ -630,21 +631,24 @@ static void codeVectorCompare(
|
||||
assert( p5==0 || pExpr->op!=op );
|
||||
assert( p5==SQLITE_NULLEQ || pExpr->op==op );
|
||||
|
||||
p5 |= SQLITE_STOREP2;
|
||||
if( opx==TK_LE ) opx = TK_LT;
|
||||
if( opx==TK_GE ) opx = TK_GT;
|
||||
if( op==TK_LE ) opx = TK_LT;
|
||||
if( op==TK_GE ) opx = TK_GT;
|
||||
if( op==TK_NE ) opx = TK_EQ;
|
||||
|
||||
regLeft = exprCodeSubselect(pParse, pLeft);
|
||||
regRight = exprCodeSubselect(pParse, pRight);
|
||||
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 1, dest);
|
||||
for(i=0; 1 /*Loop exits by "break"*/; i++){
|
||||
int regFree1 = 0, regFree2 = 0;
|
||||
Expr *pL, *pR;
|
||||
int r1, r2;
|
||||
assert( i>=0 && i<nLeft );
|
||||
if( addrCmp ) sqlite3VdbeJumpHere(v, addrCmp);
|
||||
r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, ®Free1);
|
||||
r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, ®Free2);
|
||||
codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5, isCommuted);
|
||||
addrCmp = sqlite3VdbeCurrentAddr(v);
|
||||
codeCompare(pParse, pL, pR, opx, r1, r2, addrDone, p5, isCommuted);
|
||||
testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
|
||||
testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
|
||||
testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
|
||||
@ -653,26 +657,32 @@ static void codeVectorCompare(
|
||||
testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
|
||||
sqlite3ReleaseTempReg(pParse, regFree1);
|
||||
sqlite3ReleaseTempReg(pParse, regFree2);
|
||||
if( (opx==TK_LT || opx==TK_GT) && i<nLeft-1 ){
|
||||
addrCmp = sqlite3VdbeAddOp0(v, OP_ElseEq);
|
||||
testcase(opx==TK_LT); VdbeCoverageIf(v,opx==TK_LT);
|
||||
testcase(opx==TK_GT); VdbeCoverageIf(v,opx==TK_GT);
|
||||
}
|
||||
if( p5==SQLITE_NULLEQ ){
|
||||
sqlite3VdbeAddOp2(v, OP_Integer, 0, dest);
|
||||
}else{
|
||||
sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, dest, r2);
|
||||
}
|
||||
if( i==nLeft-1 ){
|
||||
break;
|
||||
}
|
||||
if( opx==TK_EQ ){
|
||||
sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v);
|
||||
p5 |= SQLITE_KEEPNULL;
|
||||
}else if( opx==TK_NE ){
|
||||
sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v);
|
||||
p5 |= SQLITE_KEEPNULL;
|
||||
sqlite3VdbeAddOp2(v, OP_NotNull, dest, addrDone); VdbeCoverage(v);
|
||||
}else{
|
||||
assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE );
|
||||
sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone);
|
||||
VdbeCoverageIf(v, op==TK_LT);
|
||||
VdbeCoverageIf(v, op==TK_GT);
|
||||
VdbeCoverageIf(v, op==TK_LE);
|
||||
VdbeCoverageIf(v, op==TK_GE);
|
||||
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrDone);
|
||||
if( i==nLeft-2 ) opx = op;
|
||||
}
|
||||
}
|
||||
sqlite3VdbeJumpHere(v, addrCmp);
|
||||
sqlite3VdbeResolveLabel(v, addrDone);
|
||||
if( op==TK_NE ){
|
||||
sqlite3VdbeAddOp2(v, OP_Not, dest, dest);
|
||||
}
|
||||
}
|
||||
|
||||
#if SQLITE_MAX_EXPR_DEPTH>0
|
||||
|
@ -2104,9 +2104,7 @@ struct CollSeq {
|
||||
** operator is NULL. It is added to certain comparison operators to
|
||||
** prove that the operands are always NOT NULL.
|
||||
*/
|
||||
#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */
|
||||
#define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */
|
||||
#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */
|
||||
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
|
||||
#define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */
|
||||
|
||||
|
76
src/vdbe.c
76
src/vdbe.c
@ -1915,8 +1915,7 @@ case OP_Cast: { /* in1 */
|
||||
** Synopsis: IF r[P3]==r[P1]
|
||||
**
|
||||
** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then
|
||||
** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then
|
||||
** store the result of comparison in register P2.
|
||||
** jump to address P2.
|
||||
**
|
||||
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
|
||||
** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
|
||||
@ -1942,10 +1941,6 @@ case OP_Cast: { /* in1 */
|
||||
** If neither operand is NULL the result is the same as it would be if
|
||||
** the SQLITE_NULLEQ flag were omitted from P5.
|
||||
**
|
||||
** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
|
||||
** content of r[P2] is only changed if the new value is NULL or 0 (false).
|
||||
** In other words, a prior r[P2] value will not be overwritten by 1 (true).
|
||||
**
|
||||
** This opcode saves the result of comparison for use by the new
|
||||
** OP_Jump opcode.
|
||||
*/
|
||||
@ -1955,17 +1950,12 @@ case OP_Cast: { /* in1 */
|
||||
** This works just like the Eq opcode except that the jump is taken if
|
||||
** the operands in registers P1 and P3 are not equal. See the Eq opcode for
|
||||
** additional information.
|
||||
**
|
||||
** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
|
||||
** content of r[P2] is only changed if the new value is NULL or 1 (true).
|
||||
** In other words, a prior r[P2] value will not be overwritten by 0 (false).
|
||||
*/
|
||||
/* Opcode: Lt P1 P2 P3 P4 P5
|
||||
** Synopsis: IF r[P3]<r[P1]
|
||||
**
|
||||
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
|
||||
** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5 store
|
||||
** the result of comparison (0 or 1 or NULL) into register P2.
|
||||
** jump to address P2.
|
||||
**
|
||||
** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
|
||||
** reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL
|
||||
@ -2051,17 +2041,10 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
|
||||
** The jump is taken if the SQLITE_JUMPIFNULL bit is set.
|
||||
*/
|
||||
iCompare = 1; /* Operands are not equal */
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
pOut = &aMem[pOp->p2];
|
||||
memAboutToChange(p, pOut);
|
||||
MemSetTypeFlag(pOut, MEM_Null);
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
}else{
|
||||
VdbeBranchTaken(2,3);
|
||||
if( pOp->p5 & SQLITE_JUMPIFNULL ){
|
||||
goto jump_to_p2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
@ -2132,66 +2115,39 @@ compare_op:
|
||||
assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) );
|
||||
pIn1->flags = flags1;
|
||||
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
pOut = &aMem[pOp->p2];
|
||||
if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
|
||||
/* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
|
||||
** and prevents OP_Ne from overwriting NULL with 0. This flag
|
||||
** is only used in contexts where either:
|
||||
** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0)
|
||||
** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1)
|
||||
** Therefore it is not necessary to check the content of r[P2] for
|
||||
** NULL. */
|
||||
assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq );
|
||||
assert( res2==0 || res2==1 );
|
||||
testcase( res2==0 && pOp->opcode==OP_Eq );
|
||||
testcase( res2==1 && pOp->opcode==OP_Eq );
|
||||
testcase( res2==0 && pOp->opcode==OP_Ne );
|
||||
testcase( res2==1 && pOp->opcode==OP_Ne );
|
||||
if( (pOp->opcode==OP_Eq)==res2 ) break;
|
||||
}
|
||||
memAboutToChange(p, pOut);
|
||||
MemSetTypeFlag(pOut, MEM_Int);
|
||||
pOut->u.i = res2;
|
||||
REGISTER_TRACE(pOp->p2, pOut);
|
||||
}else{
|
||||
VdbeBranchTaken(res2!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
|
||||
if( res2 ){
|
||||
goto jump_to_p2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Opcode: ElseNotEq * P2 * * *
|
||||
/* Opcode: ElseEq * P2 * * *
|
||||
**
|
||||
** This opcode must follow an OP_Lt or OP_Gt comparison operator. There
|
||||
** can be zero or more OP_ReleaseReg opcodes intervening, but no other
|
||||
** opcodes are allowed to occur between this instruction and the previous
|
||||
** OP_Lt or OP_Gt. Furthermore, the prior OP_Lt or OP_Gt must have the
|
||||
** SQLITE_STOREP2 bit set in the P5 field.
|
||||
** OP_Lt or OP_Gt.
|
||||
**
|
||||
** If result of an OP_Eq comparison on the same two operands as the
|
||||
** prior OP_Lt or OP_Gt would have been NULL or false (0), then then
|
||||
** jump to P2. If the result of an OP_Eq comparison on the two previous
|
||||
** operands would have been true (1), then fall through.
|
||||
** prior OP_Lt or OP_Gt would have been true, then jump to P2.
|
||||
** If the result of an OP_Eq comparison on the two previous
|
||||
** operands would have been false or NULL, then fall through.
|
||||
*/
|
||||
case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
|
||||
case OP_ElseEq: { /* same as TK_ESCAPE, jump */
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/* Verify the preconditions of this opcode - that it follows an OP_Lt or
|
||||
** OP_Gt with the SQLITE_STOREP2 flag set, with zero or more intervening
|
||||
** OP_ReleaseReg opcodes */
|
||||
** OP_Gt with zero or more intervening OP_ReleaseReg opcodes */
|
||||
int iAddr;
|
||||
for(iAddr = (int)(pOp - aOp) - 1; ALWAYS(iAddr>=0); iAddr--){
|
||||
if( aOp[iAddr].opcode==OP_ReleaseReg ) continue;
|
||||
assert( aOp[iAddr].opcode==OP_Lt || aOp[iAddr].opcode==OP_Gt );
|
||||
assert( aOp[iAddr].p5 & SQLITE_STOREP2 );
|
||||
break;
|
||||
}
|
||||
#endif /* SQLITE_DEBUG */
|
||||
VdbeBranchTaken(iCompare!=0, 2);
|
||||
if( iCompare!=0 ) goto jump_to_p2;
|
||||
VdbeBranchTaken(iCompare==0, 2);
|
||||
if( iCompare==0 ) goto jump_to_p2;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2503,13 +2459,13 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
|
||||
}
|
||||
|
||||
/* Opcode: ZeroOrNull P1 P2 P3 * *
|
||||
** Synopsis: r[P2] = (P1,P3 NOT NULL) ? 0 : NULL;
|
||||
** Synopsis: r[P2] = 0 OR NULL
|
||||
**
|
||||
** If both registers P1 and P3 are NOT NULL, then store a zero in
|
||||
** register P2. If either register P1 or register P3 or both contain
|
||||
** a NULL then store a NULL in register P2.
|
||||
** If all both registers P1 and P3 are NOT NULL, then store a zero in
|
||||
** register P2. If either registers P1 or P3 are NULL then put
|
||||
** a NULL in register P2.
|
||||
*/
|
||||
case OP_ZeroOrNull: { /* in1, out2, in3 */
|
||||
case OP_ZeroOrNull: { /* in1, in2, out2, in3 */
|
||||
if( (aMem[pOp->p1].flags & MEM_Null)!=0
|
||||
|| (aMem[pOp->p3].flags & MEM_Null)!=0
|
||||
){
|
||||
|
@ -1487,11 +1487,7 @@ char *sqlite3VdbeDisplayComment(
|
||||
char c;
|
||||
zSynopsis = zOpName += nOpName + 1;
|
||||
if( strncmp(zSynopsis,"IF ",3)==0 ){
|
||||
if( pOp->p5 & SQLITE_STOREP2 ){
|
||||
sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3);
|
||||
}else{
|
||||
sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3);
|
||||
}
|
||||
zSynopsis = zAlt;
|
||||
}
|
||||
for(ii=0; (c = zSynopsis[ii])!=0; ii++){
|
||||
|
Loading…
Reference in New Issue
Block a user