Esempio n. 1
0
TEST(Simplifier, CondJmp) {
  IRUnit unit{test_context};
  Simplifier sim{unit};
  BCMarker marker = BCMarker::Dummy();

  // Folding Conv*ToBool
  {
    auto val = unit.gen(Conjure, marker, Type::Int);
    auto cnv = unit.gen(ConvIntToBool, marker, val->dst());
    auto jcc = unit.gen(JmpZero, marker, unit.defBlock(), cnv->dst());

    auto result = sim.simplify(jcc, false);

    EXPECT_EQ(nullptr, result.dst);
    ASSERT_EQ(1, result.instrs.size());
    EXPECT_MATCH(result.instrs[0], JmpZero, val->dst());
  }

  // Folding in negation
  {
    auto val = unit.gen(Conjure, marker, Type::Bool);
    auto neg = unit.gen(XorBool, marker, val->dst(), unit.cns(true));
    auto jcc = unit.gen(JmpZero, marker, unit.defBlock(), neg->dst());

    auto result = sim.simplify(jcc, false);

    EXPECT_EQ(nullptr, result.dst);
    ASSERT_EQ(1, result.instrs.size());
    EXPECT_MATCH(result.instrs[0], JmpNZero, val->dst());
  }
}
Esempio n. 2
0
TEST(Simplifier, DoubleCmp) {
  IRUnit unit{test_context};
  BCMarker dummy = BCMarker::Dummy();

  // Lt(X:Dbl, Y:Int) --> LtDbl(X, ConvIntToDbl(Y))
  {
    auto x = unit.gen(Conjure, dummy, Type::Dbl);
    auto y = unit.gen(Conjure, dummy, Type::Int);
    auto lt = unit.gen(Lt, dummy, x->dst(), y->dst());
    auto result = simplify(unit, lt, false);

    auto conv = result.instrs[0];
    EXPECT_MATCH(conv, ConvIntToDbl, y->dst());
    EXPECT_MATCH(result.instrs[1], LtDbl, x->dst(), conv->dst());
    EXPECT_EQ(result.instrs[1]->dst(), result.dst);
  }

  // Lt(X:Dbl, 10) --> LtDbl(X, 10.0)
  {
    auto x  = unit.gen(Conjure, dummy, Type::Dbl);
    auto lt = unit.gen(Lt, dummy, x->dst(), unit.cns(10));
    auto result = simplify(unit, lt, false);

    EXPECT_MATCH(result.instrs[0], LtDbl, x->dst(), unit.cns(10.0));
  }
}
Esempio n. 3
0
TEST(Simplifier, Count) {
  IRUnit unit{test_context};
  Simplifier sim{unit};
  BCMarker dummy = BCMarker::Dummy();

  // Count($null) --> 0
  {
    auto null = unit.gen(Conjure, dummy, Type::Null);
    auto count = unit.gen(Count, dummy, null->dst());
    auto result = sim.simplify(count, false);

    EXPECT_NE(nullptr, result.dst);
    EXPECT_EQ(0, result.instrs.size());
    EXPECT_EQ(0, result.dst->intVal());
  }

  // Count($bool_int_dbl_str) --> 1
  {
    auto ty = Type::Bool | Type::Int | Type::Dbl | Type::Str | Type::Res;
    auto val = unit.gen(Conjure, dummy, ty);
    auto count = unit.gen(Count, dummy, val->dst());
    auto result = sim.simplify(count, false);

    EXPECT_NE(nullptr, result.dst);
    EXPECT_EQ(0, result.instrs.size());
    EXPECT_EQ(1, result.dst->intVal());
  }

  // Count($array_no_kind) --> CountArray($array_no_kind)
  {
    auto arr = unit.gen(Conjure, dummy, Type::Arr);
    auto count = unit.gen(Count, dummy, arr->dst());
    auto result = sim.simplify(count, false);

    EXPECT_NE(nullptr, result.dst);
    EXPECT_EQ(1, result.instrs.size());
    EXPECT_MATCH(result.instrs[0], CountArray, arr->dst());
  }

  // Count($array_not_nvtw) --> CountArrayFast($array_not_nvtw)
  {
    auto ty = Type::Arr.specialize(ArrayData::kPackedKind);
    auto arr = unit.gen(Conjure, dummy, ty);
    auto count = unit.gen(Count, dummy, arr->dst());
    auto result = sim.simplify(count, false);

    EXPECT_NE(nullptr, result.dst);
    EXPECT_EQ(1, result.instrs.size());
    EXPECT_MATCH(result.instrs[0], CountArrayFast, arr->dst());
  }

  // Count($some_obj) --> Count($some_obj)
  {
    auto obj = unit.gen(Conjure, dummy, Type::Obj);
    auto count = unit.gen(Count, dummy, obj->dst());
    auto result = sim.simplify(count, false);
    EXPECT_NO_CHANGE(result);
  }

}
Esempio n. 4
0
TEST(Simplifier, JumpFuse) {
  BCMarker dummy = BCMarker::Dummy();
  IRUnit unit(test_context);
  Simplifier sim(unit);

  {
    // JmpZero(Eq(X, true)) --> JmpEq(X, false)
    auto taken = unit.defBlock();
    auto lhs = unit.cns(true);
    auto rhs = unit.gen(Conjure, dummy, Type::Bool);
    auto eq  = unit.gen(Eq, dummy, lhs, rhs->dst());
    auto jmp = unit.gen(JmpZero, dummy, taken, eq->dst());
    auto result = sim.simplify(jmp, false);

    EXPECT_EQ(nullptr, result.dst);
    EXPECT_EQ(2, result.instrs.size());

    // This is a dead Eq instruction; an artifact of weirdness in the
    // implementation. Should go away.
    EXPECT_FALSE(result.instrs[0]->isControlFlow());

    EXPECT_MATCH(result.instrs[1], JmpEq, taken, rhs->dst(), unit.cns(false));
  }

  {
    // JmpNZero(Neq(X:Int, Y:Int)) --> JmpNeqInt(X, Y)
    auto taken = unit.defBlock();
    auto x = unit.gen(Conjure, dummy, Type::Int);
    auto y = unit.gen(Conjure, dummy, Type::Int);

    auto neq = unit.gen(Neq, dummy, x->dst(), y->dst());
    auto jmp = unit.gen(JmpNZero, dummy, taken, neq->dst());
    auto result = sim.simplify(jmp, false);

    EXPECT_EQ(nullptr, result.dst);
    EXPECT_EQ(2, result.instrs.size());
    EXPECT_FALSE(result.instrs[0]->isControlFlow());  // dead Neq
    EXPECT_MATCH(result.instrs[1], JmpNeqInt, taken, x->dst(), y->dst());
  }

  {
    // JmpNZero(Neq(X:Cls, Y:Cls)) --> JmpNeq(X, Y)
    auto taken = unit.defBlock();
    auto x = unit.gen(Conjure, dummy, Type::Bool);
    auto y = unit.gen(Conjure, dummy, Type::Bool);

    auto neq = unit.gen(Neq, dummy, x->dst(), y->dst());
    auto jmp = unit.gen(JmpNZero, dummy, taken, neq->dst());
    auto result = sim.simplify(jmp, false);

    EXPECT_EQ(nullptr, result.dst);
    EXPECT_EQ(1, result.instrs.size());
    EXPECT_MATCH(result.instrs[0], JmpNeq, taken, x->dst(), y->dst());
  }
}
Esempio n. 5
0
TEST(PredictionOpts, basic) {
    UNUSED auto const bcctx = BCContext { BCMarker::Dummy(), 0 };
    IRUnit unit{test_context};

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

    auto ptr = unit.gen(Conjure, bcctx, TPtrToGen);
    auto ldm = unit.gen(LdMem, bcctx, TGen, ptr->dst());
    auto inc = unit.gen(IncRef, bcctx, ldm->dst());
    auto ckt = unit.gen(CheckType, bcctx, TInt, taken, ldm->dst());
    ckt->setNext(end);
    entry->push_back({ptr, ldm, inc, ckt});

    taken->push_back(unit.gen(Halt, bcctx));
    end->push_back(unit.gen(Halt, bcctx));

    optimizePredictions(unit);

    // It should have pushed LdMem and IncRef to each successor block, with the
    // narrowed type on the fallthrough block.
    {
        ASSERT_EQ(2, entry->instrs().size());
        EXPECT_MATCH(entry->back(), CheckTypeMem, TInt, taken);
        EXPECT_EQ(end, entry->back().next());
    }

    {
        ASSERT_EQ(3, taken->instrs().size());
        auto takenIt = taken->begin();
        auto& ldmem = *takenIt;
        auto& incref = *(++takenIt);
        EXPECT_MATCH(ldmem, LdMem, TGen, ptr->dst());
        EXPECT_MATCH(incref, IncRef, ldmem.dst());
    }

    {
        ASSERT_EQ(4, end->instrs().size());
        auto endIt = end->begin();
        auto& ldmem = *endIt;
        auto& incref = *(++endIt);
        auto& mov = *(++endIt);
        EXPECT_MATCH(ldmem, LdMem, TInt, ptr->dst());
        EXPECT_MATCH(incref, IncRef, ldmem.dst());
        EXPECT_MATCH(mov, Mov, ldmem.dst());
        EXPECT_EQ(ckt->dst(), mov.dst());
    }
}
Esempio n. 6
0
TEST(JumpOpts, optimizeCondTraceExit) {
  BCMarker marker = BCMarker::Dummy();

  IRUnit unit{test_context};

  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, ReqBindJmpData(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, ReqBindJmpData(bcoff2));
  fallthru->push_back({sync2, bind2});

  optimizeJumps(unit);

  EXPECT_EQ(nullptr, entry->next());
  EXPECT_EQ(nullptr, entry->taken());
  auto const& back = entry->back();
  auto const* data = back.extra<ReqBindJccData>();
  EXPECT_MATCH(back, ReqBindJmpZero, val->dst());
  EXPECT_EQ(bcoff1, data->taken);
  EXPECT_EQ(bcoff2, data->notTaken);
}
Esempio n. 7
0
TEST(JumpOpts, optimizeSideExitCheck) {
  BCMarker marker = BCMarker::Dummy();
  IRUnit unit{test_context};

  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 chk = unit.gen(CheckStk, marker, Type::Int, taken,
                      StackOffset(0), sp->dst());
  chk->setNext(fallthru);
  entry->push_back({fp, sp, chk});

  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_MATCH(sideExit, SideExitGuardStk);
  EXPECT_EQ(bcoff, sideExit.extra<SideExitGuardData>()->taken);
}
Esempio n. 8
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);
  }
}