Skip to content

Commit

Permalink
Avoid redundant/equivalent unsigned comparison mutations
Browse files Browse the repository at this point in the history
Fixes #240.
  • Loading branch information
afd committed Jul 17, 2024
1 parent c8f140e commit cc2c1f0
Show file tree
Hide file tree
Showing 5 changed files with 526 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,12 @@ class MutationReplaceBinaryOperator : public Mutation {
const clang::ASTContext& ast_context) const;

[[nodiscard]] bool IsRedundantReplacementForBooleanValuedOperator(
clang::BinaryOperatorKind operator_kind) const;
clang::BinaryOperatorKind operator_kind,
const clang::ASTContext& ast_context) const;

[[nodiscard]] bool IsRedundantReplacementForUnsignedComparison(
clang::BinaryOperatorKind operator_kind,
const clang::ASTContext& ast_context) const;

[[nodiscard]] bool IsRedundantReplacementForArithmeticOperator(
clang::BinaryOperatorKind operator_kind,
Expand Down
69 changes: 67 additions & 2 deletions src/libdredd/src/mutation_replace_binary_operator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ MutationReplaceBinaryOperator::MutationReplaceBinaryOperator(
bool MutationReplaceBinaryOperator::IsRedundantReplacementOperator(
clang::BinaryOperatorKind operator_kind,
const clang::ASTContext& ast_context) const {
if (IsRedundantReplacementForBooleanValuedOperator(operator_kind)) {
if (IsRedundantReplacementForBooleanValuedOperator(operator_kind,
ast_context)) {
return true;
}
if (IsRedundantReplacementForArithmeticOperator(operator_kind, ast_context)) {
Expand Down Expand Up @@ -1024,7 +1025,12 @@ MutationReplaceBinaryOperator::ClangOperatorKindToProtobufOperatorKind(

bool MutationReplaceBinaryOperator::
IsRedundantReplacementForBooleanValuedOperator(
clang::BinaryOperatorKind operator_kind) const {
clang::BinaryOperatorKind operator_kind,
const clang::ASTContext& ast_context) const {
if (IsRedundantReplacementForUnsignedComparison(operator_kind, ast_context)) {
return true;
}

switch (binary_operator_->getOpcode()) {
// From
// https://people.cs.umass.edu/~rjust/publ/non_redundant_mutants_jstvr_2014.pdf:
Expand All @@ -1050,6 +1056,65 @@ bool MutationReplaceBinaryOperator::
}
}

bool MutationReplaceBinaryOperator::IsRedundantReplacementForUnsignedComparison(
clang::BinaryOperatorKind operator_kind,
const clang::ASTContext& ast_context) const {
if (binary_operator_->getLHS()->getType()->isUnsignedIntegerType() &&
binary_operator_->getRHS()->getType()->isUnsignedIntegerType()) {
if (MutationReplaceExpr::ExprIsEquivalentToInt(*binary_operator_->getLHS(),
0, ast_context)) {
// LHS is 0
switch (binary_operator_->getOpcode()) {
case clang::BO_GT:
case clang::BO_LE:
// "0 > a" is subsumed by "false"
// "0 <= a" is subsumed by "true"
return true;
case clang::BO_EQ:
if (operator_kind == clang::BO_GE) {
// "0 == a" equivalent to "0 >= a"
return true;
}
break;
case clang::BO_NE:
if (operator_kind == clang::BO_LT) {
// "0 != a" equivalent to "0 < a"
return true;
}
break;
default:
break;
}
}
if (MutationReplaceExpr::ExprIsEquivalentToInt(*binary_operator_->getRHS(),
0, ast_context)) {
// RHS is 0
switch (binary_operator_->getOpcode()) {
case clang::BO_LT:
case clang::BO_GE:
// "a < 0" is subsumed by "false"
// "a >= 0" is subsumed by "true"
return true;
case clang::BO_EQ:
if (operator_kind == clang::BO_LE) {
// "a == 0" equivalent to "a <= 0"
return true;
}
break;
case clang::BO_NE:
if (operator_kind == clang::BO_GT) {
// "a != 0" equivalent to "a > 0"
return true;
}
break;
default:
break;
}
}
}
return false;
}

bool MutationReplaceBinaryOperator::IsRedundantReplacementForArithmeticOperator(
clang::BinaryOperatorKind operator_kind,
const clang::ASTContext& ast_context) const {
Expand Down
49 changes: 49 additions & 0 deletions test/single_file/unsigned_comparisons_with_zero.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#include <stdbool.h>

bool x_eq_zero(unsigned x) {
return x == 0u;
}

bool x_ne_zero(unsigned x) {
return x != 0u;
}

bool x_gt_zero(unsigned x) {
return x > 0u;
}

bool x_ge_zero(unsigned x) {
return x >= 0u;
}

bool x_lt_zero(unsigned x) {
return x < 0u;
}

bool x_le_zero(unsigned x) {
return x <= 0u;
}

bool zero_eq_x(unsigned x) {
return 0u == x;
}

bool zero_ne_x(unsigned x) {
return 0u != x;
}

bool zero_gt_x(unsigned x) {
return 0u > x;
}

bool zero_ge_x(unsigned x) {
return 0u >= x;
}

bool zero_lt_x(unsigned x) {
return 0u < x;
}

bool zero_le_x(unsigned x) {
return 0u <= x;
}
206 changes: 206 additions & 0 deletions test/single_file/unsigned_comparisons_with_zero.c.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

#ifdef _MSC_VER
#define thread_local __declspec(thread)
#elif __APPLE__
#define thread_local __thread
#else
#include <threads.h>
#endif

static thread_local int __dredd_some_mutation_enabled = 1;
static bool __dredd_enabled_mutation(int local_mutation_id) {
static thread_local int initialized = 0;
static thread_local uint64_t enabled_bitset[3];
if (!initialized) {
int some_mutation_enabled = 0;
const char* dredd_environment_variable = getenv("DREDD_ENABLED_MUTATION");
if (dredd_environment_variable) {
char* temp = malloc(strlen(dredd_environment_variable) + 1);
strcpy(temp, dredd_environment_variable);
char* token;
token = strtok(temp, ",");
while(token) {
int value = atoi(token);
int local_value = value - 0;
if (local_value >= 0 && local_value < 168) {
enabled_bitset[local_value / 64] |= ((uint64_t) 1 << (local_value % 64));
some_mutation_enabled = 1;
}
token = strtok(NULL, ",");
}
free(temp);
}
initialized = 1;
__dredd_some_mutation_enabled = some_mutation_enabled;
}
return enabled_bitset[local_mutation_id / 64] & ((uint64_t) 1 << (local_mutation_id % 64));
}

static unsigned int __dredd_replace_expr_unsigned_int_zero(unsigned int arg, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return ~(arg);
if (__dredd_enabled_mutation(local_mutation_id + 1)) return 1;
return arg;
}

static unsigned int __dredd_replace_expr_unsigned_int_lvalue(unsigned int* arg, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return (*arg);
if (__dredd_enabled_mutation(local_mutation_id + 0)) return ++((*arg));
if (__dredd_enabled_mutation(local_mutation_id + 1)) return --((*arg));
return (*arg);
}

static unsigned int __dredd_replace_expr_unsigned_int(unsigned int arg, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return !(arg);
if (__dredd_enabled_mutation(local_mutation_id + 1)) return ~(arg);
if (__dredd_enabled_mutation(local_mutation_id + 2)) return 0;
if (__dredd_enabled_mutation(local_mutation_id + 3)) return 1;
return arg;
}

static int __dredd_replace_binary_operator_NE_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 != arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 > arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 < arg2;
return arg1 != arg2;
}

static int __dredd_replace_binary_operator_NE_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 != arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 > arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 < arg2;
return arg1 != arg2;
}

static int __dredd_replace_binary_operator_LT_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 < arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 != arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 <= arg2;
return arg1 < arg2;
}

static int __dredd_replace_binary_operator_LT_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 < arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 != arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 <= arg2;
return arg1 < arg2;
}

static int __dredd_replace_binary_operator_LE_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 <= arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 == arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 < arg2;
return arg1 <= arg2;
}

static int __dredd_replace_binary_operator_LE_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 <= arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 == arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 < arg2;
return arg1 <= arg2;
}

static int __dredd_replace_binary_operator_GT_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 > arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 != arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 >= arg2;
return arg1 > arg2;
}

