ykjit / yk

yk packages
https://ykjit.github.io/yk/
Other
28 stars 7 forks source link

Turn guards into more obviously straight-line code. #1288

Closed ltratt closed 2 weeks ago

ltratt commented 2 weeks ago

Previously a guard generated code along the lines of:

; jmp >check_cond
; guard_fail:
; mov rdi, [rbp]
; mov rsi, QWORD deoptid
; mov rdx, rbp
; mov rax, QWORD __yk_deopt as i64
; call rax
; check_cond:
; cmp Rb(WR0.code()), inst.expect() as i8 // `as` intentional.
; jne <guard_fail

Since we expect the common case to be that guards succeed, this has an unnecessary JMP, then a wodge of cold code (i.e. that we'll rarely execute). The CPU will probably do a good job of getting rid of the JMPs obvious costs, but we're putting undue pressure on the icache.

This commit changes guards so that they're just:

; cmp Rb(WR0.code()), inst.expect() as i8 // `as` intentional.
; jne =>fail_label

We then put all the fail_labels in a chunk at the end. In essence these contain the "call __yk_deopt" code, though (when we have more than one guard) we shrink this code down too (see the comment in the commit).

At the moment any savings this introduces are swamped by our loads/stores of the spill allocator, but it's The Right Thing To Do, so we might as well do it now.