Exemple #1
0
OffsetSet instrSuccOffsets(Op* opc, const Unit* unit) {
  OffsetSet succBcOffs;
  Op* bcStart = (Op*)(unit->entry());

  if (!instrIsControlFlow(*opc)) {
    Offset succOff = opc + instrLen(opc) - bcStart;
    succBcOffs.insert(succOff);
    return succBcOffs;
  }

  if (instrAllowsFallThru(*opc)) {
    Offset succOff = opc + instrLen(opc) - bcStart;
    succBcOffs.insert(succOff);
  }

  if (isSwitch(*opc)) {
    foreachSwitchTarget(opc, [&](Offset& offset) {
        succBcOffs.insert(offset + opc - bcStart);
      });
  } else {
    Offset target = instrJumpTarget(bcStart, opc - bcStart);
    if (target != InvalidAbsoluteOffset) {
      succBcOffs.insert(target);
    }
  }
  return succBcOffs;
}
Exemple #2
0
OffsetSet instrSuccOffsets(PC opc, const Unit* unit) {
  OffsetSet succBcOffs;
  auto const bcStart = unit->entry();
  auto const op = peek_op(opc);

  if (!instrIsControlFlow(op)) {
    Offset succOff = opc + instrLen(opc) - bcStart;
    succBcOffs.insert(succOff);
    return succBcOffs;
  }

  if (instrAllowsFallThru(op)) {
    Offset succOff = opc + instrLen(opc) - bcStart;
    succBcOffs.insert(succOff);
  }

  if (isSwitch(op)) {
    foreachSwitchTarget(opc, [&](Offset offset) {
      succBcOffs.insert(offset + opc - bcStart);
    });
  } else {
    Offset target = instrJumpTarget(bcStart, opc - bcStart);
    if (target != InvalidAbsoluteOffset) {
      succBcOffs.insert(target);
    }
  }
  return succBcOffs;
}
Exemple #3
0
void
IRTranslator::translateBranchOp(const NormalizedInstruction& i) {
  auto const op = i.op();
  assert(op == OpJmpZ || op == OpJmpNZ);

  Offset takenOffset    = i.offset() + i.imm[0].u_BA;
  Offset fallthruOffset = i.offset() + instrLen((Op*)(i.pc()));

  auto jmpFlags = instrJmpFlags(i);

  if (i.nextOffset == takenOffset) {
    always_assert(RuntimeOption::EvalJitPGORegionSelector == "hottrace");
    // invert the branch
    if (op == OpJmpZ) {
      HHIR_EMIT(JmpNZ, fallthruOffset, jmpFlags);
    } else {
      HHIR_EMIT(JmpZ,  fallthruOffset, jmpFlags);
    }
    return;
  }

  if (op == OpJmpZ) {
    HHIR_EMIT(JmpZ,  takenOffset, jmpFlags);
  } else {
    HHIR_EMIT(JmpNZ, takenOffset, jmpFlags);
  }
}
Exemple #4
0
void
IRTranslator::translateBranchOp(const NormalizedInstruction& i) {
  auto const op = i.op();
  assert(op == OpJmpZ || op == OpJmpNZ);

  Offset takenOffset    = i.offset() + i.imm[0].u_BA;
  Offset fallthruOffset = i.offset() + instrLen((Op*)(i.pc()));
  assert(i.breaksTracelet ||
         i.nextOffset == takenOffset ||
         i.nextOffset == fallthruOffset);
  assert(!i.includeBothPaths || !i.breaksTracelet);

  if (i.breaksTracelet || i.nextOffset == fallthruOffset) {
    if (op == OpJmpZ) {
      HHIR_EMIT(JmpZ,  takenOffset, fallthruOffset, i.includeBothPaths);
    } else {
      HHIR_EMIT(JmpNZ, takenOffset, fallthruOffset, i.includeBothPaths);
    }
    return;
  }
  assert(i.nextOffset == takenOffset);
  // invert the branch
  if (op == OpJmpZ) {
    HHIR_EMIT(JmpNZ, fallthruOffset, takenOffset, i.includeBothPaths);
  } else {
    HHIR_EMIT(JmpZ,  fallthruOffset, takenOffset, i.includeBothPaths);
  }
}
Exemple #5
0
void printInstr(const Unit* unit, PC pc) {
  std::cout << "  " << std::setw(4) << (pc - unit->entry()) << ":" <<
               (isCF(pc) ? "C":" ") <<
               (isTF(pc) ? "T":" ") <<
               (isFF(pc) ? "F":" ") <<
               std::setw(3) << instrLen(pc) <<
               " " << instrToString(pc, unit) << std::endl;
}
Exemple #6
0
void printInstr(const Unit* unit, PC pc) {
  Opcode* op = (Opcode*)pc;
  std::cout << "  " << std::setw(4) << (pc - unit->entry()) << ":" <<
               (isCF(pc) ? "C":" ") <<
               (isTF(pc) ? "T":" ") <<
               (isFF(pc) ? "F":" ") <<
               std::setw(3) << instrLen(op) <<
               " " << instrToString(op, unit) << std::endl; 
}
Exemple #7
0
// Place internal breakpoints to get out of the current function. This may place
// multiple internal breakpoints, and it may place them more than one frame up.
// Some instructions can cause PHP to be invoked without an explicit call. A set
// which causes a destructor to run, a iteration init which causes an object's
// next() method to run, a RetC which causes destructors to run, etc. This
// recgonizes such cases and ensures we have internal breakpoints to cover the
// destination(s) of such instructions.
void CmdFlowControl::setupStepOuts() {
  // Existing step outs should be cleaned up before making new ones.
  assert(!hasStepOuts());
  auto fp = g_context->getFP();
  if (!fp) return; // No place to step out to!
  Offset returnOffset;
  bool fromVMEntry;
  while (!hasStepOuts()) {
    fp = g_context->getPrevVMState(fp, &returnOffset, nullptr, &fromVMEntry);
    // If we've run off the top of the stack, just return having setup no
    // step outs. This will cause cmds like Next and Out to just let the program
    // run, which is appropriate.
    if (!fp) break;
    Unit* returnUnit = fp->m_func->unit();
    PC returnPC = returnUnit->at(returnOffset);
    TRACE(2, "CmdFlowControl::setupStepOuts: at '%s' offset %d opcode %s\n",
          fp->m_func->fullName()->data(), returnOffset,
          opcodeToName(*reinterpret_cast<const Op*>(returnPC)));
    // Don't step out to generated functions, keep looking.
    if (fp->m_func->line1() == 0) continue;
    if (fromVMEntry) {
      TRACE(2, "CmdFlowControl::setupStepOuts: VM entry\n");
      // We only execute this for opcodes which invoke more PHP, and that does
      // not include switches. Thus, we'll have at most two destinations.
      assert(!isSwitch(*reinterpret_cast<const Op*>(returnPC)) &&
        (numSuccs(reinterpret_cast<const Op*>(returnPC)) <= 2));
      // Set an internal breakpoint after the instruction if it can fall thru.
      if (instrAllowsFallThru(*reinterpret_cast<const Op*>(returnPC))) {
        Offset nextOffset = returnOffset + instrLen((Op*)returnPC);
        TRACE(2, "CmdFlowControl: step out to '%s' offset %d (fall-thru)\n",
              fp->m_func->fullName()->data(), nextOffset);
        m_stepOut1 = StepDestination(returnUnit, nextOffset);
      }
      // Set an internal breakpoint at the target of a control flow instruction.
      // A good example of a control flow op that invokes PHP is IterNext.
      if (instrIsControlFlow(*reinterpret_cast<const Op*>(returnPC))) {
        Offset target =
          instrJumpTarget(reinterpret_cast<const Op*>(returnPC), 0);
        if (target != InvalidAbsoluteOffset) {
          Offset targetOffset = returnOffset + target;
          TRACE(2, "CmdFlowControl: step out to '%s' offset %d (jump target)\n",
                fp->m_func->fullName()->data(), targetOffset);
          m_stepOut2 = StepDestination(returnUnit, targetOffset);
        }
      }
      // If we have no place to step out to, then unwind another frame and try
      // again. The most common case that leads here is Ret*, which does not
      // fall-thru and has no encoded target.
    } else {
      TRACE(2, "CmdFlowControl: step out to '%s' offset %d\n",
            fp->m_func->fullName()->data(), returnOffset);
      m_stepOut1 = StepDestination(returnUnit, returnOffset);
    }
  }
}
int PCFilter::addRanges(const Unit* unit, const OffsetRangeVec& offsets) {
  int counter = 0;
  for (auto range = offsets.cbegin(); range != offsets.cend(); ++range) {
    for (PC pc = unit->at(range->m_base); pc < unit->at(range->m_past);
         pc += instrLen((Opcode*)pc)) {
      addPC(pc);
      counter++;
    }
  }
  return counter;
}
Exemple #9
0
int PCFilter::addRanges(const Unit* unit, const OffsetRangeVec& offsets) {
  int counter = 0;
  for (OffsetRangeVec::const_iterator it = offsets.begin();
       it != offsets.end(); ++it) {
    for (PC pc = unit->at(it->m_base); pc < unit->at(it->m_past);
         pc += instrLen((Opcode*)pc)) {
      addPC(pc);
      counter++;
    }
  }
  return counter;
}
// Ensure we interpret all code at the given offsets. This sets up a guard for
// each piece of tranlated code to ensure we punt ot the interpreter when the
// debugger is attached.
static void blacklistRangesInJit(const Unit* unit,
                                 const OffsetRangeVec& offsets) {
  for (OffsetRangeVec::const_iterator it = offsets.begin();
       it != offsets.end(); ++it) {
    for (PC pc = unit->at(it->m_base); pc < unit->at(it->m_past);
         pc += instrLen((Opcode*)pc)) {
      transl()->addDbgBLPC(pc);
    }
  }
  if (!transl()->addDbgGuards(unit)) {
    Logger::Warning("Failed to set breakpoints in Jitted code");
  }
}
Exemple #11
0
/*
 * This function returns the offset of instruction i's branch target.
 * This is normally the offset corresponding to the branch being
 * taken.  However, if i does not break a trace and it's followed in
 * the trace by the instruction in the taken branch, then this
 * function returns the offset of the i's fall-through instruction.
 * In that case, the invertCond output argument is set to true;
 * otherwise it's set to false.
 */
