diff --git a/bootstraptest/test_yjit.rb b/bootstraptest/test_yjit.rb index f19629ec0e4e85..e9ce905e2c7073 100644 --- a/bootstraptest/test_yjit.rb +++ b/bootstraptest/test_yjit.rb @@ -5535,3 +5535,23 @@ def compiled_method # Resume the fiber — compiled_method's iseq must still be valid fiber.resume.to_s } + +# regression test for register mapping of methods with over 256 locals +# [Bug #22074] +assert_equal "ok", %q{ + source = +"def many_locals\n" + source << " total = 0\n" + + 128.times do |i| + source << " y#{i} = 1\n" + source << " x#{i} = Object.new\n" + end + + source << " total += 1\n" + source << " raise total.inspect unless total == 1\n" + source << "end\n" + + eval(source) + many_locals + "ok" +} diff --git a/yjit/src/backend/ir.rs b/yjit/src/backend/ir.rs index 3fb67bc7cc1584..16a6ad49b357d4 100644 --- a/yjit/src/backend/ir.rs +++ b/yjit/src/backend/ir.rs @@ -242,7 +242,9 @@ impl Opnd let last_idx = stack_size as i32 + VM_ENV_DATA_SIZE as i32 - 1; assert!(last_idx <= idx, "Local index {} must be >= last local index {}", idx, last_idx); assert!(idx <= last_idx + num_locals as i32, "Local index {} must be < last local index {} + local size {}", idx, last_idx, num_locals); - RegOpnd::Local((last_idx + num_locals as i32 - idx) as u8) + // Indices that don't fit in u8 are capped to u8::MAX, which is greater than MAX_CTX_LOCALS. + let local_idx = last_idx + num_locals as i32 - idx; + RegOpnd::Local(local_idx.try_into().unwrap_or(u8::MAX)) } else { assert!(idx < stack_size as i32); RegOpnd::Stack((stack_size as i32 - idx - 1) as u8)