mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 8.2.3442: Vim9: || and && are not handled at compile time
Problem: Vim9: || and && are not handled at compile time when possible. Solution: When using constants generate fewer instructions.
This commit is contained in:
@@ -1218,6 +1218,38 @@ def Test_disassemble_and_or()
|
|||||||
instr)
|
instr)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def AndConstant(arg: any): string
|
||||||
|
if true && arg
|
||||||
|
return "yes"
|
||||||
|
endif
|
||||||
|
if false && arg
|
||||||
|
return "never"
|
||||||
|
endif
|
||||||
|
return "no"
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_disassemble_and_constant()
|
||||||
|
assert_equal("yes", AndConstant(1))
|
||||||
|
assert_equal("no", AndConstant(false))
|
||||||
|
var instr = execute('disassemble AndConstant')
|
||||||
|
assert_match('AndConstant\_s*' ..
|
||||||
|
'if true && arg\_s*' ..
|
||||||
|
'0 LOAD arg\[-1\]\_s*' ..
|
||||||
|
'1 COND2BOOL\_s*' ..
|
||||||
|
'2 JUMP_IF_FALSE -> 5\_s*' ..
|
||||||
|
'return "yes"\_s*' ..
|
||||||
|
'3 PUSHS "yes"\_s*' ..
|
||||||
|
'4 RETURN\_s*' ..
|
||||||
|
'endif\_s*' ..
|
||||||
|
'if false && arg\_s*' ..
|
||||||
|
'return "never"\_s*' ..
|
||||||
|
'endif\_s*' ..
|
||||||
|
'return "no"\_s*' ..
|
||||||
|
'5 PUSHS "no"\_s*' ..
|
||||||
|
'6 RETURN',
|
||||||
|
instr)
|
||||||
|
enddef
|
||||||
|
|
||||||
def ForLoop(): list<number>
|
def ForLoop(): list<number>
|
||||||
var res: list<number>
|
var res: list<number>
|
||||||
for i in range(3)
|
for i in range(3)
|
||||||
@@ -1734,25 +1766,31 @@ def Test_disassemble_invert_bool()
|
|||||||
enddef
|
enddef
|
||||||
|
|
||||||
def ReturnBool(): bool
|
def ReturnBool(): bool
|
||||||
var name: bool = 1 && 0 || 1
|
var one = 1
|
||||||
|
var zero = 0
|
||||||
|
var name: bool = one && zero || one
|
||||||
return name
|
return name
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_disassemble_return_bool()
|
def Test_disassemble_return_bool()
|
||||||
var instr = execute('disassemble ReturnBool')
|
var instr = execute('disassemble ReturnBool')
|
||||||
assert_match('ReturnBool\_s*' ..
|
assert_match('ReturnBool\_s*' ..
|
||||||
'var name: bool = 1 && 0 || 1\_s*' ..
|
'var one = 1\_s*' ..
|
||||||
'0 PUSHNR 1\_s*' ..
|
'0 STORE 1 in $0\_s*' ..
|
||||||
'1 COND2BOOL\_s*' ..
|
'var zero = 0\_s*' ..
|
||||||
'2 JUMP_IF_COND_FALSE -> 5\_s*' ..
|
'1 STORE 0 in $1\_s*' ..
|
||||||
'3 PUSHNR 0\_s*' ..
|
'var name: bool = one && zero || one\_s*' ..
|
||||||
'4 COND2BOOL\_s*' ..
|
'2 LOAD $0\_s*' ..
|
||||||
'5 JUMP_IF_COND_TRUE -> 8\_s*' ..
|
'3 COND2BOOL\_s*' ..
|
||||||
'6 PUSHNR 1\_s*' ..
|
'4 JUMP_IF_COND_FALSE -> 7\_s*' ..
|
||||||
'7 COND2BOOL\_s*' ..
|
'5 LOAD $1\_s*' ..
|
||||||
'\d STORE $0\_s*' ..
|
'6 COND2BOOL\_s*' ..
|
||||||
|
'7 JUMP_IF_COND_TRUE -> 10\_s*' ..
|
||||||
|
'8 LOAD $0\_s*' ..
|
||||||
|
'9 COND2BOOL\_s*' ..
|
||||||
|
'10 STORE $2\_s*' ..
|
||||||
'return name\_s*' ..
|
'return name\_s*' ..
|
||||||
'\d\+ LOAD $0\_s*' ..
|
'\d\+ LOAD $2\_s*' ..
|
||||||
'\d\+ RETURN',
|
'\d\+ RETURN',
|
||||||
instr)
|
instr)
|
||||||
assert_equal(true, InvertBool())
|
assert_equal(true, InvertBool())
|
||||||
|
@@ -755,6 +755,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
3442,
|
||||||
/**/
|
/**/
|
||||||
3441,
|
3441,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -221,6 +221,7 @@ typedef struct {
|
|||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
JUMP_ALWAYS,
|
JUMP_ALWAYS,
|
||||||
|
JUMP_NEVER,
|
||||||
JUMP_IF_FALSE, // pop and jump if false
|
JUMP_IF_FALSE, // pop and jump if false
|
||||||
JUMP_AND_KEEP_IF_TRUE, // jump if top of stack is truthy, drop if not
|
JUMP_AND_KEEP_IF_TRUE, // jump if top of stack is truthy, drop if not
|
||||||
JUMP_AND_KEEP_IF_FALSE, // jump if top of stack is falsy, drop if not
|
JUMP_AND_KEEP_IF_FALSE, // jump if top of stack is falsy, drop if not
|
||||||
|
@@ -2847,7 +2847,7 @@ generate_ppconst(cctx_T *cctx, ppconst_T *ppconst)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check that the last item of "ppconst" is a bool.
|
* Check that the last item of "ppconst" is a bool, if there is an item.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
check_ppconst_bool(ppconst_T *ppconst)
|
check_ppconst_bool(ppconst_T *ppconst)
|
||||||
@@ -4845,7 +4845,8 @@ compile_expr7(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
if (cctx->ctx_skip != SKIP_YES
|
||||||
|
&& generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
r = compile_load(arg, p, cctx, TRUE, TRUE);
|
r = compile_load(arg, p, cctx, TRUE, TRUE);
|
||||||
}
|
}
|
||||||
@@ -5240,6 +5241,7 @@ compile_and_or(
|
|||||||
{
|
{
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
garray_T end_ga;
|
garray_T end_ga;
|
||||||
|
int save_skip = cctx->ctx_skip;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Repeat until there is no following "||" or "&&"
|
* Repeat until there is no following "||" or "&&"
|
||||||
@@ -5251,7 +5253,10 @@ compile_and_or(
|
|||||||
long save_sourcing_lnum;
|
long save_sourcing_lnum;
|
||||||
int start_ctx_lnum = cctx->ctx_lnum;
|
int start_ctx_lnum = cctx->ctx_lnum;
|
||||||
int save_lnum;
|
int save_lnum;
|
||||||
|
int const_used;
|
||||||
int status;
|
int status;
|
||||||
|
jumpwhen_T jump_when = opchar == '|'
|
||||||
|
? JUMP_IF_COND_TRUE : JUMP_IF_COND_FALSE;
|
||||||
|
|
||||||
if (next != NULL)
|
if (next != NULL)
|
||||||
{
|
{
|
||||||
@@ -5274,14 +5279,38 @@ compile_and_or(
|
|||||||
status = check_ppconst_bool(ppconst);
|
status = check_ppconst_bool(ppconst);
|
||||||
if (status != FAIL)
|
if (status != FAIL)
|
||||||
{
|
{
|
||||||
// TODO: use ppconst if the value is a constant
|
// Use the last ppconst if possible.
|
||||||
generate_ppconst(cctx, ppconst);
|
if (ppconst->pp_used > 0)
|
||||||
|
{
|
||||||
|
typval_T *tv = &ppconst->pp_tv[ppconst->pp_used - 1];
|
||||||
|
int is_true = tv2bool(tv);
|
||||||
|
|
||||||
// Every part must evaluate to a bool.
|
if ((is_true && opchar == '|')
|
||||||
status = bool_on_stack(cctx);
|
|| (!is_true && opchar == '&'))
|
||||||
if (status != FAIL)
|
{
|
||||||
status = ga_grow(&end_ga, 1);
|
// For "false && expr" and "true || expr" the "expr"
|
||||||
|
// does not need to be evaluated.
|
||||||
|
cctx->ctx_skip = SKIP_YES;
|
||||||
|
clear_tv(tv);
|
||||||
|
tv->v_type = VAR_BOOL;
|
||||||
|
tv->vval.v_number = is_true ? VVAL_TRUE : VVAL_FALSE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For "true && expr" and "false || expr" only "expr"
|
||||||
|
// needs to be evaluated.
|
||||||
|
--ppconst->pp_used;
|
||||||
|
jump_when = JUMP_NEVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Every part must evaluate to a bool.
|
||||||
|
status = bool_on_stack(cctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (status != FAIL)
|
||||||
|
status = ga_grow(&end_ga, 1);
|
||||||
cctx->ctx_lnum = save_lnum;
|
cctx->ctx_lnum = save_lnum;
|
||||||
if (status == FAIL)
|
if (status == FAIL)
|
||||||
{
|
{
|
||||||
@@ -5289,10 +5318,15 @@ compile_and_or(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*(((int *)end_ga.ga_data) + end_ga.ga_len) = instr->ga_len;
|
if (jump_when != JUMP_NEVER)
|
||||||
++end_ga.ga_len;
|
{
|
||||||
generate_JUMP(cctx, opchar == '|'
|
if (cctx->ctx_skip != SKIP_YES)
|
||||||
? JUMP_IF_COND_TRUE : JUMP_IF_COND_FALSE, 0);
|
{
|
||||||
|
*(((int *)end_ga.ga_data) + end_ga.ga_len) = instr->ga_len;
|
||||||
|
++end_ga.ga_len;
|
||||||
|
}
|
||||||
|
generate_JUMP(cctx, jump_when, 0);
|
||||||
|
}
|
||||||
|
|
||||||
// eval the next expression
|
// eval the next expression
|
||||||
SOURCING_LNUM = save_sourcing_lnum;
|
SOURCING_LNUM = save_sourcing_lnum;
|
||||||
@@ -5302,6 +5336,7 @@ compile_and_or(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const_used = ppconst->pp_used;
|
||||||
if ((opchar == '|' ? compile_expr3(arg, cctx, ppconst)
|
if ((opchar == '|' ? compile_expr3(arg, cctx, ppconst)
|
||||||
: compile_expr4(arg, cctx, ppconst)) == FAIL)
|
: compile_expr4(arg, cctx, ppconst)) == FAIL)
|
||||||
{
|
{
|
||||||
@@ -5309,6 +5344,20 @@ compile_and_or(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// "0 || 1" results in true, "1 && 0" results in false.
|
||||||
|
if (ppconst->pp_used == const_used + 1)
|
||||||
|
{
|
||||||
|
typval_T *tv = &ppconst->pp_tv[ppconst->pp_used - 1];
|
||||||
|
|
||||||
|
if (tv->v_type == VAR_NUMBER
|
||||||
|
&& (tv->vval.v_number == 1 || tv->vval.v_number == 0))
|
||||||
|
{
|
||||||
|
tv->vval.v_number = tv->vval.v_number == 1
|
||||||
|
? VVAL_TRUE : VVAL_FALSE;
|
||||||
|
tv->v_type = VAR_BOOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
p = may_peek_next_line(cctx, *arg, &next);
|
p = may_peek_next_line(cctx, *arg, &next);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5317,26 +5366,32 @@ compile_and_or(
|
|||||||
ga_clear(&end_ga);
|
ga_clear(&end_ga);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
generate_ppconst(cctx, ppconst);
|
|
||||||
|
|
||||||
// Every part must evaluate to a bool.
|
if (cctx->ctx_skip != SKIP_YES && ppconst->pp_used == 0)
|
||||||
if (bool_on_stack(cctx) == FAIL)
|
// Every part must evaluate to a bool.
|
||||||
|
if (bool_on_stack(cctx) == FAIL)
|
||||||
|
{
|
||||||
|
ga_clear(&end_ga);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_ga.ga_len > 0)
|
||||||
{
|
{
|
||||||
ga_clear(&end_ga);
|
// Fill in the end label in all jumps.
|
||||||
return FAIL;
|
generate_ppconst(cctx, ppconst);
|
||||||
}
|
while (end_ga.ga_len > 0)
|
||||||
|
{
|
||||||
|
isn_T *isn;
|
||||||
|
|
||||||
// Fill in the end label in all jumps.
|
--end_ga.ga_len;
|
||||||
while (end_ga.ga_len > 0)
|
isn = ((isn_T *)instr->ga_data)
|
||||||
{
|
|
||||||
isn_T *isn;
|
|
||||||
|
|
||||||
--end_ga.ga_len;
|
|
||||||
isn = ((isn_T *)instr->ga_data)
|
|
||||||
+ *(((int *)end_ga.ga_data) + end_ga.ga_len);
|
+ *(((int *)end_ga.ga_data) + end_ga.ga_len);
|
||||||
isn->isn_arg.jump.jump_where = instr->ga_len;
|
isn->isn_arg.jump.jump_where = instr->ga_len;
|
||||||
|
}
|
||||||
|
ga_clear(&end_ga);
|
||||||
}
|
}
|
||||||
ga_clear(&end_ga);
|
|
||||||
|
cctx->ctx_skip = save_skip;
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
@@ -5487,6 +5487,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
case JUMP_ALWAYS:
|
case JUMP_ALWAYS:
|
||||||
when = "JUMP";
|
when = "JUMP";
|
||||||
break;
|
break;
|
||||||
|
case JUMP_NEVER:
|
||||||
|
iemsg("JUMP_NEVER should not be used");
|
||||||
|
break;
|
||||||
case JUMP_AND_KEEP_IF_TRUE:
|
case JUMP_AND_KEEP_IF_TRUE:
|
||||||
when = "JUMP_AND_KEEP_IF_TRUE";
|
when = "JUMP_AND_KEEP_IF_TRUE";
|
||||||
break;
|
break;
|
||||||
|
Reference in New Issue
Block a user