Austin Wise
Go to home page

Async Part 4 - What does the Hack compiler do with async functions

In the last article we looked at how the C# transforms async functions into executable code. Starting from this article we will start looking at how async functions in the Hack language are executed.

Example function

For this investigation we will be looking at a simple async function:

async function addAsync(int $a, int $b): Awaitable<int> {
  $sum = $a + $b;
  await HH\Asio\usleep(1000);
  return $sum + 42;

Setting up our HHVM environment

To compile HHVM and Hack we will perform the following steps:

  • Install Debian 10
  • Follow the directions for building from source
  • To enable disassembly, we will use the following command to configure while building:
  • To see the the HHVM bytecode and intermediate representation of our program, we will use this command to execute it:
    TRACE=hhir:1,printir:2 HPHP_TRACE_FILE=/dev/stdout ./hhvm -vEval.DumpBytecode=1 ./test.hack

What Hack does with our async function

Let’s see what the HHVM bytecode generated by the compiler looks like:

Function (can_async_eager_ret) addAsync
 Param: a int (HH\int)
 Param: b int (HH\int)
 Ret:  int (HH\Awaitable<HH\int>)
maxStackCells: 6
numLocals: 3
numIterators: 0
  // line 5
    0: VerifyParamType L:0
    2: VerifyParamType L:1
  // line 2
    4: CGetL L:1:1
    7: CGetL2 L:0:0
   10: Add
   11: SetL L:2
   13: PopC
  // line 3
   14: NullUninit
   15: NullUninit
   16: Int 1000
   25: FCallFuncD <> 1 1 "0" 21 (46) "" "HH\\Asio\\usleep"
   37: Dup
   38: IsTypeC Null
   40: JmpNZ 6 (46)
   45: Await
   46: PopC
  // line 4
   47: Int 42
   56: CGetL2 L:2:2
   59: Add
   60: VerifyRetTypeC
   61: RetC

We can see here that the async support is directly built into the bytecode. At offset 45 we can see the Await instruction. The await operator is a first class operation in the HHVM bytecode. Unlike C#, the Hack compiler does not transform our function into state machine. We will have to go deeper into the HHVM compiler next time to see what’s going on.

Next time we will go deeper into HHVM intermediate representation to figure out what is going on with our async function at runtime.