Beispiel #1
0
const uint8_t* ImmVector::findLastMember() const {
  ASSERT(m_length > 0);

  // Loop that does basically the same as numStackValues(), except
  // stop at the last.
  const uint8_t* vec = m_start;
  const LocationCode locCode = LocationCode(*vec++);
  const int numLocImms = numLocationCodeImms(locCode);
  for (int i = 0; i < numLocImms; ++i) {
    decodeVariableSizeImm(&vec);
  }

  for (;;) {
    const uint8_t* ret = vec;
    MemberCode code = MemberCode(*vec++);
    if (memberCodeHasImm(code)) {
      decodeVariableSizeImm(&vec);
    }
    if (vec - m_start == m_length) {
      return ret;
    }
    ASSERT(vec - m_start < m_length);
  }

  NOT_REACHED();
}
Beispiel #2
0
ArgUnion getImm(PC const origPC, int idx, const Unit* unit) {
  auto pc = origPC;
  auto const UNUSED op = decode_op(pc);
  assert(idx >= 0 && idx < numImmediates(op));
  ArgUnion retval;
  retval.u_NA = 0;
  int cursor = 0;
  for (cursor = 0; cursor < idx; cursor++) {
    // Advance over this immediate.
    pc += immSize(origPC, cursor);
  }
  always_assert(cursor == idx);
  auto const type = immType(op, idx);
  if (type == IVA || type == LA || type == IA) {
    retval.u_IVA = decodeVariableSizeImm(&pc);
  } else if (type == KA) {
    assert(unit != nullptr);
    retval.u_KA = decode_member_key(pc, unit);
  } else if (!immIsVector(op, cursor)) {
    always_assert(type != RATA);  // Decode RATAs with a different function.
    memcpy(&retval.bytes, pc, immSize(origPC, idx));
  }
  always_assert(numImmediates(op) > idx);
  return retval;
}
Beispiel #3
0
MInstrLocation getMLocation(const Op* opcode) {
  auto immVec = getImmVector(opcode);
  auto vec = immVec.vec();
  auto const lcode = LocationCode(*vec++);
  auto const imm = numLocationCodeImms(lcode) ? decodeVariableSizeImm(&vec)
                                              : 0;
  return {lcode, imm};
}
Beispiel #4
0
bool Unit::MetaHandle::nextArg(MetaInfo& info) {
  ASSERT(index && cur && ptr);
  uint8* end = (uint8*)index + index[*index + cur + 2];
  ASSERT(ptr <= end);
  if (ptr == end) return false;
  info.m_kind = (Unit::MetaInfo::Kind)*ptr++;
  info.m_arg = *ptr++;
  info.m_data = decodeVariableSizeImm(&ptr);
  return true;
}
Beispiel #5
0
// A ContSuspend marks a return point from a generator or async
// function. Execution will resume at this function later, and the
// Continuation associated with this function can predict where.
void CmdNext::setupStepCont(ActRec* fp, PC pc) {
  // ContSuspend is followed by the label where execution will continue.
  DEBUG_ONLY auto ops = reinterpret_cast<const Op*>(pc);
  assert(ops[0] == OpContSuspend || ops[0] == OpContSuspendK);
  ++pc;
  int32_t label = decodeVariableSizeImm(&pc);
  c_Continuation* cont = frame_continuation(fp);
  Offset nextInst = cont->getExecutionOffset(label);
  assert(nextInst != InvalidAbsoluteOffset);
  m_stepContTag = cont->actRec();
  TRACE(2, "CmdNext: patch for cont step at '%s' offset %d\n",
        fp->m_func->fullName()->data(), nextInst);
  m_stepCont = StepDestination(fp->m_func->unit(), nextInst);
}
Beispiel #6
0
// Await / Yield opcodes mark a suspend points of async functions and
// generators. Execution will resume at this function later after the
// opcode.
void CmdNext::setupStepSuspend(ActRec* fp, PC pc) {
  // Yield is followed by the label where execution will continue.
  auto const op = decode_op(pc);
  assert(op == OpAwait || op == OpYield || op == OpYieldK);
  if (op == OpAwait) {
    decodeVariableSizeImm(&pc);
  }
  Offset nextInst = fp->func()->unit()->offsetOf(pc);
  assert(nextInst != InvalidAbsoluteOffset);
  m_stepResumableId = fp;
  TRACE(2, "CmdNext: patch for resumable step at '%s' offset %d\n",
        fp->m_func->fullName()->data(), nextInst);
  m_stepResumable = StepDestination(fp->m_func->unit(), nextInst);
}
Beispiel #7
0
int64 decodeMemberCodeImm(const unsigned char** immPtr, MemberCode mcode) {
  switch (mcode) {
    case MEL:
    case MPL:
      return decodeVariableSizeImm(immPtr);

    case MET:
    case MPT:
      return decodeImm<int32>(immPtr);

    case MEI:
      return decodeImm<int64>(immPtr);

    default:
      not_reached();
  }
}
Beispiel #8
0
std::vector<MVectorItem> getMVector(const Op* opcode) {
  auto immVec = getImmVector(opcode);
  std::vector<MVectorItem> result;
  auto it = immVec.vec();
  auto end = it + immVec.size();

  // Skip the LocationCode and its immediate
  auto const lcode = LocationCode(*it++);
  if (numLocationCodeImms(lcode)) decodeVariableSizeImm(&it);

  while (it < end) {
    auto const mcode = MemberCode(*it++);
    auto const imm = memberCodeHasImm(mcode) ? decodeMemberCodeImm(&it, mcode)
                                             : 0;
    result.push_back({mcode, imm});
  }

  return result;
}
Beispiel #9
0
ArgUnion getImm(const Opcode* opcode, int idx) {
  const Opcode* p = opcode + 1;
  assert(idx >= 0 && idx < numImmediates(*opcode));
  ArgUnion retval;
  retval.u_NA = 0;
  int cursor = 0;
  for (cursor = 0; cursor < idx; cursor++) {
    // Advance over this immediate.
    p += immSize(opcode, cursor);
  }
  always_assert(cursor == idx);
  ArgType type = immType(*opcode, idx);
  if (type == IVA || type == HA || type == IA) {
    retval.u_IVA = decodeVariableSizeImm(&p);
  } else if (!immIsVector(*opcode, cursor)) {
    memcpy(&retval.bytes, p, immSize(opcode, idx));
  }
  always_assert(numImmediates(*opcode) > idx);
  return retval;
}
Beispiel #10
0
ArgUnion getImm(const Op* opcode, int idx) {
  const Op* p = opcode + 1;
  assert(idx >= 0 && idx < numImmediates(*opcode));
  ArgUnion retval;
  retval.u_NA = 0;
  int cursor = 0;
  for (cursor = 0; cursor < idx; cursor++) {
    // Advance over this immediate.
    p += immSize(opcode, cursor);
  }
  always_assert(cursor == idx);
  auto const type = immType(*opcode, idx);
  if (type == IVA || type == LA || type == IA) {
    retval.u_IVA = decodeVariableSizeImm((const uint8_t**)&p);
  } else if (!immIsVector(*opcode, cursor)) {
    always_assert(type != RATA);  // Decode RATAs with a different function.
    memcpy(&retval.bytes, p, immSize(opcode, idx));
  }
  always_assert(numImmediates(*opcode) > idx);
  return retval;
}
Beispiel #11
0
int64_t decodeMemberCodeImm(const unsigned char** immPtr, MemberCode mcode) {
  switch (mcode) {
    case MEL:
    case MPL:
      return decodeVariableSizeImm(immPtr);

    case MET:
    case MPT:
    case MQT:
      return decodeImm<int32_t>(immPtr);

    case MEI:
      return decodeImm<int64_t>(immPtr);

    case MEC:
    case MPC:
    case MW:
    case InvalidMemberCode:
      break;
  }
  not_reached();
}
Beispiel #12
0
void print_instr(Output& out, const FuncInfo& finfo, PC pc) {
  auto const startPc = pc;

  auto rel_label = [&] (Offset off) {
    auto const tgt = startPc - finfo.unit->at(0) + off;
    return jmp_label(finfo, tgt);
  };

  auto print_minstr = [&] {
    auto const immVec = ImmVector::createFromStream(pc);
    pc += immVec.size() + sizeof(int32_t) + sizeof(int32_t);
    auto vec = immVec.vec();
    auto const lcode = static_cast<LocationCode>(*vec++);

    out.fmt(" <{}", locationCodeString(lcode));
    if (numLocationCodeImms(lcode)) {
      always_assert(numLocationCodeImms(lcode) == 1);
      out.fmt(":${}", loc_name(finfo, decodeVariableSizeImm(&vec)));
    }

    while (vec < pc) {
      auto const mcode = static_cast<MemberCode>(*vec++);
      out.fmt(" {}", memberCodeString(mcode));
      auto const imm = [&] { return decodeMemberCodeImm(&vec, mcode); };
      switch (memberCodeImmType(mcode)) {
      case MCodeImm::None:
        break;
      case MCodeImm::Local:
        out.fmt(":${}", loc_name(finfo, imm()));
        break;
      case MCodeImm::String:
        out.fmt(":{}", escaped(finfo.unit->lookupLitstrId(imm())));
        break;
      case MCodeImm::Int:
        out.fmt(":{}", imm());
        break;
      }
    }
    assert(vec == pc);

    out.fmt(">");
  };

  auto print_switch = [&] {
    auto const vecLen = decode<int32_t>(pc);
    out.fmt(" <");
    for (auto i = int32_t{0}; i < vecLen; ++i) {
      auto const off = decode<Offset>(pc);
      FTRACE(1, "sw label: {}\n", off);
      out.fmt("{}{}", i != 0 ? " " : "", rel_label(off));
    }
    out.fmt(">");
  };

  auto print_sswitch = [&] {
    auto const vecLen = decode<int32_t>(pc);
    out.fmt(" <");
    for (auto i = int32_t{0}; i < vecLen; ++i) {
      auto const strId  = decode<Id>(pc);
      auto const offset = decode<Offset>(pc);
      out.fmt("{}{}:{}",
        i != 0 ? " " : "",
        strId == -1 ? "-" : escaped(finfo.unit->lookupLitstrId(strId)),
        rel_label(offset)
      );
    }
    out.fmt(">");
  };

  auto print_itertab = [&] {
    auto const vecLen = decode<int32_t>(pc);
    out.fmt(" <");
    for (auto i = int32_t{0}; i < vecLen; ++i) {
      auto const kind = static_cast<IterKind>(decode<int32_t>(pc));
      auto const id   = decode<int32_t>(pc);
      auto const kindStr = [&]() -> const char* {
        switch (kind) {
        case KindOfIter:   return "(Iter)";
        case KindOfMIter:  return "(MIter)";
        case KindOfCIter:  return "(CIter)";
        }
        not_reached();
      }();
      out.fmt("{}{} {}", i != 0 ? ", " : "", kindStr, id);
    }
    out.fmt(">");
  };

  auto print_stringvec = [&] {
    auto const vecLen = decode<int32_t>(pc);
    out.fmt(" <");
    for (auto i = uint32_t{0}; i < vecLen; ++i) {
      auto const str = finfo.unit->lookupLitstrId(decode<int32_t>(pc));
      out.fmt("{}{}", i != 0 ? " " : "", escaped(str));
    }
    out.fmt(">");
  };

#define IMM_MA     print_minstr();
#define IMM_BLA    print_switch();
#define IMM_SLA    print_sswitch();
#define IMM_ILA    print_itertab();
#define IMM_IVA    out.fmt(" {}", decodeVariableSizeImm(&pc));
#define IMM_I64A   out.fmt(" {}", decode<int64_t>(pc));
#define IMM_LA     out.fmt(" ${}", loc_name(finfo, decodeVariableSizeImm(&pc)));
#define IMM_IA     out.fmt(" {}", decodeVariableSizeImm(&pc));
#define IMM_DA     out.fmt(" {}", decode<double>(pc));
#define IMM_SA     out.fmt(" {}", \
                           escaped(finfo.unit->lookupLitstrId(decode<Id>(pc))));