static int __dredd_replace_binary_operator_GT_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 > arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 != arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 >= arg2;
return arg1 > arg2;
}

static int __dredd_replace_binary_operator_GE_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 >= arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 == arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 > arg2;
return arg1 >= arg2;
}

static int __dredd_replace_binary_operator_GE_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 >= arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 == arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 > arg2;
return arg1 >= arg2;
}

static int __dredd_replace_binary_operator_EQ_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 == arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 >= arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 <= arg2;
return arg1 == arg2;
}

static int __dredd_replace_binary_operator_EQ_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(unsigned int arg1, unsigned int arg2, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg1 == arg2;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 >= arg2;
if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 <= arg2;
return arg1 == arg2;
}

static bool __dredd_replace_expr_bool(bool arg, int local_mutation_id) {
if (!__dredd_some_mutation_enabled) return arg;
if (__dredd_enabled_mutation(local_mutation_id + 0)) return !(arg);
if (__dredd_enabled_mutation(local_mutation_id + 1)) return 1;
if (__dredd_enabled_mutation(local_mutation_id + 2)) return 0;
return arg;
}

#include <stdbool.h>

bool x_eq_zero(unsigned x) {
if (!__dredd_enabled_mutation(13)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_EQ_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(__dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 0), 2) , __dredd_replace_expr_unsigned_int_zero(0u, 6), 8), 10); }
}

