Exemple #1
0
void emitAwait(HTS& env, int32_t numIters) {
  auto const resumeOffset = nextBcOff(env);
  assert(curFunc(env)->isAsync());

  if (curFunc(env)->isAsyncGenerator()) PUNT(Await-AsyncGenerator);

  auto const exitSlow   = makeExitSlow(env);

  if (!topC(env)->isA(Type::Obj)) PUNT(Await-NonObject);

  auto const child = popC(env);
  gen(env, JmpZero, exitSlow, gen(env, IsWaitHandle, child));

  // cns() would ODR-use these
  auto const kSucceeded = c_WaitHandle::STATE_SUCCEEDED;
  auto const kFailed    = c_WaitHandle::STATE_FAILED;

  auto const state = gen(env, LdWHState, child);
  auto const failed = gen(env, EqInt, state, cns(env, kFailed));
  gen(env, JmpNZero, exitSlow, failed);

  env.irb->ifThenElse(
    [&] (Block* taken) {
      auto const succeeded = gen(env, EqInt, state, cns(env, kSucceeded));
      gen(env, JmpNZero, taken, succeeded);
    },
    [&] { // Next: the wait handle is not finished, we need to suspend
      if (resumed(env)) {
        implAwaitR(env, child, resumeOffset);
      } else {
        implAwaitE(env, child, resumeOffset, numIters);
      }
    },
    [&] { // Taken: retrieve the result from the wait handle
      auto const res = gen(env, LdWHResult, child);
      gen(env, IncRef, res);
      gen(env, DecRef, child);
      push(env, res);
    }
  );
}
Exemple #2
0
void emitAwait(IRGS& env, int32_t numIters) {
  auto const resumeOffset = nextBcOff(env);
  assertx(curFunc(env)->isAsync());

  if (curFunc(env)->isAsyncGenerator()) PUNT(Await-AsyncGenerator);

  auto const exitSlow   = makeExitSlow(env);

  if (!topC(env)->isA(TObj)) PUNT(Await-NonObject);

  auto const child = popC(env);
  gen(env, JmpZero, exitSlow, gen(env, IsWaitHandle, child));

  // cns() would ODR-use these
  auto const kSucceeded = c_WaitHandle::STATE_SUCCEEDED;
  auto const kFailed    = c_WaitHandle::STATE_FAILED;

  auto const state = gen(env, LdWHState, child);

  /*
   * HHBBC may have proven something about the inner type of this wait handle.
   *
   * So, we may have an assertion on the type of the top of the stack after
   * this instruction.  We know the next bytecode instruction is reachable from
   * fallthrough on the Await, so if it is an AssertRATStk 0, anything coming
   * out of the wait handle must be a subtype of that type, so this is a safe
   * and conservative way to do this optimization (even if our successor
   * bytecode offset is a jump target from things we aren't thinking about
   * here).
   */
  auto const knownTy = [&] {
    auto pc = curUnit(env)->at(resumeOffset);
    if (*reinterpret_cast<const Op*>(pc) != Op::AssertRATStk) return TInitCell;
    ++pc;
    auto const stkLoc = decodeVariableSizeImm(&pc);
    if (stkLoc != 0) return TInitCell;
    auto const rat = decodeRAT(curUnit(env), pc);
    auto const ty = ratToAssertType(env, rat);
    return ty ? *ty : TInitCell;
  }();

  ifThenElse(
    env,
    [&] (Block* taken) {
      auto const succeeded = gen(env, EqInt, state, cns(env, kSucceeded));
      gen(env, JmpNZero, taken, succeeded);
    },
    [&] { // Next: the wait handle is not finished, we need to suspend
      auto const failed = gen(env, EqInt, state, cns(env, kFailed));
      gen(env, JmpNZero, exitSlow, failed);
      if (resumed(env)) {
        implAwaitR(env, child, resumeOffset);
      } else {
        implAwaitE(env, child, resumeOffset, numIters);
      }
    },
    [&] { // Taken: retrieve the result from the wait handle
      auto const res = gen(env, LdWHResult, knownTy, child);
      gen(env, IncRef, res);
      gen(env, DecRef, child);
      push(env, res);
    }
  );
}