From 48b8885b4bb9b46f154dd7c8004310449fdce44b Mon Sep 17 00:00:00 2001 From: Jonathan Foo Date: Sat, 13 Jul 2024 12:06:30 +0100 Subject: [PATCH 1/4] Feat: avoid mutation on implicit cast l-value used as reference --- src/libdredd/src/mutate_visitor.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libdredd/src/mutate_visitor.cc b/src/libdredd/src/mutate_visitor.cc index 64e97163..022d0bd0 100644 --- a/src/libdredd/src/mutate_visitor.cc +++ b/src/libdredd/src/mutate_visitor.cc @@ -441,6 +441,19 @@ void MutateVisitor::HandleExpr(clang::Expr* expr) { return; } + // Avoid mutation on implicit cast when its underlying value is a bitfield + // l-value that is subsequently passed by reference. Any expression that is + // later passed by reference is a child of MaterializeTemporaryExpr, which + // represents a prvalue temporary that is written into memory so that a + // reference can bind to it. + if (const auto* cast_expr = llvm::dyn_cast(expr)) { + if (cast_expr->getSubExpr()->refersToBitField() && + GetFirstParentOfType( + *expr, compiler_instance_->getASTContext()) != nullptr) { + return; + } + } + if (optimise_mutations_) { // If an expression is the direct child of a cast expression, do not mutate // it unless the cast is an l-value to r-value cast. In an l-value to From 01bb022c1f62ade8b4832f2583d25f21c65131a2 Mon Sep 17 00:00:00 2001 From: Jonathan Foo Date: Sat, 13 Jul 2024 12:11:31 +0100 Subject: [PATCH 2/4] Feat: add test case for issue 259 --- .../single_file/bitfield_reference_passing.cc | 10 ++++ .../bitfield_reference_passing.cc.expected | 54 +++++++++++++++++++ ...tfield_reference_passing.cc.noopt.expected | 54 +++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 test/single_file/bitfield_reference_passing.cc create mode 100644 test/single_file/bitfield_reference_passing.cc.expected create mode 100644 test/single_file/bitfield_reference_passing.cc.noopt.expected diff --git a/test/single_file/bitfield_reference_passing.cc b/test/single_file/bitfield_reference_passing.cc new file mode 100644 index 00000000..454a4af2 --- /dev/null +++ b/test/single_file/bitfield_reference_passing.cc @@ -0,0 +1,10 @@ +template void bloop(T& x) { } + +struct foo { + int b : 2; +}; + +int main() { + const foo d = foo(); + bloop(d.b); +} \ No newline at end of file diff --git a/test/single_file/bitfield_reference_passing.cc.expected b/test/single_file/bitfield_reference_passing.cc.expected new file mode 100644 index 00000000..2e8b993f --- /dev/null +++ b/test/single_file/bitfield_reference_passing.cc.expected @@ -0,0 +1,54 @@ +#include +#include +#include +#include + + +#ifdef _MSC_VER +#define thread_local __declspec(thread) +#elif __APPLE__ +#define thread_local __thread +#endif + +static thread_local bool __dredd_some_mutation_enabled = true; +static bool __dredd_enabled_mutation(int local_mutation_id) { + static thread_local bool initialized = false; + static thread_local uint64_t enabled_bitset[1]; + if (!initialized) { + bool some_mutation_enabled = false; + const char* dredd_environment_variable = std::getenv("DREDD_ENABLED_MUTATION"); + if (dredd_environment_variable != nullptr) { + std::string contents(dredd_environment_variable); + while (true) { + size_t pos = contents.find(","); + std::string token = (pos == std::string::npos ? contents : contents.substr(0, pos)); + if (!token.empty()) { + int value = std::stoi(token); + int local_value = value - 0; + if (local_value >= 0 && local_value < 1) { + enabled_bitset[local_value / 64] |= (static_cast(1) << (local_value % 64)); + some_mutation_enabled = true; + } + } + if (pos == std::string::npos) { + break; + } + contents.erase(0, pos + 1); + } + } + initialized = true; + __dredd_some_mutation_enabled = some_mutation_enabled; + } + return (enabled_bitset[local_mutation_id / 64] & (static_cast(1) << (local_mutation_id % 64))) != 0; +} + +template void bloop(T& x) { } + +struct foo { + int b : 2; +}; + +int main() { + const foo d = foo(); + if (!__dredd_enabled_mutation(0)) { bloop(d.b); } +} \ No newline at end of file diff --git a/test/single_file/bitfield_reference_passing.cc.noopt.expected b/test/single_file/bitfield_reference_passing.cc.noopt.expected new file mode 100644 index 00000000..2e8b993f --- /dev/null +++ b/test/single_file/bitfield_reference_passing.cc.noopt.expected @@ -0,0 +1,54 @@ +#include +#include +#include +#include + + +#ifdef _MSC_VER +#define thread_local __declspec(thread) +#elif __APPLE__ +#define thread_local __thread +#endif + +static thread_local bool __dredd_some_mutation_enabled = true; +static bool __dredd_enabled_mutation(int local_mutation_id) { + static thread_local bool initialized = false; + static thread_local uint64_t enabled_bitset[1]; + if (!initialized) { + bool some_mutation_enabled = false; + const char* dredd_environment_variable = std::getenv("DREDD_ENABLED_MUTATION"); + if (dredd_environment_variable != nullptr) { + std::string contents(dredd_environment_variable); + while (true) { + size_t pos = contents.find(","); + std::string token = (pos == std::string::npos ? contents : contents.substr(0, pos)); + if (!token.empty()) { + int value = std::stoi(token); + int local_value = value - 0; + if (local_value >= 0 && local_value < 1) { + enabled_bitset[local_value / 64] |= (static_cast(1) << (local_value % 64)); + some_mutation_enabled = true; + } + } + if (pos == std::string::npos) { + break; + } + contents.erase(0, pos + 1); + } + } + initialized = true; + __dredd_some_mutation_enabled = some_mutation_enabled; + } + return (enabled_bitset[local_mutation_id / 64] & (static_cast(1) << (local_mutation_id % 64))) != 0; +} + +template void bloop(T& x) { } + +struct foo { + int b : 2; +}; + +int main() { + const foo d = foo(); + if (!__dredd_enabled_mutation(0)) { bloop(d.b); } +} \ No newline at end of file From 81f5b552f6abfe1efe1824f59c59939055635214 Mon Sep 17 00:00:00 2001 From: Jonathan Foo Date: Sat, 13 Jul 2024 12:45:27 +0100 Subject: [PATCH 3/4] Fix: testcase newline --- test/single_file/bitfield_reference_passing.cc | 2 +- test/single_file/bitfield_reference_passing.cc.expected | 2 +- test/single_file/bitfield_reference_passing.cc.noopt.expected | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/single_file/bitfield_reference_passing.cc b/test/single_file/bitfield_reference_passing.cc index 454a4af2..cf7381c6 100644 --- a/test/single_file/bitfield_reference_passing.cc +++ b/test/single_file/bitfield_reference_passing.cc @@ -7,4 +7,4 @@ struct foo { int main() { const foo d = foo(); bloop(d.b); -} \ No newline at end of file +} diff --git a/test/single_file/bitfield_reference_passing.cc.expected b/test/single_file/bitfield_reference_passing.cc.expected index 2e8b993f..ada685ae 100644 --- a/test/single_file/bitfield_reference_passing.cc.expected +++ b/test/single_file/bitfield_reference_passing.cc.expected @@ -51,4 +51,4 @@ struct foo { int main() { const foo d = foo(); if (!__dredd_enabled_mutation(0)) { bloop(d.b); } -} \ No newline at end of file +} diff --git a/test/single_file/bitfield_reference_passing.cc.noopt.expected b/test/single_file/bitfield_reference_passing.cc.noopt.expected index 2e8b993f..ada685ae 100644 --- a/test/single_file/bitfield_reference_passing.cc.noopt.expected +++ b/test/single_file/bitfield_reference_passing.cc.noopt.expected @@ -51,4 +51,4 @@ struct foo { int main() { const foo d = foo(); if (!__dredd_enabled_mutation(0)) { bloop(d.b); } -} \ No newline at end of file +} From fd187ea41d136574629d6c6fdc8e12aa424a57e2 Mon Sep 17 00:00:00 2001 From: Jonathan Foo Date: Sat, 13 Jul 2024 12:46:15 +0100 Subject: [PATCH 4/4] Fix: comment about exact cast used --- src/libdredd/src/mutate_visitor.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libdredd/src/mutate_visitor.cc b/src/libdredd/src/mutate_visitor.cc index 022d0bd0..d268522c 100644 --- a/src/libdredd/src/mutate_visitor.cc +++ b/src/libdredd/src/mutate_visitor.cc @@ -441,9 +441,9 @@ void MutateVisitor::HandleExpr(clang::Expr* expr) { return; } - // Avoid mutation on implicit cast when its underlying value is a bitfield + // Avoid mutation on cast when its underlying value is a bitfield // l-value that is subsequently passed by reference. Any expression that is - // later passed by reference is a child of MaterializeTemporaryExpr, which + // passed by reference is a child of MaterializeTemporaryExpr, which // represents a prvalue temporary that is written into memory so that a // reference can bind to it. if (const auto* cast_expr = llvm::dyn_cast(expr)) {