diff options
| author | Max Bernstein <rubybugs@bernsteinbear.com> | 2025-10-27 18:53:28 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-27 22:53:28 +0000 |
| commit | 8d45e1f34e9a169987587d99a837b4ee035d7000 (patch) | |
| tree | 081f85ca1c075bfef97ad775c333b9af17413480 | |
| parent | d97fb3b424cebb812012a4e8a497a88510be9b72 (diff) | |
ZJIT: Fix internal compiler error looking up profiles for trace_getinstancevariable (#14969)
We treat getinstancevariable differently from other opcodes: it does not
look at the stack for its self operand, but instead looks at
`cfp->self`. In some cases, we might see the `trace_` variant in the
front-end, so make sure we treat that the same.
Example repro:
```
def test
@foo
end
28.times do
test
end
trace = TracePoint.trace(:call) do |tp|
puts tp.method_id
end
trace.enable do
30.times do
test
end
end
```
| -rw-r--r-- | zjit/src/hir.rs | 29 |
1 files changed, 28 insertions, 1 deletions
diff --git a/zjit/src/hir.rs b/zjit/src/hir.rs index 61014d5fd4..e948ee4452 100644 --- a/zjit/src/hir.rs +++ b/zjit/src/hir.rs @@ -4021,7 +4021,11 @@ pub fn iseq_to_hir(iseq: *const rb_iseq_t) -> Result<Function, ParseError> { .try_into() .unwrap(); - if opcode == YARVINSN_getinstancevariable { + // If TracePoint has been enabled after we have collected profiles, we'll see + // trace_getinstancevariable in the ISEQ. We have to treat it like getinstancevariable + // for profiling purposes: there is no operand on the stack to look up; we have + // profiled cfp->self. + if opcode == YARVINSN_getinstancevariable || opcode == YARVINSN_trace_getinstancevariable { profiles.profile_self(&exit_state, self_param); } else { profiles.profile_stack(&exit_state); @@ -7409,6 +7413,29 @@ mod tests { } #[test] + fn test_trace_getinstancevariable() { + eval(" + def test = @foo + test + trace = TracePoint.trace(:call) { |tp| } + trace.enable { test } + "); + assert_contains_opcode("test", YARVINSN_trace_getinstancevariable); + assert_snapshot!(hir_string("test"), @r" + fn test@<compiled>:2: + bb0(): + EntryPoint interpreter + v1:BasicObject = LoadSelf + Jump bb2(v1) + bb1(v4:BasicObject): + EntryPoint JIT(0) + Jump bb2(v4) + bb2(v6:BasicObject): + SideExit UnhandledYARVInsn(trace_getinstancevariable) + "); + } + + #[test] fn test_setinstancevariable() { eval(" def test = @foo = 1 |
