Пример #1
0
void implCondJmp(HTS& env, Offset taken, bool negate, SSATmp* src) {
  auto const flags = instrJmpFlags(*env.currentNormalizedInstruction);
  if (flags & JmpFlagEndsRegion) {
    spillStack(env);
  }
  if (env.mode == IRGenMode::CFG && (flags & JmpFlagNextIsMerge)) {
    spillStack(env);
    bccfMergeSPHack(env);
  }
  auto const target = getBlock(env, taken);
  assert(target != nullptr);
  auto const boolSrc = gen(env, ConvCellToBool, src);
  gen(env, DecRef, src);
  gen(env, negate ? JmpZero : JmpNZero, target, boolSrc);
}
Пример #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
  );
}
Пример #3
0
void interpOne(IRGS& env,
               folly::Optional<Type> outType,
               int popped,
               int pushed,
               InterpOneData& idata) {
  auto const unit = curUnit(env);
  spillStack(env);
  env.irb->exceptionStackBoundary();
  auto const op = unit->getOpcode(bcOff(env));

  auto& iInfo = getInstrInfo(op);
  if (iInfo.type == jit::InstrFlags::OutFDesc) {
    env.fpiStack.push(FPIInfo { sp(env), env.irb->spOffset(), nullptr });
  } else if (isFCallStar(op) && !env.fpiStack.empty()) {
    env.fpiStack.pop();
  }

  idata.bcOff = bcOff(env);
  idata.cellsPopped = popped;
  idata.cellsPushed = pushed;
  idata.opcode = op;

  gen(
    env,
    opcodeChangesPC(idata.opcode) ? InterpOneCF : InterpOne,
    outType,
    idata,
    sp(env),
    fp(env)
  );
  assertx(env.irb->stackDeficit() == 0);
}
Пример #4
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));
}
Пример #5
0
void interpOne(IRGS& env,
               folly::Optional<Type> outType,
               int popped,
               int pushed,
               InterpOneData& idata) {
  auto const unit = curUnit(env);
  spillStack(env);
  env.irb->exceptionStackBoundary();
  auto const op = unit->getOp(bcOff(env));

  idata.bcOff = bcOff(env);
  idata.cellsPopped = popped;
  idata.cellsPushed = pushed;
  idata.opcode = op;

  gen(
    env,
    opcodeChangesPC(idata.opcode) ? InterpOneCF : InterpOne,
    outType,
    idata,
    sp(env),
    fp(env)
  );
  assertx(env.irb->stackDeficit() == 0);
}
Пример #6
0
void implCondJmp(HTS& env, Offset taken, bool negate, SSATmp* src) {
  auto const flags = instrJmpFlags(*env.currentNormalizedInstruction);
  if (flags & JmpFlagEndsRegion) {
    spillStack(env);
  }
  if (env.mode == IRGenMode::CFG && (flags & JmpFlagNextIsMerge)) {
    // Before jumping to a merge point we have to ensure that the
    // stack pointer is sync'ed.  Without an ExceptionBarrier the
    // SpillStack can be removed by DCE (especially since merge points
    // start with a DefSP to block SP-chain walking).
    gen(env, ExceptionBarrier, spillStack(env));
  }
  auto const target = getBlock(env, taken);
  assert(target != nullptr);
  auto const boolSrc = gen(env, ConvCellToBool, src);
  gen(env, DecRef, src);
  gen(env, negate ? JmpZero : JmpNZero, target, boolSrc);
}
Пример #7
0
void prepareForHHBCMergePoint(IRGS& env) {
  spillStack(env);

  // For bytecode-level merge points, we need to make sure the spOffset
  // is the same on all incoming edges going to a merge point.  This
  // would "just happen" if we didn't still have instructions that
  // redefine StkPtrs, but calls still need to do that for now, so we
  // need this hack.
  auto spOff = IRSPOffset{-(env.irb->syncedSpLevel() - env.irb->spOffset())};
  gen(env, AdjustSP, IRSPOffsetData { spOff }, sp(env));
}
Пример #8
0
void endRegion(IRGS& env, SrcKey nextSk) {
  FTRACE(1, "------------------- endRegion ---------------------------\n");
  if (!fp(env)) {
    // The function already returned.  There's no reason to generate code to
    // try to go to the next part of it.
    return;
  }
  spillStack(env);
  gen(env, AdjustSP, IRSPOffsetData { offsetFromIRSP(env, BCSPOffset{0}) },
      sp(env));
  gen(env, ReqBindJmp, ReqBindJmpData { nextSk }, sp(env));
}
Пример #9
0
void jmpImpl(HTS& env, Offset offset, JmpFlags flags) {
  if (env.mode == IRGenMode::CFG) {
    if (flags & JmpFlagNextIsMerge) {
      gen(env, ExceptionBarrier, spillStack(env));
    }
    auto target = getBlock(env, offset);
    assert(target != nullptr);
    gen(env, Jmp, target);
    return;
  }
  if (!(flags & JmpFlagEndsRegion)) return;
  gen(env, Jmp, makeExit(env, offset));
}
Пример #10
0
Block* makeExitOpt(HTS& env, TransID transId) {
  assert(!isInlining(env));
  auto const targetBcOff = bcOff(env);
  auto const exit = env.unit.defBlock(Block::Hint::Unlikely);
  BlockPusher blockPusher(*env.irb, makeMarker(env, targetBcOff), exit);
  spillStack(env);
  gen(env, AdjustSP, StackOffset { offsetFromSP(env, 0) }, sp(env));
  gen(env,
      ReqRetranslateOpt,
      ReqRetransOptData{transId, SrcKey{curSrcKey(env), targetBcOff}},
      sp(env));
  return exit;
}
Пример #11
0
void implCondJmp(HTS& env, Offset taken, bool negate, SSATmp* src) {
  auto const flags = instrJmpFlags(*env.currentNormalizedInstruction);
  if (flags & JmpFlagEndsRegion) {
    spillStack(env);
  }
  if ((flags & JmpFlagNextIsMerge) != 0) {
    prepareForHHBCMergePoint(env);
  }
  auto const target = getBlock(env, taken);
  assertx(target != nullptr);
  auto const boolSrc = gen(env, ConvCellToBool, src);
  gen(env, DecRef, src);
  gen(env, negate ? JmpZero : JmpNZero, target, boolSrc);
}
Пример #12
0
void endRegion(HTS& env, Offset nextPc) {
  if (nextPc >= curFunc(env)->past()) {
    // We have fallen off the end of the func's bytecodes. This happens
    // when the function's bytecodes end with an unconditional
    // backwards jump so that nextPc is out of bounds and causes an
    // assertion failure in unit.cpp. The common case for this comes
    // from the default value funclets, which are placed after the end
    // of the function, with an unconditional branch back to the start
    // of the function. So you should see this in any function with
    // default params.
    return;
  }
  prepareForNextHHBC(env, nullptr, nextPc, true);
  auto const stack = spillStack(env);
  gen(env, SyncABIRegs, fp(env), stack);
  gen(env, ReqBindJmp, ReqBindJmpData(nextPc));
}
Пример #13
0
void emitSSwitch(HTS& env, const ImmVector& iv) {
  const int numCases = iv.size() - 1;

  /*
   * We use a fast path translation with a hashtable if none of the
   * cases are numeric strings and if the input is actually a string.
   *
   * Otherwise we do a linear search through the cases calling string
   * conversion routines.
   */
  const bool fastPath =
    topC(env)->isA(Type::Str) &&
    std::none_of(iv.strvec(), iv.strvec() + numCases,
      [&](const StrVecItem& item) {
        return curUnit(env)->lookupLitstrId(item.str)->isNumeric();
      }
    );

  auto const testVal = popC(env);

  std::vector<LdSSwitchData::Elm> cases(numCases);
  for (int i = 0; i < numCases; ++i) {
    auto const& kv = iv.strvec()[i];
    cases[i].str  = curUnit(env)->lookupLitstrId(kv.str);
    cases[i].dest = bcOff(env) + kv.dest;
  }

  LdSSwitchData data;
  data.numCases   = numCases;
  data.cases      = &cases[0];
  data.defaultOff = bcOff(env) + iv.strvec()[iv.size() - 1].dest;

  auto const dest = gen(env,
                        fastPath ? LdSSwitchDestFast
                                 : LdSSwitchDestSlow,
                        data,
                        testVal);
  gen(env, DecRef, testVal);
  auto const stack = spillStack(env);
  gen(env, SyncABIRegs, fp(env), stack);
  gen(env, JmpSSwitchDest, dest);
}
Пример #14
0
void emitNewPackedArray(HTS& env, int32_t numArgs) {
  if (numArgs > kPackedCapCodeThreshold) {
    PUNT(NewPackedArray-UnrealisticallyHuge);
  }

  auto const array = gen(
    env,
    AllocPackedArray,
    PackedArrayData { static_cast<uint32_t>(numArgs) }
  );
  static constexpr auto kMaxUnrolledInitArray = 8;
  if (numArgs > kMaxUnrolledInitArray) {
    spillStack(env);
    gen(
      env,
      InitPackedArrayLoop,
      InitPackedArrayLoopData {
        offsetFromSP(env, 0),
        static_cast<uint32_t>(numArgs)
      },
      array,
      sp(env)
    );
    discard(env, numArgs);
    push(env, array);
    return;
  }

  for (int i = 0; i < numArgs; ++i) {
    gen(
      env,
      InitPackedArray,
      IndexData { static_cast<uint32_t>(numArgs - i - 1) },
      array,
      popC(env)
    );
  }
  push(env, array);
}
Пример #15
0
void emitNewStructArray(HTS& env, const ImmVector& immVec) {
  auto const numArgs = immVec.size();
  auto const ids = immVec.vec32();

  // The NewPackedArray opcode's helper needs array values passed to it
  // via the stack.  We use spillStack() to flush the eval stack and
  // obtain a pointer to the topmost item; if over-flushing becomes
  // a problem then we should refactor the NewPackedArray opcode to
  // take its values directly as SSA operands.
  spillStack(env);

  NewStructData extra;
  extra.offset  = offsetFromSP(env, 0);
  extra.numKeys = numArgs;
  extra.keys    = new (env.unit.arena()) StringData*[numArgs];
  for (auto i = size_t{0}; i < numArgs; ++i) {
    extra.keys[i] = curUnit(env)->lookupLitstrId(ids[i]);
  }

  discard(env, numArgs);
  push(env, gen(env, NewStructArray, extra, sp(env)));
}
Пример #16
0
/*
 * Attempts to begin inlining, and returns whether or not it successed.
 *
 * When doing gen-time inlining, we set up a series of IR instructions
 * that looks like this:
 *
 *   fp0  = DefFP
 *   sp   = DefSP<offset>
 *
 *   // ... normal stuff happens ...
 *
 *   // FPI region:
 *     SpillFrame sp, ...
 *     // ... probably some StStks due to argument expressions
 *     fp2   = DefInlineFP<func,retBC,retSP,off> sp
 *
 *         // ... callee body ...
 *
 *     InlineReturn fp2
 *
 * In DCE we attempt to remove the InlineReturn and DefInlineFP instructions if
 * they aren't needed.
 */
