Simplify the comparison opcodes in the bytecode engine, for a performance

improvement.

FossilOrigin-Name: f2af5868be83c65dbc593dafd4357fdb7d5d740128e9225dd1b2de16947012b3
This commit is contained in:
drh 2021-03-29 20:01:04 +00:00
commit 269b7d09bd
6 changed files with 85 additions and 95 deletions

View File

@ -1,5 +1,5 @@
C Alternative\simplementation\sof\sthe\scomparison\sopcode\sspeed-up\sof\ncheck-in\s[4a8805d9a66dc888]\sthat\sshould\spass\smuster\swith\sUBSAN.
D 2021-03-28T23:37:56.856
C Simplify\sthe\scomparison\sopcodes\sin\sthe\sbytecode\sengine,\sfor\sa\sperformance\nimprovement.
D 2021-03-29T20:01:04.041
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 030391f7a19e74e10ce9f2791f26158b34ee303c13993add67dff87501673d49
F src/expr.c 1a63403c5b06f762efc74f88ba59ea1ff58c9897bc74b36561ac4e259083e61e
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 c626ed23dedf2501e1cd97bc3e3e78dd95fc773c507827b0a0ba3cf14b008142
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 36624d3740a8d095eee061bcc5037deabddb88a53444ec1a956a8af7684efa43
R 66682903058dce39de324695f35d0b74
P afb18f64541effaeaada2d72c7c91adfe5ec3e2b1418c0bc281083125fb5badb ebe100de55ccdf6abccde5d8e6e96099b6e8dc3527f1441265e2b86b6661a66b
R 1674bcaa46be0ff1550b889637821f20
U drh
Z 8dbf744cc2e2ce2434fc7ddbd088d521
Z f8c6b001941ca13365c55ee27c658dc6

View File

@ -1 +1 @@
afb18f64541effaeaada2d72c7c91adfe5ec3e2b1418c0bc281083125fb5badb
f2af5868be83c65dbc593dafd4357fdb7d5d740128e9225dd1b2de16947012b3

View File

@ -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, &regFree1);
r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, &regFree2);
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
@ -4054,8 +4064,9 @@ expr_code_doover:
}else{
r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pLeft, pExpr->pRight, op,
r1, r2, inReg, SQLITE_STOREP2 | p5,
sqlite3VdbeAddOp2(v, OP_Integer, 1, inReg);
codeCompare(pParse, pLeft, pExpr->pRight, op, r1, r2,
sqlite3VdbeCurrentAddr(v)+2, p5,
ExprHasProperty(pExpr,EP_Commuted));
assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
@ -4063,6 +4074,11 @@ expr_code_doover:
assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
if( p5==SQLITE_NULLEQ ){
sqlite3VdbeAddOp2(v, OP_Integer, 0, inReg);
}else{
sqlite3VdbeAddOp3(v, OP_ZeroOrNull, r1, inReg, r2);
}
testcase( regFree1==0 );
testcase( regFree2==0 );
}

View File

@ -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 */

View File

@ -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,9 +1941,8 @@ 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.
*/
/* Opcode: Ne P1 P2 P3 P4 P5
** Synopsis: IF r[P3]!=r[P1]
@ -1952,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
@ -1985,6 +1978,9 @@ case OP_Cast: { /* in1 */
** numeric, then a numeric comparison is used. If the two values
** are of different types, then numbers are considered less than
** strings and strings are considered less than blobs.
**
** This opcode saves the result of comparison for use by the new
** OP_Jump opcode.
*/
/* Opcode: Le P1 P2 P3 P4 P5
** Synopsis: IF r[P3]<=r[P1]
@ -2044,17 +2040,10 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** then the result is always NULL.
** The jump is taken if the SQLITE_JUMPIFNULL bit is set.
*/
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
iCompare = 1; /* Operands are not equal */
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;
}
iCompare = 1; /* Operands are not equal */
VdbeBranchTaken(2,3);
if( pOp->p5 & SQLITE_JUMPIFNULL ){
goto jump_to_p2;
}
break;
}
@ -2118,6 +2107,7 @@ compare_op:
}else{
res2 = sqlite3aGTb[pOp->opcode];
}
iCompare = res;
/* Undo any changes made by applyAffinity() to the input registers. */
assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) );
@ -2125,67 +2115,39 @@ compare_op:
assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) );
pIn1->flags = flags1;
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
iCompare = res;
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;
}
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;
}
@ -2496,6 +2458,24 @@ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
break;
}
/* Opcode: ZeroOrNull P1 P2 P3 * *
** Synopsis: r[P2] = 0 OR NULL
**
** 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, in2, out2, in3 */
if( (aMem[pOp->p1].flags & MEM_Null)!=0
|| (aMem[pOp->p3].flags & MEM_Null)!=0
){
sqlite3VdbeMemSetNull(aMem + pOp->p2);
}else{
sqlite3VdbeMemSetInt64(aMem + pOp->p2, 0);
}
break;
}
/* Opcode: NotNull P1 P2 * * *
** Synopsis: if r[P1]!=NULL goto P2
**

View File

@ -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);
}
sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3);
zSynopsis = zAlt;
}
for(ii=0; (c = zSynopsis[ii])!=0; ii++){