From 8c89dc3af5e44abda41106a5c259575466e84e8d Mon Sep 17 00:00:00 2001 From: Lukas Rothenberger Date: Wed, 18 Dec 2024 10:06:43 +0100 Subject: [PATCH 1/3] feat: minor improvement to doall and reduction detection --- .../pattern_detectors/do_all_detector.py | 21 +++++++++++++++++-- .../pattern_detectors/reduction_detector.py | 21 +++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/discopop_explorer/pattern_detectors/do_all_detector.py b/discopop_explorer/pattern_detectors/do_all_detector.py index ad04eb6ce..569f78203 100644 --- a/discopop_explorer/pattern_detectors/do_all_detector.py +++ b/discopop_explorer/pattern_detectors/do_all_detector.py @@ -396,8 +396,25 @@ def __check_loop_dependencies( return True elif dep.dtype == DepType.WAW: # check WAW dependencies - # handled by variable classification - pass + if ( + dep.metadata_intra_iteration_dep is None + or dep.metadata_inter_iteration_dep is None + or dep.metadata_intra_call_dep is None + or dep.metadata_inter_call_dep is None + ): + # no metadata created + # handled by variable classification + pass + else: + # metadata exists + + cond_2 = len([cf for cf in called_functions_lineids if cf in dep.metadata_inter_call_dep]) > 0 + cond_4 = root_loop.start_position() in dep.metadata_inter_iteration_dep + # if cond_1 or cond_2 or cond_3: + if cond_2 or cond_4: + return True + # handled by variable classification + pass else: raise ValueError("Unsupported dependency type: ", dep.dtype) diff --git a/discopop_explorer/pattern_detectors/reduction_detector.py b/discopop_explorer/pattern_detectors/reduction_detector.py index 4cd81eb7a..533237b8f 100644 --- a/discopop_explorer/pattern_detectors/reduction_detector.py +++ b/discopop_explorer/pattern_detectors/reduction_detector.py @@ -331,8 +331,25 @@ def __check_loop_dependencies( return True elif dep.dtype == DepType.WAW: # check WAW dependencies - # handled by variable classification - pass + if ( + dep.metadata_intra_iteration_dep is None + or dep.metadata_inter_iteration_dep is None + or dep.metadata_intra_call_dep is None + or dep.metadata_inter_call_dep is None + ): + # no metadata created + # handled by variable classification + pass + else: + # metadata exists + + cond_2 = len([cf for cf in called_functions_lineids if cf in dep.metadata_inter_call_dep]) > 0 + cond_4 = root_loop.start_position() in dep.metadata_inter_iteration_dep + # if cond_1 or cond_2 or cond_3: + if cond_2 or cond_4: + return True + # handled by variable classification + pass else: raise ValueError("Unsupported dependency type: ", dep.dtype) From f8c5734ffe2558989b055d1fa042ca679d5f54b7 Mon Sep 17 00:00:00 2001 From: Lukas Rothenberger Date: Wed, 18 Dec 2024 10:17:28 +0100 Subject: [PATCH 2/3] test: add end to end test --- .../calls/preventing/simple_2/__init__.py | 0 .../calls/preventing/simple_2/src/Makefile | 17 ++++ .../calls/preventing/simple_2/src/code.cpp | 25 ++++++ .../do_all/calls/preventing/simple_2/test.py | 90 +++++++++++++++++++ 4 files changed, 132 insertions(+) create mode 100644 test/end_to_end/do_all/calls/preventing/simple_2/__init__.py create mode 100644 test/end_to_end/do_all/calls/preventing/simple_2/src/Makefile create mode 100644 test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp create mode 100644 test/end_to_end/do_all/calls/preventing/simple_2/test.py diff --git a/test/end_to_end/do_all/calls/preventing/simple_2/__init__.py b/test/end_to_end/do_all/calls/preventing/simple_2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/end_to_end/do_all/calls/preventing/simple_2/src/Makefile b/test/end_to_end/do_all/calls/preventing/simple_2/src/Makefile new file mode 100644 index 000000000..a64882be3 --- /dev/null +++ b/test/end_to_end/do_all/calls/preventing/simple_2/src/Makefile @@ -0,0 +1,17 @@ +all: clean prog + +prog: code.o + $(CXX) -o prog code.o $(CXXFLAGS) + +code.o: + $(CXX) -c -S -emit-llvm -o code.ll code.cpp $(CXXFLAGS) + rm -rf .discopop + $(CXX) -c -o code.o code.cpp $(CXXFLAGS) + +clean: + rm -rf .discopop + rm -rf src/.discopop + find . -not -name code.cpp -not -name Makefile -not -path **/FileMapping.txt -delete + +veryclean: clean + rm -f FileMapping.txt diff --git a/test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp b/test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp new file mode 100644 index 000000000..ec2a98b71 --- /dev/null +++ b/test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp @@ -0,0 +1,25 @@ +#include +#include + +void prevent_doall(double* tmp, int i){ + for(int j = 0; j < 10; j++){ + tmp[j] = i; + } +} + +int main(int argc, const char* argv[]) { + static int n = 10; + double *x = (double *) malloc(n * sizeof(double)); + // Initialize x, y + for(int i = 0; i < n; ++i){ + x[i] = i; + } + + // parallelizable + int index; + for(int i = 0; i < n; ++i){ + prevent_doall(x, i); + } + free(x); +return 0; +} diff --git a/test/end_to_end/do_all/calls/preventing/simple_2/test.py b/test/end_to_end/do_all/calls/preventing/simple_2/test.py new file mode 100644 index 000000000..0d261e19a --- /dev/null +++ b/test/end_to_end/do_all/calls/preventing/simple_2/test.py @@ -0,0 +1,90 @@ +import copy +import os +import pathlib +import unittest + +import jsonpickle + +from discopop_library.result_classes.DetectionResult import DetectionResult +from test.utils.existence.existence_utils import check_patterns_for_FN, check_patterns_for_FP +from test.utils.subprocess_wrapper.command_execution_wrapper import run_cmd +from test.utils.validator_classes.DoAllInfoForValidation import DoAllInfoForValidation + +from discopop_library.ConfigProvider.config_provider import run as run_config_provider +from discopop_library.ConfigProvider.ConfigProviderArguments import ConfigProviderArguments + + +class TestMethods(unittest.TestCase): + @classmethod + def setUpClass(self): + current_dir = pathlib.Path(__file__).parent.resolve() + dp_build_dir = run_config_provider( + ConfigProviderArguments( + return_dp_build_dir=True, + return_dp_source_dir=False, + return_llvm_bin_dir=False, + return_full_config=False, + return_version_string=False, + ) + ) + + env_vars = dict(os.environ) + + src_dir = os.path.join(current_dir, "src") + os.chdir(src_dir) + + # create FileMapping + cmd = os.path.join(dp_build_dir, "scripts", "dp-fmap") + run_cmd(cmd, src_dir, env_vars) + + # build + env_vars["CC"] = os.path.join(dp_build_dir, "scripts", "CC_wrapper.sh") + env_vars["CXX"] = os.path.join(dp_build_dir, "scripts", "CXX_wrapper.sh") + env_vars["DP_PROJECT_ROOT_DIR"] = src_dir + cmd = "make" + run_cmd(cmd, src_dir, env_vars) + + # execute instrumented program + run_cmd("./prog", src_dir, env_vars) + # execute DiscoPoP analysis + + cwd = os.path.join(src_dir, ".discopop") + cmd = "discopop_explorer --enable-patterns doall,reduction" + run_cmd(cmd, cwd, env_vars) + + self.src_dir = src_dir + self.env_vars = env_vars + + test_output_file = os.path.join(self.src_dir, ".discopop", "explorer", "detection_result_dump.json") + # load detection results + with open(test_output_file, "r") as f: + tmp_str = f.read() + self.test_output: DetectionResult = jsonpickle.decode(tmp_str) + + @classmethod + def tearDownClass(self): + run_cmd("make veryclean", self.src_dir, self.env_vars) + + def test(self): + for pattern_type in self.test_output.patterns.__dict__: + amount_of_identified_patterns = len(self.test_output.patterns.__dict__[pattern_type]) + if pattern_type == "do_all": + expected_lines = ["1:14", "1:5"] + with self.subTest("check for FP"): + res, msg = check_patterns_for_FP( + self, + pattern_type, + copy.deepcopy(expected_lines), + self.test_output.patterns.__dict__[pattern_type], + ) + self.assertTrue(res, msg) + with self.subTest("check for FN"): + res, msg = check_patterns_for_FN( + self, + pattern_type, + copy.deepcopy(expected_lines), + self.test_output.patterns.__dict__[pattern_type], + ) + self.assertTrue(res, msg) + else: + self.assertEqual(amount_of_identified_patterns, 0) From 871b27b124c300b9ade22a763d822c25073a521b Mon Sep 17 00:00:00 2001 From: Lukas Rothenberger Date: Wed, 18 Dec 2024 10:28:55 +0100 Subject: [PATCH 3/3] test: add end to end test --- .../calls/preventing/simple_2/src/code.cpp | 2 +- .../calls/preventing/simple_3/__init__.py | 0 .../calls/preventing/simple_3/src/Makefile | 17 ++++ .../calls/preventing/simple_3/src/code.cpp | 32 +++++++ .../do_all/calls/preventing/simple_3/test.py | 90 +++++++++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 test/end_to_end/do_all/calls/preventing/simple_3/__init__.py create mode 100644 test/end_to_end/do_all/calls/preventing/simple_3/src/Makefile create mode 100644 test/end_to_end/do_all/calls/preventing/simple_3/src/code.cpp create mode 100644 test/end_to_end/do_all/calls/preventing/simple_3/test.py diff --git a/test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp b/test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp index ec2a98b71..406c60f60 100644 --- a/test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp +++ b/test/end_to_end/do_all/calls/preventing/simple_2/src/code.cpp @@ -15,7 +15,7 @@ int main(int argc, const char* argv[]) { x[i] = i; } - // parallelizable + // not parallelizable int index; for(int i = 0; i < n; ++i){ prevent_doall(x, i); diff --git a/test/end_to_end/do_all/calls/preventing/simple_3/__init__.py b/test/end_to_end/do_all/calls/preventing/simple_3/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/end_to_end/do_all/calls/preventing/simple_3/src/Makefile b/test/end_to_end/do_all/calls/preventing/simple_3/src/Makefile new file mode 100644 index 000000000..a64882be3 --- /dev/null +++ b/test/end_to_end/do_all/calls/preventing/simple_3/src/Makefile @@ -0,0 +1,17 @@ +all: clean prog + +prog: code.o + $(CXX) -o prog code.o $(CXXFLAGS) + +code.o: + $(CXX) -c -S -emit-llvm -o code.ll code.cpp $(CXXFLAGS) + rm -rf .discopop + $(CXX) -c -o code.o code.cpp $(CXXFLAGS) + +clean: + rm -rf .discopop + rm -rf src/.discopop + find . -not -name code.cpp -not -name Makefile -not -path **/FileMapping.txt -delete + +veryclean: clean + rm -f FileMapping.txt diff --git a/test/end_to_end/do_all/calls/preventing/simple_3/src/code.cpp b/test/end_to_end/do_all/calls/preventing/simple_3/src/code.cpp new file mode 100644 index 000000000..8b1cb77a9 --- /dev/null +++ b/test/end_to_end/do_all/calls/preventing/simple_3/src/code.cpp @@ -0,0 +1,32 @@ +#include + +void not_prevent_doall(double* tmp, int i){ + int z = i + tmp[i]; +} + +void prevent_doall(double* tmp, int i){ + for(int j = 0; j < 10; j++){ // wo w + tmp[j] = i; + } +} + +int main(int argc, const char* argv[]) { + static int n = 10; + double *x = (double *) malloc(n * sizeof(double)); + // Initialize x, y + for(int i = 0; i < n; ++i){ + x[i] = i; + } + + // not parallelizable + for(int i = 0; i < n; ++i){ + prevent_doall(x, i); + } + + // parallelizable + for(int i = 0; i < n; ++i){ + not_prevent_doall(x, i); + } + free(x); +return 0; +} diff --git a/test/end_to_end/do_all/calls/preventing/simple_3/test.py b/test/end_to_end/do_all/calls/preventing/simple_3/test.py new file mode 100644 index 000000000..47e288926 --- /dev/null +++ b/test/end_to_end/do_all/calls/preventing/simple_3/test.py @@ -0,0 +1,90 @@ +import copy +import os +import pathlib +import unittest + +import jsonpickle + +from discopop_library.result_classes.DetectionResult import DetectionResult +from test.utils.existence.existence_utils import check_patterns_for_FN, check_patterns_for_FP +from test.utils.subprocess_wrapper.command_execution_wrapper import run_cmd +from test.utils.validator_classes.DoAllInfoForValidation import DoAllInfoForValidation + +from discopop_library.ConfigProvider.config_provider import run as run_config_provider +from discopop_library.ConfigProvider.ConfigProviderArguments import ConfigProviderArguments + + +class TestMethods(unittest.TestCase): + @classmethod + def setUpClass(self): + current_dir = pathlib.Path(__file__).parent.resolve() + dp_build_dir = run_config_provider( + ConfigProviderArguments( + return_dp_build_dir=True, + return_dp_source_dir=False, + return_llvm_bin_dir=False, + return_full_config=False, + return_version_string=False, + ) + ) + + env_vars = dict(os.environ) + + src_dir = os.path.join(current_dir, "src") + os.chdir(src_dir) + + # create FileMapping + cmd = os.path.join(dp_build_dir, "scripts", "dp-fmap") + run_cmd(cmd, src_dir, env_vars) + + # build + env_vars["CC"] = os.path.join(dp_build_dir, "scripts", "CC_wrapper.sh") + env_vars["CXX"] = os.path.join(dp_build_dir, "scripts", "CXX_wrapper.sh") + env_vars["DP_PROJECT_ROOT_DIR"] = src_dir + cmd = "make" + run_cmd(cmd, src_dir, env_vars) + + # execute instrumented program + run_cmd("./prog", src_dir, env_vars) + # execute DiscoPoP analysis + + cwd = os.path.join(src_dir, ".discopop") + cmd = "discopop_explorer --enable-patterns doall,reduction" + run_cmd(cmd, cwd, env_vars) + + self.src_dir = src_dir + self.env_vars = env_vars + + test_output_file = os.path.join(self.src_dir, ".discopop", "explorer", "detection_result_dump.json") + # load detection results + with open(test_output_file, "r") as f: + tmp_str = f.read() + self.test_output: DetectionResult = jsonpickle.decode(tmp_str) + + @classmethod + def tearDownClass(self): + run_cmd("make veryclean", self.src_dir, self.env_vars) + + def test(self): + for pattern_type in self.test_output.patterns.__dict__: + amount_of_identified_patterns = len(self.test_output.patterns.__dict__[pattern_type]) + if pattern_type == "do_all": + expected_lines = ["1:17", "1:8", "1:27"] + with self.subTest("check for FP"): + res, msg = check_patterns_for_FP( + self, + pattern_type, + copy.deepcopy(expected_lines), + self.test_output.patterns.__dict__[pattern_type], + ) + self.assertTrue(res, msg) + with self.subTest("check for FN"): + res, msg = check_patterns_for_FN( + self, + pattern_type, + copy.deepcopy(expected_lines), + self.test_output.patterns.__dict__[pattern_type], + ) + self.assertTrue(res, msg) + else: + self.assertEqual(amount_of_identified_patterns, 0)