Example #1
0
TimelineBar::Marker *TimelineBar::findMarker(QVector<Marker> &markers, QRectF markerRect, QPointF pos)
{
  QFontMetrics fm(Formatter::PreferredFont());

  for(Marker &m : markers)
  {
    QRectF r = markerRect;
    r.setLeft(qMax(m_markerRect.left() + borderWidth * 2, offsetOf(m.eidStart)));
    r.setRight(qMin(m_markerRect.right() - borderWidth, offsetOf(m.eidEnd + 1)));
    r.setHeight(fm.height() + borderWidth * 2);

    if(r.width() <= borderWidth * 2)
      continue;

    if(r.contains(pos))
    {
      return &m;
    }

    if(!m.children.isEmpty() && m.expanded)
    {
      QRectF childRect = r;
      childRect.setTop(r.bottom() + borderWidth * 2);
      childRect.setBottom(markerRect.bottom());

      Marker *res = findMarker(m.children, childRect, pos);

      if(res)
        return res;
    }
  }

  return NULL;
}
QVariant CityModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == Qt::TextAlignmentRole) {
        return int(Qt::AlignRight | Qt::AlignVCenter);
    } else if (role == Qt::DisplayRole) {
        if (index.row() == index.column())
            return 0;
        int offset = offsetOf(index.row(), index.column());
        return distances[offset];
    }
    return QVariant();
}
bool CityModel::setData(const QModelIndex &index,
                        const QVariant &value, int role)
{
    if (index.isValid() && index.row() != index.column()
            && role == Qt::EditRole) {
        int offset = offsetOf(index.row(), index.column());
        distances[offset] = value.toInt();

        QModelIndex transposedIndex = createIndex(index.column(),
                                                  index.row());
        emit dataChanged(index, index);
        emit dataChanged(transposedIndex, transposedIndex);
        return true;
    }
    return false;
}
Example #4
0
void Label::bind(uint32_t address, Bytecode* code) {
    assert(!isBound());
    if (code == 0) {
      code = _code;
    }
    assert(code != 0);
    _bci = address;
    for (uint32_t i = 0; i < _relocations.size(); i++) {
        uint32_t relocBci = _relocations[i];
        int32_t offset = offsetOf(relocBci);
        assert((int16_t)offset == offset);
        assert(code->getInt16(relocBci) == 0x1ead);
        code->setInt16(relocBci, offset);
    }
    _relocations.clear();
}
Example #5
0
void XDebugHook::onOpcode(PC pc) {
  auto server = XDEBUG_GLOBAL(Server);
  if (server == nullptr) {
    return;
  }

  // Likely case is that there was no break command.
  auto brk = server->getAndClearBreak();
  if (LIKELY(brk == nullptr)) {
    return;
  }

  server->log("Request thread received break command");

  VMRegAnchor anchor;

  auto const unit = vmfp()->func()->unit();
  auto const line = unit->getLineNumber(unit->offsetOf(pc));
  auto const filepath = const_cast<StringData*>(unit->filepath());
  auto const transpath = File::TranslatePath(String(filepath));

  // XDebugServer::breakpoint will send the response for the command before the
  // break command, but we first need to send a response for the break command.
  auto response = xdebug_xml_node_init("response");
  server->addXmlns(*response);

  auto const& cmd_str  = brk->getCommandStr();
  auto const& trans_id = brk->getTransactionId();

  // Manually add status and reason.  XDebugServer still thinks we're running
  // because we haven't run XDebugServer::breakpoint yet.
  xdebug_xml_add_attribute(response, "status", "break");
  xdebug_xml_add_attribute(response, "reason", "ok");

  // Ditto with command, XDebugServer is tracking the command before the break.
  xdebug_xml_add_attribute_dup(response, "command", cmd_str.data());
  xdebug_xml_add_attribute_dup(response, "transaction_id", trans_id.data());

  delete brk;

  server->sendMessage(*response);
  xdebug_xml_node_dtor(response);

  // Now we can go into a command loop.
  server->breakpoint(transpath, init_null(), init_null(), line);
}
Example #6
0
 void compileFunction(Symbol functionDefinitionStatement)
 {
     if (functionDefinitionStatement.cache<FunctionDefinitionCache>()->
         getCompilingFlag()) {
         // TODO: Give more details about what's being evaluated and how that came to call this
         spanOf(functionDefinitionStatement).end().throwError(
             "Function called during its own compilation");
     }
     functionDefinitionStatement.cache<FunctionDefinitionCache>()->
         setCompilingFlag(true);
     _epilogueStack.push(SymbolLabel());
     //Symbol type = typeOf(functionDefinitionStatement);
     //Symbol returnType = type[1].symbol();
     //_returnTypeStack.push(returnType);
     int stackAdjust = offsetOf(functionDefinitionStatement);
     if (stackAdjust != 0)
         addAdjustStackPointer(-stackAdjust);
     _stackOffset = 0;
     compileStatementSequence(functionDefinitionStatement[4].array());
     Symbol type = typeOf(functionDefinitionStatement);
     Symbol returnType = type[1].symbol();
     int returnTypeSize = (sizeOf(returnType) + 3) & -4;
     int parametersSize = 0;
     SymbolArray parameterTypes = type[2].array();
     for (int i = 0; i < parameterTypes.count(); ++i)
         parametersSize += (sizeOf(parameterTypes[i]) + 3) & -4;
     if (_reachable && returnType.atom() != atomVoid) {
         // TODO: Give more details about how it got there
         spanOf(functionDefinitionStatement).end().throwError(
             "Control reaches end of non-Void function");
     }
     addLabel(_epilogueStack.pop());
     addLoadWordFromStackRelativeAddress(returnTypeSize + stackAdjust);
     addMoveBlock(0, stackAdjust + 4 + parametersSize, 1 + returnTypeSize/4);
     if (stackAdjust != 0)
         addAdjustStackPointer(stackAdjust);
     add(Symbol(atomReturn));
     //_returnTypeStack.pop();
     functionDefinitionStatement.cache<FunctionDefinitionCache>()->setCompilingFlag(false);
     functionDefinitionStatement[5].label().setTarget(_firstBasicBlock.target());
 }
