Resolve identifiers to direct indices during compilation instead of doing runtime `wcsicmp` string scans in `lookup_identifier()`. This adds new opcodes that access local variables, function arguments, class properties, and loop variables by index, eliminating the linear string comparison overhead for the most common cases. **New opcodes:** - `OP_local` / `OP_assign_local` / `OP_set_local` - read/write local vars and args by index - `OP_local_prop` / `OP_assign_local_prop` / `OP_set_local_prop` - read/write class properties by index - `OP_step_local` / `OP_incc_local` - For-loop step/increment with indexed counter - `OP_enumnext_local` - For-Each iteration with indexed loop variable This mirrors jscript's `OP_local` / `bind_local()` optimization (`dlls/jscript/compile.c`) and addresses the FIXME in `lookup_identifier()` noting that class property access should be bound at compile time. Names that cannot be resolved at compile time (globals in Execute/ExecuteGlobal code, dynamic vars, host objects) continue to use the existing string-based path. The last commit also adds a fast path for SAFEARRAY iteration that bypasses the `IEnumVARIANT` COM vtable dispatch and copies elements directly from the array data, eliminating one intermediate `VariantCopy` per iteration. ### Local variable / argument / class property access (1M iterations, 10 reads per iteration) | Benchmark | Windows (VM) | Wine master | Wine (7 commits) | **Speedup** | vs Windows | |-----------|-------------|-------------|-------------------|---------|------------| | Local vars | 156 ms | 413 ms | 234 ms | **1.8x** | 1.5x slower | | Arguments | 171 ms | 357 ms | 231 ms | **1.5x** | 1.4x slower | | Class props | 265 ms | 393 ms | 226 ms | **1.7x** | 0.9x (faster!) | ### For-loop variants (10M iterations) | Benchmark | Windows (VM) | Wine master | Wine (7 commits) | **Speedup** | vs Windows | |-----------|-------------|-------------|-------------------|---------|------------| | Empty For | 117 ms | 370 ms | 270 ms | **1.4x** | 2.3x slower | | For + assign | 203 ms | 613 ms | 430 ms | **1.4x** | 2.1x slower | | Do-While manual | 437 ms | 646 ms | 513 ms | **1.3x** | 1.2x slower | | For Step 2 | 62 ms | 370 ms | 271 ms | **1.4x** | 4.4x slower | | Nested For | 125 ms | 387 ms | 268 ms | **1.4x** | 2.1x slower | | For local Sub | 109 ms | 294 ms | 251 ms | **1.2x** | 2.3x slower | | For R8 counter | 203 ms | 2,812 ms | 2,529 ms | **1.1x** | 12x slower | | For-Each array | 7 ms | 286 ms | 62 ms | **4.6x** | 8.9x slower | The remaining 4-13x gap vs Windows in For-loops is from `VarAdd`/`VarCmp` going through full `VariantChangeTypeEx` machinery (including locale alloc/free per call). A separate MR ([!10528](https://gitlab.winehq.org/wine/wine/-/merge_requests/10529)) adds I2/I4 fast paths in oleaut32 that bring integer loops within 1.2-1.9x of Windows when combined with this branch. -- v18: https://gitlab.winehq.org/wine/wine/-/merge_requests/10515