Ejemplo n.º 1
0
void emitContEnter(HTS& env) {
  auto const returnOffset = nextBcOff(env);
  assert(curClass(env));
  assert(curClass(env)->classof(c_AsyncGenerator::classof()) ||
         curClass(env)->classof(c_Generator::classof()));
  assert(curFunc(env)->contains(returnOffset));

  // Load generator's FP and resume address.
  auto const genObj = ldThis(env);
  auto const genFp  = gen(env, LdContActRec, genObj);
  auto resumeAddr   = gen(env, LdContResumeAddr, genObj);

  // Make sure function enter hook is called if needed.
  auto const exitSlow = makeExitSlow(env);
  gen(env, CheckSurpriseFlags, exitSlow);

  // Exit to interpreter if resume address is not known.
  resumeAddr = gen(env, CheckNonNull, exitSlow, resumeAddr);

  // Sync stack.
  auto const stack = spillStack(env);

  // Enter generator.
  auto returnBcOffset = returnOffset - curFunc(env)->base();
  gen(env, ContEnter, stack, fp(env), genFp, resumeAddr,
    cns(env, returnBcOffset));
}
Ejemplo n.º 2
0
void emitContEnter(IRGS& env) {
  auto const returnOffset = nextBcOff(env);
  assertx(curClass(env));
  assertx(curClass(env)->classof(c_AsyncGenerator::classof()) ||
          curClass(env)->classof(c_Generator::classof()));
  assertx(curFunc(env)->contains(returnOffset));

  auto isAsync = curClass(env)->classof(c_AsyncGenerator::classof());
  // Load generator's FP and resume address.
  auto const genObj = ldThis(env);
  auto const genFp  = gen(env, LdContActRec, IsAsyncData(isAsync), genObj);
  auto resumeAddr   = gen(env, LdContResumeAddr, IsAsyncData(isAsync), genObj);

  // Make sure function enter hook is called if needed.
  auto const exitSlow = makeExitSlow(env);
  gen(env, CheckSurpriseFlags, exitSlow, fp(env));

  // Exit to interpreter if resume address is not known.
  resumeAddr = gen(env, CheckNonNull, exitSlow, resumeAddr);

  spillStack(env);
  env.irb->exceptionStackBoundary();
  auto returnBcOffset = returnOffset - curFunc(env)->base();
  gen(
    env,
    ContEnter,
    ContEnterData { offsetFromIRSP(env, BCSPOffset{0}), returnBcOffset },
    sp(env),
    fp(env),
    genFp,
    resumeAddr
  );
}
Ejemplo n.º 3
0
void emitContCurrent(HTS& env) {
  assert(curClass(env));
  auto const cont = ldThis(env);
  gen(env, ContStartedCheck, makeExitSlow(env), cont);
  auto const offset = cns(env, offsetof(c_Generator, m_value));
  auto const value = gen(env, LdContField, Type::Cell, cont, offset);
  pushIncRef(env, value);
}
Ejemplo n.º 4
0
void emitContCheck(HTS& env, int32_t checkStarted) {
  assert(curClass(env));
  assert(curClass(env)->classof(c_AsyncGenerator::classof()) ||
         curClass(env)->classof(c_Generator::classof()));
  auto const cont = ldThis(env);
  gen(env, ContPreNext, makeExitSlow(env), cont,
    cns(env, static_cast<bool>(checkStarted)));
}
Ejemplo n.º 5
0
void emitContCurrent(IRGS& env) {
  assertx(curClass(env));
  auto const cont = ldThis(env);
  gen(env, ContStartedCheck, IsAsyncData(false), makeExitSlow(env), cont);
  auto const offset = cns(env,
    offsetof(Generator, m_value) - Generator::objectOff());
  auto const value = gen(env, LdContField, TCell, cont, offset);
  pushIncRef(env, value);
}
Ejemplo n.º 6
0
void emitCGetG(IRGS& env) {
  auto const exit = makeExitSlow(env);
  auto const name = topC(env);
  if (!name->isA(TStr)) PUNT(CGetG-NonStrName);
  auto const ptr = gen(env, LdGblAddr, exit, name);
  destroyName(env, name);
  pushIncRef(
    env,
    gen(env, LdMem, TCell, gen(env, UnboxPtr, ptr))
  );
}
Ejemplo n.º 7
0
void emitBareThis(HTS& env, BareThisOp subop) {
  if (!curClass(env)) {
    interpOne(env, Type::InitNull, 0); // will raise notice and push null
    return;
  }
  auto const ctx = gen(env, LdCtx, fp(env));
  if (subop == BareThisOp::NeverNull) {
    env.irb->setThisAvailable();
  } else {
    gen(env, CheckCtxThis, makeExitSlow(env), ctx);
  }
  pushIncRef(env, gen(env, CastCtxThis, ctx));
}
Ejemplo n.º 8
0
void emitInitThisLoc(HTS& env, int32_t id) {
  if (!curClass(env)) {
    // Do nothing if this is null
    return;
  }
  auto const ldrefExit = makeExit(env);
  auto const oldLoc    = ldLoc(env, id, ldrefExit, DataTypeCountness);
  auto const ctx       = gen(env, LdCtx, fp(env));
  gen(env, CheckCtxThis, makeExitSlow(env), ctx);
  auto const this_     = gen(env, CastCtxThis, ctx);
  gen(env, IncRef, this_);
  stLocRaw(env, id, fp(env), this_);
  gen(env, DecRef, oldLoc);
}
Ejemplo n.º 9
0
void emitWHResult(IRGS& env) {
  assertx(topC(env)->isA(TObj));
  auto const exitSlow = makeExitSlow(env);
  auto const child = popC(env);
  // In most conditions, this will be optimized out by the simplifier.
  // We already need to setup a side-exit for the !succeeded case.
  gen(env, JmpZero, exitSlow, gen(env, IsWaitHandle, child));
  static_assert(
    c_WaitHandle::STATE_SUCCEEDED == 0,
    "we test state for non-zero, success must be zero"
  );
  gen(env, JmpNZero, exitSlow, gen(env, LdWHState, child));
  auto const res = gen(env, LdWHResult, TInitCell, child);
  gen(env, IncRef, res);
  gen(env, DecRef, child);
  push(env, res);
}
Ejemplo n.º 10
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);
    }
  );
}
Ejemplo n.º 11
0
void surpriseCheck(HTS& env, Offset relOffset) {
  if (relOffset < 0) {
    auto const exit = makeExitSlow(env);
    gen(env, CheckSurpriseFlags, exit);
  }
}
Ejemplo n.º 12
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);
    }
  );
}