From aab16fcdec05c32363f7f8d1fca05a08000674ca Mon Sep 17 00:00:00 2001 From: Stanislav Pankevich Date: Sun, 12 May 2019 16:58:24 +0200 Subject: [PATCH] AND-OR replacement mutator: handle the edge case produced by csmith (closes #501) --- CHANGELOG.md | 1 + lib/Mutators/AndOrReplacementMutator.cpp | 47 +++++------ tests/DriverTests.cpp | 64 ++++++++++++++- .../mutators/and_or_replacement/module.c | 79 +++++++++++++++++++ 4 files changed, 167 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39273ed04..4f36f8c4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/Mutators/AndOrReplacementMutator.cpp b/lib/Mutators/AndOrReplacementMutator.cpp index b7d85df24..feef06483 100644 --- a/lib/Mutators/AndOrReplacementMutator.cpp +++ b/lib/Mutators/AndOrReplacementMutator.cpp @@ -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(firstBranch->getOperand(0))); - assert(sourceInst); + /// Operand #0 is a comparison instruction or simply a scalar value. + Value *sourceValue = dyn_cast(firstBranch->getOperand(0)); + assert(sourceValue); /// Left branch value is somehow operand #2, right is #1. BasicBlock *firstBranchLeftBB = @@ -131,7 +131,7 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern1( dyn_cast(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 @@ -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(firstBranch->getOperand(0))); - assert(sourceInst); + /// Operand #0 is a comparison instruction or simply a scalar value. + Value *sourceValue = (dyn_cast(firstBranch->getOperand(0))); + assert(sourceValue); /// Left branch value is somehow operand #2, right is #1. BasicBlock *firstBranchLeftBB = @@ -201,10 +201,10 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern2( assert(firstBranchRightBB); BasicBlock *secondBranchLeftBB = - dyn_cast(secondBranch->getOperand(2)); + dyn_cast(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 @@ -283,11 +283,11 @@ llvm::Value *AndOrReplacementMutator::applyMutationANDToOR_Pattern3( auto firstBranchLeftBB = dyn_cast(firstBranch->getOperand(2)); auto firstBranchRightBB = dyn_cast(firstBranch->getOperand(1)); - Instruction *firstBranchConditionInst = - (dyn_cast(firstBranch->getOperand(0))); + Value *firstBranchConditionValue = dyn_cast(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 @@ -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(firstBranch->getOperand(0))); - assert(sourceInst); + /// Operand #0 is a comparison instruction or simply a scalar value. + Value *sourceValue = dyn_cast(firstBranch->getOperand(0)); + assert(sourceValue); /// Left branch value is somehow operand #2, right is #1. BasicBlock *firstBranchRightBB = @@ -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 @@ -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(firstBranch->getOperand(0))); - assert(sourceInst); + /// Operand #0 is a comparison instruction or simply a scalar value. + Value *sourceValue = (dyn_cast(firstBranch->getOperand(0))); + assert(sourceValue); /// Left branch value is somehow operand #2, right is #1. BasicBlock *firstBranchLeftBB = @@ -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 @@ -481,11 +481,12 @@ llvm::Value *AndOrReplacementMutator::applyMutationORToAND_Pattern3( auto firstBranchLeftBB = dyn_cast(firstBranch->getOperand(2)); auto firstBranchRightBB = dyn_cast(firstBranch->getOperand(1)); - Instruction *firstBranchConditionInst = - (dyn_cast(firstBranch->getOperand(0))); + + Value *firstBranchConditionValue = dyn_cast(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 diff --git a/tests/DriverTests.cpp b/tests/DriverTests.cpp index 5c45e2476..7fbc43718 100644 --- a/tests/DriverTests.cpp +++ b/tests/DriverTests.cpp @@ -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(); @@ -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()); } diff --git a/tests/fixtures/mutators/and_or_replacement/module.c b/tests/fixtures/mutators/and_or_replacement/module.c index c84a9a583..325a4105f 100644 --- a/tests/fixtures/mutators/and_or_replacement/module.c +++ b/tests/fixtures/mutators/and_or_replacement/module.c @@ -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; @@ -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; +}