Example #1
0
void do_optimize(const Index& index, FuncAnalysis ainfo) {
  FTRACE(2, "{:-^70}\n", "Optimize Func");

  visit_blocks("first pass", index, ainfo, first_pass);

  /*
   * Note, it's useful to do dead block removal before DCE, so it can
   * remove code relating to the branch to the dead block.
   */
  remove_unreachable_blocks(index, ainfo);

  if (options.LocalDCE) {
    visit_blocks("local DCE", index, ainfo, local_dce);
  }
  if (options.GlobalDCE) {
    global_dce(index, ainfo);
    assert(check(*ainfo.ctx.func));
    /*
     * Global DCE can change types of locals across blocks.  See
     * dce.cpp for an explanation.
     *
     * We need to perform a final type analysis before we do anything
     * else.
     */
    ainfo = analyze_func(index, ainfo.ctx);
  }

  if (options.InsertAssertions) {
    visit_blocks("insert assertions", index, ainfo, insert_assertions);
  }
}
Example #2
0
File: parser.c Project: fwum/fwum
file_contents parse(parse_source source) {
	file_contents contents;
    contents.imports = ll_new();
	contents.enums = ll_new();
    contents.unions = ll_new();
	contents.structs = ll_new();
	contents.functions = ll_new();
	optional op = get_token(&source);
	while(op_has(op)) {
		parse_token current = *((parse_token*)op_get(op));
		if(equals(current.data, new_slice("struct"))) {
			struct_declaration *dec = analyze_struct(&source);
			ll_add_last(contents.structs, dec);
		} else if(equals(current.data, new_slice("union"))) {
			struct_declaration *dec = analyze_struct(&source);
			ll_add_last(contents.unions, dec);
		} else if(equals(current.data, new_slice("func"))) {
			func_declaration *func = analyze_func(&source);
			ll_add_last(contents.functions, func);
		} else if(equals(current.data, new_slice("import"))) {
            import_declaration *imp = parse_import(&source);
            ll_add_last(contents.imports, imp);
        } else if(equals(current.data, new_slice("enum"))) {
			enum_declaration *enm = parse_enum(&source);
			ll_add_last(contents.enums, enm);
		}//TODO: Throw an error for an unexpected token
		op = get_token(&source);
	}
	return contents;
}
Example #3
0
void do_optimize(const Index& index, const FuncAnalysis& ainfo) {
  FTRACE(2, "{:-^70}\n", "Optimize Func");

  visit_blocks("first pass", index, ainfo, first_pass);

  /*
   * Note, it's useful to do dead block removal before DCE, so it can
   * remove code relating to the branch to the dead block.
   *
   * If we didn't remove jumps to dead blocks, we replace all
   * supposedly unreachable blocks with fatal instructions.
   *
   * TODO(#3751005): removedeadblocks doesn't remove the contents of
   * the blocks which it should.
   */
  if (!options.RemoveDeadBlocks) {
    for (auto& blk : ainfo.rpoBlocks) {
      auto const& state = ainfo.bdata[blk->id].stateIn;
      if (state.initialized) continue;
      auto const srcLoc = blk->hhbcs.front().srcLoc;
      blk->hhbcs = {
        bc_with_loc(srcLoc, bc::String { s_unreachable.get() }),
        bc_with_loc(srcLoc, bc::Fatal { FatalOp::Runtime })
      };
      blk->fallthrough = nullptr;
    }
  }

  if (options.LocalDCE) {
    visit_blocks("local DCE", index, ainfo, local_dce);
  }
  if (options.GlobalDCE) {
    global_dce(index, ainfo);
    assert(check(*ainfo.ctx.func));
  }

  if (options.InsertAssertions) {
    /*
     * Global DCE can change types of locals across blocks.  See
     * dce.cpp for an explanation.
     *
     * We need to perform a final type analysis before we insert type
     * assertions.
     */
    auto const ainfo2 = analyze_func(index, ainfo.ctx);
    visit_blocks("insert assertions", index, ainfo2, insert_assertions);
  }
}
Example #4
0
void collect_func(Stats& stats, const Index& index, php::Func& func) {
  if (!func.cls) {
    ++stats.totalFunctions;
    if (func.attrs & AttrPersistent) {
      ++stats.persistentFunctions;
    }
    if (func.attrs & AttrUnique) {
      ++stats.uniqueFunctions;
    }
  }

  auto const ty = index.lookup_return_type_raw(&func);

  add_type(stats.returns, ty);

  for (auto& blk : func.blocks) {
    if (blk->id == NoBlockId) continue;
    for (auto& bc : blk->hhbcs) {
      collect_simple(stats, bc);
    }
  }

  if (!options.extendedStats) return;

  auto const ctx = Context { func.unit, &func, func.cls };
  auto const fa  = analyze_func(index, ctx);
  {
    Trace::Bump bumper{Trace::hhbbc, kStatsBump};
    for (auto& blk : func.blocks) {
      if (blk->id == NoBlockId) continue;
      auto state = fa.bdata[blk->id].stateIn;
      if (!state.initialized) continue;

      CollectedInfo collect { index, ctx, nullptr, nullptr };
      Interp interp { index, ctx, collect, borrow(blk), state };
      for (auto& bc : blk->hhbcs) {
        auto noop    = [] (BlockId, const State&) {};
        auto flags   = StepFlags {};
        ISS env { interp, flags, noop };
        StatsSS sss { env, stats };
        dispatch(sss, bc);
      }
    }
  }
}
Example #5
0
void optimize(Index& index, php::Program& program) {
    assert(check(program));
    trace_time tracer("optimize");
    SCOPE_EXIT { state_after("optimize", program); };

    // Counters, just for debug printing.
    std::atomic<uint32_t> total_funcs{0};
    auto round = uint32_t{0};

    /*
     * Algorithm:
     *
     * Start by running an analyze pass on every function.  During
     * analysis, information about functions or classes will be
     * requested from the Index, which initially won't really know much,
     * but will record a dependency.  This part is done in parallel: no
     * passes are mutating anything, just reading from the Index.
     *
     * After a pass, we do a single-threaded "update" step to prepare
     * for the next pass: for each function that was analyzed, note the
     * facts we learned that may aid analyzing other functions in the
     * program, and register them in the index.  At this point, if any
     * of these facts are more useful than they used to be, add all the
     * Contexts that had a dependency on the new information to the work
     * list again, in case they can do better based on the new fact.
     *
     * Repeat until the work list is empty.
     */
    auto work = initial_work(program);
    while (!work.empty()) {
        auto const results = [&] {
            trace_time trace(
                "analyzing",
                folly::format("round {} -- {} work items", round, work.size()).str()
            );
            return parallel_map(
                work,
            [&] (const Context& ctx) -> folly::Optional<FuncAnalysis> {
                total_funcs.fetch_add(1, std::memory_order_relaxed);
                return analyze_func(index, ctx);
            }
            );
        }();
        work.clear();

        ++round;
        trace_time update_time("updating");

        std::set<Context> revisit;
        for (auto i = size_t{0}; i < results.size(); ++i) {
            auto& result = *results[i];

            assert(result.ctx.func == work[i].func);
            assert(result.ctx.cls == work[i].cls);
            assert(result.ctx.unit == work[i].unit);

            auto deps = index.refine_return_type(
                            result.ctx.func, result.inferredReturn
                        );
            for (auto& d : deps) revisit.insert(d);
        }

        std::copy(begin(revisit), end(revisit), std::back_inserter(work));
    }

    if (Trace::moduleEnabledRelease(Trace::hhbbc_time, 1)) {
        Trace::traceRelease("total function visits %u\n", total_funcs.load());
    }

    /*
     * Finally, use the results of all these iterations to perform
     * optimization.  This reanalyzes every function using our
     * now-very-updated Index, and then runs optimize_func with the
     * results.
     *
     * We do this in parallel: all the shared information is queried out
     * of the index, and each thread is allowed to modify the bytecode
     * for the function it is looking at.
     *
     * NOTE: currently they can't modify anything other than the
     * bytecode/Blocks, because other threads may be doing unlocked
     * queries to php::Func and php::Class structures.
     */
    trace_time final_pass("final pass");
    work = initial_work(program);
    parallel_for_each(
        initial_work(program),
    [&] (Context ctx) {
        optimize_func(index, analyze_func(index, ctx));
    }
    );
}