Skip to content
  • Alan Wu's avatar
    cdcabd8a
    Backport 3.3: YJIT memory leak fix with additional CI fixes (#9841) · cdcabd8a
    Alan Wu authored
    merge revision(s) 2cc7a56e,b0711b1c,db5d9429: [Backport #20209]
    
    	YJIT: Avoid leaks by skipping objects with a singleton class
    
    	For receiver with a singleton class, there are multiple vectors YJIT can
    	end up retaining the object. There is a path in jit_guard_known_klass()
    	that bakes the receiver into the code, and the object could also be kept
    	alive indirectly through a path starting at the CME object baked into
    	the code.
    
    	To avoid these leaks, avoid compiling calls on objects with a singleton
    	class.
    
    	See: https://github.com/Shopify/ruby/issues/552
    
    	[Bug #20209]
    	---
    	 yjit/bindgen/src/main.rs       |  1 +
    	 yjit/src/codegen.rs            | 17 +++++++++++++++++
    	 yjit/src/cruby_bindings.inc.rs |  1 +
    	 yjit/src/stats.rs              |  2 ++
    	 4 files changed, 21 insertions(+)
    
    	YJIT: Fix tailcall and JIT entry eating up FINISH frames (#9729)
    
    	Suppose YJIT runs a rb_vm_opt_send_without_block()
    	fallback and the control frame stack looks like:
    
    	```
    	will_tailcall_bar [FINISH]
    	caller_that_used_fallback
    	```
    
    	will_tailcall_bar() runs in the interpreter and sets up a tailcall.
    	Right before JIT_EXEC() in the `send` instruction, the stack will look like:
    
    	```
    	bar [FINISH]
    	caller_that_used_fallback
    	```
    
    	Previously, JIT_EXEC() ran bar() in JIT code, which caused the `FINISH`
    	flag to return to the interpreter instead of to the JIT code running
    	caller_that_used_fallback(), causing code to run twice and probably
    	crash. Recent flaky failures on CI about "each stub expects a particular
    	iseq" are probably due to leaving methods twice in
    	`test_optimizations.rb`.
    
    	Only run JIT code from the interpreter if a new frame is pushed.
    	---
    	 test/ruby/test_optimization.rb | 11 +++++++++++
    	 vm_exec.h                      |  3 ++-
    	 2 files changed, 13 insertions(+), 1 deletion(-)
    
    	YJIT: No need to RESTORE_REG now that we reject tailcalls
    
    	Thanks to Kokubun for noticing.
    
    	Follow-up: b0711b1c
    	---
    	 vm_exec.h | 1 -
    	 1 file changed, 1 deletion(-)
    cdcabd8a
    Backport 3.3: YJIT memory leak fix with additional CI fixes (#9841)
    Alan Wu authored
    merge revision(s) 2cc7a56e,b0711b1c,db5d9429: [Backport #20209]
    
    	YJIT: Avoid leaks by skipping objects with a singleton class
    
    	For receiver with a singleton class, there are multiple vectors YJIT can
    	end up retaining the object. There is a path in jit_guard_known_klass()
    	that bakes the receiver into the code, and the object could also be kept
    	alive indirectly through a path starting at the CME object baked into
    	the code.
    
    	To avoid these leaks, avoid compiling calls on objects with a singleton
    	class.
    
    	See: https://github.com/Shopify/ruby/issues/552
    
    	[Bug #20209]
    	---
    	 yjit/bindgen/src/main.rs       |  1 +
    	 yjit/src/codegen.rs            | 17 +++++++++++++++++
    	 yjit/src/cruby_bindings.inc.rs |  1 +
    	 yjit/src/stats.rs              |  2 ++
    	 4 files changed, 21 insertions(+)
    
    	YJIT: Fix tailcall and JIT entry eating up FINISH frames (#9729)
    
    	Suppose YJIT runs a rb_vm_opt_send_without_block()
    	fallback and the control frame stack looks like:
    
    	```
    	will_tailcall_bar [FINISH]
    	caller_that_used_fallback
    	```
    
    	will_tailcall_bar() runs in the interpreter and sets up a tailcall.
    	Right before JIT_EXEC() in the `send` instruction, the stack will look like:
    
    	```
    	bar [FINISH]
    	caller_that_used_fallback
    	```
    
    	Previously, JIT_EXEC() ran bar() in JIT code, which caused the `FINISH`
    	flag to return to the interpreter instead of to the JIT code running
    	caller_that_used_fallback(), causing code to run twice and probably
    	crash. Recent flaky failures on CI about "each stub expects a particular
    	iseq" are probably due to leaving methods twice in
    	`test_optimizations.rb`.
    
    	Only run JIT code from the interpreter if a new frame is pushed.
    	---
    	 test/ruby/test_optimization.rb | 11 +++++++++++
    	 vm_exec.h                      |  3 ++-
    	 2 files changed, 13 insertions(+), 1 deletion(-)
    
    	YJIT: No need to RESTORE_REG now that we reject tailcalls
    
    	Thanks to Kokubun for noticing.
    
    	Follow-up: b0711b1c
    	---
    	 vm_exec.h | 1 -
    	 1 file changed, 1 deletion(-)
Loading