void Interprocedural::run_analysis()

in source/Interprocedural.cpp [173:269]


void Interprocedural::run_analysis(Context& context, Registry& registry) {
  LOG(1, "Computing global fixpoint...");
  auto methods_to_analyze = std::make_unique<ConcurrentSet<const Method*>>();
  for (const auto* method : *context.methods) {
    methods_to_analyze->insert(method);
  }

  std::size_t iteration = 0;
  while (methods_to_analyze->size() > 0) {
    Timer iteration_timer;
    iteration++;

    auto resident_set_size = resident_set_size_in_gb();
    context.statistics->log_resident_set_size(resident_set_size);
    LOG(1,
        "Global iteration {}. Analyzing {} methods... (Memory used, RSS: {:.2f}GB)",
        iteration,
        methods_to_analyze->size(),
        resident_set_size);

    if (iteration > Heuristics::kMaxNumberIterations) {
      ERROR(1, "Too many iterations");
      std::string message = "Unstable methods are:";
      for (const auto* method : *methods_to_analyze) {
        message.append(fmt::format("\n`{}`", method->show()));
      }
      LOG(1, message);
      throw std::runtime_error("Too many iterations, exiting.");
    }

    auto new_methods_to_analyze =
        std::make_unique<ConcurrentSet<const Method*>>();

    unsigned int threads = sparta::parallel::default_num_threads();
    if (context.options->sequential()) {
      WARNING(1, "Running sequentially!");
      threads = 1u;
    }

    std::atomic<std::size_t> method_iteration(0);
    auto queue = sparta::work_queue<const Method*>(
        [&](const Method* method) {
          method_iteration++;
          if (method_iteration % 10000 == 0) {
            LOG(1,
                "Processed {}/{} methods.",
                method_iteration.load(),
                methods_to_analyze->size());
          } else if (method_iteration % 100 == 0) {
            LOG(4,
                "Processed {}/{} methods.",
                method_iteration.load(),
                methods_to_analyze->size());
          }

          const auto old_model = registry.get(method);
          if (old_model.skip_analysis()) {
            LOG(3, "Skipping `{}`...", method->show());
            return;
          }

          auto new_model = analyze(context, registry, old_model);

          new_model.join_with(old_model);

          if (!new_model.leq(old_model)) {
            if (!context.call_graph->callees(method).empty() ||
                !context.call_graph->artificial_callees(method).empty()) {
              new_methods_to_analyze->insert(method);
            }
            for (const auto* dependency :
                 context.dependencies->dependencies(method)) {
              new_methods_to_analyze->insert(dependency);
            }
          }

          registry.set(new_model);
        },
        threads);
    context.scheduler->schedule(
        *methods_to_analyze,
        [&](const Method* method, std::size_t worker_id) {
          queue.add_item(method, worker_id);
        },
        threads);
    queue.run_all();

    LOG(2,
        "Global fixpoint iteration completed in {:.2f}s.",
        iteration_timer.duration_in_seconds());

    methods_to_analyze = std::move(new_methods_to_analyze);
  }

  context.statistics->log_number_iterations(iteration);
  LOG(2, "Global fixpoint reached.");
}