Search code examples
perlperlguts

How do Perl if-statements work under the hood?


How does Perl's interpreter do if-statements? I wanted to try to implement a simple Lazy-Basic-Block-Versioning for if-statements that use ref as a predicate, and maybe subsequently for sub-routines as well.

After studying the source code I see OP codes for everything but control flow. My interpreter experience is quite limited, but I would assume to see a OP_IF that would pop the stack for it's predicates OP codes or something along those lines. I found KEY_if which comes from the Yacc grammar, which then subsequently is used in toke.c and translated to a KW_IF, but I can't find where KW_IF is actually used.

I originally intended to just add some basic optimization with global state to peep.c, but since there is no OP_IF I'm struggling to see the big-picture of how control flow is handled.


Solution

  • if gets compiled into an and or or op.

    perl -MO=Concise,-exec -e'if ( f() ) { ... }'
    
    1  <0> enter v
    2  <;> nextstate(main 1 -e:1) v:{
    3  <0> pushmark s
    4  <#> gv[*f] s/EARLYCV
    5  <1> entersub[t2] sKS/TARG
    6  <|> and(other->7) vK/1
    7      <0> pushmark s
    8      <$> const[PV "Unimplemented"] s
    9      <@> die vK/1
    a  <@> leave[1 ref] vKP/REFC
    -e syntax OK
    
    perl -MO=Concise,-exec -e'if ( !f() ) { ... }'
    
    1  <0> enter v
    2  <;> nextstate(main 1 -e:1) v:{
    3  <0> pushmark s
    4  <#> gv[*f] s/EARLYCV
    5  <1> entersub[t2] sKS/TARG
    6  <|> or(other->7) vK/1
    7      <0> pushmark s
    8      <$> const[PV "Unimplemented"] s
    9      <@> die vK/1
    a  <@> leave[1 ref] vKP/REFC
    -e syntax OK
    

    if can also be compiled into a cond_expr op like the conditional operator (?:).

    perl -MO=Concise,-exec -e'if ( f() ) { ... } else { ... }'
    
    1  <0> enter v
    2  <;> nextstate(main 1 -e:1) v:{
    3  <0> pushmark s
    4  <#> gv[*f] s/EARLYCV
    5  <1> entersub[t2] sKS/TARG
    6  <|> cond_expr(other->7) vK/1
    7      <0> pushmark s
    8      <$> const[PV "Unimplemented"] s
    9      <@> die vK/1
               goto a
    b  <0> enter v
    c  <;> nextstate(main 5 -e:1) v
    d  <0> pushmark s
    e  <$> const[PV "Unimplemented"] s
    f  <@> die vK/1
    g  <@> leave vKP
    a  <@> leave[1 ref] vKP/REFC
    -e syntax OK
    
    

    The opcode name from Concise maps to a function named pp_name in pp*.c (with a few exceptions).