TDEF fuses TCLASS+METHOD+DEF for normal method definitions.
SDEF fuses SCLASS+METHOD+DEF for singleton method definitions.
Saves 4 bytes per method definition (8 bytes -> 4 bytes).
Falls back to unfused instructions if irep index exceeds 255.
Co-authored-by: Claude <noreply@anthropic.com>
Fuses MOVE+LOADI_0+GETIDX pattern into single instruction.
Saves 4 bytes per arr[0] access (7 bytes -> 3 bytes).
Co-authored-by: Claude <noreply@anthropic.com>
fuse MOVE+ADDI+MOVE and MOVE+SUBI+MOVE patterns into single instructions.
ADDILV/SUBILV add/subtract an immediate to a local variable in-place.
BBB format: a=local, b=working space for method call, c=immediate.
saves 5 bytes per instance (9->4 bytes), 40 occurrences in stdlib.
Co-authored-by: Claude <noreply@anthropic.com>
Change OP_MATCHERR from Z format (unconditional) to B format
(conditional on register). This allows fusing JMPIF + MATCHERR
sequence into a single MATCHERR instruction for simple patterns.
Before: JMPIF R2 target (4 bytes) + MATCHERR (1 byte) = 5 bytes
After: MATCHERR R2 (2 bytes)
Saves 3 bytes per pattern match with raise_on_fail.
Co-authored-by: Claude <noreply@anthropic.com>
Replace 4-instruction sequence (GETCONST + STRING + SEND + RAISEIF)
with single OP_MATCHERR instruction that raises NoMatchingPatternError
with "pattern not matched" message.
Bump RITE binary format version from 0300 to 0400 due to opcode
number shift.
Co-authored-by: Claude <noreply@anthropic.com>
- Add mrb_likely/mrb_unlikely macros to common.h for branch prediction
- Optimize OP_GETIDX array fast path:
- Cache RArray pointer to avoid repeated RARRAY() calls
- Single ARY_EMBED_P check instead of two (via RARRAY_LEN + RARRAY_PTR)
- Use unsigned comparison for bounds check
- Add branch prediction hints for common cases
- Convert switch statement to if-else chain for better branch prediction
Benchmark shows ~3% improvement for array read operations.
Co-authored-by: Claude <noreply@anthropic.com>
Add comprehensive benchmarks for measuring VM performance:
- vm_optimization_bench.rb: Ruby-level benchmarks covering dispatch,
arithmetic, method calls, array/hash access, loops, and recursion
- vm_dispatch_bench.c: C-level micro-benchmarks for precise measurement
These benchmarks are designed to measure the effect of potential VM
optimizations such as tail-call threading, register variables,
fused opcodes, and inline caching.
Usage:
# Ruby benchmark
./build/host/bin/mruby benchmark/vm_optimization_bench.rb
# C benchmark
cc -O2 -I include -I build/host/include \
benchmark/vm_dispatch_bench.c \
build/host/lib/libmruby.a -lm -o vm_dispatch_bench
./vm_dispatch_bench
Co-authored-by: Claude <noreply@anthropic.com>
Added error handling for file descriptors larger than FD_SETSIZE in mrb_hal_io_fdset_set and mrb_hal_io_fdset_isset functions, for posix hal.
I actually don't know how to fix this on windows, or if it needs fixing.
The purpose is to avoid using the `MRB_TT_CPTR` object.
The reasons are as follows:
- The `MRB_WORD_BOXING` setting involves object creation.
- If object creation fails, the `ary_set_t` data leaks memory.
expose the previously internal outer_class() function as a public API
for mrbgems to retrieve the enclosing class/module of a given class.
closes#6705.
Co-authored-by: Claude <noreply@anthropic.com>
wrap hash and eql callbacks with mrb_protect_error() to catch exceptions
during khash table rebuild. when an exception occurs (e.g., SystemStackError
from infinite recursion), return a safe default value and store the exception
in mrb->exc for later processing. this prevents memory leaks from orphaned
allocations when exceptions propagate through khash rebuild.
Co-authored-by: Claude <noreply@anthropic.com>
Function was only called once and contained just 2 lines of code.
Inlining directly reduces code size and improves clarity.
Co-authored-by: Claude <noreply@anthropic.com>
Add CPython-style parsing for base-10 string to integer conversion:
- Parse 9 digits at a time into decimal-base array
- Convert decimal-base to binary in single pass
- Use memory pool for temporary decimal buffer
- Use realloc for result buffer to reduce allocations
Also add digit_pairs lookup table for faster to_s output.
Performance: 2-5x faster for to_i, 60% fewer allocations.
Co-authored-by: Claude <noreply@anthropic.com>
- Add mrb_gc_protect() after arena_restore to prevent result from being collected before returning to caller
- Add comment to suspend_task_internal explaining why WAITING and DORMANT tasks can also be suspended
- Move argc/argv cast at the beginning of function with comment
This improves to_s performance for medium-sized bigints (40-50 limbs,
~800-1000 digits) by approximately 5x by enabling the divide-and-conquer
algorithm earlier.
Benchmark results:
40 limbs (772 digits): 88 us -> 18 us (5x faster)
50 limbs (964 digits): 134 us -> 25 us (5.4x faster)
Co-authored-by: Claude <noreply@anthropic.com>
Benchmarks show the previous threshold of 50 was too low, causing
Toom-3's setup overhead to outweigh its asymptotic benefits for
medium-sized numbers. Raising to 100 limbs provides:
- 2x faster at 300 limbs (192 -> 96 us)
- 2.5x faster at 120 limbs (42 -> 17 us)
- 2.6x faster at 80 limbs (31 -> 12 us)
Co-authored-by: Claude <noreply@anthropic.com>
When multiplying numbers where one is significantly larger than the other
(at least 2x size difference), split the larger number into chunks matching
the smaller number's size, multiply each chunk, and combine results. This
avoids pathological performance when Toom-3 pads asymmetric operands with
zeros.
Benchmarks show 6-16x speedup for size ratios from 10:1 to 40:1, with no
regression for symmetric cases.
Co-authored-by: Claude <noreply@anthropic.com>
mpz_init_heap() already allocates the requested size, so immediately
calling mpz_realloc() with the same size is a no-op. Remove these
redundant calls from mpz_and, mpz_or, mpz_xor, mpz_mod_2exp, and
mpz_abs.
Co-authored-by: Claude <noreply@anthropic.com>
When the output and input are the same variable, avoid unnecessary
heap allocations by modifying in place:
- mpz_neg: just flip the sign
- mpz_abs: just make sign positive
- ulshift: use mpn_lshift in-place (safe since it processes high-to-low)
Co-authored-by: Claude <noreply@anthropic.com>
Add mpz_sqr_toom3() that performs Toom-3 squaring with reduced memory
and computation:
- Only evaluates x (not y), reducing evaluation buffer from 6 to 3
- Uses recursive squaring instead of multiplication for all 5 products
- Simplifies interpolation since squared values are always positive
The specialized squaring uses the same Toom-3 structure but avoids
redundant computation when both operands are the same number.
Co-authored-by: Claude <noreply@anthropic.com>
The mpn_divexact_3 function used ap[i] in the borrow computation after
writing to rp[i]. When rp == ap (in-place operation), this read the
modified value instead of the original input, causing incorrect borrow
propagation.
This bug caused Toom-3 multiplication to produce wrong results for
certain input patterns where t6 - t5 had non-zero values followed by
zeros. The corrupted r3 coefficient then propagated errors to the final
result.
Fix by saving the original ap[i] value before writing rp[i].
Co-authored-by: Claude <noreply@anthropic.com>
move in-place optimization directly into mpz_sub() so callers
just use mpz_sub(ctx, x, x, y) and get automatic optimization.
remove separate mpz_sub_inplace() function.
Co-authored-by: Claude <noreply@anthropic.com>
when destination equals source in mpz_div_2exp(), use memmove
and mpn_rshift in-place instead of allocating a temporary.
reduces sqrt allocations by 49% since Newton iteration uses
in-place division by 2 on each iteration.
Co-authored-by: Claude <noreply@anthropic.com>
add usub_inplace() and mpz_sub_inplace() for allocation-free
subtraction when the minuend is larger than the subtrahend.
apply to Mersenne multiplication which reduces allocations by
17% and improves performance by 7-9% for small numbers.
Co-authored-by: Claude <noreply@anthropic.com>
skip two's complement conversion in mpz_and, mpz_or, mpz_xor when both
operands are positive. this avoids the per-limb make_2comp overhead and
provides up to 1.6x speedup for large bigints.
Co-authored-by: Claude <noreply@anthropic.com>
convert mpz_init_heap to mpz_init_temp for temporary quotient and
remainder variables in div_limb. these variables are now allocated
from the memory pool when possible, reducing heap allocation overhead.
the div_limb function already uses pool_save/pool_restore, so these
temporary variables are proper candidates for pool allocation.
Co-authored-by: Claude <noreply@anthropic.com>