static Offset getBranchTarget(const NormalizedInstruction& i,
                              bool& invertCond) {
  assert(instrJumpOffset((Op*)(i.pc())) != nullptr);
  Offset targetOffset = i.offset() + i.imm[1].u_BA;
  invertCond = false;

  if (!i.endsRegion && i.nextOffset == targetOffset) {
    invertCond = true;
    Offset fallthruOffset = i.offset() + instrLen((Op*)i.pc());
    targetOffset = fallthruOffset;
  }

  return targetOffset;
}
// Adds a range of PCs to the filter given a collection of offset ranges.
// Omit PCs which have opcodes that don't pass the given opcode filter.
void PCFilter::addRanges(const Unit* unit, const OffsetRangeVec& offsets,
                         OpcodeFilter isOpcodeAllowed) {
  for (auto range = offsets.cbegin(); range != offsets.cend(); ++range) {
    TRACE(3, "\toffsets [%d, %d)\n", range->m_base, range->m_past);
    for (PC pc = unit->at(range->m_base); pc < unit->at(range->m_past);
         pc += instrLen(pc)) {
      if (isOpcodeAllowed(*pc)) {
        TRACE(3, "\t\tpc %p\n", pc);
        addPC(pc);
      } else {
        TRACE(3, "\t\tpc %p -- skipping (offset %d)\n", pc, unit->offsetOf(pc));
      }
    }
  }
}
Exemple #13
0
// Removes a range of PCs to the filter given a collection of offset ranges.
// Omit PCs which have opcodes that don't pass the given opcode filter.
void PCFilter::removeRanges(const Unit* unit, const OffsetRangeVec& offsets,
                            OpcodeFilter isOpcodeAllowed) {
  for (auto range = offsets.cbegin(); range != offsets.cend(); ++range) {
    TRACE(3, "\toffsets [%d, %d) (remove)\n", range->m_base, range->m_past);
    for (PC pc = unit->at(range->m_base); pc < unit->at(range->m_past);
         pc += instrLen((Op*) pc)) {
      if (isOpcodeAllowed(*reinterpret_cast<const Op*>(pc))) {
        TRACE(3, "\t\tpc %p (remove)\n", pc);
        removePC(pc);
      } else {
        TRACE(3, "\t\tpc %p -- skipping (offset %d) (remove)\n", pc,
              unit->offsetOf(pc));
      }
    }
  }
}
// Ensure we interpret all code at the given offsets. This sets up a guard for
// each piece of translated code to ensure we punt to the interpreter when the
// debugger is attached.
static void blacklistRangesInJit(const Unit* unit,
                                 const OffsetRangeVec& offsets) {
  for (OffsetRangeVec::const_iterator it = offsets.begin();
       it != offsets.end(); ++it) {
    for (PC pc = unit->at(it->m_base); pc < unit->at(it->m_past);
         pc += instrLen((Opcode*)pc)) {
      transl()->addDbgBLPC(pc);
    }
  }
  if (!transl()->addDbgGuards(unit)) {
    Logger::Warning("Failed to set breakpoints in Jitted code");
  }
  // In this case, we may be setting a breakpoint in a tracelet which could
  // already be jitted, and present on the stack. Make sure we don't return
  // to it so we have a chance to honor breakpoints.
  g_vmContext->preventReturnsToTC();
}
Exemple #15
0
bool
Translator::isSrcKeyInBL(SrcKey sk) {
  auto unit = sk.unit();
  if (unit->isInterpretOnly()) return true;
  Lock l(m_dbgBlacklistLock);
  if (m_dbgBLSrcKey.find(sk) != m_dbgBLSrcKey.end()) {
    return true;
  }

  // Loop until the end of the basic block inclusively. This is useful for
  // function exit breakpoints, which are implemented by blacklisting the RetC
  // opcodes.
  PC pc = nullptr;
  do {
    pc = (pc == nullptr) ? unit->at(sk.offset()) : pc + instrLen(pc);
    if (m_dbgBLPC.checkPC(pc)) {
      m_dbgBLSrcKey.insert(sk);
      return true;
    }
  } while (!opcodeBreaksBB(peek_op(pc)));
  return false;
}
Exemple #16
0
Offset ProfData::transStopBcOff(TransID id) const {
  Unit*  unit      = m_transRecs[id]->func()->unit();
  Offset lastBcOff = transLastBcOff(id);
  return lastBcOff + instrLen((Op*)(unit->at(lastBcOff)));
}
Exemple #17
0
void cgCall(IRLS& env, const IRInstruction* inst) {
  auto const sp = srcLoc(env, inst, 0).reg();
  auto const fp = srcLoc(env, inst, 1).reg();
  auto const extra = inst->extra<Call>();
  auto const callee = extra->callee;
  auto const argc = extra->numParams;

  auto& v = vmain(env);
  auto& vc = vcold(env);
  auto const catchBlock = label(env, inst->taken());

  auto const calleeSP = sp[cellsToBytes(extra->spOffset.offset)];
  auto const calleeAR = calleeSP + cellsToBytes(argc);

  v << store{fp, calleeAR + AROFF(m_sfp)};
  v << storeli{safe_cast<int32_t>(extra->after), calleeAR + AROFF(m_soff)};

  if (extra->fcallAwait) {
    // This clobbers any flags that might have already been set on the callee
    // AR (e.g., by SpillFrame), but this is okay because there should never be
    // any conflicts; see the documentation in act-rec.h.
    auto const imm = static_cast<int32_t>(
      ActRec::encodeNumArgsAndFlags(argc, ActRec::Flags::IsFCallAwait)
    );
    v << storeli{imm, calleeAR + AROFF(m_numArgsAndFlags)};
  }

  auto const isNativeImplCall = callee &&
                                callee->builtinFuncPtr() &&
                                !callee->nativeFuncPtr() &&
                                argc == callee->numParams();
  if (isNativeImplCall) {
    // The assumption here is that for builtins, the generated func contains
    // only a single opcode (NativeImpl), and there are no non-argument locals.
    if (do_assert) {
      assertx(argc == callee->numLocals());
      assertx(callee->numIterators() == 0);

      auto addr = callee->getEntry();
      while (peek_op(addr) == Op::AssertRATL) {
        addr += instrLen(addr);
      }
      assertx(peek_op(addr) == Op::NativeImpl);
      assertx(addr + instrLen(addr) ==
              callee->unit()->entry() + callee->past());
    }

    v << store{v.cns(mcg->ustubs().retHelper), calleeAR + AROFF(m_savedRip)};
    if (callee->attrs() & AttrMayUseVV) {
      v << storeqi{0, calleeAR + AROFF(m_invName)};
    }
    v << lea{calleeAR, rvmfp()};

    emitCheckSurpriseFlagsEnter(v, vc, fp, Fixup(0, argc), catchBlock);

    auto const builtinFuncPtr = callee->builtinFuncPtr();
    TRACE(2, "Calling builtin preClass %p func %p\n",
          callee->preClass(), builtinFuncPtr);

    // We sometimes call this while curFunc() isn't really the builtin, so make
    // sure to record the sync point as if we are inside the builtin.
    if (FixupMap::eagerRecord(callee)) {
      auto const syncSP = v.makeReg();
      v << lea{calleeSP, syncSP};
      emitEagerSyncPoint(v, callee->getEntry(), rvmtl(), rvmfp(), syncSP);
    }

    // Call the native implementation.  This will free the locals for us in the
    // normal case.  In the case where an exception is thrown, the VM unwinder
    // will handle it for us.
    auto const done = v.makeBlock();
    v << vinvoke{CallSpec::direct(builtinFuncPtr), v.makeVcallArgs({{rvmfp()}}),
                 v.makeTuple({}), {done, catchBlock}, Fixup(0, argc)};
    env.catch_calls[inst->taken()] = CatchCall::CPP;

    v = done;
    // The native implementation already put the return value on the stack for
    // us, and handled cleaning up the arguments.  We have to update the frame
    // pointer and the stack pointer, and load the return value into the return
    // register so the trace we are returning to has it where it expects.
    // TODO(#1273094): We should probably modify the actual builtins to return
    // values via registers using the C ABI and do a reg-to-reg move.
    loadTV(v, inst->dst(), dstLoc(env, inst, 0), rvmfp()[AROFF(m_r)], true);
    v << load{rvmfp()[AROFF(m_sfp)], rvmfp()};
    emitRB(v, Trace::RBTypeFuncExit, callee->fullName()->data());
    return;
  }

  v << lea{calleeAR, rvmfp()};

  if (RuntimeOption::EvalHHIRGenerateAsserts) {
    v << syncvmsp{v.cns(0x42)};

    constexpr uint64_t kUninitializedRIP = 0xba5eba11acc01ade;
    emitImmStoreq(v, kUninitializedRIP, rvmfp()[AROFF(m_savedRip)]);
  }

  // Emit a smashable call that initially calls a recyclable service request
  // stub.  The stub and the eventual targets take rvmfp() as an argument,
  // pointing to the callee ActRec.
  auto const target = callee
    ? mcg->ustubs().immutableBindCallStub
    : mcg->ustubs().bindCallStub;

  auto const done = v.makeBlock();
  v << callphp{target, php_call_regs(), {{done, catchBlock}}};
  env.catch_calls[inst->taken()] = CatchCall::PHP;
  v = done;

  auto const dst = dstLoc(env, inst, 0);
  v << defvmret{dst.reg(0), dst.reg(1)};
}
Exemple #18
0
void print_func_body(Output& out, const FuncInfo& finfo) {
  auto const func = finfo.func;

  auto       lblIter = begin(finfo.labels);
  auto const lblStop = end(finfo.labels);
  auto       ehIter  = begin(finfo.ehStarts);
  auto const ehStop  = end(finfo.ehStarts);
  auto       bcIter  = func->unit()->at(func->base());
  auto const bcStop  = func->unit()->at(func->past());

  min_priority_queue<Offset> ehEnds;

  while (bcIter != bcStop) {
    auto const pop = reinterpret_cast<const Op*>(bcIter);
    auto const off = func->unit()->offsetOf(pop);

    // First, close any protected EH regions that are past-the-end at
    // this offset.
    while (!ehEnds.empty() && ehEnds.top() == off) {
      ehEnds.pop();
      out.dec_indent();
      out.fmtln("}}");
    }

    // Next, open any new protected regions that start at this offset.
    for (; ehIter != ehStop && ehIter->first == off; ++ehIter) {
      auto const info = finfo.ehInfo.find(ehIter->second);
      always_assert(info != end(finfo.ehInfo));
      match<void>(
        info->second,
        [&] (const EHCatch& catches) {
          out.indent();
          out.fmt(".try_catch");
          for (auto& kv : catches.blocks) {
            out.fmt(" ({} {})", kv.first, kv.second);
          }
          out.fmt(" {{");
          out.nl();
        },
        [&] (const EHFault& fault) {
          out.fmtln(".try_fault {} {{", fault.label);
        }
      );
      out.inc_indent();
      ehEnds.push(ehIter->second->m_past);
    }

    // Then, print labels if we have any.  This order keeps the labels
    // from dangling on weird sides of .try_fault or .try_catch
    // braces.
    while (lblIter != lblStop && lblIter->first < off) ++lblIter;
    if (lblIter != lblStop && lblIter->first == off) {
      out.dec_indent();
      out.fmtln("{}:", lblIter->second);
      out.inc_indent();
    }

    print_instr(out, finfo, bcIter);

    bcIter += instrLen(reinterpret_cast<const Op*>(bcIter));
  }
}
Exemple #19
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();
}
Exemple #20
0
FuncInfo find_func_info(const Func* func) {
  auto finfo = FuncInfo(func->unit(), func);

  auto label_num = uint32_t{0};
  auto gen_label = [&] (const char* kind) {
    return folly::format("{}{}", kind, label_num++).str();
  };

  auto add_target = [&] (const char* kind, Offset off) -> std::string {
    auto it = finfo.labels.find(off);
    if (it != end(finfo.labels)) return it->second;
    auto const label = gen_label(kind);
    finfo.labels[off] = label;
    return label;
  };

  auto find_jump_targets = [&] {
    auto it           = func->unit()->at(func->base());
    auto const stop   = func->unit()->at(func->past());
    auto const bcBase = reinterpret_cast<const Op*>(func->unit()->at(0));

    for (; it != stop; it += instrLen(reinterpret_cast<const Op*>(it))) {
      auto const pop = reinterpret_cast<const Op*>(it);
      auto const off = func->unit()->offsetOf(pop);
      if (isSwitch(*pop)) {
        foreachSwitchTarget(pop, [&] (Offset off) {
          add_target("L", pop - bcBase + off);
        });
        continue;
      }
      auto const target = instrJumpTarget(bcBase, off);
      if (target != InvalidAbsoluteOffset) {
        add_target("L", target);
        continue;
      }
    }
  };

  auto find_eh_entries = [&] {
    for (auto& eh : func->ehtab()) {
      finfo.ehInfo[&eh] = [&]() -> EHInfo {
        switch (eh.m_type) {
        case EHEnt::Type::Catch:
          {
            auto catches = EHCatch {};
            for (auto& kv : eh.m_catches) {
              auto const clsName = func->unit()->lookupLitstrId(kv.first);
              catches.blocks[clsName->data()] = add_target("C", kv.second);
            }
            return catches;
          }
        case EHEnt::Type::Fault:
          return EHFault { add_target("F", eh.m_fault) };
        }
        not_reached();
      }();
      finfo.ehStarts.emplace_back(eh.m_base, &eh);
    }
  };

  auto find_dv_entries = [&] {
    for (auto i = uint32_t{0}; i < func->numParams(); ++i) {
      auto& param = func->params()[i];
      if (param.hasDefaultValue()) {
        add_target("DV", func->params()[i].funcletOff());
      }
    }
  };

  find_jump_targets();
  find_eh_entries();
  find_dv_entries();
  return finfo;
}
Exemple #21
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_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_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_RATA   out.fmt(" {}", show(decodeRAT(finfo.unit, 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_KA     out.fmt(" {}", show(decode_member_key(pc, finfo.unit)));

#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 (peek_op(pc)) { OPCODES }
#undef O

  assert(pc == startPc + instrLen(startPc));

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

#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_RATA
#undef IMM_AA
#undef IMM_BA
#undef IMM_OA
#undef IMM_VSA
#undef IMM_KA

  out.nl();
}
Exemple #22
0
std::string show(const IRGS& irgs) {
  std::ostringstream out;
  auto header = [&](const std::string& str) {
    out << folly::format("+{:-^102}+\n", str);
  };

  const int32_t frameCells = irgen::resumed(irgs)
    ? 0
    : irgen::curFunc(irgs)->numSlotsInFrame();
  auto const stackDepth = irgs.irb->syncedSpLevel().offset - frameCells;
  assertx(stackDepth >= 0);
  auto spOffset = stackDepth;
  auto elem = [&](const std::string& str) {
    out << folly::format("| {:<100} |\n",
                         folly::format("{:>2}: {}",
                                       stackDepth - spOffset, str));
    assertx(spOffset > 0);
    --spOffset;
  };

  auto fpi = irgen::curFunc(irgs)->findFPI(irgen::bcOff(irgs));
  auto checkFpi = [&]() {
    if (fpi && spOffset + frameCells == fpi->m_fpOff) {
      auto fpushOff = fpi->m_fpushOff;
      auto after = fpushOff + instrLen(irgen::curUnit(irgs)->at(fpushOff));
      std::ostringstream msg;
      msg << "ActRec from ";
      irgen::curUnit(irgs)->prettyPrint(
        msg,
        Unit::PrintOpts().range(fpushOff, after)
                         .noLineNumbers()
                         .indent(0)
                         .noFuncs()
      );
      auto msgStr = msg.str();
      assertx(msgStr.back() == '\n');
      msgStr.erase(msgStr.size() - 1);
      for (unsigned i = 0; i < kNumActRecCells; ++i) elem(msgStr);
      fpi = fpi->m_parentIndex != -1
        ? &irgen::curFunc(irgs)->fpitab()[fpi->m_parentIndex]
        : nullptr;
      return true;
    }
    return false;
  };

  header(folly::format(" {} stack element(s): ",
                       stackDepth).str());
  for (auto i = 0; spOffset > 0; ) {
    assertx(i < irgen::curFunc(irgs)->maxStackCells());
    if (checkFpi()) {
      i += kNumActRecCells;
      continue;
    }

    auto const stkTy = irgs.irb->stackType(
      irgen::offsetFromIRSP(irgs, BCSPOffset{i}),
      DataTypeGeneric
    );
    auto const stkVal = irgs.irb->stackValue(
      irgen::offsetFromIRSP(irgs, BCSPOffset{i}),
      DataTypeGeneric
    );

    std::string elemStr;
    if (stkTy == TStkElem) {
      elemStr = "unknown";
    } else if (stkVal) {
      elemStr = stkVal->inst()->toString();
    } else {
      elemStr = stkTy.toString();
    }

    auto const predicted = irgen::predictedTypeFromStack(irgs, BCSPOffset{i});
    if (predicted < stkTy) {
      elemStr += folly::sformat(" (predict: {})", predicted);
    }

    elem(elemStr);
    ++i;
  }
  header("");
  out << "\n";

  header(folly::format(" {} local(s) ",
                       irgen::curFunc(irgs)->numLocals()).str());
  for (unsigned i = 0; i < irgen::curFunc(irgs)->numLocals(); ++i) {
    auto const localValue = irgs.irb->localValue(i, DataTypeGeneric);
    auto const localTy = localValue ? localValue->type()
                                    : irgs.irb->localType(i, DataTypeGeneric);
    auto str = localValue ? localValue->inst()->toString()
                          : localTy.toString();
    auto const predicted = irgs.irb->predictedLocalType(i);
    if (predicted < localTy) str += folly::sformat(" (predict: {})", predicted);

    if (localTy <= TBoxedCell) {
      auto const pred = irgs.irb->predictedInnerType(i);
      if (pred != TBottom) {
        str += folly::sformat(" (predict inner: {})", pred.toString());
      }
    }

    out << folly::format("| {:<100} |\n",
                         folly::format("{:>2}: {}", i, str));
  }
  header("");
  return out.str();
}
Exemple #23
0
void Unit::prettyPrint(std::ostream &out, size_t startOffset,
                       size_t stopOffset) const {
  std::map<Offset,const Func*> funcMap;
  for (FuncRange fr(funcs()); !fr.empty();) {
    const Func* f = fr.popFront();
    funcMap[f->base()] = f;
  }
  for (PreClassPtrVec::const_iterator it = m_preClasses.begin();
      it != m_preClasses.end(); ++it) {
    Func* const* methods = (*it)->methods();
    size_t const numMethods = (*it)->numMethods();
    for (size_t i = 0; i < numMethods; ++i) {
      funcMap[methods[i]->base()] = methods[i];
    }
  }

  std::map<Offset,const Func*>::const_iterator funcIt =
    funcMap.lower_bound(startOffset);

  const uchar* it = &m_bc[startOffset];
  int prevLineNum = -1;
  MetaHandle metaHand;
  while (it < &m_bc[stopOffset]) {
    ASSERT(funcIt == funcMap.end() || funcIt->first >= offsetOf(it));
    if (funcIt != funcMap.end() && funcIt->first == offsetOf(it)) {
      out.put('\n');
      funcIt->second->prettyPrint(out);
      ++funcIt;
    }

    int lineNum = getLineNumber(offsetOf(it));
    if (lineNum != prevLineNum) {
      out << "  // line " << lineNum << std::endl;
      prevLineNum = lineNum;
    }

    out << "  " << std::setw(4) << (it - m_bc) << ": ";
    out << instrToString((Opcode*)it, (Unit*)this);
    if (metaHand.findMeta(this, offsetOf(it))) {
      out << " #";
      Unit::MetaInfo info;
      while (metaHand.nextArg(info)) {
        int arg = info.m_arg & ~MetaInfo::VectorArg;
        const char *argKind = info.m_arg & MetaInfo::VectorArg ? "M" : "";
        switch (info.m_kind) {
          case Unit::MetaInfo::DataType:
            out << " i" << argKind << arg << ":t=" << (int)info.m_data;
            break;
          case Unit::MetaInfo::String: {
            const StringData* sd = this->lookupLitstrId(info.m_data);
            out << " i" << argKind << arg << ":s=" <<
              std::string(sd->data(), sd->size());
            break;
          }
          case Unit::MetaInfo::Class: {
            const StringData* sd = this->lookupLitstrId(info.m_data);
            out << " i" << argKind << arg << ":c=" << sd->data();
            break;
          }
          case Unit::MetaInfo::NopOut:
            out << " Nop";
            break;
          case Unit::MetaInfo::GuardedThis:
            out << " GuardedThis";
            break;
          case Unit::MetaInfo::None:
            ASSERT(false);
            break;
        }
      }
    }
    out << std::endl;
    it += instrLen((Opcode*)it);
  }
}
Exemple #24
0
size_t OfflineX86Code::printBCMapping(BCMappingInfo bcMappingInfo,
                                      size_t currBC,
                                      TCA ip) {

  TransBCMapping curr, next;
  TCA x86Start, x86Stop;
  auto const& bcMap = bcMappingInfo.bcMapping;

  curr = next = TransBCMapping { MD5(), 0, 0, 0, 0 };
  x86Start = x86Stop = 0;

  // Account for the sentinel.
  size_t mappingSize = bcMap.size() - 1;

  // Starting from currBC, find the next bytecode with a non-empty x86 range
  // that could potentially correspond to instruction ip.
  for (; currBC < mappingSize; ++currBC) {
    curr = bcMap[currBC];
    next = bcMap[currBC + 1];

    switch (bcMappingInfo.tcRegion) {
      case TCRHot:
      case TCRMain:
      case TCRProfile:
        x86Start = curr.aStart;
        x86Stop  = next.aStart;
        break;
      case TCRCold:
        x86Start = curr.acoldStart;
        x86Stop  = next.acoldStart;
        break;
      case TCRFrozen:
        x86Start = curr.afrozenStart;
        x86Stop  = next.afrozenStart;
        break;
      default:
        error("printBCMapping: unexpected TCRegion");
    }

    always_assert(x86Start <= x86Stop);
    if (x86Start >= ip && x86Start < x86Stop) break;
  }

  if (currBC < mappingSize && x86Start == ip) {
    if (auto currUnit = g_repo->getUnit(curr.md5)) {
      auto bcPast = curr.bcStart + instrLen(currUnit->at(curr.bcStart));

      currUnit->prettyPrint(std::cout,
                            Unit::PrintOpts().range(curr.bcStart,
                                                    bcPast));
    } else {
      std::cout << folly::format(
        "<<< couldn't find unit {} to print bytecode at offset {} >>>\n",
        curr.md5, curr.bcStart);
    }

    currBC++;
  }

  return currBC;
}