Example #1
0
static void recordNameAndArgs(const SrcKey& sk,
                              const StringData* name,
                              int numArgs) {
  CallRecord cr;
  cr.m_type = EncodedNameAndArgs;
  cr.m_encodedName = encodeCallAndArgs(name, numArgs);
  s_callDB.insert(std::make_pair(sk, cr));
}
Example #2
0
static void recordFunc(const SrcKey sk,
                       const Func* func) {
  FTRACE(2, "annotation: recordFunc: {}@{} {}\n",
         sk.unit()->filepath()->data(),
         sk.offset(),
         func->fullName()->data());

  s_callDB.insert(std::make_pair(sk, func));
}
Example #3
0
static void recordFunc(NormalizedInstruction& i,
                       const SrcKey& sk,
                       const Func* func) {
  FTRACE(2, "annotation: recordFunc: {}@{} {}\n",
         i.m_unit->filepath()->data(),
         sk.offset(),
         func->fullName()->data());

  CallRecord cr;
  cr.m_type = Function;
  cr.m_func = func;
  s_callDB.insert(std::make_pair(sk, cr));
  i.directCall = true;
}
Example #4
0
namespace Transl {

static const Trace::Module TRACEMOD = Trace::trans;

/*
 * A mapping from FCall instructions to the statically-known StringData*
 * that they're calling. Used to accelerate our FCall translations.
 */
enum CallRecordType {
  EncodedNameAndArgs,
  Function
};
struct CallRecord {
  CallRecordType m_type;
  union {
    const StringData* m_encodedName;
    const Func* m_func;
  };
};

typedef hphp_hash_map<SrcKey, CallRecord, SrcKey> CallDB;
static CallDB s_callDB;
/* record the max number of args to enable invalidation */
static int s_maxNumArgs;
int getMaxEncodedArgs() { return s_maxNumArgs; }

const StringData*
encodeCallAndArgs(const StringData* name, int numArgs) {
  char numArgsBuf[16];
  if (numArgs > s_maxNumArgs) s_maxNumArgs = numArgs;
  snprintf(numArgsBuf, 15, "@%d@", numArgs);
  String s = String(numArgsBuf) + String(name->data());
  return StringData::GetStaticString(s.get());
}

static void
decodeNameAndArgs(const StringData* enc, string& outName, int& outNumArgs) {
  const char* numArgs = strchr(enc->data(), '@');
  assert(numArgs && *numArgs =='@');
  numArgs++;
  outNumArgs = atoi(numArgs);
  const char* name = strchr(numArgs, '@');
  assert(name && *name == '@');
  name++;
  outName = name;
}

static void recordNameAndArgs(const SrcKey& sk,
                              const StringData* name,
                              int numArgs) {
  CallRecord cr;
  cr.m_type = EncodedNameAndArgs;
  cr.m_encodedName = encodeCallAndArgs(name, numArgs);
  s_callDB.insert(std::make_pair(sk, cr));
}

static void recordFunc(NormalizedInstruction& i,
                       const SrcKey& sk,
                       const Func* func) {
  FTRACE(2, "annotation: recordFunc: {}@{} {}\n",
         i.m_unit->filepath()->data(),
         sk.offset(),
         func->fullName()->data());

  CallRecord cr;
  cr.m_type = Function;
  cr.m_func = func;
  s_callDB.insert(std::make_pair(sk, cr));
  i.directCall = true;
}

static void recordActRecPush(NormalizedInstruction& i,
                             const Unit* unit,
                             const StringData* name,
                             const StringData* clsName,
                             bool staticCall) {
  const SrcKey& sk = i.source;
  FTRACE(2, "annotation: recordActRecPush: {}@{} {}{}{} ({}static)\n",
         unit->filepath()->data(),
         sk.offset(),
         clsName ? clsName->data() : "",
         clsName ? "::" : "",
         name,
         !staticCall ? "non" : "");

  SrcKey next(sk);
  next.advance(unit);
  const FPIEnt *fpi = curFunc()->findFPI(next.offset());
  assert(fpi);
  assert(name->isStatic());
  assert(sk.offset() == fpi->m_fpushOff);
  SrcKey fcall = sk;
  fcall.m_offset = fpi->m_fcallOff;
  assert(isFCallStar(*unit->at(fcall.offset())));
  if (clsName) {
    const Class* cls = Unit::lookupUniqueClass(clsName);
    bool magic = false;
    const Func* func = lookupImmutableMethod(cls, name, magic, staticCall);
    if (func) {
      recordFunc(i, fcall, func);
    }
    return;
  }
  const Func* func = Unit::lookupFunc(name);
  if (func && func->isNameBindingImmutable(unit)) {
    // this will never go into a call cache, so we dont need to
    // encode the args. it will be used in OpFCall below to
    // set the i->funcd.
    recordFunc(i, fcall, func);
  } else {
    // It's not enough to remember the function name; we also need to encode
    // the number of arguments and current flag disposition.
    int numArgs = getImm(unit->at(sk.offset()), 0).u_IVA;
    recordNameAndArgs(fcall, name, numArgs);
  }
}

void annotate(NormalizedInstruction* i) {
  switch(i->op()) {
    case OpFPushObjMethodD:
    case OpFPushClsMethodD:
    case OpFPushClsMethodF:
    case OpFPushFuncD: {
      // When we push predictable action records, we can use a simpler
      // translation for their corresponding FCall.
      const StringData* className = nullptr;
      const StringData* funcName = nullptr;
      if (i->op() == OpFPushFuncD) {
      	funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
      } else if (i->op() == OpFPushObjMethodD) {
        if (i->inputs[0]->valueType() != KindOfObject) break;
        const Class* cls = i->inputs[0]->rtt.valueClass();
        if (!cls) break;
        funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
        className = cls->name();
      } else if (i->op() == OpFPushClsMethodF) {
        if (!i->inputs[1]->isString() ||
            i->inputs[1]->rtt.valueString() == nullptr ||
            i->inputs[0]->valueType() != KindOfClass) {
          break;
        }
        const Class* cls = i->inputs[0]->rtt.valueClass();
        if (!cls) break;
        funcName = i->inputs[1]->rtt.valueString();
        className = cls->name();
      } else {
        assert(i->op() == OpFPushClsMethodD);
        funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
        className = curUnit()->lookupLitstrId(i->imm[2].u_SA);
      }
      assert(funcName->isStatic());
      recordActRecPush(*i, curUnit(), funcName, className,
                       i->op() == OpFPushClsMethodD ||
                       i->op() == OpFPushClsMethodF);
    } break;
    case OpFCall:
    case OpFCallArray: {
      CallRecord callRec;
      if (mapGet(s_callDB, i->source, &callRec)) {
        if (callRec.m_type == Function) {
          i->funcd = callRec.m_func;
        } else {
          assert(callRec.m_type == EncodedNameAndArgs);
          i->funcName = callRec.m_encodedName;
        }
      } else {
        i->funcName = nullptr;
      }
    } break;
    default: break;
  }
}

const StringData*
fcallToFuncName(const NormalizedInstruction* i) {
  CallRecord callRec;
  if (mapGet(s_callDB, i->source, &callRec)) {
    if (callRec.m_type == Function) {
      return callRec.m_func->name();
    }
    string name;
    int numArgs;
    decodeNameAndArgs(callRec.m_encodedName, name, numArgs);
    return StringData::GetStaticString(name.c_str());
  }
  return nullptr;
}

} } }
Example #5
0
void recordFunc(const SrcKey& sk, const Func* func) {
  CallRecord cr;
  cr.m_type = Function;
  cr.m_func = func;
  s_callDB.insert(std::make_pair(sk, cr));
}
Example #6
0
namespace Transl {

static const Trace::Module TRACEMOD = Trace::trans;

/*
 * A mapping from FCall instructions to the statically-known StringData*
 * that they're calling. Used to accelerate our FCall translations.
 */
enum CallRecordType {
  EncodedNameAndArgs,
  Function
};
struct CallRecord {
  CallRecordType m_type;
  union {
    const StringData* m_encodedName;
    const Func* m_func;
  };
};

typedef hphp_hash_map<SrcKey, CallRecord, SrcKey> CallDB;
static CallDB s_callDB;
/* record the max number of args to enable invalidation */
static int s_maxNumArgs;
int getMaxEncodedArgs() { return s_maxNumArgs; }

const StringData*
encodeCallAndArgs(const StringData* name, int numArgs) {
  char numArgsBuf[16];
  if (numArgs > s_maxNumArgs) s_maxNumArgs = numArgs;
  snprintf(numArgsBuf, 15, "@%d@", numArgs);
  String s = String(numArgsBuf) + String(name->data());
  return StringData::GetStaticString(s.get());
}

void recordNameAndArgs(const SrcKey& sk, const StringData* name, int numArgs) {
  CallRecord cr;
  cr.m_type = EncodedNameAndArgs;
  cr.m_encodedName = encodeCallAndArgs(name, numArgs);
  s_callDB.insert(std::make_pair(sk, cr));
}

void recordFunc(const SrcKey& sk, const Func* func) {
  CallRecord cr;
  cr.m_type = Function;
  cr.m_func = func;
  s_callDB.insert(std::make_pair(sk, cr));
}

static void recordActRecPush(const SrcKey& sk,
                             const Unit* unit,
                             const FPIEnt* fpi,
                             const StringData* name,
                             const StringData* clsName,
                             bool staticCall) {
  // sk is the address of a FPush* of the function whose static name
  // is name. The boundaries of FPI regions are such that we can't quite
  // find the FCall that matches this FuncD without decoding forward to
  // the end; this is not ideal, but is hopefully affordable at translation
  // time.
  ASSERT(name->isStatic());
  ASSERT(sk.offset() == fpi->m_fpushOff);
  SrcKey fcall;
  SrcKey next(sk);
  next.advance(unit);
  do {
    if (*unit->at(next.offset()) == OpFCall) {
      // Remember the last FCall in the region; the region might end
      // with UnboxR, e.g.
      fcall = next;
    }
    next.advance(unit);
  } while (next.offset() <= fpi->m_fcallOff);
  ASSERT(*unit->at(fcall.offset()) == OpFCall);
  if (clsName) {
    const Class* cls = Unit::lookupClass(clsName);
    bool magic = false;
    const Func* func = lookupImmutableMethod(cls, name, magic, staticCall);
    if (func) {
      recordFunc(fcall, func);
    }
    return;
  }
  const Func* func = Unit::lookupFunc(name);
  if (func && func->isNameBindingImmutable(unit)) {
    // this will never go into a call cache, so we dont need to
    // encode the args. it will be used in OpFCall below to
    // set the i->funcd.
    recordFunc(fcall, func);
  } else {
    // It's not enough to remember the function name; we also need to encode
    // the number of arguments and current flag disposition.
    int numArgs = getImm(unit->at(sk.offset()), 0).u_IVA;
    recordNameAndArgs(fcall, name, numArgs);
  }
}

void annotate(NormalizedInstruction* i) {
  switch(i->op()) {
    case OpFPushObjMethodD:
    case OpFPushClsMethodD:
    case OpFPushClsMethodF:
    case OpFPushFuncD: {
      // When we push predictable action records, we can use a simpler
      // translation for their corresponding FCall.
      SrcKey next(i->source);
      next.advance(curUnit());
      const StringData* className = NULL;
      const StringData* funcName = NULL;
      if (i->op() == OpFPushFuncD) {
      	funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
      } else if (i->op() == OpFPushObjMethodD) {
        if (i->inputs[0]->valueType() != KindOfObject) break;
        const Class* cls = i->inputs[0]->rtt.valueClass();
        if (!cls) break;
        funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
        className = cls->name();
      } else if (i->op() == OpFPushClsMethodF) {
        if (i->inputs[1]->rtt.valueString() == NULL ||
            i->inputs[0]->valueType() != KindOfClass) {
          break;
        }
        const Class* cls = i->inputs[0]->rtt.valueClass();
        if (!cls) break;
        funcName = i->inputs[1]->rtt.valueString();
        className = cls->name();
      } else {
        ASSERT(i->op() == OpFPushClsMethodD);
        funcName = curUnit()->lookupLitstrId(i->imm[1].u_SA);
        className = curUnit()->lookupLitstrId(i->imm[2].u_SA);
      }
      ASSERT(funcName->isStatic());
      const FPIEnt *fe = curFunc()->findFPI(next.m_offset);
      ASSERT(fe);
      recordActRecPush(i->source, curUnit(), fe, funcName, className,
                       i->op() == OpFPushClsMethodD ||
                       i->op() == OpFPushClsMethodF);
    } break;
    case OpFCall: {
      CallRecord callRec;
      if (mapGet(s_callDB, i->source, &callRec)) {
        if (callRec.m_type == Function) {
          i->funcd = callRec.m_func;
        } else {
          ASSERT(callRec.m_type == EncodedNameAndArgs);
          i->funcName = callRec.m_encodedName;
        }
      } else {
        i->funcName = NULL;
      }
    } break;
    default: break;
  }
}

} } }
Example #7
0
namespace JIT {

TRACE_SET_MOD(trans);

const StaticString s_empty("");

/*
 * A mapping from FCall instructions to the statically-known Func* that they're
 * calling. Used to accelerate our FCall translations.
 */
typedef hphp_hash_map<SrcKey,const Func*,SrcKey::Hasher> CallDB;
static CallDB s_callDB;

static void recordFunc(const SrcKey sk,
                       const Func* func) {
  FTRACE(2, "annotation: recordFunc: {}@{}{} {}\n",
         sk.unit()->filepath()->data(),
         sk.offset(),
         sk.resumed() ? "r" : "",
         func->fullName()->data());

  s_callDB.insert(std::make_pair(sk, func));
}

static const Func* lookupDirectFunc(SrcKey const sk,
                                    const StringData* fname,
                                    const StringData* clsName,
                                    bool staticCall) {
  if (clsName && !clsName->isame(s_empty.get())) {
    auto const cls = Unit::lookupUniqueClass(clsName);
    bool magic = false;
    auto const ctx = sk.func()->cls();
    return lookupImmutableMethod(cls, fname, magic, staticCall, ctx);
  }
  auto const func = Unit::lookupFunc(fname);
  if (func && func->isNameBindingImmutable(sk.unit())) {
    return func;
  }
  return nullptr;
}

static void recordActRecPush(const SrcKey sk,
                             const StringData* name,
                             const StringData* clsName,
                             bool staticCall) {
  auto unit = sk.unit();
  FTRACE(2, "annotation: recordActRecPush: {}@{} {}{}{} ({}static)\n",
         unit->filepath()->data(),
         sk.offset(),
         clsName ? clsName->data() : "",
         clsName ? "::" : "",
         name,
         !staticCall ? "non" : "");

  SrcKey next(sk);
  next.advance(unit);
  const FPIEnt *fpi = sk.func()->findFPI(next.offset());
  assert(fpi);
  assert(name->isStatic());
  assert(sk.offset() == fpi->m_fpushOff);
  auto const fcall = SrcKey { sk.func(), fpi->m_fcallOff, sk.resumed() };
  assert(isFCallStar(*reinterpret_cast<const Op*>(unit->at(fcall.offset()))));
  auto const func = lookupDirectFunc(sk, name, clsName, staticCall);
  if (func) {
    recordFunc(fcall, func);
  }
}

void annotate(NormalizedInstruction* i) {
  switch(i->op()) {
    case OpFPushObjMethodD:
    case OpFPushClsMethodD:
    case OpFPushClsMethodF:
    case OpFPushCtorD:
    case OpFPushCtor:
    case OpFPushFuncD: {
      if (RuntimeOption::RepoAuthoritative && Repo::global().UsedHHBBC) {
        break;
      }

      // When we push predictable activation records, we can use a simpler
      // translation for their corresponding FCall.
      const StringData* className = nullptr;
      const StringData* funcName = nullptr;
      if (i->op() == OpFPushFuncD) {
        funcName = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
      } else if (i->op() == OpFPushObjMethodD) {
        if (i->inputs[0]->valueType() != KindOfObject) break;
        const Class* cls = i->inputs[0]->rtt.valueClass();
        if (!cls) break;
        funcName = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
        className = cls->name();
      } else if (i->op() == OpFPushClsMethodF) {
        if (!i->inputs[1]->isString() ||
            i->inputs[1]->rtt.valueString() == nullptr ||
            i->inputs[0]->valueType() != KindOfClass) {
          break;
        }
        const Class* cls = i->inputs[0]->rtt.valueClass();
        if (!cls) break;
        funcName = i->inputs[1]->rtt.valueString();
        className = cls->name();
      } else if (i->op() == OpFPushClsMethodD) {
        funcName = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
        className = i->m_unit->lookupLitstrId(i->imm[2].u_SA);
      } else if (i->op() == OpFPushCtorD) {
        className = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
        const Class* cls = Unit::lookupUniqueClass(className);
        if (!cls) break;
        auto const ctor = cls->getCtor();
        funcName = ctor->name();
        className = ctor->cls()->name();
      } else {
        assert(i->op() == OpFPushCtor);
        const Class* cls = i->inputs[0]->rtt.valueClass();
        if (!cls) break;
        auto const ctor = cls->getCtor();
        funcName = ctor->name();
        className = ctor->cls()->name();
      }
      assert(funcName->isStatic());
      recordActRecPush(i->source, funcName, className,
                       i->op() == OpFPushClsMethodD ||
                       i->op() == OpFPushClsMethodF);
    } break;
    case OpFCall:
    case OpFCallArray: {
      if (RuntimeOption::RepoAuthoritative && Repo::global().UsedHHBBC) {
        break;
      }
      if (auto const func = folly::get_ptr(s_callDB, i->source)) {
        i->funcd = *func;
      }
    } break;
    default: break;

    case Op::FCallD: {
      auto const fpi      = i->func()->findFPI(i->source.offset());
      auto const pushOp   = i->m_unit->getOpcode(fpi->m_fpushOff);
      auto const clsName  = i->m_unit->lookupLitstrId(i->imm[1].u_SA);
      auto const funcName = i->m_unit->lookupLitstrId(i->imm[2].u_SA);
      auto const isStatic = pushOp == Op::FPushClsMethodD ||
                            pushOp == Op::FPushClsMethodF ||
                            pushOp == Op::FPushClsMethod;

      /*
       * Currently we don't attempt any of this for FPushClsMethod
       * because lookupImmutableMethod is only for situations that
       * don't involve LSB.
       */
      auto const func =
        pushOp == Op::FPushClsMethod
          ? nullptr
          : lookupDirectFunc(i->source, funcName, clsName, isStatic);

      if (func) {
        FTRACE(1, "found direct func (%s) for FCallD\n",
          func->fullName()->data());
        i->funcd = func;
      }
      break;
    }
  }
}

const StringData*
fcallToFuncName(const NormalizedInstruction* i) {
  if (i->op() == Op::FCallD) {
    return i->m_unit->lookupLitstrId(i->imm[2].u_SA);
  }
  if (RuntimeOption::RepoAuthoritative && Repo::global().UsedHHBBC) {
    return nullptr;
  }
  if (auto const func = folly::get_ptr(s_callDB, i->source)) {
    return (*func)->name();
  }
  return nullptr;
}

} }