-
Notifications
You must be signed in to change notification settings - Fork 24
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This is a simple tool that is able to exemplify how you would implement the removal of a system that you know have a determinstic result for the function calls. Signed-off-by: Daniel Ruoso <druoso@bloomberg.net>
- Loading branch information
Showing
15 changed files
with
352 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
cmake_minimum_required(VERSION 3.6) | ||
|
||
project( remove_ye_olde_feature_toggle C CXX) | ||
set(CMAKE_CXX_STANDARD 17) | ||
set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
|
||
find_package(Clang REQUIRED) | ||
find_package(clangmetatool REQUIRED) | ||
|
||
add_executable( | ||
remove_ye_olde_feature_toggle | ||
src/main.cpp | ||
# add other source names here | ||
) | ||
|
||
target_include_directories(remove_ye_olde_feature_toggle PRIVATE ${CLANG_INCLUDE_DIRS} ) | ||
target_link_libraries(remove_ye_olde_feature_toggle clangmetatool clangTooling ) | ||
|
||
clangmetatool_install(remove_ye_olde_feature_toggle) | ||
|
||
enable_testing() | ||
add_subdirectory(t) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
#include <iosfwd> | ||
#include <map> | ||
#include <tuple> | ||
#include <optional> | ||
#include <sstream> | ||
|
||
#include <clang/Frontend/FrontendAction.h> | ||
#include <clang/Tooling/Core/Replacement.h> | ||
#include <clang/Tooling/CommonOptionsParser.h> | ||
#include <clang/Tooling/Tooling.h> | ||
#include <clang/Tooling/Refactoring.h> | ||
#include <llvm/Support/CommandLine.h> | ||
#include <clang/ASTMatchers/ASTMatchFinder.h> | ||
|
||
|
||
#include <clangmetatool/meta_tool_factory.h> | ||
#include <clangmetatool/meta_tool.h> | ||
|
||
#include <clangmetatool/types/file_uid.h> | ||
#include <clangmetatool/collectors/find_calls.h> | ||
#include <clangmetatool/collectors/find_calls_data.h> | ||
#include <clangmetatool/collectors/include_graph.h> | ||
#include <clangmetatool/collectors/include_graph_data.h> | ||
#include <clangmetatool/propagation/constant_integer_propagator.h> | ||
|
||
namespace { | ||
|
||
std::optional<int64_t> ye_deterministic_val(int64_t feature_id) { | ||
if (feature_id % 3 == 0 && feature_id % 5 == 0) { | ||
return {}; | ||
} else if (feature_id % 3 == 0) { | ||
return 1; | ||
} else if (feature_id % 5 == 0) { | ||
return 0; | ||
} else { | ||
return feature_id; | ||
} | ||
} | ||
|
||
std::string get_newcode_for_ye(int64_t feature_id, int64_t value) { | ||
std::stringstream newcode_ss; | ||
newcode_ss | ||
<< value | ||
<< " /* removed ye_olde_feature_toggle " | ||
<< feature_id | ||
<< " */"; | ||
return newcode_ss.str(); | ||
} | ||
|
||
} | ||
|
||
class MyTool { | ||
clang::CompilerInstance* ci; | ||
clangmetatool::collectors::IncludeGraph ig; | ||
clangmetatool::collectors::FindCalls fc; | ||
public: | ||
MyTool(clang::CompilerInstance *_ci, clang::ast_matchers::MatchFinder *f) | ||
:ig(_ci, f), | ||
fc(_ci, f, "ye_olde_feature_toggle_is_enabled"), | ||
ci(_ci) {} | ||
void postProcessing( | ||
std::map<std::string, clang::tooling::Replacements> &replacementsMap) { | ||
clangmetatool::collectors::IncludeGraphData* igdata = ig.getData(); | ||
clang::ASTContext& ctx = ci->getASTContext(); | ||
clang::SourceManager& sm = ctx.getSourceManager(); | ||
|
||
// what is the file id for ye_olde_feature_toggle.h | ||
bool found_header = false; | ||
clangmetatool::types::FileUID header_fuid = 0; | ||
for (auto item : igdata->fuid2name) { | ||
if (item.second == "ye_olde_feature_toggle.h") { | ||
found_header = true; | ||
header_fuid = item.first; | ||
break; | ||
} | ||
} | ||
|
||
// bail early if the header was not used | ||
if (!found_header) | ||
return; | ||
|
||
// accumulate all the calls, the argument, and its optional determinstic value | ||
typedef std::tuple<const clang::CallExpr*, int64_t, std::optional<int64_t> > ye_call; | ||
std::vector<ye_call> candidates; | ||
|
||
// iterate over all the calls to that function | ||
clangmetatool::collectors::FindCallsData* fcdata = fc.getData(); | ||
for (auto call_ctx : fcdata->call_context) { | ||
const clang::CallExpr* call = call_ctx.second; | ||
if (!sm.isWrittenInMainFile(call->getBeginLoc())) | ||
continue; | ||
if (sm.isMacroBodyExpansion(call->getBeginLoc())) | ||
continue; | ||
const clang::Expr* arg = call->getArg(0); | ||
llvm::APSInt argval; | ||
if (arg->isIntegerConstantExpr(argval, ctx)) { | ||
int64_t feature_id = argval.getExtValue(); | ||
candidates.push_back | ||
( ye_call{call, feature_id, ye_deterministic_val(feature_id) } ); | ||
continue; | ||
} | ||
|
||
// let's try the const propagation. | ||
|
||
// First we need to see if it is a implicitcastexpr -> | ||
// declrefexpr -- which is the AST for using a variable | ||
auto arg_as_ice = llvm::dyn_cast<clang::ImplicitCastExpr>(arg); | ||
if (arg_as_ice == NULL) | ||
continue; | ||
|
||
const clang::Expr* subexpr = arg_as_ice->getSubExpr(); | ||
auto subexpr_as_dre = llvm::dyn_cast<clang::DeclRefExpr>(subexpr); | ||
if (subexpr_as_dre == NULL) | ||
continue; | ||
|
||
clangmetatool::propagation::ConstantIntegerPropagator p(ci); | ||
auto r = p.runPropagation(call_ctx.first, subexpr_as_dre); | ||
if (r.isUnresolved()) | ||
continue; | ||
|
||
int64_t feature_id = r.getResult(); | ||
candidates.push_back | ||
( ye_call{call, feature_id, ye_deterministic_val(feature_id) } ); | ||
} | ||
|
||
// slightly ugly bit because we have a single Replacments object per file | ||
auto replacements_emp = replacementsMap.emplace | ||
(sm.getFileEntryForID(sm.getMainFileID())->getName(), clang::tooling::Replacements()); | ||
clang::tooling::Replacements& replacements = replacements_emp.first->second; | ||
|
||
// actually work on the refactorings | ||
std::set<const clang::DeclRefExpr*> removed; | ||
for (auto ye_candidate : candidates) { | ||
const clang::CallExpr* call = std::get<0>(ye_candidate); | ||
int64_t feature_id = std::get<1>(ye_candidate); | ||
std::optional<int64_t> det_val = std::get<2>(ye_candidate); | ||
if (!det_val.has_value()) | ||
continue; | ||
|
||
std::string newcode = get_newcode_for_ye(feature_id, det_val.value()); | ||
clang::tooling::Replacement replacement(sm, call, newcode); | ||
|
||
llvm::Error err = replacements.add(replacement); | ||
// keep track of the DeclRefExpr we actually removed | ||
if (!err) | ||
removed.insert(fcdata->call_ref[call]); | ||
} | ||
|
||
// let's remove the now-unused include statements | ||
// since we're only changing the main file, we're only going to look at | ||
// main_fuid -> header_fuid edges of the include graph | ||
clangmetatool::types::FileUID main_fuid = | ||
sm.getFileEntryForID(sm.getMainFileID())->getUID(); | ||
clangmetatool::types::FileGraphEdge relevant{ main_fuid, header_fuid }; | ||
bool now_unused = true; | ||
auto refs_range = igdata->decl_references.equal_range(relevant); | ||
for ( auto it = refs_range.first; it != refs_range.second; it++ ) { | ||
// double check if we removed all references | ||
if ( removed.find(it->second) == removed.end() ) { | ||
now_unused = false; | ||
break; | ||
} | ||
} | ||
|
||
if (now_unused) { | ||
auto incs_range = igdata->include_statements.equal_range(relevant); | ||
for ( auto it = incs_range.first; it != incs_range.second; it++ ) { | ||
clang::SourceRange inc_r = it->second; | ||
unsigned int beg_line = sm.getSpellingLineNumber(inc_r.getBegin()); | ||
unsigned int end_line = sm.getSpellingLineNumber(inc_r.getEnd()); | ||
clang::SourceLocation beg_loc = | ||
sm.translateLineCol(sm.getMainFileID(), beg_line, 1); | ||
clang::SourceLocation end_loc = | ||
sm.translateLineCol(sm.getMainFileID(), end_line+1, 1); | ||
clang::CharSourceRange repl_range = | ||
clang::CharSourceRange(clang::SourceRange(beg_loc, end_loc), false); | ||
|
||
clang::tooling::Replacement replacement(sm, repl_range, ""); | ||
llvm::Error err = replacements.add(replacement); | ||
} | ||
} | ||
|
||
} | ||
}; | ||
|
||
int main(int argc, const char *argv[]) { | ||
llvm::cl::OptionCategory MyToolCategory("my-tool options"); | ||
|
||
llvm::cl::extrahelp CommonHelp( | ||
clang::tooling::CommonOptionsParser::HelpMessage); | ||
|
||
clang::tooling::CommonOptionsParser optionsParser(argc, argv, MyToolCategory); | ||
|
||
clang::tooling::RefactoringTool tool(optionsParser.getCompilations(), | ||
optionsParser.getSourcePathList()); | ||
|
||
clangmetatool::MetaToolFactory<clangmetatool::MetaTool<MyTool>> raf( | ||
tool.getReplacements()); | ||
|
||
int r = tool.runAndSave(&raf); | ||
|
||
return r; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
foreach( TEST | ||
simple_direct_usage | ||
true_vs_false | ||
non_deterministic | ||
mixed_deterministic_and_not | ||
integer_variable | ||
) | ||
add_test( | ||
NAME ${TEST} | ||
COMMAND | ||
${CMAKE_CURRENT_SOURCE_DIR}/tool_test_runner.sh | ||
$<TARGET_FILE:remove_ye_olde_feature_toggle> | ||
${CMAKE_CURRENT_SOURCE_DIR} | ||
${CMAKE_CURRENT_BINARY_DIR} | ||
${TEST} ) | ||
endforeach() |
10 changes: 10 additions & 0 deletions
10
examples/remove_ye_olde_feature_toggle/t/integer_variable.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#include <ye_olde_feature_toggle.h> | ||
int test_function() { | ||
int feature_id = 3; | ||
if (ye_olde_feature_toggle_is_enabled(feature_id)) | ||
return 1; | ||
feature_id = 5; | ||
if (ye_olde_feature_toggle_is_enabled(feature_id)) | ||
return 0; | ||
return 42; | ||
} |
9 changes: 9 additions & 0 deletions
9
examples/remove_ye_olde_feature_toggle/t/integer_variable.c.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
int test_function() { | ||
int feature_id = 3; | ||
if (1 /* removed ye_olde_feature_toggle 3 */) | ||
return 1; | ||
feature_id = 5; | ||
if (0 /* removed ye_olde_feature_toggle 5 */) | ||
return 0; | ||
return 42; | ||
} |
9 changes: 9 additions & 0 deletions
9
examples/remove_ye_olde_feature_toggle/t/mixed_deterministic_and_not.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include <ye_olde_feature_toggle.h> | ||
int test_function() { | ||
if (ye_olde_feature_toggle_is_enabled(15) || | ||
ye_olde_feature_toggle_is_enabled(3)) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
examples/remove_ye_olde_feature_toggle/t/mixed_deterministic_and_not.c.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include <ye_olde_feature_toggle.h> | ||
int test_function() { | ||
if (ye_olde_feature_toggle_is_enabled(15) || | ||
1 /* removed ye_olde_feature_toggle 3 */) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#include <ye_olde_feature_toggle.h> | ||
int test_function() { | ||
if (ye_olde_feature_toggle_is_enabled(15)) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
examples/remove_ye_olde_feature_toggle/t/non_deterministic.c.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#include <ye_olde_feature_toggle.h> | ||
int test_function() { | ||
if (ye_olde_feature_toggle_is_enabled(15)) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |
8 changes: 8 additions & 0 deletions
8
examples/remove_ye_olde_feature_toggle/t/simple_direct_usage.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#include <ye_olde_feature_toggle.h> | ||
int test_function() { | ||
if (ye_olde_feature_toggle_is_enabled(3)) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
examples/remove_ye_olde_feature_toggle/t/simple_direct_usage.c.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
int test_function() { | ||
if (1 /* removed ye_olde_feature_toggle 3 */) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |
12 changes: 12 additions & 0 deletions
12
examples/remove_ye_olde_feature_toggle/t/testinclude/ye_olde_feature_toggle.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#ifndef INCLUDED_YE_OLDE_FEATURE_TOGGLE_H | ||
#define INCLUDED_YE_OLDE_FEATURE_TOGGLE_H | ||
#ifdef __cplusplus | ||
extern "C" { | ||
#endif | ||
|
||
int ye_olde_feature_toggle_is_enabled(int feature_id); | ||
|
||
#ifdef __cplusplus | ||
} | ||
#endif | ||
#endif |
12 changes: 12 additions & 0 deletions
12
examples/remove_ye_olde_feature_toggle/t/tool_test_runner.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
#!/bin/sh | ||
# Arguments to this script are: | ||
# 1. the path to the tool as built by CMake | ||
# 2. CMAKE_CURRENT_SOURCE_DIR | ||
# 3. CMAKE_CURRENT_BINARY_DIR | ||
# 4. the name of the test case | ||
set -x | ||
set -e | ||
cp $2/$4.c $3/$4.c | ||
$1 $3/$4.c -- -I$2/testinclude | ||
diff -u $2/$4.c.expected $3/$4.c | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
#include <ye_olde_feature_toggle.h> | ||
int test_function() { | ||
if (ye_olde_feature_toggle_is_enabled(3) || | ||
ye_olde_feature_toggle_is_enabled(5) || | ||
ye_olde_feature_toggle_is_enabled(7)) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
examples/remove_ye_olde_feature_toggle/t/true_vs_false.c.expected
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
int test_function() { | ||
if (1 /* removed ye_olde_feature_toggle 3 */ || | ||
0 /* removed ye_olde_feature_toggle 5 */ || | ||
7 /* removed ye_olde_feature_toggle 7 */) { | ||
return 1; | ||
} else { | ||
return 0; | ||
} | ||
} |