bool beginInlining(IRGS& env,
                   unsigned numParams,
                   const Func* target,
                   Offset returnBcOffset) {
  auto const& fpiStack = env.irb->fpiStack();

  assertx(!fpiStack.empty() &&
    "Inlining does not support calls with the FPush* in a different Tracelet");
  assertx(returnBcOffset >= 0 && "returnBcOffset before beginning of caller");
  assertx(curFunc(env)->base() + returnBcOffset < curFunc(env)->past() &&
         "returnBcOffset past end of caller");

  FTRACE(1, "[[[ begin inlining: {}\n", target->fullName()->data());

  SSATmp** params = (SSATmp**)alloca(sizeof(SSATmp*) * numParams);
  for (unsigned i = 0; i < numParams; ++i) {
    params[numParams - i - 1] = popF(env);
  }

  auto const prevSP    = fpiStack.front().returnSP;
  auto const prevSPOff = fpiStack.front().returnSPOff;
  spillStack(env);
  auto const calleeSP  = sp(env);

  always_assert_flog(
    prevSP == calleeSP,
    "FPI stack pointer and callee stack pointer didn't match in beginInlining"
  );

  auto const& info = fpiStack.front();
  always_assert(!isFPushCuf(info.fpushOpc) && !info.interp);

  auto ctx = [&] {
    if (info.ctx || isFPushFunc(info.fpushOpc)) {
      return info.ctx;
    }

    constexpr int32_t adjust = offsetof(ActRec, m_r) - offsetof(ActRec, m_this);
    IRSPOffset ctxOff{invSPOff(env) - info.returnSPOff - adjust};
    return gen(env, LdStk, TCtx, IRSPOffsetData{ctxOff}, sp(env));
  }();

  DefInlineFPData data;
  data.target        = target;
  data.retBCOff      = returnBcOffset;
  data.fromFPushCtor = isFPushCtor(info.fpushOpc);
  data.ctx           = ctx;
  data.retSPOff      = prevSPOff;
  data.spOffset      = offsetFromIRSP(env, BCSPOffset{0});

  // Push state and update the marker before emitting any instructions so
  // they're all given markers in the callee.
  auto const key = SrcKey {
    target,
    target->getEntryForNumArgs(numParams),
    false
  };
  env.bcStateStack.emplace_back(key);
  env.inlineLevel++;
  updateMarker(env);

  auto const calleeFP = gen(env, DefInlineFP, data, calleeSP, fp(env));

  for (unsigned i = 0; i < numParams; ++i) {
    stLocRaw(env, i, calleeFP, params[i]);
  }
  const bool hasVariadicArg = target->hasVariadicCaptureParam();
  for (unsigned i = numParams; i < target->numLocals() - hasVariadicArg; ++i) {
    /*
     * Here we need to be generating hopefully-dead stores to initialize
     * non-parameter locals to KindOfUninit in case we have to leave the trace.
     */
    stLocRaw(env, i, calleeFP, cns(env, TUninit));
  }
  if (hasVariadicArg) {
    auto argNum = target->numLocals() - 1;
    always_assert(numParams <= argNum);
    stLocRaw(env, argNum, calleeFP, cns(env, staticEmptyArray()));
  }

  return true;
}
Пример #17
0
void emitSwitch(HTS& env,
                const ImmVector& iv,
                int64_t base,
                int32_t bounded) {
  int nTargets = bounded ? iv.size() - 2 : iv.size();

  SSATmp* const switchVal = popC(env);
  Type type = switchVal->type();
  assert(IMPLIES(!(type <= Type::Int), bounded));
  assert(IMPLIES(bounded, iv.size() > 2));
  SSATmp* index;
  SSATmp* ssabase = cns(env, base);
  SSATmp* ssatargets = cns(env, nTargets);

  Offset defaultOff = bcOff(env) + iv.vec32()[iv.size() - 1];
  Offset zeroOff = 0;
  if (base <= 0 && (base + nTargets) > 0) {
    zeroOff = bcOff(env) + iv.vec32()[0 - base];
  } else {
    zeroOff = defaultOff;
  }

  if (type <= Type::Null) {
    gen(env, Jmp, makeExit(env, zeroOff));
    return;
  }
  if (type <= Type::Bool) {
    Offset nonZeroOff = bcOff(env) + iv.vec32()[iv.size() - 2];
    gen(env, JmpNZero, makeExit(env, nonZeroOff), switchVal);
    gen(env, Jmp, makeExit(env, zeroOff));
    return;
  }

  if (type <= Type::Int) {
    // No special treatment needed
    index = switchVal;
  } else if (type <= Type::Dbl) {
    // switch(Double|String|Obj)Helper do bounds-checking for us, so
    // we need to make sure the default case is in the jump table,
    // and don't emit our own bounds-checking code
    bounded = false;
    index = gen(env, LdSwitchDblIndex, switchVal, ssabase, ssatargets);
  } else if (type <= Type::Str) {
    bounded = false;
    index = gen(env, LdSwitchStrIndex, switchVal, ssabase, ssatargets);
  } else if (type <= Type::Obj) {
    // switchObjHelper can throw exceptions and reenter the VM so we use the
    // catch block here.
    bounded = false;
    index = gen(env, LdSwitchObjIndex, switchVal, ssabase, ssatargets);
  } else if (type <= Type::Arr) {
    gen(env, DecRef, switchVal);
    gen(env, Jmp, makeExit(env, defaultOff));
    return;
  } else {
    PUNT(Switch-UnknownType);
  }

  std::vector<Offset> targets(iv.size());
  for (int i = 0; i < iv.size(); i++) {
    targets[i] = bcOff(env) + iv.vec32()[i];
  }

  JmpSwitchData data;
  data.base        = base;
  data.bounded     = bounded;
  data.cases       = iv.size();
  data.defaultOff  = defaultOff;
  data.targets     = &targets[0];

  spillStack(env);
  gen(env, AdjustSP, StackOffset { offsetFromSP(env, 0) }, sp(env));
  gen(env, JmpSwitchDest, data, index, sp(env));
}
Пример #18
0
/*
 * Attempts to begin inlining, and returns whether or not it successed.
 *
 * When doing gen-time inlining, we set up a series of IR instructions
 * that looks like this:
 *
 *   fp0  = DefFP
 *   sp   = DefSP<offset>
 *
 *   // ... normal stuff happens ...
 *
 *   // FPI region:
 *     SpillFrame sp, ...
 *     // ... probably some StStks due to argument expressions
 *     fp2   = DefInlineFP<func,retBC,retSP,off> sp
 *
 *         // ... callee body ...
 *
 *     InlineReturn fp2
 *
 * In DCE we attempt to remove the InlineReturn and DefInlineFP instructions if
 * they aren't needed.
 */