#define IMM_AA     out.fmt(" @A_{}", decode<Id>(pc));
#define IMM_BA     out.fmt(" {}", rel_label(decode<Offset>(pc)));
#define IMM_OA(ty) out.fmt(" {}", \
                     subopToName(static_cast<ty>(decode<uint8_t>(pc))));
#define IMM_VSA    print_stringvec();

#define IMM_NA
#define IMM_ONE(x)           IMM_##x
#define IMM_TWO(x,y)         IMM_ONE(x)       IMM_ONE(y)
#define IMM_THREE(x,y,z)     IMM_TWO(x,y)     IMM_ONE(z)
#define IMM_FOUR(x,y,z,l)    IMM_THREE(x,y,z) IMM_ONE(l)

  out.indent();
#define O(opcode, imms, ...)                              \
  case Op::opcode:                                        \
    ++pc;                                                 \
    out.fmt("{}", #opcode);                               \
    IMM_##imms                                            \
    break;
  switch (*reinterpret_cast<const Op*>(pc)) { OPCODES }
#undef O

  assert(pc == startPc + instrLen(reinterpret_cast<const Op*>(startPc)));

#undef IMM_NA
#undef IMM_ONE
#undef IMM_TWO
#undef IMM_THREE
#undef IMM_FOUR

#undef IMM_MA
#undef IMM_BLA
#undef IMM_SLA
#undef IMM_ILA
#undef IMM_IVA
#undef IMM_I64A
#undef IMM_LA
#undef IMM_IA
#undef IMM_DA
#undef IMM_SA
#undef IMM_AA
#undef IMM_BA
#undef IMM_OA
#undef IMM_VSA

  out.nl();
}
Beispiel #13
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);
    }
  );
}