Beispiel #1
0
TEST(JumpOpts, optimizeCondTraceExit) {
  BCMarker marker = BCMarker::Dummy();

  IRUnit unit{0};

  Block* entry = unit.entry();
  Block* taken = unit.defBlock();
  Block* fallthru = unit.defBlock();

  // A conditional jump that goes to "SyncABIRegs; ReqBindJmp" on both edges
  // can be coalesced into a ReqBindJmpSomething.

  auto fp = unit.gen(DefFP, marker);
  auto sp = unit.gen(DefSP, marker, StackOffset(0), fp->dst());
  auto val = unit.gen(Conjure, marker, Type::Bool);
  auto jmp = unit.gen(JmpZero, marker, taken, val->dst());
  jmp->setNext(fallthru);
  entry->push_back({fp, sp, val, jmp});

  auto bcoff1 = 10;
  auto sync1 = unit.gen(SyncABIRegs, marker, fp->dst(), sp->dst());
  auto bind1 = unit.gen(ReqBindJmp, marker, BCOffset(bcoff1));
  taken->push_back({sync1, bind1});

  auto bcoff2 = 20;
  auto sync2 = unit.gen(SyncABIRegs, marker, fp->dst(), sp->dst());
  auto bind2 = unit.gen(ReqBindJmp, marker, BCOffset(bcoff2));
  fallthru->push_back({sync2, bind2});

  optimizeJumps(unit);

  EXPECT_EQ(nullptr, entry->next());
  EXPECT_EQ(nullptr, entry->taken());
  auto const& back = entry->back();
  EXPECT_EQ(ReqBindJmpZero, back.op());
  EXPECT_EQ(val->dst(), back.src(0));
  auto const* data = back.extra<ReqBindJccData>();
  EXPECT_EQ(bcoff1, data->taken);
  EXPECT_EQ(bcoff2, data->notTaken);
}
Beispiel #2
0
void optimizeTrace(Trace* trace, IRFactory* irFactory) {
    if (RuntimeOption::EvalHHIRMemOpt) {
        optimizeMemoryAccesses(trace, irFactory);
        if (RuntimeOption::EvalDumpIR > 5) {
            std::cout << "----- HHIR after MemElim -----\n";
            trace->print(std::cout, false);
            std::cout << "---------------------------\n";
        }
    }
    if (RuntimeOption::EvalHHIRDeadCodeElim) {
        eliminateDeadCode(trace, irFactory);
        optimizeJumps(trace, irFactory);
        if (RuntimeOption::EvalDumpIR > 5) {
            std::cout << "----- HHIR after DCE -----\n";
            trace->print(std::cout, false);
            std::cout << "---------------------------\n";
        }
    }
    if (RuntimeOption::EvalHHIRGenerateAsserts) {
        insertRefCountAsserts(trace, irFactory);
    }
}
Beispiel #3
0
TEST(JumpOpts, optimizeSideExitJcc) {
  BCMarker marker = BCMarker::Dummy();
  IRUnit unit{0};

  Block* entry = unit.entry();
  Block* taken = unit.defBlock();
  Block* fallthru = unit.defBlock();

  // A conditional jump that goes to a "SyncABIRegs; ReqBindJmp" on the taken
  // edge only can turn into a SideExitJmpSomething.

  auto fp = unit.gen(DefFP, marker);
  auto sp = unit.gen(DefSP, marker, StackOffset(0), fp->dst());
  auto val = unit.gen(Conjure, marker, Type::Bool);
  auto jcc = unit.gen(JmpZero, marker, taken, val->dst());
  jcc->setNext(fallthru);
  entry->push_back({fp, sp, val, jcc});

  fallthru->push_back(unit.gen(Halt, marker));

  auto bcoff = 10;
  auto sync = unit.gen(SyncABIRegs, marker, fp->dst(), sp->dst());
  auto bind = unit.gen(ReqBindJmp, marker, BCOffset(bcoff));
  taken->push_back({sync, bind});

  optimizeJumps(unit);

  // This exercises trivial jump optimization too: since the JmpZero gets turned
  // into a non-branch, the entry block gets coalesced with the Halt block.
  EXPECT_EQ(nullptr, entry->next());
  EXPECT_EQ(nullptr, entry->taken());
  EXPECT_EQ(Halt, entry->back().op());
  auto const& sideExit = *(--entry->backIter());
  EXPECT_EQ(SideExitJmpZero, sideExit.op());
  EXPECT_EQ(bcoff, sideExit.extra<SideExitJccData>()->taken);
  EXPECT_EQ(val->dst(), sideExit.src(0));
}
Beispiel #4
0
TEST(JumpOpts, eliminateTrivial) {
  BCMarker marker = BCMarker::Dummy();

  // Trivial jumps are eliminated
  {
    IRUnit unit{test_context};

    Block* entry = unit.entry();
    Block* second = unit.defBlock();

    entry->push_back(unit.gen(Jmp, marker, second));

    // Make sure the DefLabel gets deleted
    second->push_back(unit.gen(DefLabel, marker));
    second->push_back(unit.gen(Halt, marker));

    optimizeJumps(unit);

    EXPECT_EQ(1, entry->instrs().size());
    EXPECT_MATCH(entry->front(), Halt);
    EXPECT_TRUE(entry->isExit());
  }

  // Jumps with arguments are also eliminated
  {
    IRUnit unit{test_context};

    Block* entry = unit.entry();
    Block* second = unit.defBlock();

    auto value = unit.gen(Conjure, marker, Type::Gen);
    entry->push_back(value);
    entry->push_back(unit.gen(Jmp, marker, second, value->dst()));
    second->push_back(unit.defLabel(1, marker, {0}));
    second->push_back(unit.gen(Halt, marker));

    optimizeJumps(unit);

    EXPECT_EQ(3, entry->instrs().size());
    EXPECT_MATCH(entry->back(), Halt);
    EXPECT_TRUE(entry->isExit());
  }

  // Jumps to blocks with other predecessors are not eliminated
  {
    IRUnit unit{test_context};

    Block* pred1 = unit.entry();
    Block* pred2 = unit.defBlock();
    Block* succ  = unit.defBlock();

    // pred2 is actually unreachable but this optimization pass shouldn't be
    // concerned about that
    pred1->push_back(unit.gen(Jmp, marker, succ));
    pred2->push_back(unit.gen(Jmp, marker, succ));
    succ->push_back(unit.gen(Halt, marker));

    optimizeJumps(unit);

    EXPECT_EQ(1, pred1->instrs().size());
    EXPECT_EQ(1, pred2->instrs().size());
    EXPECT_MATCH(pred1->back(), Jmp, succ);
    EXPECT_MATCH(pred2->back(), Jmp, succ);
  }
}
Beispiel #5
0
void optimizeTrace(Trace* trace, TraceBuilder* traceBuilder) {
  IRFactory* irFactory = traceBuilder->getIrFactory();
  if (RuntimeOption::EvalHHIRMemOpt) {
    optimizeMemoryAccesses(trace, irFactory);
    if (RuntimeOption::EvalDumpIR > 5) {
      std::cout << "----- HHIR after MemElim -----\n";
      trace->print(std::cout);
      std::cout << "---------------------------\n";
    }
    assert(JIT::checkCfg(trace, *irFactory));
  }
  if (RuntimeOption::EvalHHIRDeadCodeElim) {
    eliminateDeadCode(trace, irFactory);
    if (RuntimeOption::EvalDumpIR > 5) {
      std::cout << "----- HHIR after DCE -----\n";
      trace->print(std::cout);
      std::cout << "---------------------------\n";
    }
    assert(JIT::checkCfg(trace, *irFactory));
  }
  if (RuntimeOption::EvalHHIRExtraOptPass
      && (RuntimeOption::EvalHHIRCse
          || RuntimeOption::EvalHHIRSimplification)) {
    traceBuilder->optimizeTrace();
    if (RuntimeOption::EvalDumpIR > 5) {
      std::cout << "----- HHIR after CSE/Simplification -----\n";
      trace->print(std::cout);
      std::cout << "---------------------------\n";
    }
    assert(JIT::checkCfg(trace, *irFactory));
    // Cleanup any dead code left around by CSE/Simplification
    // Ideally, this would be controlled by a flag returned
    // by optimzeTrace indicating whether DCE is necessary
    if (RuntimeOption::EvalHHIRDeadCodeElim) {
      eliminateDeadCode(trace, irFactory);
      if (RuntimeOption::EvalDumpIR > 5) {
        std::cout << "----- HHIR after DCE -----\n";
        trace->print(std::cout);
        std::cout << "---------------------------\n";
      }
      assert(JIT::checkCfg(trace, *irFactory));
    }
  }
  if (RuntimeOption::EvalHHIRJumpOpts) {
    optimizeJumps(trace, irFactory);
    if (RuntimeOption::EvalDumpIR > 5) {
      std::cout << "----- HHIR after jump opts -----\n";
      trace->print(std::cout);
      std::cout << "---------------------------\n";
    }
    assert(JIT::checkCfg(trace, *irFactory));
  }
  if (RuntimeOption::EvalHHIRGenerateAsserts) {
    insertAsserts(trace, irFactory);
    if (RuntimeOption::EvalDumpIR > 5) {
      std::cout << "----- HHIR after inserting RefCnt asserts -----\n";
      trace->print(std::cout);
      std::cout << "---------------------------\n";
    }
    assert(JIT::checkCfg(trace, *irFactory));
  }
}