bool x_ne_zero(unsigned x) {
if (!__dredd_enabled_mutation(27)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_NE_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(__dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 14), 16) , __dredd_replace_expr_unsigned_int_zero(0u, 20), 22), 24); }
}

bool x_gt_zero(unsigned x) {
if (!__dredd_enabled_mutation(41)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_GT_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(__dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 28), 30) , __dredd_replace_expr_unsigned_int_zero(0u, 34), 36), 38); }
}

bool x_ge_zero(unsigned x) {
if (!__dredd_enabled_mutation(55)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_GE_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(__dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 42), 44) , __dredd_replace_expr_unsigned_int_zero(0u, 48), 50), 52); }
}

bool x_lt_zero(unsigned x) {
if (!__dredd_enabled_mutation(69)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_LT_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(__dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 56), 58) , __dredd_replace_expr_unsigned_int_zero(0u, 62), 64), 66); }
}

bool x_le_zero(unsigned x) {
if (!__dredd_enabled_mutation(83)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_LE_arg1_unsigned_int_arg2_unsigned_int_rhs_zero(__dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 70), 72) , __dredd_replace_expr_unsigned_int_zero(0u, 76), 78), 80); }
}

bool zero_eq_x(unsigned x) {
if (!__dredd_enabled_mutation(97)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_EQ_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(__dredd_replace_expr_unsigned_int_zero(0u, 84) , __dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 86), 88), 92), 94); }
}

bool zero_ne_x(unsigned x) {
if (!__dredd_enabled_mutation(111)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_NE_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(__dredd_replace_expr_unsigned_int_zero(0u, 98) , __dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 100), 102), 106), 108); }
}

bool zero_gt_x(unsigned x) {
if (!__dredd_enabled_mutation(125)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_GT_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(__dredd_replace_expr_unsigned_int_zero(0u, 112) , __dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 114), 116), 120), 122); }
}

bool zero_ge_x(unsigned x) {
if (!__dredd_enabled_mutation(139)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_GE_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(__dredd_replace_expr_unsigned_int_zero(0u, 126) , __dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 128), 130), 134), 136); }
}

bool zero_lt_x(unsigned x) {
if (!__dredd_enabled_mutation(153)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_LT_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(__dredd_replace_expr_unsigned_int_zero(0u, 140) , __dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 142), 144), 148), 150); }
}

bool zero_le_x(unsigned x) {
if (!__dredd_enabled_mutation(167)) { return __dredd_replace_expr_bool(__dredd_replace_binary_operator_LE_arg1_unsigned_int_arg2_unsigned_int_lhs_zero(__dredd_replace_expr_unsigned_int_zero(0u, 154) , __dredd_replace_expr_unsigned_int(__dredd_replace_expr_unsigned_int_lvalue(&(x), 156), 158), 162), 164); }
}
Loading

0 comments on commit cc2c1f0

Please sign in to comment.