Project

General

Profile

Misc #16956

Updated by k0kubun (Takashi Kokubun) almost 4 years ago

# What's this ticket? 
 A text explaining what attributes MJIT uses for optimizations and why they're needed. 
 This is written here in case people want to comment on this in a way that can notify me. 

 # Current state 
 ## The per-insn attribute we currently have and MJIT uses 

 ``` 
 * leaf: indicates that the instruction is "leaf" i.e. it does 
   not introduce new stack frame on top of it. 
   If an instruction handles sp, that can never be a leaf. 
 ``` 

 ## MJIT's optimizations which rely on leaf 
 * PC motion skip 
   * If leaf, an insn doesn't see cfp->pc because an exception is not thrown and an arbitrary method which may see lineno isn't called. This place also checks catch_except_p=false to make sure catch table of this iseq is not used. 
   * https://github.com/ruby/ruby/blob/daea41c3df0d63eda553c92c0ca29eaceb6d5828/tool/ruby_vm/views/_mjit_compile_pc_and_sp.erb#L10-L13 
 * Deoptimization check skip 
   * If leaf, an arbitrary method which may invalidate (i.e. TracePoint or GC.compact) the current code may not be called. 
   * https://github.com/ruby/ruby/blob/daea41c3df0d63eda553c92c0ca29eaceb6d5828/tool/ruby_vm/views/_mjit_compile_insn.erb#L61-L71 
 * Frame push omission on method inlining 
   * If leaf, any cfp won't be pushed to the stack. Thus pushing a cfp (which could be a base of what'd be pushed if it were not leaf) for an all-leaf inlined method can be skipped. Also we need the same guarantee as "PC motion skip" because cfp->pc (and even existence This is using almost direction meaning of a frame, when it's about callstack) won't be maintained at all. leaf. 
   * https://github.com/ruby/ruby/blob/daea41c3df0d63eda553c92c0ca29eaceb6d5828/mjit_compile.c#L377-L378 

 ## Fine-grained speculations 

 1. leaf: A cfp may not be pushed to the stack 
 2. An arbitrary method may not be called 
   * Obviously this is guaranteed by 1. 
 3. An exception may not be thrown 
   * MJIT assumes this from 1 and/or 2. Is this legitimate? 
 4. `mjit_call_p = false` may not be set 
   * a.k.a. JIT cancel-all. It's set by TracePoint and GC.compact. Therefore assuming it from 2 should be fair. 
 5. cfp->pc may not be read 
   * Aside from insn dispatch and catch table, cfp->pc is only read by a C method showing lineno of a callstack or calling C API like `rb_profile_frames`. If this assumption is true, we can assume this from 2. 


 # Discussions 
 ## __builtin_attr 
 Currently there's no easy way to know behaviors of a C method. We may want to annotate a C method to provide information like what's described above. 
 Apparently [Feature #16254] has this problem ("Annotation issues") in one of its motivations. And therefore converting a C method to a builtin method and annotating the method would be the most legitimate way we can foresee. 

 When we think about annotating a method with builtin insn, there can be two ways to satisfy MJIT's immediate need: 

 1. Per-method attribute 
 2. Per-insn attribute for builtin insns 

 For now I'm trying to add an attribute so that an iseq with builtin insn can be analyzed to be side-effect free. 1 is a direct representation of it. If 2 can be used to assume a builtin insn is leaf=true, a method (possibly with other non-builtin insns) can be analyzed as leaf=true using other insn's leaf attribute. When we convert a C method to a single builtin insn, both 1 and 2 work totally fine. 

 Since ko1 preferred 1 for simplicity, I'm thinking about having per-method attribute annotation (tentative DSL name: `__builtin_attr`). Because I'm still thinking about what attributes should frame push omission, the attribute I'd like to annotate will be annotated, but it's gonna be something required for "Frame push omission". "leaf".

Back