[PATCH v2 0/1] MR10921: Draft: vbscript: Fast-path interp_add and var_cmp for pure-integer operands.
For-loop counters and conditional comparisons hit VarAdd/VarCmp every iteration with trivial I2/I4 operands, but the full VARIANT conversion pipeline (VariantChangeType, locale grab/free in ucrtbase) dominates the cost. Short-circuit at the call site in interp.c when both operands are pure VT_I2/VT_I4: compare directly, or add via LONGLONG with native promotion (I2+I2 -> I4 on overflow, I4+I4 -> R8 on overflow). Alternative to !10528 Keeps the optimisation scoped to vbscript c ### Compared to !10528 (oleaut32 version) | Scenario | master | !10528 (oleaut32) | this MR (vbscript) | |---|---:|---:|---:| | Empty For (10M) | 2678 | 364 (7.4x) | **320 (8.2x)** | | If-condition in loop | 3678 | 718 (5.1x) | **632 (5.8x)** | | Local var reads | 1247 | 403 (3.1x) | **382 (3.25x)** | | Class prop read | 289 | 164 (1.8x) | 171 (1.73x) | | For R8 counter | 2910 | 2764 | 2625 | | String concat | 70 | 70 | 70 |allers rather than adding branches to oleaut32 generic VarAdd/VarCmp. -- v2: vbscript: Fast-path interp_add and var_cmp for pure-integer operands. https://gitlab.winehq.org/wine/wine/-/merge_requests/10921
From: Francis De Brabandere <francisdb@gmail.com> For-loop counters and conditional comparisons hit VarAdd/VarCmp every iteration with trivial I2/I4 operands, but the full VARIANT conversion pipeline (VariantChangeType, locale grab/free in ucrtbase) dominates the cost. Short-circuit at the call site in interp.c when both operands are pure VT_I2/VT_I4: compare directly, or add via LONGLONG with native promotion (I2+I2 -> I4 on overflow, I4+I4 -> R8 on overflow). Keeps the optimisation scoped to vbscript callers rather than adding branches to oleaut32 generic VarAdd/VarCmp. --- dlls/vbscript/interp.c | 70 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index ac2bbc64ce6..48e965fb134 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -1668,6 +1668,55 @@ static HRESULT interp_erase(exec_ctx_t *ctx) return hres; } +/* Pure-integer fast-paths for VarAdd/VarCmp. VBScript For-loop counters and + * conditional comparisons hit these every iteration; the full VARIANT pipeline + * (VariantChangeType, locale grab/free) dominates. Returns TRUE if the + * operands were both VT_I2/VT_I4 and the fast-path took care of the operation. + * Overflow promotion mirrors native: I2+I2 -> I4, I4+I4 -> R8. */ +static BOOL try_int_add(VARIANT *l, VARIANT *r, VARIANT *out) +{ + LONGLONG lv, rv, sum; + + if((V_VT(l) != VT_I2 && V_VT(l) != VT_I4) || + (V_VT(r) != VT_I2 && V_VT(r) != VT_I4)) + return FALSE; + + lv = V_VT(l) == VT_I2 ? V_I2(l) : V_I4(l); + rv = V_VT(r) == VT_I2 ? V_I2(r) : V_I4(r); + sum = lv + rv; + + if(V_VT(l) == VT_I2 && V_VT(r) == VT_I2) { + if(sum >= SHRT_MIN && sum <= SHRT_MAX) { + V_VT(out) = VT_I2; + V_I2(out) = sum; + }else { + V_VT(out) = VT_I4; + V_I4(out) = sum; + } + }else if(sum >= LONG_MIN && sum <= LONG_MAX) { + V_VT(out) = VT_I4; + V_I4(out) = sum; + }else { + V_VT(out) = VT_R8; + V_R8(out) = sum; + } + return TRUE; +} + +static BOOL try_int_cmp(VARIANT *l, VARIANT *r, HRESULT *out) +{ + LONG lv, rv; + + if((V_VT(l) != VT_I2 && V_VT(l) != VT_I4) || + (V_VT(r) != VT_I2 && V_VT(r) != VT_I4)) + return FALSE; + + lv = V_VT(l) == VT_I2 ? V_I2(l) : V_I4(l); + rv = V_VT(r) == VT_I2 ? V_I2(r) : V_I4(r); + *out = lv < rv ? VARCMP_LT : lv > rv ? VARCMP_GT : VARCMP_EQ; + return TRUE; +} + static HRESULT interp_step(exec_ctx_t *ctx) { const BSTR ident = ctx->instr->arg2.bstr; @@ -1693,7 +1742,8 @@ static HRESULT interp_step(exec_ctx_t *ctx) V_VT(&zero) = VT_I2; V_I2(&zero) = 0; - hres = VarCmp(stack_top(ctx, 0), &zero, ctx->script->lcid, 0); + if(!try_int_cmp(stack_top(ctx, 0), &zero, &hres)) + hres = VarCmp(stack_top(ctx, 0), &zero, ctx->script->lcid, 0); if(FAILED(hres)) goto loop_not_initialized; @@ -1708,7 +1758,8 @@ static HRESULT interp_step(exec_ctx_t *ctx) return E_FAIL; } - hres = VarCmp(ref.u.v, stack_top(ctx, 1), ctx->script->lcid, 0); + if(!try_int_cmp(ref.u.v, stack_top(ctx, 1), &hres)) + hres = VarCmp(ref.u.v, stack_top(ctx, 1), ctx->script->lcid, 0); if(FAILED(hres)) goto loop_not_initialized; @@ -2279,12 +2330,16 @@ static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r, unsigned flags) BOOL r_lit = flags & CMP_RIGHT_LITERAL; VARTYPE lvt = V_VT(l) & VT_TYPEMASK; VARTYPE rvt = V_VT(r) & VT_TYPEMASK; + HRESULT cmp; TRACE("%s %s\n", debugstr_variant(l), debugstr_variant(r)); if(is_unsupported_script_vt(lvt) || is_unsupported_script_vt(rvt)) return MAKE_VBSERROR(VBSE_INVALID_TYPELIB_VARIABLE); + if(try_int_cmp(l, r, &cmp)) + return cmp; + /* BSTR vs numeric LITERAL: coerce BSTR to a number, parse failure raises * type-mismatch (error 13). */ if((lvt == VT_BSTR && is_numeric_vt(rvt) && r_lit) || @@ -2600,7 +2655,8 @@ static HRESULT interp_add(exec_ctx_t *ctx) hres = stack_pop_val(ctx, &l); if(SUCCEEDED(hres)) { - hres = VarAdd(l.v, r.v, &v); + if(!try_int_add(l.v, r.v, &v)) + hres = VarAdd(l.v, r.v, &v); release_val(&l); } release_val(&r); @@ -2796,9 +2852,11 @@ static HRESULT interp_incc(exec_ctx_t *ctx) return E_FAIL; } - hres = VarAdd(stack_top(ctx, 0), ref.u.v, &v); - if(FAILED(hres)) - return hres; + if(!try_int_add(stack_top(ctx, 0), ref.u.v, &v)) { + hres = VarAdd(stack_top(ctx, 0), ref.u.v, &v); + if(FAILED(hres)) + return hres; + } VariantClear(ref.u.v); *ref.u.v = v; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10921
Inconclusive results, might pick this up later -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10921#note_140336
This merge request was closed by Francis De Brabandere. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10921
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)