From: Francis De Brabandere <francisdb@gmail.com> Emit OP_step_local and OP_incc_local instead of OP_step and OP_incc when the loop counter is a known local variable or function argument. This eliminates runtime lookup_identifier() calls on every loop iteration. This mirrors jscript's approach of resolving locals at compile time. --- dlls/vbscript/compile.c | 52 ++++++++++++++++++++++++++++++---------- dlls/vbscript/interp.c | 52 ++++++++++++++++++++++++++++++++++++++++ dlls/vbscript/vbscript.h | 2 ++ 3 files changed, 93 insertions(+), 13 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 4ba0c9c4d33..c837ed0c905 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -917,8 +917,12 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st statement_ctx_t loop_ctx = {2}; unsigned step_instr, loop_start, instr; BSTR identifier; + int local_ref; + BOOL is_local; HRESULT hres; + is_local = bind_local(ctx, stat->identifier, &local_ref); + identifier = alloc_bstr_arg(ctx, stat->identifier); if(!identifier) return E_OUTOFMEMORY; @@ -930,11 +934,19 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st return E_OUTOFMEMORY; /* FIXME: Assign should happen after both expressions evaluation. */ - instr = push_instr(ctx, OP_assign_ident); - if(!instr) - return E_OUTOFMEMORY; - instr_ptr(ctx, instr)->arg1.bstr = identifier; - instr_ptr(ctx, instr)->arg2.uint = 0; + if(is_local) { + instr = push_instr(ctx, OP_assign_local); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.lng = local_ref; + instr_ptr(ctx, instr)->arg2.uint = 0; + }else { + instr = push_instr(ctx, OP_assign_ident); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.bstr = identifier; + instr_ptr(ctx, instr)->arg2.uint = 0; + } hres = compile_expression(ctx, stat->to_expr); if(FAILED(hres)) @@ -960,10 +972,17 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st if(!loop_ctx.for_end_label) return E_OUTOFMEMORY; - step_instr = push_instr(ctx, OP_step); - if(!step_instr) - return E_OUTOFMEMORY; - instr_ptr(ctx, step_instr)->arg2.bstr = identifier; + if(is_local) { + step_instr = push_instr(ctx, OP_step_local); + if(!step_instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, step_instr)->arg2.lng = local_ref; + }else { + step_instr = push_instr(ctx, OP_step); + if(!step_instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, step_instr)->arg2.bstr = identifier; + } instr_ptr(ctx, step_instr)->arg1.uint = loop_ctx.for_end_label; if(!emit_catch(ctx, 2)) @@ -976,10 +995,17 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st /* We need a separated OP_step here so that errors jump to the end-of-loop catch. */ ctx->loc = stat->stat.loc; - instr = push_instr(ctx, OP_incc); - if(!instr) - return E_OUTOFMEMORY; - instr_ptr(ctx, instr)->arg1.bstr = identifier; + if(is_local) { + instr = push_instr(ctx, OP_incc_local); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.lng = local_ref; + }else { + instr = push_instr(ctx, OP_incc); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.bstr = identifier; + } instr = push_instr(ctx, OP_step); if(!instr) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 5ac33888549..40219492796 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -1694,6 +1694,39 @@ static HRESULT interp_step(exec_ctx_t *ctx) return S_OK; } +static HRESULT interp_step_local(exec_ctx_t *ctx) +{ + const int ref = ctx->instr->arg2.lng; + VARIANT *v; + BOOL gteq_zero; + VARIANT zero; + HRESULT hres; + + TRACE("%d\n", ref); + + v = ref < 0 ? ctx->args - ref - 1 : ctx->vars + ref; + + V_VT(&zero) = VT_I2; + V_I2(&zero) = 0; + hres = VarCmp(stack_top(ctx, 0), &zero, ctx->script->lcid, 0); + if(FAILED(hres)) + return hres; + + gteq_zero = hres == VARCMP_GT || hres == VARCMP_EQ; + + hres = VarCmp(v, stack_top(ctx, 1), ctx->script->lcid, 0); + if(FAILED(hres)) + return hres; + + if(hres == VARCMP_EQ || hres == (gteq_zero ? VARCMP_LT : VARCMP_GT)) { + ctx->instr++; + }else { + stack_popn(ctx, 2); + instr_jmp(ctx, ctx->instr->arg1.uint); + } + return S_OK; +} + static HRESULT interp_newenum(exec_ctx_t *ctx) { variant_val_t v; @@ -2622,6 +2655,25 @@ static HRESULT interp_incc(exec_ctx_t *ctx) return S_OK; } +static HRESULT interp_incc_local(exec_ctx_t *ctx) +{ + const int ref = ctx->instr->arg1.lng; + VARIANT *lvar, v; + HRESULT hres; + + TRACE("%d\n", ref); + + lvar = ref < 0 ? ctx->args - ref - 1 : ctx->vars + ref; + + hres = VarAdd(stack_top(ctx, 0), lvar, &v); + if(FAILED(hres)) + return hres; + + VariantClear(lvar); + *lvar = v; + return S_OK; +} + static HRESULT interp_with(exec_ctx_t *ctx) { VARIANT *v; diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index b15cd3c8069..f1010b60b9e 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -269,6 +269,7 @@ typedef enum { X(idiv, 1, 0, 0) \ X(imp, 1, 0, 0) \ X(incc, 1, ARG_BSTR, 0) \ + X(incc_local, 1, ARG_INT, 0) \ X(int, 1, ARG_INT, 0) \ X(is, 1, 0, 0) \ X(local, 1, ARG_INT, 0) \ @@ -301,6 +302,7 @@ typedef enum { X(set_member, 1, ARG_BSTR, ARG_UINT) \ X(stack, 1, ARG_UINT, 0) \ X(step, 0, ARG_ADDR, ARG_BSTR) \ + X(step_local, 0, ARG_ADDR, ARG_INT) \ X(stop, 1, 0, 0) \ X(string, 1, ARG_STR, 0) \ X(sub, 1, 0, 0) \ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10515