To: vim_dev@googlegroups.com Subject: Patch 8.2.3442 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3442 Problem: Vim9: || and && are not handled at compile time when possible. Solution: When using constants generate fewer instructions. Files: src/vim9.h, src/vim9compile.c, src/vim9execute.c, src/testdir/test_vim9_disassemble.vim *** ../vim-8.2.3441/src/vim9.h 2021-09-13 18:25:50.456525311 +0200 --- src/vim9.h 2021-09-15 19:55:46.370902701 +0200 *************** *** 221,226 **** --- 221,227 ---- typedef enum { JUMP_ALWAYS, + JUMP_NEVER, 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_FALSE, // jump if top of stack is falsy, drop if not *** ../vim-8.2.3441/src/vim9compile.c 2021-09-13 18:25:50.456525311 +0200 --- src/vim9compile.c 2021-09-16 11:44:01.352095465 +0200 *************** *** 2847,2853 **** } /* ! * Check that the last item of "ppconst" is a bool. */ static int check_ppconst_bool(ppconst_T *ppconst) --- 2847,2853 ---- } /* ! * Check that the last item of "ppconst" is a bool, if there is an item. */ static int check_ppconst_bool(ppconst_T *ppconst) *************** *** 4845,4851 **** } else { ! if (generate_ppconst(cctx, ppconst) == FAIL) return FAIL; r = compile_load(arg, p, cctx, TRUE, TRUE); } --- 4845,4852 ---- } else { ! if (cctx->ctx_skip != SKIP_YES ! && generate_ppconst(cctx, ppconst) == FAIL) return FAIL; r = compile_load(arg, p, cctx, TRUE, TRUE); } *************** *** 5240,5245 **** --- 5241,5247 ---- { garray_T *instr = &cctx->ctx_instr; garray_T end_ga; + int save_skip = cctx->ctx_skip; /* * Repeat until there is no following "||" or "&&" *************** *** 5251,5257 **** --- 5253,5262 ---- long save_sourcing_lnum; int start_ctx_lnum = cctx->ctx_lnum; int save_lnum; + int const_used; int status; + jumpwhen_T jump_when = opchar == '|' + ? JUMP_IF_COND_TRUE : JUMP_IF_COND_FALSE; if (next != NULL) { *************** *** 5274,5287 **** status = check_ppconst_bool(ppconst); if (status != FAIL) { ! // TODO: use ppconst if the value is a constant ! generate_ppconst(cctx, ppconst); ! // 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; if (status == FAIL) { --- 5279,5316 ---- status = check_ppconst_bool(ppconst); if (status != FAIL) { ! // Use the last ppconst if possible. ! if (ppconst->pp_used > 0) ! { ! typval_T *tv = &ppconst->pp_tv[ppconst->pp_used - 1]; ! int is_true = tv2bool(tv); ! if ((is_true && opchar == '|') ! || (!is_true && opchar == '&')) ! { ! // 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; if (status == FAIL) { *************** *** 5289,5298 **** return FAIL; } ! *(((int *)end_ga.ga_data) + end_ga.ga_len) = instr->ga_len; ! ++end_ga.ga_len; ! generate_JUMP(cctx, opchar == '|' ! ? JUMP_IF_COND_TRUE : JUMP_IF_COND_FALSE, 0); // eval the next expression SOURCING_LNUM = save_sourcing_lnum; --- 5318,5332 ---- return FAIL; } ! if (jump_when != JUMP_NEVER) ! { ! if (cctx->ctx_skip != SKIP_YES) ! { ! *(((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 SOURCING_LNUM = save_sourcing_lnum; *************** *** 5302,5307 **** --- 5336,5342 ---- return FAIL; } + const_used = ppconst->pp_used; if ((opchar == '|' ? compile_expr3(arg, cctx, ppconst) : compile_expr4(arg, cctx, ppconst)) == FAIL) { *************** *** 5309,5314 **** --- 5344,5363 ---- 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); } *************** *** 5317,5342 **** ga_clear(&end_ga); return FAIL; } - generate_ppconst(cctx, ppconst); ! // Every part must evaluate to a bool. ! if (bool_on_stack(cctx) == FAIL) ! { ! ga_clear(&end_ga); ! return FAIL; ! } ! // Fill in the end label in all jumps. ! while (end_ga.ga_len > 0) { ! isn_T *isn; ! --end_ga.ga_len; ! isn = ((isn_T *)instr->ga_data) + *(((int *)end_ga.ga_data) + end_ga.ga_len); ! isn->isn_arg.jump.jump_where = instr->ga_len; } ! ga_clear(&end_ga); } return OK; --- 5366,5397 ---- ga_clear(&end_ga); return FAIL; } ! if (cctx->ctx_skip != SKIP_YES && ppconst->pp_used == 0) ! // 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) { ! // Fill in the end label in all jumps. ! generate_ppconst(cctx, ppconst); ! while (end_ga.ga_len > 0) ! { ! isn_T *isn; ! --end_ga.ga_len; ! isn = ((isn_T *)instr->ga_data) + *(((int *)end_ga.ga_data) + end_ga.ga_len); ! isn->isn_arg.jump.jump_where = instr->ga_len; ! } ! ga_clear(&end_ga); } ! ! cctx->ctx_skip = save_skip; } return OK; *** ../vim-8.2.3441/src/vim9execute.c 2021-09-13 18:25:50.456525311 +0200 --- src/vim9execute.c 2021-09-15 20:02:39.930491080 +0200 *************** *** 5487,5492 **** --- 5487,5495 ---- case JUMP_ALWAYS: when = "JUMP"; break; + case JUMP_NEVER: + iemsg("JUMP_NEVER should not be used"); + break; case JUMP_AND_KEEP_IF_TRUE: when = "JUMP_AND_KEEP_IF_TRUE"; break; *** ../vim-8.2.3441/src/testdir/test_vim9_disassemble.vim 2021-09-13 18:25:50.456525311 +0200 --- src/testdir/test_vim9_disassemble.vim 2021-09-15 22:32:30.847957856 +0200 *************** *** 1218,1223 **** --- 1218,1255 ---- instr) 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 var res: list for i in range(3) *************** *** 1734,1758 **** enddef def ReturnBool(): bool ! var name: bool = 1 && 0 || 1 return name enddef def Test_disassemble_return_bool() var instr = execute('disassemble ReturnBool') assert_match('ReturnBool\_s*' .. ! 'var name: bool = 1 && 0 || 1\_s*' .. ! '0 PUSHNR 1\_s*' .. ! '1 COND2BOOL\_s*' .. ! '2 JUMP_IF_COND_FALSE -> 5\_s*' .. ! '3 PUSHNR 0\_s*' .. ! '4 COND2BOOL\_s*' .. ! '5 JUMP_IF_COND_TRUE -> 8\_s*' .. ! '6 PUSHNR 1\_s*' .. ! '7 COND2BOOL\_s*' .. ! '\d STORE $0\_s*' .. 'return name\_s*' .. ! '\d\+ LOAD $0\_s*' .. '\d\+ RETURN', instr) assert_equal(true, InvertBool()) --- 1766,1796 ---- enddef def ReturnBool(): bool ! var one = 1 ! var zero = 0 ! var name: bool = one && zero || one return name enddef def Test_disassemble_return_bool() var instr = execute('disassemble ReturnBool') assert_match('ReturnBool\_s*' .. ! 'var one = 1\_s*' .. ! '0 STORE 1 in $0\_s*' .. ! 'var zero = 0\_s*' .. ! '1 STORE 0 in $1\_s*' .. ! 'var name: bool = one && zero || one\_s*' .. ! '2 LOAD $0\_s*' .. ! '3 COND2BOOL\_s*' .. ! '4 JUMP_IF_COND_FALSE -> 7\_s*' .. ! '5 LOAD $1\_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*' .. ! '\d\+ LOAD $2\_s*' .. '\d\+ RETURN', instr) assert_equal(true, InvertBool()) *** ../vim-8.2.3441/src/version.c 2021-09-15 12:53:14.029281661 +0200 --- src/version.c 2021-09-16 16:14:06.588318608 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3442, /**/ -- The History of every major Galactic Civilization tends to pass through three distinct and recognizable phases, those of Survival, Inquiry and Sophistication, otherwise known as the How, Why and Where phases. For instance, the first phase is characterized by the question 'How can we eat?' the second by the question 'Why do we eat?' and the third by the question 'Where shall we have lunch?' -- Douglas Adams, "The Hitchhiker's Guide to the Galaxy" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///