Navigation Menu

Skip to content

Commit

Permalink
Merge pull request #513 from mull-project/fix-and-or-edge-case
Browse files Browse the repository at this point in the history
AND-OR replacement mutator: handle the edge case produced by csmith
  • Loading branch information
stanislaw committed May 12, 2019
2 parents c3d321d + aab16fc commit 386b87c
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 24 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
- Add junk detection for AndOrReplacement mutator #509
- Add junk detection for MathDiv mutator #511
- Add junk detection for MathMul mutator #511
- Handle edge case for AND-OR mutator #501

## [0.2.0] - 02 May 2019

Expand Down
47 changes: 24 additions & 23 deletions lib/Mutators/AndOrReplacementMutator.cpp
Expand Up @@ -115,9 +115,9 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern1(
assert(secondBranch != nullptr);
assert(secondBranch->isConditional());

/// Operand #0 is a comparison instruction.
Instruction *sourceInst = (dyn_cast<Instruction>(firstBranch->getOperand(0)));
assert(sourceInst);
/// Operand #0 is a comparison instruction or simply a scalar value.
Value *sourceValue = dyn_cast<Value>(firstBranch->getOperand(0));
assert(sourceValue);

/// Left branch value is somehow operand #2, right is #1.
BasicBlock *firstBranchLeftBB =
Expand All @@ -131,7 +131,7 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern1(
dyn_cast<BasicBlock>(secondBranch->getOperand(2));

BranchInst *replacement =
BranchInst::Create(secondBranchLeftBB, firstBranchLeftBB, sourceInst);
BranchInst::Create(secondBranchLeftBB, firstBranchLeftBB, sourceValue);

/// If I add a named instruction, and the name already exist
/// in a basic block, then LLVM will make another unique name of it
Expand Down Expand Up @@ -188,9 +188,9 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern2(
assert(secondBranch != nullptr);
assert(secondBranch->isConditional());

/// Operand #0 is a comparison instruction.
Instruction *sourceInst = (dyn_cast<Instruction>(firstBranch->getOperand(0)));
assert(sourceInst);
/// Operand #0 is a comparison instruction or simply a scalar value.
Value *sourceValue = (dyn_cast<Value>(firstBranch->getOperand(0)));
assert(sourceValue);

/// Left branch value is somehow operand #2, right is #1.
BasicBlock *firstBranchLeftBB =
Expand All @@ -201,10 +201,10 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern2(
assert(firstBranchRightBB);

BasicBlock *secondBranchLeftBB =
dyn_cast<BasicBlock>(secondBranch->getOperand(2));
dyn_cast<BasicBlock>(secondBranch->getOperand(2));

BranchInst *replacement =
BranchInst::Create(firstBranchRightBB, secondBranchLeftBB, sourceInst);
BranchInst::Create(firstBranchRightBB, secondBranchLeftBB, sourceValue);

/// If I add a named instruction, and the name already exist
/// in a basic block, then LLVM will make another unique name of it
Expand Down Expand Up @@ -283,11 +283,11 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern3(

auto firstBranchLeftBB = dyn_cast<BasicBlock>(firstBranch->getOperand(2));
auto firstBranchRightBB = dyn_cast<BasicBlock>(firstBranch->getOperand(1));
Instruction *firstBranchConditionInst =
(dyn_cast<Instruction>(firstBranch->getOperand(0)));
Value *firstBranchConditionValue = dyn_cast<Value>(firstBranch->getOperand(0));
assert(firstBranchConditionValue);

BranchInst *replacement = BranchInst::Create(
firstBranchRightBB, firstBranchLeftBB, firstBranchConditionInst);
firstBranchRightBB, firstBranchLeftBB, firstBranchConditionValue);

/// If I add a named instruction, and the name already exist
/// in a basic block, then LLVM will make another unique name of it
Expand All @@ -313,9 +313,9 @@ llvm::Value *AndOrReplacementMutator::applyMutationORToAND_Pattern1(
assert(secondBranch != nullptr);
assert(secondBranch->isConditional());

/// Operand #0 is a comparison instruction.
Instruction *sourceInst = (dyn_cast<Instruction>(firstBranch->getOperand(0)));
assert(sourceInst);
/// Operand #0 is a comparison instruction or simply a scalar value.
Value *sourceValue = dyn_cast<Value>(firstBranch->getOperand(0));
assert(sourceValue);

/// Left branch value is somehow operand #2, right is #1.
BasicBlock *firstBranchRightBB =
Expand All @@ -330,7 +330,7 @@ llvm::Value *AndOrReplacementMutator::applyMutationORToAND_Pattern1(
assert(secondBranchRightBB);

BranchInst *replacement =
BranchInst::Create(firstBranchRightBB, secondBranchRightBB, sourceInst);
BranchInst::Create(firstBranchRightBB, secondBranchRightBB, sourceValue);

/// If I add a named instruction, and the name already exist
/// in a basic block, then LLVM will make another unique name of it
Expand Down Expand Up @@ -386,9 +386,9 @@ llvm::Value *AndOrReplacementMutator::applyMutationORToAND_Pattern2(
assert(secondBranch != nullptr);
assert(secondBranch->isConditional());

/// Operand #0 is a comparison instruction.
Instruction *sourceInst = (dyn_cast<Instruction>(firstBranch->getOperand(0)));
assert(sourceInst);
/// Operand #0 is a comparison instruction or simply a scalar value.
Value *sourceValue = (dyn_cast<Value>(firstBranch->getOperand(0)));
assert(sourceValue);

/// Left branch value is somehow operand #2, right is #1.
BasicBlock *firstBranchLeftBB =
Expand All @@ -403,7 +403,7 @@ llvm::Value *AndOrReplacementMutator::applyMutationORToAND_Pattern2(
assert(secondBranchRightBB);

BranchInst *replacement =
BranchInst::Create(secondBranchRightBB, firstBranchLeftBB, sourceInst);
BranchInst::Create(secondBranchRightBB, firstBranchLeftBB, sourceValue);

/// If I add a named instruction, and the name already exist
/// in a basic block, then LLVM will make another unique name of it
Expand Down Expand Up @@ -481,11 +481,12 @@ llvm::Value *AndOrReplacementMutator::applyMutationORToAND_Pattern3(

auto firstBranchLeftBB = dyn_cast<BasicBlock>(firstBranch->getOperand(2));
auto firstBranchRightBB = dyn_cast<BasicBlock>(firstBranch->getOperand(1));
Instruction *firstBranchConditionInst =
(dyn_cast<Instruction>(firstBranch->getOperand(0)));

Value *firstBranchConditionValue = dyn_cast<Value>(firstBranch->getOperand(0));
assert(firstBranchConditionValue);

BranchInst *replacement = BranchInst::Create(
firstBranchRightBB, firstBranchLeftBB, firstBranchConditionInst);
firstBranchRightBB, firstBranchLeftBB, firstBranchConditionValue);

/// If I add a named instruction, and the name already exist
/// in a basic block, then LLVM will make another unique name of it
Expand Down
64 changes: 63 additions & 1 deletion tests/DriverTests.cpp
Expand Up @@ -400,7 +400,7 @@ TEST(Driver, SimpleTest_ANDORReplacementMutator) {
finder, metrics, junkDetector);

auto result = Driver.Run();
ASSERT_EQ(8U, result->getTests().size());
ASSERT_EQ(12U, result->getTests().size());

auto mutants = result->getMutationResults().begin();

Expand Down Expand Up @@ -510,6 +510,68 @@ TEST(Driver, SimpleTest_ANDORReplacementMutator) {
ASSERT_EQ(ExecutionStatus::Passed, mutant8_2->getExecutionResult().status);
}

/// Edge case for Pattern #1: OR expression that always evaluates to a scalar
/// value but also contains a dummy function call (presence of a dummy
/// function makes the Branch instruction to be generated).
{
auto mutant1 = (mutants++)->get();
ASSERT_EQ(ExecutionStatus::Passed,
mutant1->getTest()->getExecutionResult().status);
ASSERT_EQ("test_OR_operator_always_scalars_case_with_function_call_pattern1",
mutant1->getTest()->getTestName());
ASSERT_EQ(ExecutionStatus::Passed, mutant1->getExecutionResult().status);

auto mutant2 = (mutants++)->get();
ASSERT_EQ(ExecutionStatus::Passed,
mutant2->getTest()->getExecutionResult().status);
ASSERT_EQ("test_OR_operator_always_scalars_case_with_function_call_pattern1",
mutant2->getTest()->getTestName());
ASSERT_EQ(ExecutionStatus::Passed, mutant2->getExecutionResult().status);
}

/// Edge case for Pattern #3: OR expression that always evaluates to a scalar
/// value but also contains a dummy function call (presence of a dummy
/// function makes the Branch instruction to be generated).
{
auto mutant = (mutants++)->get();
ASSERT_EQ(ExecutionStatus::Passed,
mutant->getTest()->getExecutionResult().status);
ASSERT_EQ("test_OR_operator_always_scalars_case_with_function_call_pattern3",
mutant->getTest()->getTestName());
ASSERT_EQ(ExecutionStatus::Passed, mutant->getExecutionResult().status);
}

/// Edge case for Pattern #1: AND expression that always evaluates to a scalar
/// value but also contains a dummy function call (presence of a dummy
/// function makes the Branch instruction to be generated).
{
auto mutant1 = (mutants++)->get();
ASSERT_EQ(ExecutionStatus::Passed,
mutant1->getTest()->getExecutionResult().status);
ASSERT_EQ("test_AND_operator_always_scalars_case_with_function_call_pattern1",
mutant1->getTest()->getTestName());
ASSERT_EQ(ExecutionStatus::Passed, mutant1->getExecutionResult().status);

auto mutant2 = (mutants++)->get();
ASSERT_EQ(ExecutionStatus::Passed,
mutant2->getTest()->getExecutionResult().status);
ASSERT_EQ("test_AND_operator_always_scalars_case_with_function_call_pattern1",
mutant2->getTest()->getTestName());
ASSERT_EQ(ExecutionStatus::Passed, mutant2->getExecutionResult().status);
}

/// Edge case for Pattern #3: AND expression that always evaluates to a scalar
/// value but also contains a dummy function call (presence of a dummy
/// function makes the Branch instruction to be generated).
{
auto mutant = (mutants++)->get();
ASSERT_EQ(ExecutionStatus::Passed,
mutant->getTest()->getExecutionResult().status);
ASSERT_EQ("test_AND_operator_always_scalars_case_with_function_call_pattern3",
mutant->getTest()->getTestName());
ASSERT_EQ(ExecutionStatus::Passed, mutant->getExecutionResult().status);
}

ASSERT_EQ(mutants, result->getMutationResults().end());
}

Expand Down
79 changes: 79 additions & 0 deletions tests/fixtures/mutators/and_or_replacement/module.c
Expand Up @@ -84,6 +84,57 @@ int testee_compound_OR_then_OR_operator(int A, int B, int C) {
}
}

/// Edge case: OR expression that always evaluates to a scalar value but also
/// contains a dummy function call (presence of a dummy function makes the
/// Branch instruction to be generated).
/// This case is based on https://github.com/mull-project/mull/issues/501.
/// The code below is based on the code generated by the csmith:
/// a() { ((b(), 9) || 9, 0) || a; }
void dummy() {}
int testee_OR_operator_always_scalars_case_with_function_call_pattern1(int A) {
if ((((dummy(), 0) || 1), 1) || A) {
printf("left branch\n");
return 1;
} else {
printf("right branch\n");
return 0;
}
}

int testee_OR_operator_always_scalars_case_with_function_call_pattern3(int A) {
if (((dummy(), 9) || 9), A) {
return 1;
} else {
printf("right branch\n");
return 0;
}
}

/// Edge case: AND expression that always evaluates to a scalar value but also
/// contains a dummy function call (presence of a dummy function makes the
/// Branch instruction to be generated).
/// This case is based on https://github.com/mull-project/mull/issues/501.
/// The code below is based on the code generated by the csmith:
/// a() { ((b(), 9) || 9, 0) || a; }
int testee_AND_operator_always_scalars_case_with_function_call_pattern1(int A) {
if ((((dummy(), 0) && 1), 1) && A) {
printf("left branch\n");
return 1;
} else {
printf("right branch\n");
return 0;
}
}

int testee_AND_operator_always_scalars_case_with_function_call_pattern3(int A) {
if (((dummy(), 9) && 9), A) {
return 1;
} else {
printf("right branch\n");
return 0;
}
}

int test_AND_operator_2branches() {
if (testee_AND_operator_2branches(1, 3, 2) == 3) {
return SUCCESS;
Expand Down Expand Up @@ -139,3 +190,31 @@ int test_compound_OR_then_OR_operator() {
}
return FAILURE;
}

int test_OR_operator_always_scalars_case_with_function_call_pattern1() {
if (testee_OR_operator_always_scalars_case_with_function_call_pattern1(1) == 1) {
return SUCCESS;
}
return FAILURE;
}

int test_OR_operator_always_scalars_case_with_function_call_pattern3() {
if (testee_OR_operator_always_scalars_case_with_function_call_pattern3(1) == 1) {
return SUCCESS;
}
return FAILURE;
}

int test_AND_operator_always_scalars_case_with_function_call_pattern1() {
if (testee_AND_operator_always_scalars_case_with_function_call_pattern1(1) == 1) {
return SUCCESS;
}
return FAILURE;
}

int test_AND_operator_always_scalars_case_with_function_call_pattern3() {
if (testee_AND_operator_always_scalars_case_with_function_call_pattern3(1) == 1) {
return SUCCESS;
}
return FAILURE;
}

0 comments on commit 386b87c

Please sign in to comment.