Ejemplo n.º 1
0
// Form a trace of the async stack starting with the currently running
// generator, if any. For now we just toss in the function name and
// id, as well as the pseudo-frames for context breaks at explicit
// joins. Later we'll add more, like file and line, hopefully function
// args, wait handle status, etc.
static Array createAsyncStacktrace() {
  Array trace;
  auto currentWaitHandle = HHVM_FN(asio_get_running)();
  if (currentWaitHandle.isNull()) return trace;
  Array depStack =
    objToWaitableWaitHandle(currentWaitHandle)->t_getdependencystack();
  for (ArrayIter iter(depStack); iter; ++iter) {
    if (iter.secondRef().isNull()) {
      trace.append(Array(staticEmptyArray()));
    } else {
      auto wh = objToWaitableWaitHandle(iter.secondRef().toObject());
      auto parents = wh->t_getparents();
      Array ancestors;
      for (ArrayIter piter(parents); piter; ++piter) {
        // Note: the parent list contains no nulls.
        auto parent = objToWaitableWaitHandle(piter.secondRef().toObject());
        ancestors.append(parent->t_getname());
      }
      Array frameData;
      frameData.set(s_function, wh->t_getname(), true);
      frameData.set(s_id, wh->t_getid(), true);
      frameData.set(s_ancestors, ancestors, true);
      // Async function wait handles may have a source location to add.
      if (wh->getKind() == c_WaitHandle::Kind::AsyncFunction) {
        auto afwh = static_cast<c_AsyncFunctionWaitHandle*>(wh);
        addAsyncFunctionLocation(frameData, *afwh);
      }
      trace.append(frameData);
    }
  }
  return trace;
}
Ejemplo n.º 2
0
void emitNewMixedArray(HTS& env, int32_t capacity) {
  if (capacity == 0) {
    push(env, cns(env, staticEmptyArray()));
  } else {
    push(env, gen(env, NewMixedArray, cns(env, capacity)));
  }
}
Ejemplo n.º 3
0
void emitNewArray(HTS& env, int32_t capacity) {
  if (capacity == 0) {
    push(env, cns(env, staticEmptyArray()));
  } else {
    if (auto newCap = PackedArray::getMaxCapInPlaceFast(capacity)) {
      assert(newCap > static_cast<uint32_t>(capacity));
      capacity = newCap;
    }
    push(env, gen(env, NewArray, cns(env, capacity)));
  }
}
Ejemplo n.º 4
0
void autoTypecheck(const Unit* unit) {
  if (RuntimeOption::RepoAuthoritative ||
      !RuntimeOption::AutoTypecheck ||
      tl_doneAutoTypecheck ||
      !unit->isHHFile()) {
    return;
  }
  tl_doneAutoTypecheck = true;

  vm_call_user_func("\\HH\\Client\\typecheck_and_error", staticEmptyArray());
}
Ejemplo n.º 5
0
void emitCastArray(HTS& env) {
  auto const src = popC(env);
  push(
    env,
    [&] {
      if (src->isA(Type::Arr))  return src;
      if (src->isA(Type::Null)) return cns(env, staticEmptyArray());
      if (src->isA(Type::Bool)) return gen(env, ConvBoolToArr, src);
      if (src->isA(Type::Dbl))  return gen(env, ConvDblToArr, src);
      if (src->isA(Type::Int))  return gen(env, ConvIntToArr, src);
      if (src->isA(Type::Str))  return gen(env, ConvStrToArr, src);
      if (src->isA(Type::Obj))  return gen(env, ConvObjToArr, src);
      return gen(env, ConvCellToArr, src);
    }()
  );
}
Ejemplo n.º 6
0
TEST(Type, RuntimeType) {
  auto sd = StringData::MakeMalloced("", 0);
  SCOPE_EXIT { sd->destruct(); };

  HPHP::JIT::RuntimeType rt(sd);
  Type t = Type(rt);
  EXPECT_TRUE(t.subtypeOf(Type::Str));
  EXPECT_FALSE(t.subtypeOf(Type::Int));

  rt = HPHP::JIT::RuntimeType(staticEmptyArray());
  t = Type(rt);
  EXPECT_TRUE(t.subtypeOf(Type::Arr));
  EXPECT_FALSE(t.subtypeOf(Type::Str));

  rt = HPHP::JIT::RuntimeType(true);
  t = Type(rt);
  EXPECT_TRUE(t.subtypeOf(Type::Bool));
  EXPECT_FALSE(t.subtypeOf(Type::Obj));

  rt = HPHP::JIT::RuntimeType((int64_t) 1);
  t = Type(rt);
  EXPECT_TRUE(t.subtypeOf(Type::Int));
  EXPECT_FALSE(t.subtypeOf(Type::Dbl));

  rt = HPHP::JIT::RuntimeType(DataType::KindOfObject,
                              DataType::KindOfInvalid);
  rt = rt.setKnownClass(SystemLib::s_TraversableClass);
  t = Type(rt);
  EXPECT_TRUE(t.subtypeOf(Type::Obj));
  EXPECT_FALSE(Type::Obj.subtypeOf(t));
  EXPECT_FALSE(Type::Int.subtypeOf(t));
  HPHP::JIT::RuntimeType rt1 =
    HPHP::JIT::RuntimeType(DataType::KindOfObject,
                              DataType::KindOfInvalid);
  rt1 = rt1.setKnownClass(SystemLib::s_IteratorClass);
  Type t1 = Type(rt1);
  EXPECT_TRUE(t1.subtypeOf(Type::Obj));
  EXPECT_TRUE(t1.subtypeOf(t));
  EXPECT_FALSE(Type::Obj.subtypeOf(t1));
  EXPECT_FALSE(t.subtypeOf(t1));
  EXPECT_FALSE(t.subtypeOf(Type::Str));
  EXPECT_FALSE(Type::Int.subtypeOf(t));
}
Ejemplo n.º 7
0
ArrayData* ArrayData::GetScalarArray(ArrayData* arr,
                                     const ScalarArrayKey& key) {
  if (arr->empty() && !arr->isDict()) return staticEmptyArray();
  assert(key == GetScalarArrayKey(arr));

  ArrayDataMap::accessor acc;
  if (s_arrayDataMap.insert(acc, key)) {
    ArrayData* ad;

    if (arr->isVectorData() && !arr->isPacked() && !arr->isDict()) {
      ad = PackedArray::ConvertStatic(arr);
    } else {
      ad = arr->copyStatic();
    }
    assert(ad->isStatic());
    ad->onSetEvalScalar();
    acc->second = ad;
  }
  return acc->second;
}
Ejemplo n.º 8
0
GlobalNameValueTableWrapper::GlobalNameValueTableWrapper(
  NameValueTable* tab) : NameValueTableWrapper(tab) {

  Variant arr(staticEmptyArray());
#define X(s,v) tab->set(makeStaticString(#s), v.asTypedValue());

  X(argc,                 init_null_variant);
  X(argv,                 init_null_variant);
  X(_SERVER,              arr);
  X(_GET,                 arr);
  X(_POST,                arr);
  X(_COOKIE,              arr);
  X(_FILES,               arr);
  X(_ENV,                 arr);
  X(_REQUEST,             arr);
  X(HTTP_RAW_POST_DATA,   init_null_variant);
  X(http_response_header, init_null_variant);
#undef X

  g_variables = this;
}
Ejemplo n.º 9
0
GlobalsArray::GlobalsArray(NameValueTable* tab)
  : ArrayData(kGlobalsKind)
  , m_tab(tab)
{
  Variant arr(staticEmptyArray());
#define X(s,v) tab->set(makeStaticString(#s), v.asTypedValue());

  X(argc,                 init_null_variant);
  X(argv,                 init_null_variant);
  X(_SERVER,              arr);
  X(_GET,                 arr);
  X(_POST,                arr);
  X(_COOKIE,              arr);
  X(_FILES,               arr);
  X(_ENV,                 arr);
  X(_REQUEST,             arr);
  X(_SESSION,             arr);
  X(HTTP_RAW_POST_DATA,   init_null_variant);
#undef X

  g_variables.set(this);
  assertx(hasExactlyOneRef());
}
Ejemplo n.º 10
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;
}
Ejemplo n.º 11
0
NEVER_INLINE
ArrayData* EmptyArray::Copy(const ArrayData*) { return staticEmptyArray(); }
Ejemplo n.º 12
0
ArrayData *ArrayData::Create() {
  return staticEmptyArray();
}
Ejemplo n.º 13
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;
}
Ejemplo n.º 14
0
ArrayData* ArrayData::GetScalarArray(ArrayData* arr) {
  if (arr->empty() && !arr->isDict()) return staticEmptyArray();
  auto key = GetScalarArrayKey(arr);
  return GetScalarArray(arr, key);
}