bool beginInlining(IRGS& env,
                   unsigned numParams,
                   const Func* target,
                   Offset returnBcOffset) {
  assertx(!env.fpiStack.empty() &&
    "Inlining does not support calls with the FPush* in a different Tracelet");
  assertx(returnBcOffset >= 0 && "returnBcOffset before beginning of caller");
  assertx(curFunc(env)->base() + returnBcOffset < curFunc(env)->past() &&
         "returnBcOffset past end of caller");

  FTRACE(1, "[[[ begin inlining: {}\n", target->fullName()->data());

  SSATmp* params[numParams];
  for (unsigned i = 0; i < numParams; ++i) {
    params[numParams - i - 1] = popF(env);
  }

  auto const prevSP    = env.fpiStack.top().returnSP;
  auto const prevSPOff = env.fpiStack.top().returnSPOff;
  spillStack(env);
  auto const calleeSP  = sp(env);

  always_assert_flog(
    prevSP == calleeSP,
    "FPI stack pointer and callee stack pointer didn't match in beginInlining"
  );

  // This can only happen if the code is unreachable, in which case
  // the FPush* can punt if it gets a TBottom.
  if (env.fpiStack.top().spillFrame == nullptr) return false;

  auto const sframe = env.fpiStack.top().spillFrame;

  DefInlineFPData data;
  data.target        = target;
  data.retBCOff      = returnBcOffset;
  data.fromFPushCtor = sframe->extra<ActRecInfo>()->fromFPushCtor;
  data.ctx           = sframe->src(2);
  data.retSPOff      = prevSPOff;
  data.spOffset      = offsetFromIRSP(env, BCSPOffset{0});

  // Push state and update the marker before emitting any instructions so
  // they're all given markers in the callee.
  auto const key = SrcKey {
    target,
    target->getEntryForNumArgs(numParams),
    false
  };
  env.bcStateStack.emplace_back(key);
  env.inlineLevel++;
  updateMarker(env);

  auto const calleeFP = gen(env, DefInlineFP, data, calleeSP, fp(env));

  for (unsigned i = 0; i < numParams; ++i) {
    stLocRaw(env, i, calleeFP, params[i]);
  }
  const bool hasVariadicArg = target->hasVariadicCaptureParam();
  for (unsigned i = numParams; i < target->numLocals() - hasVariadicArg; ++i) {
    /*
     * Here we need to be generating hopefully-dead stores to initialize
     * non-parameter locals to KindOfUninit in case we have to leave the trace.
     */
    stLocRaw(env, i, calleeFP, cns(env, TUninit));
  }
  if (hasVariadicArg) {
    auto argNum = target->numLocals() - 1;
    always_assert(numParams <= argNum);
    stLocRaw(env, argNum, calleeFP, cns(env, staticEmptyArray()));
  }

  env.fpiActiveStack.push(std::make_pair(env.fpiStack.top().returnSP,
                                         env.fpiStack.top().returnSPOff));
  env.fpiStack.pop();

  return true;
}
Пример #19
0
/*
 * When doing gen-time inlining, we set up a series of IR instructions
 * that looks like this:
 *
 *   fp0  = DefFP
 *   sp0  = DefSP<offset>
 *
 *   // ... normal stuff happens ...
 *   // sp_pre = some SpillStack, or maybe the DefSP
 *
 *   // FPI region:
 *     sp1   = SpillStack sp_pre, ...
 *     sp2   = SpillFrame sp1, ...
 *     // ... possibly more spillstacks due to argument expressions
 *     sp3   = SpillStack sp2, -argCount
 *     fp2   = DefInlineFP<func,retBC,retSP,off> sp2 sp1
 *     sp4   = ReDefSP<spOffset,spansCall> sp1 fp2
 *
 *         // ... callee body ...
 *
 *           = InlineReturn fp2
 *
 * [ sp5  = ResetSP<spOffset> fp0 ]
 *
 * The rest of the code then depends on sp5, and not any of the StkPtr
 * tree going through the callee body.  The sp5 tmp has the same view
 * of the stack as sp1 did, which represents what the stack looks like
 * before the return address is pushed but after the activation record
 * is popped.
 *
 * In DCE we attempt to remove the SpillFrame, InlineReturn, and
 * DefInlineFP instructions if they aren't needed.
 *
 * ReDefSP takes sp1, the stack pointer from before the inlined frame.
 * This SSATmp may be used for determining stack types in the
 * simplifier, or stack values if the inlined body doesn't contain a
 * call---these instructions both take an extradata `spansCall' which
 * is true iff a Call occured anywhere between the the definition of
 * its first argument and itself.
 */
