Codegen for short-circuiting operators
commitcdb8d4e105dab6b6174bd03e45204d1319b83e34
authorVladimir Matveev <vladima@fb.com>
Mon, 26 Feb 2018 16:11:08 +0000 (26 08:11 -0800)
committerHhvm Bot <hhvm-bot@users.noreply.github.com>
Mon, 26 Feb 2018 16:29:35 +0000 (26 08:29 -0800)
tree426dd489196de6bf60a1a2727d860b8fe901834b
parent2d5d2f4eb4d3d918f3aa221e3fce1e85fef1c68d
Codegen for short-circuiting operators

Summary:
tl;dr; as in title - fixes several issues related to codegen for short-circuiting operators.
For many cases when emitting `JmpZ` or `JmpNZ` we can statically determine if condition will always be true or false - for these cases we emit `Jmp` instead. However we still emitted code for both branches of arguments coersion even though some of it will definitely be unreachable - this uncovered the problem with HHVM verifier.
For example for code
```lang=php
new f($s || 'default value')
```
we might generate something like
```
    FPushCtorD ...
    CGetL $s
    JmpNZ L0 # check first argument
    Jmp L0   # second argument is always truthy value, unconditionally jump to loading True
    False    # load False and jump to end of conditional expression - unreachable (*1)
    Jmp L1
  L0:
    True
  L1: (*2)
```
Per HHVM verification rules, stack depth on instruction that follows `Jmp` (and other instructions that unconditionally transfer control flow) is assumed to be 0. This means that at the point (*2) where control flow merges we'll get different depths since in regular case it will be 2 [constructor, True] and in case that starts at (*1) it will be 1 [False] which is illegal.
In this diff we make `emit_jmpz` / `emit_jmpnz` return two extra things:
- was generated instruction sequence ended with `Jmp`
- was label that was passed to `emit_*` ever used

With this bits of information we can be more precise when emitting code, i.e.:
- if for instruction sequence `[e1; e2]` control flow should fallthrough from from e1 to e2 and e1 ends with `Jmp` - we can omit e2 since it will definitely be unreachable
- if for instruction sequence we know that E1 never jumps to L1 (L1 is definitely not used) - we can omit E3 since it will definitely be unreachable
```
E1 (might jump to L1)
E2
JMP L_END
L1:
E3
L_END:

Reviewed By: oulgen

Differential Revision: D7075742

fbshipit-source-id: 2b6820236f2555037763dc77df7a9d44475fc727
hphp/hack/src/hhbc/emit_expression.ml
hphp/hack/src/hhbc/emit_statement.ml