Example #7
0
void throwable_init(ObjectData* throwable) {
  assertx(is_throwable(throwable));
  assertx(throwable_has_expected_props());

  auto trace = HHVM_FN(debug_backtrace)(exception_get_trace_options());
  cellMove(
    make_tv<KindOfArray>(trace.detach()), throwable->propVec()[s_traceIdx]);

  VMRegAnchor _;
  auto const fp = vmfp();
  if (UNLIKELY(!fp)) return;
  if (UNLIKELY(fp->func()->isBuiltin())) {
    throwable_init_file_and_line_from_builtin(throwable);
  } else {
    assertx(throwable->propVec()[s_fileIdx].m_type == KindOfNull);
    assertx(throwable->propVec()[s_lineIdx].m_type == KindOfNull);
    auto const unit = fp->func()->unit();
    auto const file = const_cast<StringData*>(unit->filepath());
    auto const line = unit->getLineNumber(unit->offsetOf(vmpc()));
    cellDup(make_tv<KindOfString>(file), throwable->propVec()[s_fileIdx]);
    cellDup(make_tv<KindOfInt64>(line), throwable->propVec()[s_lineIdx]);
  }
}
Example #8
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);
  }
}
Example #9
0
Array createBacktrace(const BacktraceArgs& btArgs) {
  auto bt = Array::Create();

  // If there is a parser frame, put it at the beginning of the backtrace.
  if (btArgs.m_parserFrame) {
    bt.append(
      make_map_array(
        s_file, btArgs.m_parserFrame->filename,
        s_line, btArgs.m_parserFrame->lineNumber
      )
    );
  }

  VMRegAnchor _;
  // If there are no VM frames, we're done.
  if (!rds::header() || !vmfp()) return bt;

  int depth = 0;
  ActRec* fp = nullptr;
  Offset pc = 0;

  // Get the fp and pc of the top frame (possibly skipping one frame).

  if (btArgs.m_skipTop) {
    fp = getPrevActRec(vmfp(), &pc);
    // We skipped over the only VM frame, we're done.
    if (!fp) return bt;
  } else {
    fp = vmfp();
    auto const unit = fp->func()->unit();
    assert(unit);
    pc = unit->offsetOf(vmpc());
  }

  // Handle the top frame.
  if (btArgs.m_withSelf) {
    // Builtins don't have a file and line number.
    if (!fp->func()->isBuiltin()) {
      auto const unit = fp->func()->unit();
      assert(unit);
      auto const filename = fp->func()->filename();

      ArrayInit frame(btArgs.m_parserFrame ? 4 : 2, ArrayInit::Map{});
      frame.set(s_file, Variant{const_cast<StringData*>(filename)});
      frame.set(s_line, unit->getLineNumber(pc));
      if (btArgs.m_parserFrame) {
        frame.set(s_function, s_include);
        frame.set(s_args, Array::Create(btArgs.m_parserFrame->filename));
      }
      bt.append(frame.toVariant());
      depth++;
    }
  }

  // Handle the subsequent VM frames.
  Offset prevPc = 0;
  for (auto prevFp = getPrevActRec(fp, &prevPc);
       fp != nullptr && (btArgs.m_limit == 0 || depth < btArgs.m_limit);
       fp = prevFp, pc = prevPc,
         prevFp = getPrevActRec(fp, &prevPc)) {
    // Do not capture frame for HPHP only functions.
    if (fp->func()->isNoInjection()) continue;

    ArrayInit frame(7, ArrayInit::Map{});

    auto const curUnit = fp->func()->unit();
    auto const curOp = *reinterpret_cast<const Op*>(curUnit->at(pc));
    auto const isReturning =
      curOp == Op::RetC || curOp == Op::RetV ||
      curOp == Op::CreateCont || curOp == Op::Await ||
      fp->localsDecRefd();

    // Builtins and generators don't have a file and line number
    if (prevFp && !prevFp->func()->isBuiltin()) {
      auto const prevUnit = prevFp->func()->unit();
      auto prevFile = prevUnit->filepath();
      if (prevFp->func()->originalFilename()) {
        prevFile = prevFp->func()->originalFilename();
      }
      assert(prevFile);
      frame.set(s_file, Variant{const_cast<StringData*>(prevFile)});

      // In the normal method case, the "saved pc" for line number printing is
      // pointing at the cell conversion (Unbox/Pop) instruction, not the call
      // itself. For multi-line calls, this instruction is associated with the
      // subsequent line which results in an off-by-n. We're subtracting one
      // in order to look up the line associated with the FCall/FCallArray
      // instruction. Exception handling and the other opcodes (ex. BoxR)
      // already do the right thing. The emitter associates object access with
      // the subsequent expression and this would be difficult to modify.
      auto const opAtPrevPc =
        *reinterpret_cast<const Op*>(prevUnit->at(prevPc));
      Offset pcAdjust = 0;
      if (opAtPrevPc == Op::PopR ||
          opAtPrevPc == Op::UnboxR ||
          opAtPrevPc == Op::UnboxRNop) {
        pcAdjust = 1;
      }
      frame.set(s_line,
                prevFp->func()->unit()->getLineNumber(prevPc - pcAdjust));
    }

    // Check for include.
    String funcname{const_cast<StringData*>(fp->func()->name())};
    if (fp->func()->isClosureBody()) {
      // Strip the file hash from the closure name.
      String fullName{const_cast<StringData*>(fp->func()->baseCls()->name())};
      funcname = fullName.substr(0, fullName.find(';'));
    }

    // Check for pseudomain.
    if (funcname.empty()) {
      if (!prevFp && !btArgs.m_withPseudoMain) continue;
      else if (!prevFp) funcname = s_main;
      else funcname = s_include;
    }

    frame.set(s_function, funcname);

    if (!funcname.same(s_include)) {
      // Closures have an m_this but they aren't in object context.
      auto ctx = arGetContextClass(fp);
      if (ctx != nullptr && !fp->func()->isClosureBody()) {
        frame.set(s_class, Variant{const_cast<StringData*>(ctx->name())});
        if (fp->hasThis() && !isReturning) {
          if (btArgs.m_withThis) {
            frame.set(s_object, Object(fp->getThis()));
          }
          frame.set(s_type, s_arrow);
        } else {
          frame.set(s_type, s_double_colon);
        }
      }
    }

    bool const mayUseVV = fp->func()->attrs() & AttrMayUseVV;

    auto const withNames = btArgs.m_withArgNames;
    auto const withValues = btArgs.m_withArgValues;
    if (!btArgs.m_withArgNames && !btArgs.m_withArgValues) {
      // do nothing
    } else if (funcname.same(s_include)) {
      if (depth != 0) {
        auto filepath = const_cast<StringData*>(curUnit->filepath());
        frame.set(s_args, make_packed_array(filepath));
      }
    } else if (!RuntimeOption::EnableArgsInBacktraces || isReturning) {
      // Provide an empty 'args' array to be consistent with hphpc.
      frame.set(s_args, empty_array());
    } else {
      auto args = Array::Create();
      auto const nparams = fp->func()->numNonVariadicParams();
      auto const nargs = fp->numArgs();
      auto const nformals = std::min<int>(nparams, nargs);

      if (UNLIKELY(mayUseVV) &&
          UNLIKELY(fp->hasVarEnv() && fp->getVarEnv()->getFP() != fp)) {
        // VarEnv is attached to eval or debugger frame, other than the current
        // frame. Access locals thru VarEnv.
        auto varEnv = fp->getVarEnv();
        auto func = fp->func();
        for (int i = 0; i < nformals; i++) {
          auto const argname = func->localVarName(i);
          auto const tv = varEnv->lookup(argname);

          Variant val;
          if (tv != nullptr) { // the variable hasn't been unset
            val = withValues ? tvAsVariant(tv) : "";
          }

          if (withNames) {
            args.set(String(const_cast<StringData*>(argname)), val);
          } else {
            args.append(val);
          }
        }
      } else {
        for (int i = 0; i < nformals; i++) {
          Variant val = withValues ? tvAsVariant(frame_local(fp, i)) : "";

          if (withNames) {
            auto const argname = fp->func()->localVarName(i);
            args.set(String(const_cast<StringData*>(argname)), val);
          } else {
            args.append(val);
          }
        }
      }

      // Builtin extra args are not stored in varenv.
      if (UNLIKELY(mayUseVV) && nargs > nparams && fp->hasExtraArgs()) {
        for (int i = nparams; i < nargs; i++) {
          auto arg = fp->getExtraArg(i - nparams);
          args.append(tvAsVariant(arg));
        }
      }
      frame.set(s_args, args);
    }

    if (btArgs.m_withMetadata && !isReturning) {
      if (UNLIKELY(mayUseVV) && UNLIKELY(fp->hasVarEnv())) {
        auto tv = fp->getVarEnv()->lookup(s_86metadata.get());
        if (tv != nullptr && tv->m_type != KindOfUninit) {
          frame.set(s_metadata, tvAsVariant(tv));
        }
      } else {
        auto local = fp->func()->lookupVarId(s_86metadata.get());
        if (local != kInvalidId) {
          auto tv = frame_local(fp, local);
          if (tv->m_type != KindOfUninit) {
            frame.set(s_metadata, tvAsVariant(tv));
          }
        }
      }
    }

    bt.append(frame.toVariant());
    depth++;
  }

  return bt;
}
Example #10
0
static void fixFunctionBasedOnStackMaps(
    State& state, CodeBlock* codeBlock, JITCode* jitCode, GeneratedFunction generatedFunction,
    StackMaps::RecordMap& recordMap, bool didSeeUnwindInfo)
{
    Graph& graph = state.graph;
    VM& vm = graph.m_vm;
    StackMaps stackmaps = jitCode->stackmaps;
    
    int localsOffset =
        offsetOfStackRegion(recordMap, state.capturedStackmapID) + graph.m_nextMachineLocal;
    
    int varargsSpillSlotsOffset;
    if (state.varargsSpillSlotsStackmapID != UINT_MAX)
        varargsSpillSlotsOffset = offsetOfStackRegion(recordMap, state.varargsSpillSlotsStackmapID);
    else
        varargsSpillSlotsOffset = 0;
    
    for (unsigned i = graph.m_inlineVariableData.size(); i--;) {
        InlineCallFrame* inlineCallFrame = graph.m_inlineVariableData[i].inlineCallFrame;
        
        if (inlineCallFrame->argumentsRegister.isValid())
            inlineCallFrame->argumentsRegister += localsOffset;
        
        if (inlineCallFrame->argumentCountRegister.isValid())
            inlineCallFrame->argumentCountRegister += localsOffset;
        
        for (unsigned argument = inlineCallFrame->arguments.size(); argument-- > 1;) {
            inlineCallFrame->arguments[argument] =
                inlineCallFrame->arguments[argument].withLocalsOffset(localsOffset);
        }
        
        if (inlineCallFrame->isClosureCall) {
            inlineCallFrame->calleeRecovery =
                inlineCallFrame->calleeRecovery.withLocalsOffset(localsOffset);
        }
    }
    
    if (codeBlock->usesArguments()) {
        codeBlock->setArgumentsRegister(
            VirtualRegister(codeBlock->argumentsRegister().offset() + localsOffset));
    }

    MacroAssembler::Label stackOverflowException;

    {
        CCallHelpers checkJIT(&vm, codeBlock);
        
        // At this point it's perfectly fair to just blow away all state and restore the
        // JS JIT view of the universe.
        checkJIT.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0);
        checkJIT.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
        MacroAssembler::Call callLookupExceptionHandler = checkJIT.call();
        checkJIT.jumpToExceptionHandler();

        stackOverflowException = checkJIT.label();
        checkJIT.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0);
        checkJIT.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
        MacroAssembler::Call callLookupExceptionHandlerFromCallerFrame = checkJIT.call();
        checkJIT.jumpToExceptionHandler();

        auto linkBuffer = std::make_unique<LinkBuffer>(
            vm, checkJIT, codeBlock, JITCompilationMustSucceed);
        linkBuffer->link(callLookupExceptionHandler, FunctionPtr(lookupExceptionHandler));
        linkBuffer->link(callLookupExceptionHandlerFromCallerFrame, FunctionPtr(lookupExceptionHandlerFromCallerFrame));

        state.finalizer->handleExceptionsLinkBuffer = WTF::move(linkBuffer);
    }

    ExitThunkGenerator exitThunkGenerator(state);
    exitThunkGenerator.emitThunks();
    if (exitThunkGenerator.didThings()) {
        RELEASE_ASSERT(state.finalizer->osrExit.size());
        RELEASE_ASSERT(didSeeUnwindInfo);
        
        auto linkBuffer = std::make_unique<LinkBuffer>(
            vm, exitThunkGenerator, codeBlock, JITCompilationMustSucceed);
        
        RELEASE_ASSERT(state.finalizer->osrExit.size() == state.jitCode->osrExit.size());
        
        for (unsigned i = 0; i < state.jitCode->osrExit.size(); ++i) {
            OSRExitCompilationInfo& info = state.finalizer->osrExit[i];
            OSRExit& exit = jitCode->osrExit[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling OSR stackmap #", exit.m_stackmapID, " for ", exit.m_codeOrigin, "\n");

            auto iter = recordMap.find(exit.m_stackmapID);
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            info.m_thunkAddress = linkBuffer->locationOf(info.m_thunkLabel);
            exit.m_patchableCodeOffset = linkBuffer->offsetOf(info.m_thunkJump);
            
            for (unsigned j = exit.m_values.size(); j--;) {
                ExitValue value = exit.m_values[j];
                if (!value.isInJSStackSomehow())
                    continue;
                if (!value.virtualRegister().isLocal())
                    continue;
                exit.m_values[j] = value.withVirtualRegister(
                    VirtualRegister(value.virtualRegister().offset() + localsOffset));
            }
            
            if (verboseCompilationEnabled()) {
                DumpContext context;
                dataLog("    Exit values: ", inContext(exit.m_values, &context), "\n");
            }
        }
        
        state.finalizer->exitThunksLinkBuffer = WTF::move(linkBuffer);
    }

    if (!state.getByIds.isEmpty() || !state.putByIds.isEmpty() || !state.checkIns.isEmpty()) {
        CCallHelpers slowPathJIT(&vm, codeBlock);
        
        CCallHelpers::JumpList exceptionTarget;
        
        for (unsigned i = state.getByIds.size(); i--;) {
            GetByIdDescriptor& getById = state.getByIds[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling GetById stackmap #", getById.stackmapID(), "\n");
            
            auto iter = recordMap.find(getById.stackmapID());
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            for (unsigned i = 0; i < iter->value.size(); ++i) {
                StackMaps::Record& record = iter->value[i];
            
                RegisterSet usedRegisters = usedRegistersFor(record);
                
                GPRReg result = record.locations[0].directGPR();
                GPRReg base = record.locations[1].directGPR();
                
                JITGetByIdGenerator gen(
                    codeBlock, getById.codeOrigin(), usedRegisters, JSValueRegs(base),
                    JSValueRegs(result), NeedToSpill);
                
                MacroAssembler::Label begin = slowPathJIT.label();

                MacroAssembler::Call call = callOperation(
                    state, usedRegisters, slowPathJIT, getById.codeOrigin(), &exceptionTarget,
                    operationGetByIdOptimize, result, gen.stubInfo(), base, getById.uid());

                gen.reportSlowPathCall(begin, call);

                getById.m_slowPathDone.append(slowPathJIT.jump());
                getById.m_generators.append(gen);
            }
        }
        
        for (unsigned i = state.putByIds.size(); i--;) {
            PutByIdDescriptor& putById = state.putByIds[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling PutById stackmap #", putById.stackmapID(), "\n");
            
            auto iter = recordMap.find(putById.stackmapID());
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            for (unsigned i = 0; i < iter->value.size(); ++i) {
                StackMaps::Record& record = iter->value[i];
                
                RegisterSet usedRegisters = usedRegistersFor(record);
                
                GPRReg base = record.locations[0].directGPR();
                GPRReg value = record.locations[1].directGPR();
                
                JITPutByIdGenerator gen(
                    codeBlock, putById.codeOrigin(), usedRegisters, JSValueRegs(base),
                    JSValueRegs(value), GPRInfo::patchpointScratchRegister, NeedToSpill,
                    putById.ecmaMode(), putById.putKind());
                
                MacroAssembler::Label begin = slowPathJIT.label();
                
                MacroAssembler::Call call = callOperation(
                    state, usedRegisters, slowPathJIT, putById.codeOrigin(), &exceptionTarget,
                    gen.slowPathFunction(), gen.stubInfo(), value, base, putById.uid());
                
                gen.reportSlowPathCall(begin, call);
                
                putById.m_slowPathDone.append(slowPathJIT.jump());
                putById.m_generators.append(gen);
            }
        }

        for (unsigned i = state.checkIns.size(); i--;) {
            CheckInDescriptor& checkIn = state.checkIns[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling checkIn stackmap #", checkIn.stackmapID(), "\n");
            
            auto iter = recordMap.find(checkIn.stackmapID());
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            for (unsigned i = 0; i < iter->value.size(); ++i) {
                StackMaps::Record& record = iter->value[i];
                RegisterSet usedRegisters = usedRegistersFor(record);
                GPRReg result = record.locations[0].directGPR();
                GPRReg obj = record.locations[1].directGPR();
                StructureStubInfo* stubInfo = codeBlock->addStubInfo(); 
                stubInfo->codeOrigin = checkIn.codeOrigin();
                stubInfo->patch.baseGPR = static_cast<int8_t>(obj);
                stubInfo->patch.valueGPR = static_cast<int8_t>(result);
                stubInfo->patch.usedRegisters = usedRegisters;
                stubInfo->patch.spillMode = NeedToSpill;

                MacroAssembler::Label begin = slowPathJIT.label();

                MacroAssembler::Call slowCall = callOperation(
                    state, usedRegisters, slowPathJIT, checkIn.codeOrigin(), &exceptionTarget,
                    operationInOptimize, result, stubInfo, obj, checkIn.m_id);

                checkIn.m_slowPathDone.append(slowPathJIT.jump());
                
                checkIn.m_generators.append(CheckInGenerator(stubInfo, slowCall, begin));
            }
        }
        
        exceptionTarget.link(&slowPathJIT);
        MacroAssembler::Jump exceptionJump = slowPathJIT.jump();
        
        state.finalizer->sideCodeLinkBuffer = std::make_unique<LinkBuffer>(vm, slowPathJIT, codeBlock, JITCompilationMustSucceed);
        state.finalizer->sideCodeLinkBuffer->link(
            exceptionJump, state.finalizer->handleExceptionsLinkBuffer->entrypoint());
        
        for (unsigned i = state.getByIds.size(); i--;) {
            generateICFastPath(
                state, codeBlock, generatedFunction, recordMap, state.getByIds[i],
                sizeOfGetById());
        }
        for (unsigned i = state.putByIds.size(); i--;) {
            generateICFastPath(
                state, codeBlock, generatedFunction, recordMap, state.putByIds[i],
                sizeOfPutById());
        }

        for (unsigned i = state.checkIns.size(); i--;) {
            generateCheckInICFastPath(
                state, codeBlock, generatedFunction, recordMap, state.checkIns[i],
                sizeOfIn()); 
        } 
    }
    
    adjustCallICsForStackmaps(state.jsCalls, recordMap);
    
    for (unsigned i = state.jsCalls.size(); i--;) {
        JSCall& call = state.jsCalls[i];

        CCallHelpers fastPathJIT(&vm, codeBlock);
        call.emit(fastPathJIT);
        
        char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset;
        
        LinkBuffer linkBuffer(vm, fastPathJIT, startOfIC, sizeOfCall());
        if (!linkBuffer.isValid()) {
            dataLog("Failed to insert inline cache for call because we thought the size would be ", sizeOfCall(), " but it ended up being ", fastPathJIT.m_assembler.codeSize(), " prior to compaction.\n");
            RELEASE_ASSERT_NOT_REACHED();
        }
        
        MacroAssembler::AssemblerType_T::fillNops(
            startOfIC + linkBuffer.size(), sizeOfCall() - linkBuffer.size());
        
        call.link(vm, linkBuffer);
    }
    
    adjustCallICsForStackmaps(state.jsCallVarargses, recordMap);
    
    for (unsigned i = state.jsCallVarargses.size(); i--;) {
        JSCallVarargs& call = state.jsCallVarargses[i];
        
        CCallHelpers fastPathJIT(&vm, codeBlock);
        call.emit(fastPathJIT, graph, varargsSpillSlotsOffset);
        
        char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset;
        size_t sizeOfIC = sizeOfICFor(call.node());

        LinkBuffer linkBuffer(vm, fastPathJIT, startOfIC, sizeOfIC);
        if (!linkBuffer.isValid()) {
            dataLog("Failed to insert inline cache for varargs call (specifically, ", Graph::opName(call.node()->op()), ") because we thought the size would be ", sizeOfIC, " but it ended up being ", fastPathJIT.m_assembler.codeSize(), " prior to compaction.\n");
            RELEASE_ASSERT_NOT_REACHED();
        }
        
        MacroAssembler::AssemblerType_T::fillNops(
            startOfIC + linkBuffer.size(), sizeOfIC - linkBuffer.size());
        
        call.link(vm, linkBuffer, state.finalizer->handleExceptionsLinkBuffer->entrypoint());
    }
    
    RepatchBuffer repatchBuffer(codeBlock);

    auto iter = recordMap.find(state.handleStackOverflowExceptionStackmapID);
    // It's sort of remotely possible that we won't have an in-band exception handling
    // path, for some kinds of functions.
    if (iter != recordMap.end()) {
        for (unsigned i = iter->value.size(); i--;) {
            StackMaps::Record& record = iter->value[i];
            
            CodeLocationLabel source = CodeLocationLabel(
                bitwise_cast<char*>(generatedFunction) + record.instructionOffset);

            RELEASE_ASSERT(stackOverflowException.isSet());

            repatchBuffer.replaceWithJump(source, state.finalizer->handleExceptionsLinkBuffer->locationOf(stackOverflowException));
        }
    }
    
    iter = recordMap.find(state.handleExceptionStackmapID);
    // It's sort of remotely possible that we won't have an in-band exception handling
    // path, for some kinds of functions.
    if (iter != recordMap.end()) {
        for (unsigned i = iter->value.size(); i--;) {
            StackMaps::Record& record = iter->value[i];
            
            CodeLocationLabel source = CodeLocationLabel(
                bitwise_cast<char*>(generatedFunction) + record.instructionOffset);
            
            repatchBuffer.replaceWithJump(source, state.finalizer->handleExceptionsLinkBuffer->entrypoint());
        }
    }
    
    for (unsigned exitIndex = 0; exitIndex < jitCode->osrExit.size(); ++exitIndex) {
        OSRExitCompilationInfo& info = state.finalizer->osrExit[exitIndex];
        OSRExit& exit = jitCode->osrExit[exitIndex];
        iter = recordMap.find(exit.m_stackmapID);
        
        Vector<const void*> codeAddresses;
        
        if (iter != recordMap.end()) {
            for (unsigned i = iter->value.size(); i--;) {
                StackMaps::Record& record = iter->value[i];
                
                CodeLocationLabel source = CodeLocationLabel(
                    bitwise_cast<char*>(generatedFunction) + record.instructionOffset);
                
                codeAddresses.append(bitwise_cast<char*>(generatedFunction) + record.instructionOffset + MacroAssembler::maxJumpReplacementSize());
                
                if (info.m_isInvalidationPoint)
                    jitCode->common.jumpReplacements.append(JumpReplacement(source, info.m_thunkAddress));
                else
                    repatchBuffer.replaceWithJump(source, info.m_thunkAddress);
            }
        }
        
        if (graph.compilation())
            graph.compilation()->addOSRExitSite(codeAddresses);
    }
}
Example #11
0
static void fixFunctionBasedOnStackMaps(
    State& state, CodeBlock* codeBlock, JITCode* jitCode, GeneratedFunction generatedFunction,
    StackMaps::RecordMap& recordMap)
{
    Graph& graph = state.graph;
    VM& vm = graph.m_vm;
    StackMaps stackmaps = jitCode->stackmaps;
    
    int localsOffset = offsetOfStackRegion(recordMap, state.capturedStackmapID) + graph.m_nextMachineLocal;
    int varargsSpillSlotsOffset = offsetOfStackRegion(recordMap, state.varargsSpillSlotsStackmapID);
    
    for (unsigned i = graph.m_inlineVariableData.size(); i--;) {
        InlineCallFrame* inlineCallFrame = graph.m_inlineVariableData[i].inlineCallFrame;
        
        if (inlineCallFrame->argumentCountRegister.isValid())
            inlineCallFrame->argumentCountRegister += localsOffset;
        
        for (unsigned argument = inlineCallFrame->arguments.size(); argument-- > 1;) {
            inlineCallFrame->arguments[argument] =
                inlineCallFrame->arguments[argument].withLocalsOffset(localsOffset);
        }
        
        if (inlineCallFrame->isClosureCall) {
            inlineCallFrame->calleeRecovery =
                inlineCallFrame->calleeRecovery.withLocalsOffset(localsOffset);
        }

        if (graph.hasDebuggerEnabled())
            codeBlock->setScopeRegister(codeBlock->scopeRegister() + localsOffset);
    }
    
    MacroAssembler::Label stackOverflowException;

    {
        CCallHelpers checkJIT(&vm, codeBlock);
        
        // At this point it's perfectly fair to just blow away all state and restore the
        // JS JIT view of the universe.
        checkJIT.copyCalleeSavesToVMCalleeSavesBuffer();
        checkJIT.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0);
        checkJIT.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
        MacroAssembler::Call callLookupExceptionHandler = checkJIT.call();
        checkJIT.jumpToExceptionHandler();

        stackOverflowException = checkJIT.label();
        checkJIT.copyCalleeSavesToVMCalleeSavesBuffer();
        checkJIT.move(MacroAssembler::TrustedImmPtr(&vm), GPRInfo::argumentGPR0);
        checkJIT.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR1);
        MacroAssembler::Call callLookupExceptionHandlerFromCallerFrame = checkJIT.call();
        checkJIT.jumpToExceptionHandler();

        auto linkBuffer = std::make_unique<LinkBuffer>(
            vm, checkJIT, codeBlock, JITCompilationCanFail);
        if (linkBuffer->didFailToAllocate()) {
            state.allocationFailed = true;
            return;
        }
        linkBuffer->link(callLookupExceptionHandler, FunctionPtr(lookupExceptionHandler));
        linkBuffer->link(callLookupExceptionHandlerFromCallerFrame, FunctionPtr(lookupExceptionHandlerFromCallerFrame));

        state.finalizer->handleExceptionsLinkBuffer = WTF::move(linkBuffer);
    }

    ExitThunkGenerator exitThunkGenerator(state);
    exitThunkGenerator.emitThunks();
    if (exitThunkGenerator.didThings()) {
        RELEASE_ASSERT(state.finalizer->osrExit.size());
        
        auto linkBuffer = std::make_unique<LinkBuffer>(
            vm, exitThunkGenerator, codeBlock, JITCompilationCanFail);
        if (linkBuffer->didFailToAllocate()) {
            state.allocationFailed = true;
            return;
        }
        
        RELEASE_ASSERT(state.finalizer->osrExit.size() == state.jitCode->osrExit.size());
        
        for (unsigned i = 0; i < state.jitCode->osrExit.size(); ++i) {
            OSRExitCompilationInfo& info = state.finalizer->osrExit[i];
            OSRExit& exit = jitCode->osrExit[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling OSR stackmap #", exit.m_stackmapID, " for ", exit.m_codeOrigin, "\n");

            auto iter = recordMap.find(exit.m_stackmapID);
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            info.m_thunkAddress = linkBuffer->locationOf(info.m_thunkLabel);
            exit.m_patchableCodeOffset = linkBuffer->offsetOf(info.m_thunkJump);
            
            for (unsigned j = exit.m_values.size(); j--;)
                exit.m_values[j] = exit.m_values[j].withLocalsOffset(localsOffset);
            for (ExitTimeObjectMaterialization* materialization : exit.m_materializations)
                materialization->accountForLocalsOffset(localsOffset);
            
            if (verboseCompilationEnabled()) {
                DumpContext context;
                dataLog("    Exit values: ", inContext(exit.m_values, &context), "\n");
                if (!exit.m_materializations.isEmpty()) {
                    dataLog("    Materializations: \n");
                    for (ExitTimeObjectMaterialization* materialization : exit.m_materializations)
                        dataLog("        Materialize(", pointerDump(materialization), ")\n");
                }
            }
        }
        
        state.finalizer->exitThunksLinkBuffer = WTF::move(linkBuffer);
    }

    if (!state.getByIds.isEmpty() || !state.putByIds.isEmpty() || !state.checkIns.isEmpty()) {
        CCallHelpers slowPathJIT(&vm, codeBlock);
        
        CCallHelpers::JumpList exceptionTarget;
        
        for (unsigned i = state.getByIds.size(); i--;) {
            GetByIdDescriptor& getById = state.getByIds[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling GetById stackmap #", getById.stackmapID(), "\n");
            
            auto iter = recordMap.find(getById.stackmapID());
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            CodeOrigin codeOrigin = state.jitCode->common.codeOrigins[getById.callSiteIndex().bits()];
            for (unsigned i = 0; i < iter->value.size(); ++i) {
                StackMaps::Record& record = iter->value[i];
            
                RegisterSet usedRegisters = usedRegistersFor(record);
                
                GPRReg result = record.locations[0].directGPR();
                GPRReg base = record.locations[1].directGPR();
                
                JITGetByIdGenerator gen(
                    codeBlock, codeOrigin, getById.callSiteIndex(), usedRegisters, JSValueRegs(base),
                    JSValueRegs(result), NeedToSpill);
                
                MacroAssembler::Label begin = slowPathJIT.label();

                MacroAssembler::Call call = callOperation(
                    state, usedRegisters, slowPathJIT, codeOrigin, &exceptionTarget,
                    operationGetByIdOptimize, result, gen.stubInfo(), base, getById.uid());

                gen.reportSlowPathCall(begin, call);

                getById.m_slowPathDone.append(slowPathJIT.jump());
                getById.m_generators.append(gen);
            }
        }
        
        for (unsigned i = state.putByIds.size(); i--;) {
            PutByIdDescriptor& putById = state.putByIds[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling PutById stackmap #", putById.stackmapID(), "\n");
            
            auto iter = recordMap.find(putById.stackmapID());
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            CodeOrigin codeOrigin = state.jitCode->common.codeOrigins[putById.callSiteIndex().bits()];
            for (unsigned i = 0; i < iter->value.size(); ++i) {
                StackMaps::Record& record = iter->value[i];
                
                RegisterSet usedRegisters = usedRegistersFor(record);
                
                GPRReg base = record.locations[0].directGPR();
                GPRReg value = record.locations[1].directGPR();
                
                JITPutByIdGenerator gen(
                    codeBlock, codeOrigin, putById.callSiteIndex(), usedRegisters, JSValueRegs(base),
                    JSValueRegs(value), GPRInfo::patchpointScratchRegister, NeedToSpill,
                    putById.ecmaMode(), putById.putKind());
                
                MacroAssembler::Label begin = slowPathJIT.label();
                
                MacroAssembler::Call call = callOperation(
                    state, usedRegisters, slowPathJIT, codeOrigin, &exceptionTarget,
                    gen.slowPathFunction(), gen.stubInfo(), value, base, putById.uid());
                
                gen.reportSlowPathCall(begin, call);
                
                putById.m_slowPathDone.append(slowPathJIT.jump());
                putById.m_generators.append(gen);
            }
        }

        for (unsigned i = state.checkIns.size(); i--;) {
            CheckInDescriptor& checkIn = state.checkIns[i];
            
            if (verboseCompilationEnabled())
                dataLog("Handling checkIn stackmap #", checkIn.stackmapID(), "\n");
            
            auto iter = recordMap.find(checkIn.stackmapID());
            if (iter == recordMap.end()) {
                // It was optimized out.
                continue;
            }
            
            CodeOrigin codeOrigin = state.jitCode->common.codeOrigins[checkIn.callSiteIndex().bits()];
            for (unsigned i = 0; i < iter->value.size(); ++i) {
                StackMaps::Record& record = iter->value[i];
                RegisterSet usedRegisters = usedRegistersFor(record);
                GPRReg result = record.locations[0].directGPR();
                GPRReg obj = record.locations[1].directGPR();
                StructureStubInfo* stubInfo = codeBlock->addStubInfo(AccessType::In); 
                stubInfo->codeOrigin = codeOrigin;
                stubInfo->callSiteIndex = checkIn.callSiteIndex();
                stubInfo->patch.baseGPR = static_cast<int8_t>(obj);
                stubInfo->patch.valueGPR = static_cast<int8_t>(result);
                stubInfo->patch.usedRegisters = usedRegisters;
                stubInfo->patch.spillMode = NeedToSpill;

                MacroAssembler::Label begin = slowPathJIT.label();

                MacroAssembler::Call slowCall = callOperation(
                    state, usedRegisters, slowPathJIT, codeOrigin, &exceptionTarget,
                    operationInOptimize, result, stubInfo, obj, checkIn.m_uid);

                checkIn.m_slowPathDone.append(slowPathJIT.jump());
                
                checkIn.m_generators.append(CheckInGenerator(stubInfo, slowCall, begin));
            }
        }
        
        exceptionTarget.link(&slowPathJIT);
        MacroAssembler::Jump exceptionJump = slowPathJIT.jump();
        
        state.finalizer->sideCodeLinkBuffer = std::make_unique<LinkBuffer>(vm, slowPathJIT, codeBlock, JITCompilationCanFail);
        if (state.finalizer->sideCodeLinkBuffer->didFailToAllocate()) {
            state.allocationFailed = true;
            return;
        }
        state.finalizer->sideCodeLinkBuffer->link(
            exceptionJump, state.finalizer->handleExceptionsLinkBuffer->entrypoint());
        
        for (unsigned i = state.getByIds.size(); i--;) {
            generateICFastPath(
                state, codeBlock, generatedFunction, recordMap, state.getByIds[i],
                sizeOfGetById());
        }
        for (unsigned i = state.putByIds.size(); i--;) {
            generateICFastPath(
                state, codeBlock, generatedFunction, recordMap, state.putByIds[i],
                sizeOfPutById());
        }

        for (unsigned i = state.checkIns.size(); i--;) {
            generateCheckInICFastPath(
                state, codeBlock, generatedFunction, recordMap, state.checkIns[i],
                sizeOfIn()); 
        } 
    }
    
    adjustCallICsForStackmaps(state.jsCalls, recordMap);
    
    for (unsigned i = state.jsCalls.size(); i--;) {
        JSCall& call = state.jsCalls[i];

        CCallHelpers fastPathJIT(&vm, codeBlock);
        call.emit(fastPathJIT, state.jitCode->stackmaps.stackSizeForLocals());

        char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset;

        generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfCall(), "JSCall inline cache", [&] (LinkBuffer& linkBuffer, CCallHelpers&, bool) {
            call.link(vm, linkBuffer);
        });
    }
    
    adjustCallICsForStackmaps(state.jsCallVarargses, recordMap);
    
    for (unsigned i = state.jsCallVarargses.size(); i--;) {
        JSCallVarargs& call = state.jsCallVarargses[i];
        
        CCallHelpers fastPathJIT(&vm, codeBlock);
        call.emit(fastPathJIT, varargsSpillSlotsOffset);

        char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset;
        size_t sizeOfIC = sizeOfICFor(call.node());

        generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfIC, "varargs call inline cache", [&] (LinkBuffer& linkBuffer, CCallHelpers&, bool) {
            call.link(vm, linkBuffer, state.finalizer->handleExceptionsLinkBuffer->entrypoint());
        });
    }

    adjustCallICsForStackmaps(state.jsTailCalls, recordMap);

    for (unsigned i = state.jsTailCalls.size(); i--;) {
        JSTailCall& call = state.jsTailCalls[i];

        CCallHelpers fastPathJIT(&vm, codeBlock);
        call.emit(*state.jitCode.get(), fastPathJIT);

        char* startOfIC = bitwise_cast<char*>(generatedFunction) + call.m_instructionOffset;
        size_t sizeOfIC = call.estimatedSize();

        generateInlineIfPossibleOutOfLineIfNot(state, vm, codeBlock, fastPathJIT, startOfIC, sizeOfIC, "tail call inline cache", [&] (LinkBuffer& linkBuffer, CCallHelpers&, bool) {
            call.link(vm, linkBuffer);
        });
    }
    
    auto iter = recordMap.find(state.handleStackOverflowExceptionStackmapID);
    // It's sort of remotely possible that we won't have an in-band exception handling
    // path, for some kinds of functions.
    if (iter != recordMap.end()) {
        for (unsigned i = iter->value.size(); i--;) {
            StackMaps::Record& record = iter->value[i];
            
            CodeLocationLabel source = CodeLocationLabel(
                bitwise_cast<char*>(generatedFunction) + record.instructionOffset);

            RELEASE_ASSERT(stackOverflowException.isSet());

            MacroAssembler::replaceWithJump(source, state.finalizer->handleExceptionsLinkBuffer->locationOf(stackOverflowException));
        }
    }
    
    iter = recordMap.find(state.handleExceptionStackmapID);
    // It's sort of remotely possible that we won't have an in-band exception handling
    // path, for some kinds of functions.
    if (iter != recordMap.end()) {
        for (unsigned i = iter->value.size(); i--;) {
            StackMaps::Record& record = iter->value[i];
            
            CodeLocationLabel source = CodeLocationLabel(
                bitwise_cast<char*>(generatedFunction) + record.instructionOffset);
            
            MacroAssembler::replaceWithJump(source, state.finalizer->handleExceptionsLinkBuffer->entrypoint());
        }
    }
    
    for (unsigned exitIndex = 0; exitIndex < jitCode->osrExit.size(); ++exitIndex) {
        OSRExitCompilationInfo& info = state.finalizer->osrExit[exitIndex];
        OSRExit& exit = jitCode->osrExit[exitIndex];
        iter = recordMap.find(exit.m_stackmapID);
        
        Vector<const void*> codeAddresses;
        
        if (iter != recordMap.end()) {
            for (unsigned i = iter->value.size(); i--;) {
                StackMaps::Record& record = iter->value[i];
                
                CodeLocationLabel source = CodeLocationLabel(
                    bitwise_cast<char*>(generatedFunction) + record.instructionOffset);
                
                codeAddresses.append(bitwise_cast<char*>(generatedFunction) + record.instructionOffset + MacroAssembler::maxJumpReplacementSize());
                
                if (info.m_isInvalidationPoint)
                    jitCode->common.jumpReplacements.append(JumpReplacement(source, info.m_thunkAddress));
                else
                    MacroAssembler::replaceWithJump(source, info.m_thunkAddress);
            }
        }
        
        if (graph.compilation())
            graph.compilation()->addOSRExitSite(codeAddresses);
    }
}
Example #12
0
void TimelineBar::paintMarkers(QPainter &p, const QVector<Marker> &markers,
                               const QVector<uint32_t> &draws, QRectF markerRect)
{
  if(markers.isEmpty() && draws.isEmpty())
    return;

  QTextOption to;

  to.setWrapMode(QTextOption::NoWrap);
  to.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);

  QFontMetrics fm(Formatter::PreferredFont());

  // store a reference of what a completely elided string looks like
  QString tooshort = fm.elidedText(lit("asd"), Qt::ElideRight, fm.height());

  for(const Marker &m : markers)
  {
    QRectF r = markerRect;
    r.setLeft(qMax(m_dataArea.left() + borderWidth * 3, offsetOf(m.eidStart)));
    r.setRight(qMin(m_dataArea.right() - borderWidth, offsetOf(m.eidEnd + 1)));
    r.setHeight(fm.height() + borderWidth * 2);

    if(r.width() <= borderWidth * 2)
      continue;

    QColor backColor = m.color;
    if(r.contains(m_lastPos))
      backColor.setAlpha(150);

    p.setPen(QPen(palette().brush(QPalette::Text), 1.0));
    p.fillRect(r, QBrush(backColor));
    p.drawRect(r);

    p.setPen(QPen(QBrush(contrastingColor(backColor, palette().color(QPalette::Text))), 1.0));

    r.setLeft(r.left() + margin);

    int plusWidth = fm.width(QLatin1Char('+'));
    if(r.width() > plusWidth)
    {
      QRectF plusRect = r;
      plusRect.setWidth(plusWidth);

      QTextOption plusOption = to;
      plusOption.setAlignment(Qt::AlignCenter | Qt::AlignVCenter);

      p.drawText(plusRect, m.expanded ? lit("-") : lit("+"), plusOption);

      r.setLeft(r.left() + plusWidth + margin);
    }

    QString elided = fm.elidedText(m.name, Qt::ElideRight, r.width());

    // if everything was elided, just omit the title entirely
    if(elided == tooshort)
      elided = QString();

    r.setLeft(qRound(r.left() + margin));

    p.drawText(r, elided, to);

    if(m.expanded)
    {
      QRectF childRect = r;
      childRect.setTop(r.bottom() + borderWidth * 2);
      childRect.setBottom(markerRect.bottom());

      paintMarkers(p, m.children, m.draws, childRect);
    }
  }

  p.setRenderHint(QPainter::Antialiasing);

  for(uint32_t d : draws)
  {
    QRectF r = markerRect;
    r.setLeft(qMax(m_dataArea.left() + borderWidth * 3, offsetOf(d)));
    r.setRight(qMin(m_dataArea.right() - borderWidth, offsetOf(d + 1)));
    r.setHeight(fm.height() + borderWidth * 2);

    QPainterPath path;
    path.addRoundedRect(r, 5, 5);

    p.setPen(QPen(palette().brush(QPalette::Text), 1.0));
    p.fillPath(path, d == m_Ctx.CurEvent() ? Qt::green : Qt::blue);
    p.drawPath(path);
  }

  p.setRenderHint(QPainter::Antialiasing, false);
}
Example #13
0
void TimelineBar::paintEvent(QPaintEvent *e)
{
  QPainter p(viewport());

  p.setFont(font());
  p.setRenderHint(QPainter::TextAntialiasing);

  // draw boundaries and background
  {
    QRectF r = viewport()->rect();

    p.fillRect(r, palette().brush(QPalette::Window));

    r = r.marginsRemoved(QMargins(borderWidth + margin, borderWidth + margin, borderWidth + margin,
                                  borderWidth + margin));

    p.fillRect(r, palette().brush(QPalette::Base));
    p.drawRect(r);
  }

  QTextOption to;

  to.setWrapMode(QTextOption::NoWrap);
  to.setAlignment(Qt::AlignLeft | Qt::AlignVCenter);

  QFontMetrics fm = p.fontMetrics();

  {
    QRectF titleRect = m_eidAxisRect;
    titleRect.setLeft(titleRect.left() - m_titleWidth);
    titleRect.setWidth(m_titleWidth);

    p.setPen(QPen(palette().brush(QPalette::Text), 1.0));

    // add an extra margin for the text
    p.drawText(titleRect.marginsRemoved(QMarginsF(margin, 0, 0, 0)), eidAxisTitle, to);

    titleRect.setLeft(titleRect.left() - margin);
    titleRect.setTop(titleRect.top() - margin);
    p.drawLine(titleRect.bottomLeft(), titleRect.bottomRight());
    p.drawLine(titleRect.topRight(), titleRect.bottomRight());
  }

  QRectF eidAxisRect = m_eidAxisRect;

  p.drawLine(eidAxisRect.bottomLeft(), eidAxisRect.bottomRight() + QPointF(margin, 0));

  p.drawLine(m_highlightingRect.topLeft(), m_highlightingRect.topRight());

  if(m_Draws.isEmpty())
    return;

  eidAxisRect.setLeft(m_eidAxisRect.left() + m_pan);

  uint32_t maxEID = m_Draws.isEmpty() ? 0 : m_Draws.back();

  to.setAlignment(Qt::AlignCenter | Qt::AlignVCenter);

  p.setFont(Formatter::PreferredFont());

  QRectF hoverRect = eidAxisRect;

  // clip labels to the visible section
  p.setClipRect(m_eidAxisRect.marginsAdded(QMargins(0, margin, margin, 0)));

  // draw where we're hovering
  {
    QPoint pos = viewport()->mapFromGlobal(QCursor::pos());

    if(m_dataArea.contains(pos))
    {
      uint32_t hoverEID = eventAt(pos.x());

      hoverRect.setLeft(offsetOf(hoverEID));
      hoverRect.setWidth(m_eidAxisLabelWidth);

      // recentre
      hoverRect.moveLeft(hoverRect.left() - m_eidAxisLabelWidth / 2 + m_eidWidth / 2);

      QColor backCol = palette().color(QPalette::Base);

      if(getLuminance(backCol) < 0.2f)
        backCol = backCol.lighter(120);
      else
        backCol = backCol.darker(120);

      QRectF backRect = hoverRect.marginsAdded(QMargins(0, margin - borderWidth, 0, 0));

      backRect.setLeft(qMax(backRect.left(), m_eidAxisRect.left() + 1));

      p.fillRect(backRect, backCol);

      p.drawText(hoverRect, QString::number(hoverEID), to);

      // re-add the top margin so the lines match up with the border around the EID axis
      hoverRect = hoverRect.marginsAdded(QMargins(0, margin, 0, 0));

      if(hoverRect.left() >= m_eidAxisRect.left())
        p.drawLine(hoverRect.topLeft(), hoverRect.bottomLeft());
      p.drawLine(hoverRect.topRight(), hoverRect.bottomRight());

      // shrink the rect a bit for clipping against labels below
      hoverRect.setX(qRound(hoverRect.x() + 0.5));
      hoverRect.setWidth(int(hoverRect.width()));
    }
    else
    {
      hoverRect = QRectF();
    }
  }

  QRectF labelRect = eidAxisRect;
  labelRect.setWidth(m_eidAxisLabelWidth);

  // iterate through the EIDs from 0, starting from possible a negative offset if the user has
  // panned to the right.
  for(uint32_t i = 0; i <= maxEID; i += m_eidAxisLabelStep)
  {
    labelRect.moveLeft(offsetOf(i) - labelRect.width() / 2 + m_eidWidth / 2);

    // check if this label is visible at all, but don't draw labels that intersect with the hovered
    // number
    if(labelRect.right() >= 0 && !labelRect.intersects(hoverRect))
      p.drawText(labelRect, QString::number(i), to);

    // check if labelRect is off the edge of the screen
    if(labelRect.left() >= m_eidAxisRect.right())
      break;
  }

  // stop clipping
  p.setClipRect(viewport()->rect());

  // clip the markers
  p.setClipRect(m_markerRect);

  {
    QPen pen = p.pen();
    paintMarkers(p, m_RootMarkers, m_RootDraws, m_markerRect);
    p.setPen(pen);
  }

  // stop clipping
  p.setClipRect(viewport()->rect());

  QRectF currentRect = eidAxisRect;

  // draw the current label and line
  {
    uint32_t curEID = m_Ctx.CurEvent();

    currentRect.setLeft(offsetOf(curEID));
    currentRect.setWidth(
        qMax(m_eidAxisLabelWidth, m_eidAxisLabelTextWidth + dataBarHeight + margin * 2));

    // recentre
    currentRect.moveLeft(currentRect.left() - currentRect.width() / 2 + m_eidWidth / 2);

    // remember where the middle would have been, without clamping
    qreal realMiddle = currentRect.center().x();

    // clamp the position from the left or right side
    if(currentRect.left() < eidAxisRect.left())
      currentRect.moveLeft(eidAxisRect.left());
    else if(currentRect.right() > eidAxisRect.right())
      currentRect.moveRight(eidAxisRect.right());

    // re-add the top margin so the lines match up with the border around the EID axis
    QRectF currentBackRect = currentRect.marginsAdded(QMargins(0, margin, 0, 0));

    p.fillRect(currentBackRect, palette().brush(QPalette::Base));
    p.drawRect(currentBackRect);

    // draw the 'current marker' pixmap
    const QPixmap &px = Pixmaps::flag_green(devicePixelRatio());
    p.drawPixmap(currentRect.topLeft() + QPointF(margin, 1), px, px.rect());

    // move to where the text should be and draw it
    currentRect.setLeft(currentRect.left() + margin * 2 + dataBarHeight);
    p.drawText(currentRect, QString::number(curEID), to);

    // draw a line from the bottom of the shadow downwards
    QPointF currentTop = currentRect.center();
    currentTop.setX(int(qBound(eidAxisRect.left(), realMiddle, eidAxisRect.right() - 2.0)) + 0.5);
    currentTop.setY(currentRect.bottom());

    QPointF currentBottom = currentTop;
    currentBottom.setY(m_markerRect.bottom());

    p.drawLine(currentTop, currentBottom);
  }

  to.setAlignment(Qt::AlignLeft | Qt::AlignTop);

  if(!m_UsageTarget.isEmpty() || !m_HistoryTarget.isEmpty())
  {
    p.setRenderHint(QPainter::Antialiasing);

    QRectF highlightLabel = m_highlightingRect.marginsRemoved(uniformMargins(margin));

    highlightLabel.setX(highlightLabel.x() + margin);

    QString text;

    if(!m_HistoryTarget.isEmpty())
      text = tr("Pixel history for %1").arg(m_HistoryTarget);
    else
      text = tr("Usage for %1:").arg(m_UsageTarget);

    p.drawText(highlightLabel, text, to);

    const int triRadius = fm.averageCharWidth();
    const int triHeight = fm.ascent();

    QPainterPath triangle;
    triangle.addPolygon(
        QPolygonF({QPoint(0, triHeight), QPoint(triRadius * 2, triHeight), QPoint(triRadius, 0)}));
    triangle.closeSubpath();

    enum
    {
      ReadUsage,
      WriteUsage,
      ReadWriteUsage,
      ClearUsage,
      BarrierUsage,

      HistoryPassed,
      HistoryFailed,

      UsageCount,
    };

    const QColor colors[UsageCount] = {
        // read
        QColor(Qt::red),
        // write
        QColor(Qt::green),
        // read/write
        QColor(Qt::yellow),
        // clear
        QColor(Qt::blue),
        // barrier
        QColor(Qt::magenta),

        // pass
        QColor(Qt::green),
        // fail
        QColor(Qt::red),
    };

    // draw the key
    if(m_HistoryTarget.isEmpty())
    {
      // advance past the first text to draw the key
      highlightLabel.setLeft(highlightLabel.left() + fm.width(text));

      text = lit(" Reads ( ");
      p.drawText(highlightLabel, text, to);
      highlightLabel.setLeft(highlightLabel.left() + fm.width(text));

      QPainterPath path = triangle.translated(aliasAlign(highlightLabel.topLeft()));
      p.fillPath(path, colors[ReadUsage]);
      p.drawPath(path);
      highlightLabel.setLeft(highlightLabel.left() + triRadius * 2);

      text = lit(" ), Writes ( ");
      p.drawText(highlightLabel, text, to);
      highlightLabel.setLeft(highlightLabel.left() + fm.width(text));

      path = triangle.translated(aliasAlign(highlightLabel.topLeft()));
      p.fillPath(path, colors[WriteUsage]);
      p.drawPath(path);
      highlightLabel.setLeft(highlightLabel.left() + triRadius * 2);

      text = lit(" ), Read/Write ( ");
      p.drawText(highlightLabel, text, to);
      highlightLabel.setLeft(highlightLabel.left() + fm.width(text));

      path = triangle.translated(aliasAlign(highlightLabel.topLeft()));
      p.fillPath(path, colors[ReadWriteUsage]);
      p.drawPath(path);
      highlightLabel.setLeft(highlightLabel.left() + triRadius * 2);

      if(m_Ctx.CurPipelineState().SupportsBarriers())
      {
        text = lit(" ) Barriers ( ");
        p.drawText(highlightLabel, text, to);
        highlightLabel.setLeft(highlightLabel.left() + fm.width(text));

        path = triangle.translated(aliasAlign(highlightLabel.topLeft()));
        p.fillPath(path, colors[BarrierUsage]);
        p.drawPath(path);
        highlightLabel.setLeft(highlightLabel.left() + triRadius * 2);
      }

      text = lit(" ), and Clears ( ");
      p.drawText(highlightLabel, text, to);
      highlightLabel.setLeft(highlightLabel.left() + fm.width(text));

      path = triangle.translated(aliasAlign(highlightLabel.topLeft()));
      p.fillPath(path, colors[ClearUsage]);
      p.drawPath(path);
      highlightLabel.setLeft(highlightLabel.left() + triRadius * 2);

      text = lit(" )");
      p.drawText(highlightLabel, text, to);
    }

    PipRanges pipranges[UsageCount];

    QRectF pipsRect = m_highlightingRect.marginsRemoved(uniformMargins(margin));

    pipsRect.setX(pipsRect.x() + margin + m_titleWidth);
    pipsRect.setHeight(triHeight + margin);
    pipsRect.moveBottom(m_highlightingRect.bottom());

    p.setClipRect(pipsRect);

    qreal leftClip = -triRadius * 2.0;
    qreal rightClip = pipsRect.width() + triRadius * 10.0;

    if(!m_HistoryEvents.isEmpty())
    {
      for(const PixelModification &mod : m_HistoryEvents)
      {
        qreal pos = offsetOf(mod.eventId) + m_eidWidth / 2 - triRadius;

        if(pos < leftClip || pos > rightClip)
          continue;

        if(mod.Passed())
          pipranges[HistoryPassed].push(pos, triRadius);
        else
          pipranges[HistoryFailed].push(pos, triRadius);
      }
    }
    else
    {
      for(const EventUsage &use : m_UsageEvents)
      {
        qreal pos = offsetOf(use.eventId) + m_eidWidth / 2 - triRadius;

        if(pos < leftClip || pos > rightClip)
          continue;

        if(((int)use.usage >= (int)ResourceUsage::VS_RWResource &&
            (int)use.usage <= (int)ResourceUsage::All_RWResource) ||
           use.usage == ResourceUsage::GenMips || use.usage == ResourceUsage::Copy ||
           use.usage == ResourceUsage::Resolve)
        {
          pipranges[ReadWriteUsage].push(pos, triRadius);
        }
        else if(use.usage == ResourceUsage::StreamOut || use.usage == ResourceUsage::ResolveDst ||
                use.usage == ResourceUsage::ColorTarget ||
                use.usage == ResourceUsage::DepthStencilTarget || use.usage == ResourceUsage::CopyDst)
        {
          pipranges[WriteUsage].push(pos, triRadius);
        }
        else if(use.usage == ResourceUsage::Clear)
        {
          pipranges[ClearUsage].push(pos, triRadius);
        }
        else if(use.usage == ResourceUsage::Barrier)
        {
          pipranges[BarrierUsage].push(pos, triRadius);
        }
        else
        {
          pipranges[ReadUsage].push(pos, triRadius);
        }
      }
    }

    for(int i = 0; i < UsageCount; i++)
    {
      QPainterPath path = pipranges[i].makePath(triRadius, triHeight, pipsRect.y());

      if(!path.isEmpty())
      {
        p.drawPath(path);
        p.fillPath(path, colors[i]);
      }
    }
  }
  else
  {
    QRectF highlightLabel = m_highlightingRect;
    highlightLabel = highlightLabel.marginsRemoved(uniformMargins(margin));

    highlightLabel.setX(highlightLabel.x() + margin);

    p.drawText(highlightLabel, tr("No resource selected for highlighting."), to);
  }
}