void beginInlining(HTS& env,
                   unsigned numParams,
                   const Func* target,
                   Offset returnBcOffset) {
  assert(!env.fpiStack.empty() &&
    "Inlining does not support calls with the FPush* in a different Tracelet");
  assert(returnBcOffset >= 0 && "returnBcOffset before beginning of caller");
  assert(curFunc(env)->base() + returnBcOffset < curFunc(env)->past() &&
         "returnBcOffset past end of caller");

  FTRACE(1, "[[[ begin inlining: {}\n", target->fullName()->data());

  SSATmp* params[numParams];
  for (unsigned i = 0; i < numParams; ++i) {
    params[numParams - i - 1] = popF(env);
  }

  auto const prevSP    = env.fpiStack.top().returnSP;
  auto const prevSPOff = env.fpiStack.top().returnSPOff;
  spillStack(env);
  auto const calleeSP  = sp(env);

  always_assert_flog(
    env.fpiStack.top().spillFrame != nullptr,
    "Couldn't find SpillFrame for inlined call on sp {}."
    " Was the FPush instruction interpreted?\n{}",
    *calleeSP->inst(), env.irb->unit()
  );

  auto const sframe = env.fpiStack.top().spillFrame;

  DefInlineFPData data;
  data.target        = target;
  data.retBCOff      = returnBcOffset;
  data.fromFPushCtor = sframe->extra<ActRecInfo>()->isFromFPushCtor();
  data.ctx           = sframe->src(2);
  data.retSPOff      = prevSPOff;
  data.spOffset      = offsetFromSP(env, 0);

  // Push state and update the marker before emitting any instructions so
  // they're all given markers in the callee.
  auto const key = SrcKey {
    target,
    target->getEntryForNumArgs(numParams),
    false
  };
  env.bcStateStack.emplace_back(key);
  updateMarker(env);

  auto const calleeFP = gen(env, DefInlineFP, data, calleeSP, prevSP, fp(env));
  gen(env, ReDefSP, StackOffset{target->numLocals()}, fp(env));

  for (unsigned i = 0; i < numParams; ++i) {
    stLocRaw(env, i, calleeFP, params[i]);
  }
  for (unsigned i = numParams; i < target->numLocals(); ++i) {
    /*
     * Here we need to be generating hopefully-dead stores to initialize
     * non-parameter locals to KindOfUninit in case we have to leave the trace.
     */
    stLocRaw(env, i, calleeFP, cns(env, Type::Uninit));
  }

  env.fpiActiveStack.push(std::make_pair(env.fpiStack.top().returnSP,
                                         env.fpiStack.top().returnSPOff));
  env.fpiStack.pop();
}
Пример #20
0
void prepareForSideExit(IRGS& env) { spillStack(env); }