diff --git a/CMakeLists.txt b/CMakeLists.txt index ab00d41f1..53fc5a5d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,8 @@ For more information, please refer to https://discopop-project.github.io/discopo endif() endif() +find_package(Boost REQUIRED) + message(STATUS "Using LLVM version ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") list(APPEND CMAKE_MODULE_PATH ${LLVM_CMAKE_DIR}) @@ -93,6 +95,10 @@ file(APPEND "${DiscoPoP_SOURCE_DIR}/discopop_library/ConfigProvider/assets/build file(APPEND "${DiscoPoP_SOURCE_DIR}/discopop_library/ConfigProvider/assets/build_config.py" "DP_RTLIB_VERBOSE=\"${DP_RTLIB_VERBOSE}\"\n") file(APPEND "${DiscoPoP_SOURCE_DIR}/discopop_library/ConfigProvider/assets/build_config.py" "DP_NUM_WORKERS=\"${DP_NUM_WORKERS}\"\n") file(APPEND "${DiscoPoP_SOURCE_DIR}/discopop_library/ConfigProvider/assets/build_config.py" "DP_INTERNAL_TIMER=\"${DP_INTERNAL_TIMER}\"\n") +file(APPEND "${DiscoPoP_SOURCE_DIR}/discopop_library/ConfigProvider/assets/build_config.py" "DP_CALLTREE_PROFILING=\"${DP_CALLTREE_PROFILING}\"\n") +file(APPEND "${DiscoPoP_SOURCE_DIR}/discopop_library/ConfigProvider/assets/build_config.py" "DP_MEMORY_REGION_DEALIASING=\"${DP_MEMORY_REGION_DEALIASING}\"\n") + + # print DiscoPoP configuration file(READ ${DiscoPoP_SOURCE_DIR}/discopop_library/ConfigProvider/assets/build_config.py DiscoPoP_CONFIGURATION) diff --git a/discopop_explorer/PEGraphX.py b/discopop_explorer/PEGraphX.py index 3ee63a790..b3fa69a82 100644 --- a/discopop_explorer/PEGraphX.py +++ b/discopop_explorer/PEGraphX.py @@ -142,13 +142,21 @@ class Dependency: sink_line: Optional[LineID] = None intra_iteration: bool = False intra_iteration_level: int = -1 - metadata_intra_iteration_dep: Optional[List[LineID]] = None - metadata_inter_iteration_dep: Optional[List[LineID]] = None - metadata_intra_call_dep: Optional[List[LineID]] = None - metadata_inter_call_dep: Optional[List[LineID]] = None + metadata_intra_iteration_dep: List[LineID] + metadata_inter_iteration_dep: List[LineID] + metadata_intra_call_dep: List[LineID] + metadata_inter_call_dep: List[LineID] + metadata_sink_ancestors: List[LineID] + metadata_source_ancestors: List[LineID] def __init__(self, type: EdgeType): self.etype = type + self.metadata_intra_iteration_dep = [] + self.metadata_inter_iteration_dep = [] + self.metadata_intra_call_dep = [] + self.metadata_inter_call_dep = [] + self.metadata_sink_ancestors = [] + self.metadata_source_ancestors = [] def __str__(self): return self.var_name if self.var_name is not None else str(self.etype) @@ -572,30 +580,27 @@ def parse_dependency(dep: DependenceItem) -> Dependency: d.var_name = dep.var_name d.memory_region = dep.memory_region # parse metadata - if ";" in dep.metadata: - for md in dep.metadata.split(";"): + if len(dep.metadata) > 0: + for md in dep.metadata.split(" "): if len(md) == 0: continue - md_type = md[: md.index(":")] - md_raw_values = md[md.index(":") + 1 :] + # unpack metadata + md_type = md[: md.index("[")] + md_raw_values = md[md.index("[") + 1 : -1] md_values = [tmp for tmp in md_raw_values.split(",") if len(tmp) > 0] - - if md_type == "intra_iteration_dep": - if d.metadata_intra_iteration_dep is None: - d.metadata_intra_iteration_dep = [] + # store metadata + if md_type == "IAI": d.metadata_intra_iteration_dep += md_values - elif md_type == "inter_iteration_dep": - if d.metadata_inter_iteration_dep is None: - d.metadata_inter_iteration_dep = [] + elif md_type == "IEI": d.metadata_inter_iteration_dep += md_values - elif md_type == "intra_call_dep": - if d.metadata_intra_call_dep is None: - d.metadata_intra_call_dep = [] + elif md_type == "IAC": d.metadata_intra_call_dep += md_values - elif md_type == "inter_call_dep": - if d.metadata_inter_call_dep is None: - d.metadata_inter_call_dep = [] + elif md_type == "IEC": d.metadata_inter_call_dep += md_values + elif md_type == "SINK_ANC": + d.metadata_sink_ancestors += md_values + elif md_type == "SOURCE_ANC": + d.metadata_source_ancestors += md_values else: raise ValueError("Unknown metadata type: ", md_type) return d diff --git a/discopop_explorer/parser.py b/discopop_explorer/parser.py index ce895e24a..c950ec9c0 100644 --- a/discopop_explorer/parser.py +++ b/discopop_explorer/parser.py @@ -12,7 +12,7 @@ from collections import defaultdict from dataclasses import dataclass from os.path import abspath, dirname -from typing import Any, List, Tuple +from typing import Any, Dict, List, Tuple from lxml import objectify # type:ignore @@ -113,6 +113,31 @@ def __map_dummy_nodes(cu_dict): def __parse_dep_file(dep_fd, output_path: str) -> Tuple[List[DependenceItem], List[LoopData]]: dependencies_list: List[DependenceItem] = [] loop_data_list: List[LoopData] = [] + + # read dependency metadata + dependency_metadata_lines = [] + if os.path.exists(os.path.join(output_path, "dependency_metadata.txt")): + with open(os.path.join(output_path, "dependency_metadata.txt"), "r") as dep_metadata_fd: + dependency_metadata_lines = dep_metadata_fd.readlines() + dependency_metadata: Dict[Tuple[Any, Any, Any, Any, Any], List[str]] = dict() + for line in dependency_metadata_lines: + line = line.replace("\n", "") + split_line = line.split(" ") + if split_line[0].startswith("#"): + continue + type = split_line[0] + sink = split_line[1] + source = split_line[2] + var = split_line[3] + AAvar = split_line[4] + line_metadata = " ".join( + [split_line[5], split_line[6], split_line[7], split_line[8], split_line[9], split_line[10]] + ) # IAC, IAI, IEC, IEI, SINK_ANC, SOURCE_ANC) + key_tuple = sink, source, type, var, AAvar + if key_tuple not in dependency_metadata: + dependency_metadata[key_tuple] = [] + dependency_metadata[key_tuple].append(line_metadata) + # read static dependencies static_dependency_lines = [] if not os.path.exists(os.path.join(output_path, "static_dependencies.txt")): @@ -155,7 +180,7 @@ def __parse_dep_file(dep_fd, output_path: str) -> Tuple[List[DependenceItem], Li var_str = "" if len(source_fields) == 1 else source_fields[1] var_name = "" aa_var_name = "" - metadata = "" + metadata = [] if len(var_str) > 0: if "(" in var_str: split_var_str = var_str.split("(") @@ -163,13 +188,21 @@ def __parse_dep_file(dep_fd, output_path: str) -> Tuple[List[DependenceItem], Li aa_var_name = split_var_str[1][ : split_var_str[1].index(")") ] # name of the allocated variable which is accessed, i.e. variable name after anti aliasing - metadata_str = split_var_str[1][split_var_str[1].index(")") + 1 :] - if "[" in metadata_str: - metadata = metadata_str[1:-1] else: # compatibility with results created without alias analysis var_name = var_str - dependencies_list.append(DependenceItem(sink, source_fields[0], type, var_name, aa_var_name, metadata)) + # retrieve metadata + key_tuple = sink, source_fields[0], type, var_name, aa_var_name + if key_tuple in dependency_metadata: + metadata = dependency_metadata[key_tuple] + # register dependencies + if len(metadata) == 0: + dependencies_list.append(DependenceItem(sink, source_fields[0], type, var_name, aa_var_name, "")) + else: + for md_set in metadata: + dependencies_list.append( + DependenceItem(sink, source_fields[0], type, var_name, aa_var_name, md_set) + ) return dependencies_list, loop_data_list diff --git a/discopop_explorer/pattern_detectors/do_all_detector.py b/discopop_explorer/pattern_detectors/do_all_detector.py index 96d6f1a24..398a55867 100644 --- a/discopop_explorer/pattern_detectors/do_all_detector.py +++ b/discopop_explorer/pattern_detectors/do_all_detector.py @@ -24,7 +24,7 @@ LineID, MemoryRegion, DepType, - NodeID, + NodeID, Dependency, ) from ..utils import classify_loop_variables, filter_for_hotspots from ..variable import Variable @@ -83,6 +83,8 @@ def run_detection(pet: PEGraphX, hotspots, reduction_info: List[ReductionInfo]) result: List[DoAllInfo] = [] nodes = pet.all_nodes(LoopNode) + warnings.warn("DOALL DETECTION CURRENTLY ASSUMES THE EXISTENCE OF DEPENDENCY METADATA!") + # remove reduction loops print("ASDF: ", [r.node_id for r in reduction_info]) print("Nodes: ", [n.start_position() for n in nodes]) @@ -239,10 +241,6 @@ def __check_loop_dependencies( memory_regions_defined_in_loop.update(mem_regs) for source, target, dep in deps: - - if root_loop.start_position() == "1:153": - pass - # todo: move this calculation to the innermost point possible to reduce computation costs # get metadata for dependency dep_source_nesting_level = __calculate_nesting_level(pet, root_loop, source) @@ -264,13 +262,27 @@ def __check_loop_dependencies( if pet.is_loop_index(dep.var_name, loop_start_lines, root_children_cus): continue + # ignore dependencies where either source or sink do not lie within root_loop + if len(dep.metadata_source_ancestors) > 0 and len(dep.metadata_sink_ancestors) > 0: + if not ( + (root_loop.start_position() in dep.metadata_sink_ancestors) + and root_loop.start_position() in dep.metadata_source_ancestors + ): + tmp = root_loop.start_position() + continue + # targeted variable is not read-only if dep.dtype == DepType.INIT: continue elif dep.dtype == DepType.RAW: # check RAW dependencies # RAW problematic, if it is not an intra-iteration RAW - if not dep.intra_iteration: + cond_1 = (len(dep.metadata_intra_iteration_dep) == 0) and parent_function_lineid in ( + dep.metadata_intra_call_dep if dep.metadata_intra_call_dep is not None else [] + ) + cond_2 = len([cf for cf in called_functions_lineids if cf in dep.metadata_inter_call_dep]) > 0 + cond_3 = len([t for t in parent_loops if t in dep.metadata_inter_iteration_dep]) > 0 + if (cond_1) or ((cond_2) and (cond_3)): return True # if it is an intra iteration dependency, it is problematic if it belongs to a parent loop else: @@ -278,20 +290,53 @@ def __check_loop_dependencies( if pet.node_at(source) in root_children_cus and pet.node_at(target) in root_children_cus: pass else: - return True + # check if metadata exists + if dep.metadata_intra_iteration_dep is not None: + for t in dep.metadata_intra_iteration_dep: + if t in parent_loops: + return True + return False + else: + return True elif dep.dtype == DepType.WAR: # check WAR dependencies # WAR problematic, if it is not an intra-iteration WAR and the variable is not private or firstprivate - if not dep.intra_iteration: + if ( + not dep.intra_iteration + and (dep.metadata_intra_iteration_dep is None or len(dep.metadata_intra_iteration_dep) == 0) + and parent_function_lineid + in (dep.metadata_intra_call_dep if dep.metadata_intra_call_dep is not None else []) + ) or ( + ( + False + if dep.metadata_inter_call_dep is None + else (len([cf for cf in called_functions_lineids if cf in dep.metadata_inter_call_dep]) > 0) + ) + and ( + False + if dep.metadata_inter_iteration_dep is None + else (len([t for t in parent_loops if t in dep.metadata_inter_iteration_dep]) > 0) + ) + ): if dep.var_name not in [v.name for v in first_privates + privates + last_privates]: # check if variable is defined inside loop if dep.memory_region not in memory_regions_defined_in_loop: return True + # check if the definitions of the accessed variable originates from a function call + if __check_for_problematic_function_argument_access(pet, source, target, dep): + return True # if it is an intra iteration dependency, it is problematic if it belongs to a parent loop elif dep.intra_iteration_level > root_loop.get_nesting_level(pet): - tmp = root_loop.get_nesting_level(pet) - return True + tmp_nesting_level = root_loop.get_nesting_level(pet) + # check if metadata exists + if len(dep.metadata_intra_iteration_dep) != 0: + for t in dep.metadata_intra_iteration_dep: + if t in parent_loops: + return True + return False + else: + return True elif dep.dtype == DepType.WAW: # check WAW dependencies # handled by variable classification @@ -386,3 +431,32 @@ def __get_called_functions(pet: PEGraphX, root_loop: LoopNode) -> List[LineID]: # convert node ids of called functions to line ids return [pet.node_at(n).start_position() for n in called_functions] + +def __check_for_problematic_function_argument_access(pet: PEGraphX, source: NodeID, target: NodeID, dep: Dependency) -> bool: + """duplicates exists: do_all_detector <-> reduction_detector !""" + # check if the "same" function argument is accessed and it is a pointer type. + # if so, return True. Else. return false + + # find accessed function argument for source + source_pf = pet.get_parent_function(pet.node_at(source)) + source_accessed_pf_args = [a for a in source_pf.args if a.name == dep.var_name] + if len(source_accessed_pf_args) == 0: + return False + + # find accessed function argument for target + target_pf = pet.get_parent_function(pet.node_at(target)) + target_accessed_pf_args = [a for a in target_pf.args if a.name == dep.var_name] + if len(target_accessed_pf_args) == 0: + return False + + # check for overlap in accessed args + + for source_a in source_accessed_pf_args: + for target_a in target_accessed_pf_args: + if source_a == target_a: + # found overlap + # check for pointer type + if "*" in source_a.type: + return True + # not problematic + return False diff --git a/discopop_explorer/pattern_detectors/reduction_detector.py b/discopop_explorer/pattern_detectors/reduction_detector.py index 718302dfc..201fa2fab 100644 --- a/discopop_explorer/pattern_detectors/reduction_detector.py +++ b/discopop_explorer/pattern_detectors/reduction_detector.py @@ -8,7 +8,7 @@ from multiprocessing import Pool -from typing import List, cast, Set +from typing import List, cast, Set, Tuple from alive_progress import alive_bar # type: ignore @@ -22,7 +22,7 @@ LineID, DepType, EdgeType, - NodeID, + NodeID, MemoryRegion, Dependency, ) from ..utils import filter_for_hotspots, is_reduction_var, classify_loop_variables from ..variable import Variable @@ -142,6 +142,17 @@ def __detect_reduction(pet: PEGraphX, root: LoopNode) -> bool: parent_function_lineid = pet.get_parent_function(root).start_position() called_functions_lineids = __get_called_functions(pet, root) + # get variables which are defined inside the loop + defined_inside_loop: List[Tuple[Variable, Set[MemoryRegion]]] = [] + tmp_loop_variables = pet.get_variables(root_children_cus) + for var in tmp_loop_variables: + if ":" in var.defLine: + file_id = int(var.defLine.split(":")[0]) + def_line_num = int(var.defLine.split(":")[1]) + for rc_cu in root_children_cus: + if file_id == rc_cu.file_id and def_line_num >= rc_cu.start_line and def_line_num <= rc_cu.end_line: + defined_inside_loop.append((var, tmp_loop_variables[var])) + if __check_loop_dependencies( pet, root, @@ -152,6 +163,7 @@ def __detect_reduction(pet: PEGraphX, root: LoopNode) -> bool: fp, p, lp, + defined_inside_loop, parent_loops, parent_function_lineid, called_functions_lineids, @@ -176,9 +188,11 @@ def __check_loop_dependencies( first_privates: List[Variable], privates: List[Variable], last_privates: List[Variable], + defined_inside_loop: List[Tuple[Variable, Set[MemoryRegion]]], parent_loops: List[LineID], parent_function_lineid: LineID, called_functions_lineids: List[LineID], + ) -> bool: """Returns True, if dependencies between the respective subgraphs chave been found. Returns False otherwise, which results in the potential suggestion of a Reduction pattern.""" @@ -191,6 +205,11 @@ def __check_loop_dependencies( deps.update([(s, t, d) for s, t, d in pet.in_edges(n, EdgeType.DATA) if s in loop_children_ids]) deps.update([(s, t, d) for s, t, d in pet.out_edges(n, EdgeType.DATA) if t in loop_children_ids]) + # get memory regions which are defined inside the loop + memory_regions_defined_in_loop = set() + for var, mem_regs in defined_inside_loop: + memory_regions_defined_in_loop.update(mem_regs) + for source, target, dep in deps: # check if targeted variable is readonly inside loop if pet.is_readonly_inside_loop_body( @@ -207,6 +226,15 @@ def __check_loop_dependencies( if pet.is_loop_index(dep.var_name, loop_start_lines, root_children_cus): continue + # ignore dependencies where either source or sink do not lie within root_loop + if len(dep.metadata_source_ancestors) > 0 and len(dep.metadata_sink_ancestors) > 0: + if not ( + (root_loop.start_position() in dep.metadata_sink_ancestors) + and root_loop.start_position() in dep.metadata_source_ancestors + ): + tmp = root_loop.start_position() + continue + # targeted variable is not read-only if dep.dtype == DepType.INIT: continue @@ -224,14 +252,52 @@ def __check_loop_dependencies( else: # RAW does not target a reduction variable. # RAW problematic, if it is not an intra-iteration RAW. - if not dep.intra_iteration: + if ( + not dep.intra_iteration + and (dep.metadata_intra_iteration_dep is None or len(dep.metadata_intra_iteration_dep) == 0) + and parent_function_lineid + in (dep.metadata_intra_call_dep if dep.metadata_intra_call_dep is not None else []) + ) or ( + ( + False + if dep.metadata_inter_call_dep is None + else (len([cf for cf in called_functions_lineids if cf in dep.metadata_inter_call_dep]) > 0) + ) + and ( + False + if dep.metadata_inter_iteration_dep is None + else (len([t for t in parent_loops if t in dep.metadata_inter_iteration_dep]) > 0) + ) + ): return True elif dep.dtype == DepType.WAR: # check WAR dependencies # WAR problematic, if it is not an intra-iteration WAR and the variable is not private or firstprivate - if not dep.intra_iteration: + if ( + not dep.intra_iteration + and (dep.metadata_intra_iteration_dep is None or len(dep.metadata_intra_iteration_dep) == 0) + and parent_function_lineid + in (dep.metadata_intra_call_dep if dep.metadata_intra_call_dep is not None else []) + ) or ( + ( + False + if dep.metadata_inter_call_dep is None + else (len([cf for cf in called_functions_lineids if cf in dep.metadata_inter_call_dep]) > 0) + ) + and ( + False + if dep.metadata_inter_iteration_dep is None + else (len([t for t in parent_loops if t in dep.metadata_inter_iteration_dep]) > 0) + ) + ): if dep.var_name not in [v.name for v in first_privates + privates + last_privates]: - return True + # check if variable is defined inside loop + if dep.memory_region not in memory_regions_defined_in_loop: + return True + # check if the definitions of the accessed variable originates from a function call + if __check_for_problematic_function_argument_access(pet, source, target, dep): + pass + return True elif dep.dtype == DepType.WAW: # check WAW dependencies # handled by variable classification @@ -286,3 +352,32 @@ def __get_called_functions(pet: PEGraphX, root_loop: LoopNode) -> List[LineID]: # convert node ids of called functions to line ids return [pet.node_at(n).start_position() for n in called_functions] + +def __check_for_problematic_function_argument_access(pet: PEGraphX, source: NodeID, target: NodeID, dep: Dependency) -> bool: + """duplicates exists: do_all_detector <-> reduction_detector !""" + # check if the "same" function argument is accessed and it is a pointer type. + # if so, return True. Else. return false + + # find accessed function argument for source + source_pf = pet.get_parent_function(pet.node_at(source)) + source_accessed_pf_args = [a for a in source_pf.args if a.name == dep.var_name] + if len(source_accessed_pf_args) == 0: + return False + + # find accessed function argument for target + target_pf = pet.get_parent_function(pet.node_at(target)) + target_accessed_pf_args = [a for a in target_pf.args if a.name == dep.var_name] + if len(target_accessed_pf_args) == 0: + return False + + # check for overlap in accessed args + + for source_a in source_accessed_pf_args: + for target_a in target_accessed_pf_args: + if source_a == target_a: + # found overlap + # check for pointer type + if "*" in source_a.type: + return True + # not problematic + return False diff --git a/docs/setup/discopop.md b/docs/setup/discopop.md index d9208a088..c9f93f0af 100644 --- a/docs/setup/discopop.md +++ b/docs/setup/discopop.md @@ -33,6 +33,9 @@ where `` can consist of any combination of the following flags and - `-DDP_PTHREAD_COMPATIBILITY_MODE=[0|1]` – If your application uses PThreads, please specify this flag to serialize the calls to the DiscoPoP runtime functions. Note, however, that this can negatively influence the runtime of the profiling. - `-DDP_NUM_WORKERS=` – Specify the number of worker threads available for the dependency analysis during profiling. Default: `3` worker threads. `0` can be used to disable the creation of additional threads for the analysis. - `-DDP_HYBRID_PROFILING=[0|1]` – Enbale hybrid profiling. Default: `1`. +- `-DDP_CALLTREE_PROFILING=[0|1]` – Enable creation of a call tree during profiling to create extended dependency metadata for improved result quality. Negatively impacts profiling performance. +- `-DDP_MEMORY_REGION_DEALIASING=[0|1]`: Enable or disable the generation of dependency de-aliasing information. Reduces potential false positive parallelization suggestions, but increases the profiling overhead. + #### Development and debugging - `-DDP_RTLIB_VERBOSE=[0|1]` – Enable verbose output during profiling. diff --git a/rtlib/CMakeLists.txt b/rtlib/CMakeLists.txt index 30cca649f..21c26b1a1 100644 --- a/rtlib/CMakeLists.txt +++ b/rtlib/CMakeLists.txt @@ -17,6 +17,13 @@ set(DiscoPoP_SOURCES memory/MemoryManager.cpp memory/Signature.cpp + + calltree/CallTreeNode.cpp + calltree/CallTree.cpp + calltree/CallTreeGlobals.cpp + calltree/DependencyMetadata.cpp + calltree/MetaDataQueueElement.cpp + calltree/utils.cpp injected_functions/dp_add_bb_deps.cpp injected_functions/dp_alloca.cpp @@ -72,6 +79,22 @@ if(DEFINED DP_INTERNAL_TIMER) endif() endif() +if(DEFINED DP_CALLTREE_PROFILING) + if(NOT ${DP_CALLTREE_PROFILING} EQUAL 0) + target_compile_definitions(DiscoPoP_RT PUBLIC DP_CALLTREE_PROFILING=${DP_CALLTREE_PROFILING}) + message(STATUS "WARNING: DiscoPoP configuration: DP_CALLTREE_PROFILING enabled. Reduces profiling speed.") + endif() +endif() + + +if(DEFINED DP_MEMORY_REGION_DEALIASING) + if(NOT ${DP_MEMORY_REGION_DEALIASING} EQUAL 0) + target_compile_definitions(DiscoPoP_RT PUBLIC DP_MEMORY_REGION_DEALIASING=${DP_MEMORY_REGION_DEALIASING}) + message(STATUS "WARNING: DiscoPoP configuration: DP_MEMORY_REGION_DEALIASING enabled. Reduces profiling speed.") + endif() +endif() + + # end of compiler flags install(TARGETS DiscoPoP_RT ARCHIVE DESTINATION lib) diff --git a/rtlib/calltree/CallTree.cpp b/rtlib/calltree/CallTree.cpp new file mode 100644 index 000000000..56ae01849 --- /dev/null +++ b/rtlib/calltree/CallTree.cpp @@ -0,0 +1,94 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +# include "CallTree.hpp" + +namespace __dp +{ + CallTree::CallTree(){ + current = make_shared(); + } + + CallTree::~CallTree(){ + } + + unsigned int CallTree::get_node_count(){ + return call_tree_node_count.load(); + } + + std::shared_ptr CallTree::get_current_node_ptr(){ + return current; + } + + void CallTree::enter_function(unsigned int function_id){ + current = make_shared(current, CallTreeNodeType::Function, function_id, 0); + } + + void CallTree::enter_loop(unsigned int loop_id){ + current = make_shared(current, CallTreeNodeType::Loop, loop_id, 0); + } + + void CallTree::enter_iteration(unsigned int iteration_id){ + // identify loop id of nearest loop + shared_ptr node_ptr = get_current_node_ptr(); + if(! node_ptr){ + return; + } + unsigned int loop_id = 0; + while(node_ptr.get()->get_node_type() != CallTreeNodeType::Root){ + if(node_ptr.get()->get_node_type() == CallTreeNodeType::Loop){ + // found nearest loop node + loop_id = node_ptr.get()->get_loop_or_function_id(); + break; + } + // continue search with parent node + node_ptr = node_ptr.get()->get_parent_ptr(); + } + // create iteration node + current = make_shared(node_ptr, CallTreeNodeType::Iteration, loop_id, iteration_id); + } + + void CallTree::exit_function(){ + // set current to the parent of the closest function + shared_ptr node_ptr = get_current_node_ptr(); + if(! node_ptr){ + return; + } + while(node_ptr->get_node_type() != CallTreeNodeType::Root){ + if(node_ptr->get_node_type() == CallTreeNodeType::Function){ + // found closest function node + break; + } + // continue search with parent + node_ptr = node_ptr->get_parent_ptr(); + } + current = node_ptr->get_parent_ptr(); + } + + void CallTree::exit_loop(){ + // set current to the parent of the closest loop + shared_ptr node_ptr = get_current_node_ptr(); + if(! node_ptr){ + return; + } + while(node_ptr->get_node_type() != CallTreeNodeType::Root){ + if(node_ptr->get_node_type() == CallTreeNodeType::Loop){ + // found closest loop node + break; + } + // continue search with parent + node_ptr = node_ptr->get_parent_ptr(); + } + current = node_ptr->get_parent_ptr(); + } + +} // namespace __dp diff --git a/rtlib/calltree/CallTree.hpp b/rtlib/calltree/CallTree.hpp new file mode 100644 index 000000000..1e9f561a4 --- /dev/null +++ b/rtlib/calltree/CallTree.hpp @@ -0,0 +1,42 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#pragma once + +#include "../DPUtils.hpp" + +#include +#include "CallTreeNode.hpp" +#include "CallTreeGlobals.hpp" +#include + +namespace __dp { + +class CallTree { +public: + CallTree(); + ~CallTree(); + void enter_function(unsigned int function_id); + void exit_function(); + void enter_loop(unsigned int loop_id); + void exit_loop(); + void enter_iteration(unsigned int iteration_id); + // exit_iteration not possible, as determining the iteration end is not trivial + unsigned int get_node_count(); + std::shared_ptr get_current_node_ptr(); + +private: + std::shared_ptr current; + +}; + +} // namespace __dp diff --git a/rtlib/calltree/CallTreeGlobals.cpp b/rtlib/calltree/CallTreeGlobals.cpp new file mode 100644 index 000000000..74abae74a --- /dev/null +++ b/rtlib/calltree/CallTreeGlobals.cpp @@ -0,0 +1,15 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#include "CallTreeGlobals.hpp" + +std::atomic call_tree_node_count{0}; \ No newline at end of file diff --git a/rtlib/calltree/CallTreeGlobals.hpp b/rtlib/calltree/CallTreeGlobals.hpp new file mode 100644 index 000000000..3e11a3960 --- /dev/null +++ b/rtlib/calltree/CallTreeGlobals.hpp @@ -0,0 +1,17 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#pragma once + +#include + +extern std::atomic call_tree_node_count; \ No newline at end of file diff --git a/rtlib/calltree/CallTreeNode.cpp b/rtlib/calltree/CallTreeNode.cpp new file mode 100644 index 000000000..ac3684073 --- /dev/null +++ b/rtlib/calltree/CallTreeNode.cpp @@ -0,0 +1,74 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#include "CallTreeNode.hpp" +#include + +namespace __dp { + +CallTreeNode::CallTreeNode(){ + parent_ptr = nullptr; + type = CallTreeNodeType::Root; + loop_or_function_id = 0; + iteration_id = 0; + call_tree_node_count += 1; +} + +CallTreeNode::CallTreeNode(shared_ptr parent_ptr, CallTreeNodeType type, unsigned int loop_or_function_id, unsigned int arg_iteration_id): parent_ptr(parent_ptr), type(type), loop_or_function_id(loop_or_function_id){ + if(type == CallTreeNodeType::Iteration){ + iteration_id = arg_iteration_id; + } + else{ + iteration_id = 0; + } + call_tree_node_count += 1; +} + +CallTreeNode::~CallTreeNode(){ + call_tree_node_count -= 1; +} + +bool CallTreeNode::operator==(const CallTreeNode& other) const{ + // && (iteration_id == other.iteration_id) ignore loop id + if((type == other.type) && (loop_or_function_id == other.loop_or_function_id) ){ + if(parent_ptr && other.parent_ptr){ + if(parent_ptr.get() == other.parent_ptr.get()){ + return true; + } + return false; + } + return false; + } + else{ + return false; + } +} + +shared_ptr CallTreeNode::get_parent_ptr(){ + return parent_ptr; +} + +CallTreeNodeType CallTreeNode::get_node_type(){ + return type; +} + +unsigned int CallTreeNode::get_loop_or_function_id(){ + return loop_or_function_id; +} + +unsigned int CallTreeNode::get_iteration_id(){ + return iteration_id; +} + +} // namespace __dp + + diff --git a/rtlib/calltree/CallTreeNode.hpp b/rtlib/calltree/CallTreeNode.hpp new file mode 100644 index 000000000..4a1f5b214 --- /dev/null +++ b/rtlib/calltree/CallTreeNode.hpp @@ -0,0 +1,41 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#pragma once + +#include "../DPUtils.hpp" +#include "CallTreeNodeType.hpp" +#include "CallTreeGlobals.hpp" + +#include + +namespace __dp { + +class CallTreeNode { +public: + CallTreeNode(); + CallTreeNode(shared_ptr parent_ptr, CallTreeNodeType type, unsigned int loop_or_function_id, unsigned int iteration_id); + ~CallTreeNode(); + bool operator==(const CallTreeNode& other) const; + shared_ptr get_parent_ptr(); + CallTreeNodeType get_node_type(); + unsigned int get_loop_or_function_id(); + unsigned int get_iteration_id(); // only relevant for iteration type nodes, else always 0 +private: + CallTreeNodeType type; + unsigned int loop_or_function_id; // id of the loop or function that is represented by the current node + unsigned int iteration_id; + shared_ptr parent_ptr; + atomic* node_count_ptr; +}; + +} // namespace __dp diff --git a/rtlib/calltree/CallTreeNodeType.hpp b/rtlib/calltree/CallTreeNodeType.hpp new file mode 100644 index 000000000..eeb8d196f --- /dev/null +++ b/rtlib/calltree/CallTreeNodeType.hpp @@ -0,0 +1,24 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#pragma once + +#include "../DPUtils.hpp" + +namespace __dp { + enum class CallTreeNodeType { + Function, + Loop, + Iteration, + Root + }; +} // namespace __dp diff --git a/rtlib/calltree/DependencyMetadata.cpp b/rtlib/calltree/DependencyMetadata.cpp new file mode 100644 index 000000000..433b04578 --- /dev/null +++ b/rtlib/calltree/DependencyMetadata.cpp @@ -0,0 +1,91 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#include "DependencyMetadata.hpp" + +namespace __dp{ + DependencyMetadata::DependencyMetadata(MetaDataQueueElement mdqe, + std::set arg_intra_call_dependencies, std::set arg_intra_iteration_dependencies, + std::set arg_inter_call_dependencies, std::set arg_inter_iteration_dependencies, + std::set arg_sink_ancestors, std::set arg_source_ancestors): + type(mdqe.type), sink(mdqe.sink), source(mdqe.source), var(mdqe.var), AAvar(mdqe.AAvar), + intra_call_dependencies(arg_intra_call_dependencies), intra_iteration_dependencies(arg_intra_iteration_dependencies), + inter_call_dependencies(arg_inter_call_dependencies), inter_iteration_dependencies(arg_inter_iteration_dependencies), + sink_ancestors(arg_sink_ancestors), source_ancestors(arg_source_ancestors) + { + } + + bool DependencyMetadata::operator==(const DependencyMetadata& other) const{ + return (type == other.type) && (sink == other.sink) && (source == other.source) && (var == other.var) + && (AAvar == other.AAvar) && (intra_call_dependencies == other.intra_call_dependencies) && (intra_iteration_dependencies == other.intra_iteration_dependencies) + && (inter_call_dependencies == other.inter_call_dependencies) && (inter_iteration_dependencies == other.inter_iteration_dependencies) + && (sink_ancestors == other.sink_ancestors) && (source_ancestors == other.source_ancestors); + } + + string DependencyMetadata::toString(){ + string result = ""; + switch (type) + { + case RAW: + result += "RAW "; + break; + case WAR: + result += "WAR "; + break; + case WAW: + result += "WAW "; + break; + case INIT: + result += "INIT "; + break; + default: + break; + } + result += dputil::decodeLID(sink) + " "; + result += dputil::decodeLID(source) + " "; + result += var; + result += " "; + result += AAvar + " "; + result += "IAC["; + for(auto iac : intra_call_dependencies){ + result += dputil::decodeLID(iac) + ","; + } + result += "] "; + result += "IAI["; + for(auto iai : intra_iteration_dependencies){ + result += dputil::decodeLID(iai) + ","; + } + result += "] "; + result += "IEC["; + for(auto iec : inter_call_dependencies){ + result += dputil::decodeLID(iec) + ","; + } + result += "] "; + result += "IEI["; + for(auto iei : inter_iteration_dependencies){ + result += dputil::decodeLID(iei) + ","; + } + result += "] "; + result += "SINK_ANC["; + for(auto sink_anc : sink_ancestors){ + result += dputil::decodeLID(sink_anc) + ","; + } + result += "] "; + result += "SOURCE_ANC["; + for(auto source_anc : source_ancestors){ + result += dputil::decodeLID(source_anc) + ","; + } + result += "] "; + return result; + } + +} // namespace __dp diff --git a/rtlib/calltree/DependencyMetadata.hpp b/rtlib/calltree/DependencyMetadata.hpp new file mode 100644 index 000000000..e00a7be16 --- /dev/null +++ b/rtlib/calltree/DependencyMetadata.hpp @@ -0,0 +1,71 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#pragma once + +#include "../DPUtils.hpp" +#include "../iFunctionsTypes.hpp" +#include "MetaDataQueueElement.hpp" + +namespace __dp +{ + +class DependencyMetadata{ +public: + DependencyMetadata(MetaDataQueueElement mdqe, std::set arg_intra_call_dependencies, std::set arg_intra_iteration_dependencies, std::set arg_inter_call_dependencies, std::set arg_inter_iteration_dependencies, std::set sink_ancestors, std::set arg_source_ancestors); + DependencyMetadata(){} + bool operator==(const DependencyMetadata& other) const; + depType type; + LID sink; + LID source; + const char* var; + string AAvar; + std::set intra_call_dependencies; + std::set intra_iteration_dependencies; + std::set inter_call_dependencies; + std::set inter_iteration_dependencies; + std::set sink_ancestors; + std::set source_ancestors; + string toString(); +}; + +} // namespace __dp + +template <> +struct std::hash<__dp::DependencyMetadata> +{ + std::size_t operator()(const __dp::DependencyMetadata& k) const + { + using boost::hash_value; + using boost::hash_combine; + + // Start with a hash value of 0 . + std::size_t seed = 0; + + // Modify 'seed' by XORing and bit-shifting in + // one member of 'Key' after the other: + hash_combine(seed,hash_value(k.type)); + hash_combine(seed,hash_value(k.sink)); + hash_combine(seed,hash_value(k.source)); + hash_combine(seed,hash_value(k.var)); + hash_combine(seed,hash_value(k.AAvar)); + hash_combine(seed,hash_value(k.intra_call_dependencies)); + hash_combine(seed,hash_value(k.intra_iteration_dependencies)); + hash_combine(seed,hash_value(k.inter_call_dependencies)); + hash_combine(seed,hash_value(k.inter_iteration_dependencies)); + hash_combine(seed,hash_value(k.sink_ancestors)); + hash_combine(seed,hash_value(k.source_ancestors)); + + // Return the result. + return seed; + } +}; \ No newline at end of file diff --git a/rtlib/calltree/MetaDataQueue.cpp b/rtlib/calltree/MetaDataQueue.cpp new file mode 100644 index 000000000..c49dd000b --- /dev/null +++ b/rtlib/calltree/MetaDataQueue.cpp @@ -0,0 +1,139 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#include "MetaDataQueue.hpp" + +#include "../../share/include/timer.hpp" +#include "../iFunctionsGlobals.hpp" + +namespace __dp +{ + // TODO + MetaDataQueue::MetaDataQueue(int worker_count){ + finalize_queue = false; + + if(worker_count < 1){ + worker_count = 1; + // serial execution + } + for (size_t i = 0; i < worker_count; ++i) + { + // FIX: unnötiges move + threads.emplace_back(std::thread(processQueue, this)); + } + } + + MetaDataQueue::~MetaDataQueue(){ + } + + void MetaDataQueue::insert(MetaDataQueueElement && mdqe){ + const auto calltree_timer = Timer(timers, TimerRegion::ADD_DEP_CALLTREE_REGISTER_METADATAQUEUEELEMENT); + if(finalize_queue){ + return; + } + + // drop the element, if it was already enqueued + bool mdqe_already_enqueued = false; + // check if an equal element was already processed + enqueued_mtx.lock(); + + //mdqe_already_enqueued = enqueued.find(mdqe) != enqueued.end(); + auto contained = enqueued.insert(mdqe); + mdqe_already_enqueued = ! contained.second; + enqueued_mtx.unlock(); + + mdqe_already_enqueued = ! contained.second; + + if(mdqe_already_enqueued){ + // element already inserted to queue before + return; + } + + // insert mdqe to queue + std::lock_guard lock(queue_mtx); + queue.push(std::move(mdqe)); + //cout << "insert: " << mdqe.toString() << "\n"; + } + + int MetaDataQueue::get_size(){ + std::lock_guard lock(queue_mtx); + return queue.size(); + } + + void MetaDataQueue::blocking_finalize_queue(){ + finalize_queue = true; + // join threads + for (auto &t : threads){ + t.join(); + } + + // print results + cout << "RESULTS: \n"; + for(auto dmd: results){ + cout << dmd.toString() << "\n"; + } + + // output to file + std::cout << "Outputting dependency metadata... "; + std::ifstream ifile; + std::string line; + std::ofstream ofile; + std::string tmp(getenv("DOT_DISCOPOP_PROFILER")); + // output information about the loops + tmp += "/dependency_metadata.txt"; + ofile.open(tmp.data()); + ofile << "# IAC : intra-call-dependency \n"; + ofile << "# IAI : intra-iteration-dependency \n"; + ofile << "# IEC : inter-call-dependency \n"; + ofile << "# IEI : inter-iteration-dependency \n"; + ofile << "# SINK_ANC : entered functions and loops for sink location \n"; + ofile << "# SOURCE_ANC : entered functions and loops for source location \n"; + ofile << "# Format: \n"; + for (auto dmd : results) { + ofile << dmd.toString() << "\n"; + } + ofile.close(); + } + + void processQueue(MetaDataQueue* mdq){ + const auto calltree_timer = Timer(timers, TimerRegion::METADATAQUEUE_PROCESSQUEUE); + while(!(mdq->finalize_queue && (mdq->get_size() == 0))){ + mdq->queue_mtx.lock(); + // try fetching an element from the queue + if(mdq->queue.size() != 0){ + const auto calltree_timer = Timer(timers, TimerRegion::METADATAQUEUE_PROCESSQUEUE_FETCH); + + // fetch element from the queue + MetaDataQueueElement mdqe = mdq->queue.front(); + mdq->queue.pop(); + //std::cout << "fetched. Remaining: " << mdq->queue.size() << "\n"; + // release queue lock + mdq->queue_mtx.unlock(); + + // process element + //DependencyMetadata metadata = processQueueElement(mdqe); + + // save metadata to results + mdq->results_mtx.lock(); + mdq->results.push_back(metadata); + mdq->results_mtx.unlock(); + } + else{ + // release lock + mdq->queue_mtx.unlock(); + // sleep 5 milliseconds + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + } + } + +} // namespace __dp \ No newline at end of file diff --git a/rtlib/calltree/MetaDataQueue.hpp b/rtlib/calltree/MetaDataQueue.hpp new file mode 100644 index 000000000..fb3ed89ed --- /dev/null +++ b/rtlib/calltree/MetaDataQueue.hpp @@ -0,0 +1,54 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "MetaDataQueueElement.hpp" +#include "DependencyMetadata.hpp" + + +namespace __dp +{ +class MetaDataQueue{ +public: + MetaDataQueue(int worker_count); // worker_count: minimum 1 + ~MetaDataQueue(); + void insert(MetaDataQueueElement && mdqe); // TODO optimization potential, do not use copies here! + const std::set& get_metadata(); + int get_size(); + void blocking_finalize_queue(); + bool finalize_queue; + std::mutex queue_mtx; + std::queue queue; + std::mutex results_mtx; + std::vector results; + std::mutex enqueued_mtx; + std::unordered_set enqueued; + +private: + std::vector threads; + +}; + +static void processQueue(MetaDataQueue* mdq); +static DependencyMetadata processQueueElement(MetaDataQueueElement mdqe); + + + +} // namespace __dp \ No newline at end of file diff --git a/rtlib/calltree/MetaDataQueueElement.cpp b/rtlib/calltree/MetaDataQueueElement.cpp new file mode 100644 index 000000000..be32447c1 --- /dev/null +++ b/rtlib/calltree/MetaDataQueueElement.cpp @@ -0,0 +1,55 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#include "MetaDataQueueElement.hpp" + +namespace __dp +{ + MetaDataQueueElement::MetaDataQueueElement(depType arg_type, LID arg_sink, LID arg_source, const char* arg_var, string arg_AAvar, shared_ptr arg_sink_ctn, shared_ptr arg_source_ctn): + type(arg_type), sink(arg_sink), source(arg_source), var(arg_var), AAvar(arg_AAvar), sink_ctn(arg_sink_ctn), source_ctn(arg_source_ctn){ + } + + bool MetaDataQueueElement::operator==(const MetaDataQueueElement& other) const{ + return (type == other.type) && (sink == other.sink) && (source == other.source) && (var == other.var) + && (AAvar == other.AAvar) && (sink_ctn->get_node_type() == other.sink_ctn->get_node_type()) && (sink_ctn->get_loop_or_function_id() == other.sink_ctn->get_loop_or_function_id()) && (sink_ctn->get_iteration_id() == other.sink_ctn->get_iteration_id()) + && (source_ctn->get_node_type() == other.source_ctn->get_node_type()) && (source_ctn->get_loop_or_function_id() == other.source_ctn->get_loop_or_function_id()) && (source_ctn->get_iteration_id() == other.source_ctn->get_iteration_id()); + } + + string MetaDataQueueElement::toString(){ + string result = "MDQE( "; + switch (type) + { + case RAW: + result += "RAW "; + break; + case WAR: + result += "WAR "; + break; + case WAW: + result += "WAW "; + break; + default: + break; + } + result += dputil::decodeLID(sink) + " - " + dputil::decodeLID(source) + " "; + result += var; + result += " "; + result += AAvar + " "; + result += "sink_ctn: " + to_string(sink_ctn->get_loop_or_function_id()) + " "; + result += "it: " + to_string(sink_ctn->get_iteration_id()) + " "; + result += "source_ctn: " + to_string(source_ctn->get_loop_or_function_id()) + " "; + result += "it: " + to_string(source_ctn->get_iteration_id()) + " "; + result += ")"; + return result; + } + +} // namespace __dp \ No newline at end of file diff --git a/rtlib/calltree/MetaDataQueueElement.hpp b/rtlib/calltree/MetaDataQueueElement.hpp new file mode 100644 index 000000000..b77966fe4 --- /dev/null +++ b/rtlib/calltree/MetaDataQueueElement.hpp @@ -0,0 +1,67 @@ +/* + * This file is part of the DiscoPoP software + * (http://www.discopop.tu-darmstadt.de) + * + * Copyright (c) 2020, Technische Universitaet Darmstadt, Germany + * + * This software may be modified and distributed under the terms of + * the 3-Clause BSD License. See the LICENSE file in the package base + * directory for details. + * + */ + +#pragma once + +#include "../DPUtils.hpp" +#include "CallTreeNode.hpp" +#include +#include +#include "../iFunctionsTypes.hpp" + +namespace __dp +{ + +class MetaDataQueueElement{ +public: + MetaDataQueueElement(depType arg_type, LID arg_sink, LID arg_source, const char* arg_var, string arg_AAvar, shared_ptr arg_sink_ctn, shared_ptr arg_source_ctn); + bool operator==(const MetaDataQueueElement& other) const; + string toString(); + depType type; + LID sink; + LID source; + const char* var; + string AAvar; + shared_ptr sink_ctn; + shared_ptr source_ctn; +}; +} + +template <> +struct std::hash<__dp::MetaDataQueueElement> +{ + std::size_t operator()(const __dp::MetaDataQueueElement& k) const + { + using boost::hash_value; + using boost::hash_combine; + + // Start with a hash value of 0 . + std::size_t seed = 0; + + // Modify 'seed' by XORing and bit-shifting in + // one member of 'Key' after the other: + hash_combine(seed,hash_value(k.type)); + hash_combine(seed,hash_value(k.sink)); + hash_combine(seed,hash_value(k.source)); + hash_combine(seed,hash_value(k.var)); + hash_combine(seed,hash_value(k.AAvar)); + hash_combine(seed,hash_value(k.sink_ctn->get_node_type())); + hash_combine(seed,hash_value(k.sink_ctn->get_loop_or_function_id())); + hash_combine(seed,hash_value(k.sink_ctn->get_iteration_id())); + hash_combine(seed,hash_value(k.source_ctn->get_node_type())); + hash_combine(seed,hash_value(k.source_ctn->get_loop_or_function_id())); + hash_combine(seed,hash_value(k.source_ctn->get_iteration_id())); + + // Return the result. + return seed; + } +}; \ No newline at end of file diff --git a/rtlib/calltree/utils.cpp b/rtlib/calltree/utils.cpp new file mode 100644 index 000000000..6004f587e --- /dev/null +++ b/rtlib/calltree/utils.cpp @@ -0,0 +1,144 @@ +#include "utils.hpp" +#include "../../share/include/timer.hpp" +#include "../iFunctionsGlobals.hpp" +#include + +namespace __dp{ + +DependencyMetadata processQueueElement(MetaDataQueueElement mdqe){ + const auto calltree_timer = Timer(timers, TimerRegion::PROCESSQUEUEELEMENT); + + //cout << "processing " << mdqe.toString() << "\n"; + + shared_ptr curr_ctn_node; + // collect ancestors of sink_ctn + curr_ctn_node = mdqe.sink_ctn; + std::set> sink_ctn_ancestors; + std::set sink_ancestor_loops_and_functions; + if(curr_ctn_node){ + while(curr_ctn_node->get_node_type() != CallTreeNodeType::Root){ + if(curr_ctn_node->get_node_type() == CallTreeNodeType::Loop){ + // ignore for metadata calculation, but keep for ancestor reporting + sink_ancestor_loops_and_functions.insert(curr_ctn_node->get_loop_or_function_id()); + } + else{ + sink_ctn_ancestors.insert(curr_ctn_node); + // ancestor reporting + if(curr_ctn_node->get_node_type() == CallTreeNodeType::Function){ + sink_ancestor_loops_and_functions.insert(curr_ctn_node->get_loop_or_function_id()); + } + } + + curr_ctn_node = curr_ctn_node->get_parent_ptr(); + if(!curr_ctn_node){ + break; + } + } + } + + // collect ancestors of source_ctn + curr_ctn_node = mdqe.source_ctn; + std::set> source_ctn_ancestors; + std::set source_ancestor_loops_and_functions; + if(curr_ctn_node){ + while(curr_ctn_node->get_node_type() != CallTreeNodeType::Root){ + if(curr_ctn_node->get_node_type() == CallTreeNodeType::Loop){ + // ignore for metadata calculation, but keep for ancestor reporting + source_ancestor_loops_and_functions.insert(curr_ctn_node->get_loop_or_function_id()); + } + else{ + source_ctn_ancestors.insert(curr_ctn_node); + // ancestor reporting + if(curr_ctn_node->get_node_type() == CallTreeNodeType::Function){ + source_ancestor_loops_and_functions.insert(curr_ctn_node->get_loop_or_function_id()); + } + } + + curr_ctn_node = curr_ctn_node->get_parent_ptr(); + if(!curr_ctn_node){ + break; + } + } + } + + // determine common ancestors + std::set> common_ancestors; + for(auto sink_anc : sink_ctn_ancestors){ + for(auto source_anc : source_ctn_ancestors){ + if(sink_anc == source_anc){ + common_ancestors.insert(sink_anc); + } + } + } + + // determine disjoint ancestors + std::set> disjoint_sink_ancestors; + std::set> disjoint_source_ancestors; + for(auto sink_anc : sink_ctn_ancestors){ + bool contained = false; + for(auto source_anc : source_ctn_ancestors){ + if(sink_anc == source_anc){ + contained = true; + break; + } + } + if(!contained){ + disjoint_sink_ancestors.insert(sink_anc); + } + } + for(auto source_anc : source_ctn_ancestors){ + bool contained = false; + for(auto sink_anc : sink_ctn_ancestors){ + if(source_anc == sink_anc){ + contained = true; + break; + } + } + if(!contained){ + disjoint_source_ancestors.insert(source_anc); + } + } + //cout << "common: " << common_ancestors.size() << " disjoint sink: " << disjoint_sink_ancestors.size() << " source: " << disjoint_source_ancestors.size() << "\n"; + + // identify intra_call and intra_iteration dependencies + std::set intra_call_dependencies; + std::set intra_iteration_dependencies; + for(auto common_anc : common_ancestors){ + if(common_anc->get_node_type() == CallTreeNodeType::Function){ + intra_call_dependencies.insert(common_anc->get_loop_or_function_id()); + } + else if(common_anc->get_node_type() == CallTreeNodeType::Iteration){ + intra_iteration_dependencies.insert(common_anc->get_loop_or_function_id()); + } + } + //cout << "intra_call: " << intra_call_dependencies.size() << " intra_iteration: " << intra_iteration_dependencies.size() << "\n"; + + // identify inter_call and inter_iteration dependencies + std::set inter_call_dependencies; + std::set inter_iteration_dependencies; + for(auto disjoint_sink_anc : disjoint_sink_ancestors){ + for(auto disjoint_source_anc : disjoint_source_ancestors){ + // check for inter call dependencies + if((disjoint_sink_anc->get_node_type() == CallTreeNodeType::Function ) + && (disjoint_source_anc->get_node_type() == CallTreeNodeType::Function) + && (disjoint_sink_anc->get_loop_or_function_id() == disjoint_source_anc->get_loop_or_function_id())){ + inter_call_dependencies.insert(disjoint_sink_anc->get_loop_or_function_id()); + } + // check for inter iteration dependencies + if((disjoint_sink_anc->get_node_type() == CallTreeNodeType::Iteration) + && (disjoint_source_anc->get_node_type() == CallTreeNodeType::Iteration) + && (disjoint_sink_anc->get_parent_ptr() == disjoint_source_anc->get_parent_ptr()) + && (disjoint_sink_anc->get_parent_ptr()->get_node_type() == CallTreeNodeType::Loop) + ){ + inter_iteration_dependencies.insert(disjoint_sink_anc->get_loop_or_function_id()); + } + } + } + //cout << "inter_call: " << inter_call_dependencies.size() << " inter_iteration: " << inter_iteration_dependencies.size() << "\n"; + + return DependencyMetadata(mdqe, intra_call_dependencies, intra_iteration_dependencies, + inter_call_dependencies, inter_iteration_dependencies, + sink_ancestor_loops_and_functions, source_ancestor_loops_and_functions); + } + +} // namespace __dp \ No newline at end of file diff --git a/rtlib/calltree/utils.hpp b/rtlib/calltree/utils.hpp new file mode 100644 index 000000000..e8f3739eb --- /dev/null +++ b/rtlib/calltree/utils.hpp @@ -0,0 +1,6 @@ +#include "DependencyMetadata.hpp" +#include "MetaDataQueueElement.hpp" + +namespace __dp{ + DependencyMetadata processQueueElement(MetaDataQueueElement mdqe); +} // namespace __dp diff --git a/rtlib/iFunctions.cpp b/rtlib/iFunctions.cpp index 14dad89ff..5195172bd 100644 --- a/rtlib/iFunctions.cpp +++ b/rtlib/iFunctions.cpp @@ -22,6 +22,7 @@ #include "memory/PerfectShadow.hpp" #include "memory/ShadowMemory.hpp" #include "memory/Signature.hpp" +#include "calltree/utils.hpp" #include #include @@ -55,8 +56,15 @@ namespace __dp { /******* Helper functions *******/ + +#if DP_CALLTREE_PROFILING +void addDep(depType type, LID curr, LID depOn, const char *var, + string AAvar, ADDR addr, std::unordered_map>* thread_private_write_addr_to_call_tree_node_map, std::unordered_map>* thread_private_read_addr_to_call_tree_node_map){ +#else void addDep(depType type, LID curr, LID depOn, const char *var, - string AAvar, ADDR addr) { + string AAvar, ADDR addr){ +#endif + #ifdef DP_INTERNAL_TIMER const auto timer = Timer(timers, TimerRegion::ADD_DEP); #endif @@ -82,6 +90,52 @@ void addDep(depType type, LID curr, LID depOn, const char *var, posInDeps->second->insert(Dep(type, depOn, var, AAvar)); } + +#if DP_CALLTREE_PROFILING + // register dependency for call_tree based metadata calculation + DependencyMetadata dmd; + switch(type){ + case RAW: + // register metadata calculation + //cout << "Register metadata calculation: RAW " << decodeLID(curr) << " " << decodeLID(depOn) << " " << var << " (" << (*thread_private_write_addr_to_call_tree_node_map)[addr]->get_loop_or_function_id() << " , " << (*thread_private_write_addr_to_call_tree_node_map)[addr]->get_iteration_id() << ") " << " (" << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_loop_or_function_id() << " , " << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_iteration_id() << ")\n"; + + // process directly + dmd = processQueueElement(MetaDataQueueElement(type, curr, depOn, var, AAvar, (*thread_private_read_addr_to_call_tree_node_map)[addr], (*thread_private_write_addr_to_call_tree_node_map)[addr])); + dependency_metadata_results_mtx->lock(); + dependency_metadata_results->insert(dmd); + dependency_metadata_results_mtx->unlock(); + + //metadata_queue->insert(); // optimization potential: do not use copies here! + break; + case WAR: + // update write + // register metadata calculation + //cout << "Register metadata calculation: WAR " << decodeLID(curr) << " " << decodeLID(depOn) << " " << var << " (" << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_loop_or_function_id() << " , " << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_iteration_id() << ") " << " (" << (*thread_private_write_addr_to_call_tree_node_map)[addr]->get_loop_or_function_id() << " , " << (*thread_private_write_addr_to_call_tree_node_map)[addr]->get_iteration_id() << ")\n"; + + dmd = processQueueElement(MetaDataQueueElement(type, curr, depOn, var, AAvar, (*thread_private_write_addr_to_call_tree_node_map)[addr], (*thread_private_read_addr_to_call_tree_node_map)[addr])); + dependency_metadata_results_mtx->lock(); + dependency_metadata_results->insert(dmd); + dependency_metadata_results_mtx->unlock(); + // metadata_queue->insert(); // optimization potential: do not use copies here! + break; + case WAW: + // register metadata calculation + //cout << "Register metadata calculation: WAW " << decodeLID(curr) << " " << decodeLID(depOn) << " " << var << " (" << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_loop_or_function_id() << " , " << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_iteration_id() << ") " << " (" << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_loop_or_function_id() << " , " << (*thread_private_read_addr_to_call_tree_node_map)[addr]->get_iteration_id() << ")\n"; + dmd = processQueueElement(MetaDataQueueElement(type, curr, depOn, var, AAvar, (*thread_private_write_addr_to_call_tree_node_map)[addr], (*thread_private_write_addr_to_call_tree_node_map)[addr])); + dependency_metadata_results_mtx->lock(); + dependency_metadata_results->insert(dmd); + dependency_metadata_results_mtx->unlock(); + //metadata_queue->insert(); // optimization potential: do not use copies here! + break; + case INIT: + break; + default: + break; + } + +#endif + + if (DP_DEBUG) { cout << "inserted dep [" << decodeLID(curr) << ", "; switch (type) { @@ -325,7 +379,12 @@ void mergeDeps() { pthread_mutex_unlock(&allDepsLock); } +#if DP_CALLTREE_PROFILING +void analyzeSingleAccess(__dp::AbstractShadow *SMem, __dp::AccessInfo &access, std::unordered_map>* thread_private_write_addr_to_call_tree_node_map, std::unordered_map>* thread_private_read_addr_to_call_tree_node_map) { +#else void analyzeSingleAccess(__dp::AbstractShadow *SMem, __dp::AccessInfo &access) { +#endif + // analyze data dependences #ifdef DP_INTERNAL_TIMER const auto timer = Timer(timers, TimerRegion::ANALYZE_SINGLE_ACCESS); @@ -335,6 +394,11 @@ void analyzeSingleAccess(__dp::AbstractShadow *SMem, __dp::AccessInfo &access) { // hybrid analysis if (access.skip) { SMem->insertToRead(access.addr, access.lid); +#if DP_CALLTREE_PROFILING + //cout << "Acc1 " << access.addr << " " << access.call_tree_node_ptr << "\n"; + (*thread_private_read_addr_to_call_tree_node_map)[access.addr] = access.call_tree_node_ptr; + //cout << "Access read succ\n"; +#endif return; } // End HA @@ -342,28 +406,67 @@ void analyzeSingleAccess(__dp::AbstractShadow *SMem, __dp::AccessInfo &access) { if (lastWrite != 0) { // RAW SMem->insertToRead(access.addr, access.lid); +#if DP_CALLTREE_PROFILING + //cout << "Acc2 " << access.addr << " " << access.call_tree_node_ptr << "\n"; + (*thread_private_read_addr_to_call_tree_node_map)[access.addr] = access.call_tree_node_ptr; + //cout << "Access read succ\n"; +#endif +#if DP_CALLTREE_PROFILING + addDep(RAW, access.lid, lastWrite, access.var, access.AAvar, + access.addr, thread_private_write_addr_to_call_tree_node_map, thread_private_read_addr_to_call_tree_node_map); +#else addDep(RAW, access.lid, lastWrite, access.var, access.AAvar, access.addr); +#endif } } else { sigElement lastWrite = SMem->insertToWrite(access.addr, access.lid); +#if DP_CALLTREE_PROFILING + //cout << "Acc3 " << access.addr << " " << access.call_tree_node_ptr << "\n"; + //cout << "Acc3-0: " << write_addr_to_call_tree_node_map << "\n"; + + //cout << "Acc3-2 " << write_addr_to_call_tree_node_map << "\n"; + + (*thread_private_write_addr_to_call_tree_node_map)[access.addr] = access.call_tree_node_ptr; + //cout << "Access write succ\n"; +#endif if (lastWrite == 0) { // INIT +#if DP_CALLTREE_PROFILING + addDep(INIT, access.lid, 0, access.var, access.AAvar, + access.addr, thread_private_write_addr_to_call_tree_node_map, thread_private_read_addr_to_call_tree_node_map); +#else addDep(INIT, access.lid, 0, access.var, access.AAvar, access.addr); +#endif } else { sigElement lastRead = SMem->testInRead(access.addr); if (lastRead != 0) { // WAR +#if DP_CALLTREE_PROFILING + addDep(WAR, access.lid, lastRead, access.var, access.AAvar, + access.addr, thread_private_write_addr_to_call_tree_node_map, thread_private_read_addr_to_call_tree_node_map); +#else addDep(WAR, access.lid, lastRead, access.var, access.AAvar, access.addr); +#endif // Clear intermediate read ops SMem->insertToRead(access.addr, 0); +#if DP_CALLTREE_PROFILING + //cout << "Acc4 " << access.addr << " " << access.call_tree_node_ptr << "\n"; + (*thread_private_read_addr_to_call_tree_node_map)[access.addr] = access.call_tree_node_ptr; + //cout << "Access read succ\n"; +#endif } else { // WAW +#if DP_CALLTREE_PROFILING + addDep(WAW, access.lid, lastWrite, access.var, access.AAvar, + access.addr, thread_private_write_addr_to_call_tree_node_map, thread_private_read_addr_to_call_tree_node_map); +#else addDep(WAW, access.lid, lastWrite, access.var, access.AAvar, access.addr); +#endif } } } @@ -382,6 +485,10 @@ void *analyzeDeps(void *arg) { SMem = new ShadowMemory(SIG_ELEM_BIT, SIG_NUM_ELEM, SIG_NUM_HASH); } myMap = new depMap(); +#if DP_CALLTREE_PROFILING + std::unordered_map>* thread_private_write_addr_to_call_tree_node_map = new std::unordered_map>(); + std::unordered_map>* thread_private_read_addr_to_call_tree_node_map = new std::unordered_map>(); +#endif bool isLocked = false; while (true) { if (!isLocked) @@ -406,7 +513,11 @@ void *analyzeDeps(void *arg) { for (unsigned short i = 0; i < CHUNK_SIZE; ++i) { access = accesses[i]; +#if DP_CALLTREE_PROFILING + analyzeSingleAccess(SMem, access, thread_private_write_addr_to_call_tree_node_map, thread_private_read_addr_to_call_tree_node_map); +#else analyzeSingleAccess(SMem, access); +#endif } // delete the current chunk at the end @@ -432,6 +543,10 @@ void *analyzeDeps(void *arg) { } delete SMem; +#if DP_CALLSTACK_PROFILING + delete thread_private_write_addr_to_call_tree_node_map; + delete thread_private_read_addr_to_call_tree_node_map; +#endif pthread_mutex_unlock(&addrChunkMutexes[id]); mergeDeps(); @@ -478,6 +593,11 @@ void finalizeParallelization() { for (int i = 0; i < NUM_WORKERS; ++i) pthread_join(workers[i], NULL); + +#if DP_CALLTREE_PROFILING + //metadata_queue->blocking_finalize_queue(); +#endif + // destroy mutexes and condition variables for (int i = 0; i < NUM_WORKERS; ++i) { pthread_mutex_destroy(&addrChunkMutexes[i]); diff --git a/rtlib/iFunctionsGlobals.cpp b/rtlib/iFunctionsGlobals.cpp index 1375c6be8..c64a712d5 100644 --- a/rtlib/iFunctionsGlobals.cpp +++ b/rtlib/iFunctionsGlobals.cpp @@ -32,6 +32,10 @@ std::mutex pthread_compatibility_mutex; FunctionManager *function_manager = nullptr; LoopManager *loop_manager = nullptr; MemoryManager *memory_manager = nullptr; +CallTree *call_tree = nullptr; +//MetaDataQueue *metadata_queue = nullptr; +std::mutex* dependency_metadata_results_mtx = nullptr; +std::unordered_set* dependency_metadata_results = nullptr; // hybrid analysis ReportedBBSet *bbList = nullptr; diff --git a/rtlib/iFunctionsGlobals.hpp b/rtlib/iFunctionsGlobals.hpp index 4f4e3a7e9..60b4121b5 100644 --- a/rtlib/iFunctionsGlobals.hpp +++ b/rtlib/iFunctionsGlobals.hpp @@ -15,6 +15,8 @@ #include "../share/include/timer.hpp" #include "iFunctionsTypes.hpp" #include "memory/AbstractShadow.hpp" +#include "calltree/CallTree.hpp" +#include "calltree/DependencyMetadata.hpp" #include @@ -49,6 +51,10 @@ extern std::mutex pthread_compatibility_mutex; extern FunctionManager *function_manager; extern LoopManager *loop_manager; extern MemoryManager *memory_manager; +extern CallTree *call_tree; +//extern MetaDataQueue * metadata_queue; +extern std::mutex* dependency_metadata_results_mtx; +extern std::unordered_set* dependency_metadata_results; // hybrid analysis extern ReportedBBSet *bbList; diff --git a/rtlib/iFunctionsTypes.hpp b/rtlib/iFunctionsTypes.hpp index 34234310e..f0e0b0511 100644 --- a/rtlib/iFunctionsTypes.hpp +++ b/rtlib/iFunctionsTypes.hpp @@ -18,6 +18,10 @@ #include "loop/LoopManager.hpp" #include "memory/MemoryManager.hpp" +#if DP_CALLTREE_PROFILING +#include "calltree/CallTreeNode.hpp" +#endif + #include #include #include @@ -54,9 +58,17 @@ typedef enum { struct AccessInfo { AccessInfo(bool isRead, LID lid, char *var, std::string AAvar, ADDR addr, bool skip = false) - : isRead(isRead), lid(lid), var(var), AAvar(AAvar), addr(addr), skip(skip) {} - - AccessInfo() : isRead(false), lid(0), var(""), AAvar(""), addr(0), skip(false) {} + : isRead(isRead), lid(lid), var(var), AAvar(AAvar), addr(addr), skip(skip) { +#if DP_CALLTREE_PROFILING + call_tree_node_ptr = nullptr; +#endif + } + + AccessInfo() : isRead(false), lid(0), var(""), AAvar(""), addr(0), skip(false) { +#if DP_CALLTREE_PROFILING + call_tree_node_ptr = nullptr; +#endif + } bool isRead; // hybrid analysis @@ -66,6 +78,9 @@ struct AccessInfo { const char *var; std::string AAvar; // name of allocated variable -> "Anti Aliased Variable" ADDR addr; +#if DP_CALLTREE_PROFILING + shared_ptr call_tree_node_ptr; +#endif }; // For runtime dependency merging diff --git a/rtlib/injected_functions/dp_alloca.cpp b/rtlib/injected_functions/dp_alloca.cpp index 8aca0f553..cc2d278da 100644 --- a/rtlib/injected_functions/dp_alloca.cpp +++ b/rtlib/injected_functions/dp_alloca.cpp @@ -47,8 +47,12 @@ void __dp_alloca(LID lid, char *var, ADDR startAddr, ADDR endAddr, int64_t numBy #endif #if DP_MEMORY_REGION_DEALIASING +#if DP_STACK_ACCESS_DETECTION // create entry to list of allocatedMemoryRegions const std::string allocId = memory_manager->allocate_stack_memory(lid, startAddr, endAddr, numBytes, numElements); +#else + const std::string allocId = memory_manager->allocate_memory(lid, startAddr, endAddr, numBytes, numElements); +#endif // std::cout << "alloca: " << var << " (" << allocId << ") @ " << // dputil::decodeLID(lid) << " : " << std::hex << startAddr << " - " << // std::hex << endAddr << " -> #allocations: " << diff --git a/rtlib/injected_functions/dp_finalize.cpp b/rtlib/injected_functions/dp_finalize.cpp index 1e16f8111..7a68038c1 100644 --- a/rtlib/injected_functions/dp_finalize.cpp +++ b/rtlib/injected_functions/dp_finalize.cpp @@ -156,6 +156,35 @@ void __dp_finalize(LID lid) { delete function_manager; delete loop_manager; + +#ifdef DP_CALLTREE_PROFILING + delete call_tree; + //delete metadata_queue; + // output metadata to file + std::cout << "Outputting dependency metadata... "; + std::ifstream ifile; + std::string line; + std::ofstream ofile; + std::string tmp(getenv("DOT_DISCOPOP_PROFILER")); + // output information about the loops + tmp += "/dependency_metadata.txt"; + ofile.open(tmp.data()); + ofile << "# IAC : intra-call-dependency \n"; + ofile << "# IAI : intra-iteration-dependency \n"; + ofile << "# IEC : inter-call-dependency \n"; + ofile << "# IEI : inter-iteration-dependency \n"; + ofile << "# SINK_ANC : entered functions and loops for sink location \n"; + ofile << "# SOURCE_ANC : entered functions and loops for source location \n"; + ofile << "# Format: \n"; + for (auto dmd : *dependency_metadata_results) { + ofile << dmd.toString() << "\n"; + } + ofile.close(); + delete dependency_metadata_results_mtx; + delete dependency_metadata_results; +#endif + + *out << dputil::decodeLID(lid) << " END program" << endl; out->flush(); out->close(); diff --git a/rtlib/injected_functions/dp_func_entry.cpp b/rtlib/injected_functions/dp_func_entry.cpp index 7ccd3b006..a9ada7ba4 100644 --- a/rtlib/injected_functions/dp_func_entry.cpp +++ b/rtlib/injected_functions/dp_func_entry.cpp @@ -58,6 +58,12 @@ void __dp_func_entry(LID lid, int32_t isStart) { function_manager = new FunctionManager(); loop_manager = new LoopManager(); memory_manager = new MemoryManager(); +#if DP_CALLTREE_PROFILING + call_tree = new CallTree(); + //metadata_queue = new MetaDataQueue(6); // TODO: add Worker argument + dependency_metadata_results_mtx = new std::mutex(); + dependency_metadata_results = new std::unordered_set(); +#endif out = new ofstream(); @@ -126,6 +132,11 @@ void __dp_func_entry(LID lid, int32_t isStart) { memory_manager->enterScope("function", lid); #endif +#ifdef DP_CALLTREE_PROFILING + call_tree->enter_function(lid); +#endif + + if (isStart) *out << "START " << dputil::decodeLID(lid) << endl; diff --git a/rtlib/injected_functions/dp_func_exit.cpp b/rtlib/injected_functions/dp_func_exit.cpp index b177a5c5a..fad56ca48 100644 --- a/rtlib/injected_functions/dp_func_exit.cpp +++ b/rtlib/injected_functions/dp_func_exit.cpp @@ -78,6 +78,10 @@ void __dp_func_exit(LID lid, int32_t isExit) { #if DP_STACK_ACCESS_DETECTION memory_manager->leaveScope("function", lid); #endif + +#ifdef DP_CALLTREE_PROFILING + call_tree->exit_function(); +#endif // !TEST if (isExit == 0) { diff --git a/rtlib/injected_functions/dp_loop_entry.cpp b/rtlib/injected_functions/dp_loop_entry.cpp index e38cb3032..ad72c3d37 100644 --- a/rtlib/injected_functions/dp_loop_entry.cpp +++ b/rtlib/injected_functions/dp_loop_entry.cpp @@ -64,6 +64,11 @@ void __dp_loop_entry(LID lid, int32_t loopID) { memory_manager->enterScope("loop", lid); #endif +#ifdef DP_CALLTREE_PROFILING + call_tree->enter_loop(lid); + call_tree->enter_iteration(0); +#endif + } else { // The same loop iterates again loop_manager->iterate_loop(function_stack_level); @@ -86,6 +91,10 @@ void __dp_loop_entry(LID lid, int32_t loopID) { memory_manager->leaveScope("loop_iteration", lid); memory_manager->enterScope("loop_iteration", lid); #endif + +#ifdef DP_CALLTREE_PROFILING + call_tree->enter_iteration(0); +#endif } } } diff --git a/rtlib/injected_functions/dp_loop_exit.cpp b/rtlib/injected_functions/dp_loop_exit.cpp index 8dd7f0199..e427902f8 100644 --- a/rtlib/injected_functions/dp_loop_exit.cpp +++ b/rtlib/injected_functions/dp_loop_exit.cpp @@ -72,6 +72,10 @@ void __dp_loop_exit(LID lid, int32_t loopID) { #if DP_STACK_ACCESS_DETECTION memory_manager->leaveScope("loop", lid); #endif + +#ifdef DP_CALLTREE_PROFILING + call_tree->exit_loop(); +#endif } } diff --git a/rtlib/injected_functions/dp_read.cpp b/rtlib/injected_functions/dp_read.cpp index 2bc02b643..e3b18b5ca 100644 --- a/rtlib/injected_functions/dp_read.cpp +++ b/rtlib/injected_functions/dp_read.cpp @@ -90,6 +90,9 @@ void __dp_read(LID lid, ADDR addr, const char *var) { current.var = var; current.AAvar = getMemoryRegionIdFromAddr(var, addr); current.addr = addr; +#if DP_CALLTREE_PROFILING + current.call_tree_node_ptr = call_tree->get_current_node_ptr(); +#endif #if defined DP_NUM_WORKERS && DP_NUM_WORKERS == 0 analyzeSingleAccess(singleThreadedExecutionSMem, current); diff --git a/rtlib/injected_functions/dp_write.cpp b/rtlib/injected_functions/dp_write.cpp index bb9298670..42d48b4d8 100644 --- a/rtlib/injected_functions/dp_write.cpp +++ b/rtlib/injected_functions/dp_write.cpp @@ -93,6 +93,10 @@ void __dp_write(LID lid, ADDR addr, const char *var) { current.AAvar = getMemoryRegionIdFromAddr(var, addr); current.addr = addr; +#if DP_CALLTREE_PROFILING + current.call_tree_node_ptr = call_tree->get_current_node_ptr(); +#endif + #if defined DP_NUM_WORKERS && DP_NUM_WORKERS == 0 analyzeSingleAccess(singleThreadedExecutionSMem, current); #else diff --git a/share/include/timer.hpp b/share/include/timer.hpp index 2a06e2115..416aa1331 100644 --- a/share/include/timer.hpp +++ b/share/include/timer.hpp @@ -66,6 +66,13 @@ enum class TimerRegion : unsigned int { STACK_CHECK_ADDR_IS_OWNED_BY_SCOPE, STACK_CHECK_ADDR_IS_OWNED_BY_SCOPE_TRUE, + // Statistics regarding calltree profiling + ADD_DEP_CALLTREE_REGISTER_METADATAQUEUEELEMENT, + METADATAQUEUE_PROCESSQUEUE, + METADATAQUEUE_PROCESSQUEUEELEMENT, + METADATAQUEUE_PROCESSQUEUE_FETCH, + PROCESSQUEUEELEMENT, + SIZE_DONT_USE, }; @@ -183,11 +190,11 @@ class Timers { print(stream, " Finalizing the parallelization : ", TimerRegion::FINALIZE_PARALLELIZATION); stream << '\n'; print(stream, " Generate the dependency map : ", TimerRegion::GENERATE_STRING_DEP_MAP); - print(stream, " Add a dependency : ", TimerRegion::ADD_DEP); + print(stream, " Analyze singe accesses : ", TimerRegion::ANALYZE_SINGLE_ACCESS); + print(stream, " |- Add a dependency : ", TimerRegion::ADD_DEP); print(stream, " Merge dendencies : ", TimerRegion::MERGE_DEPS); print(stream, " Analyze the dependencies (incorrect!) : ", TimerRegion::ANALYZE_DEPS); // Incorrect due to multithreading - print(stream, " Analyze singe accesses : ", TimerRegion::ANALYZE_SINGLE_ACCESS); stream << '\n'; print(stream, " Output the dependencies : ", TimerRegion::OUTPUT_DEPS); print(stream, " Output the loops : ", TimerRegion::OUTPUT_LOOPS); @@ -210,6 +217,20 @@ class Timers { " Check for addr is owned by scope : ", TimerRegion::STACK_CHECK_ADDR_IS_OWNED_BY_SCOPE); print(stream, " Found addr is owned by scope : ", TimerRegion::STACK_CHECK_ADDR_IS_OWNED_BY_SCOPE_TRUE); + stream << "\n"; + +#if DP_CALLTREE_PROFILING + stream << "\n========== DiscoPoP TIMERS: calltree profiling ======\n"; + print(stream, " Context: Analyze singe accesses : ", TimerRegion::ANALYZE_SINGLE_ACCESS); + print(stream, " |- Context: Add a dependency : ", TimerRegion::ADD_DEP); + print(stream, " |- Register MetaDataQueueElements : ", TimerRegion::ADD_DEP_CALLTREE_REGISTER_METADATAQUEUEELEMENT); + print(stream, " MetaDataQueue: processQueue : ", TimerRegion::METADATAQUEUE_PROCESSQUEUE); + print(stream, " |- Fetch MetaDataQueueElement : ", TimerRegion::METADATAQUEUE_PROCESSQUEUE_FETCH); + print(stream, " |- processQueueElement : ", TimerRegion::METADATAQUEUE_PROCESSQUEUEELEMENT); + print(stream, " Stand-alone processQueueElement : ", TimerRegion::PROCESSQUEUEELEMENT); + stream << "\n"; +#endif + } /** diff --git a/test/end_to_end/do_all/calls/complex/src/code.cpp b/test/end_to_end/do_all/calls/complex/src/code.cpp index c9b270554..8572d56c4 100644 --- a/test/end_to_end/do_all/calls/complex/src/code.cpp +++ b/test/end_to_end/do_all/calls/complex/src/code.cpp @@ -10,7 +10,7 @@ void doall_possible(double* base, int index){ } void doall_not_possible(double* base, int index, int n){ - perform_calculation(base, index, (index + 1 % n)); + perform_calculation(base, index, (index + 422 % n)); } int main(int argc, const char* argv[]) { diff --git a/test/end_to_end/do_all/calls/complex_no_varname_duplicates/__init__.py b/test/end_to_end/do_all/calls/complex_no_varname_duplicates/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/end_to_end/do_all/calls/complex_no_varname_duplicates/src/Makefile b/test/end_to_end/do_all/calls/complex_no_varname_duplicates/src/Makefile new file mode 100644 index 000000000..a64882be3 --- /dev/null +++ b/test/end_to_end/do_all/calls/complex_no_varname_duplicates/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/complex_no_varname_duplicates/src/code.cpp b/test/end_to_end/do_all/calls/complex_no_varname_duplicates/src/code.cpp new file mode 100644 index 000000000..299195a8f --- /dev/null +++ b/test/end_to_end/do_all/calls/complex_no_varname_duplicates/src/code.cpp @@ -0,0 +1,40 @@ +#include +#include + +void perform_calculation(double* base_1, int offset_1, int offset_2){ + base_1[offset_1] = 42 + base_1[offset_2]; +} + +void doall_possible(double* base_2, int index_2){ + perform_calculation(base_2, index_2, index_2); +} + +void doall_not_possible(double* base_3, int index_3, int n_3){ + perform_calculation(base_3, index_3, (index_3 + 422 % n_3)); +} + +int main(int argc, const char* argv[]) { + static int n = 5000; static double a = 2.0; //n = 100000000; + double *x = (double *) malloc(n * sizeof(double)); + // Initialize x + + // DOALL + for(int i = 0; i < n; ++i){ + x[i] = 1.0; + } + + // DOALL + for(int i = 0; i < n; i++){ + doall_possible(x, i); + } + + // NOT DOALL + for(int i = 0; i < n; i++){ + doall_not_possible(x, i, n); + } + + + free(x);; +return 0; +} + diff --git a/test/end_to_end/do_all/calls/complex_no_varname_duplicates/test.py b/test/end_to_end/do_all/calls/complex_no_varname_duplicates/test.py new file mode 100644 index 000000000..bc7d22eb0 --- /dev/null +++ b/test/end_to_end/do_all/calls/complex_no_varname_duplicates/test.py @@ -0,0 +1,74 @@ +import os +import pathlib +import subprocess +import unittest + +import jsonpickle + +from discopop_library.result_classes.DetectionResult import DetectionResult +from test.utils.subprocess_wrapper.command_execution_wrapper import run_cmd +from test.utils.validator_classes.DoAllInfoForValidation import DoAllInfoForValidation +from subprocess import DEVNULL +from discopop_library.ConfigProvider.config_provider import run as run_config_provider +from discopop_library.ConfigProvider.ConfigProviderArguments import ConfigProviderArguments + + +class TestMethods(unittest.TestCase): + def test(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") + # 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") + cmd = "make" + run_cmd(cmd, src_dir, env_vars) + + # execute instrumented program + run_cmd("./prog", src_dir, env_vars) + + # execute DiscoPoP analysis + cmd = "discopop_explorer --enable-patterns doall,reduction" + run_cmd(cmd, os.path.join(src_dir, ".discopop"), env_vars) + # validate results + try: + self.validate_results(current_dir, src_dir) + # clean environment + run_cmd("make veryclean", src_dir, env_vars) + except Exception as ex: + # clean environment + run_cmd("make veryclean", src_dir, env_vars) + raise ex + + def validate_results(self, test_dir, src_dir): + """Check that exactly one do-all is suggested""" + test_output_file = os.path.join(src_dir, ".discopop", "explorer", "detection_result_dump.json") + # load detection results + with open(test_output_file, "r") as f: + tmp_str = f.read() + test_output: DetectionResult = jsonpickle.decode(tmp_str) + + for pattern_type in test_output.patterns.__dict__: + amount_of_identified_patterns = len(test_output.patterns.__dict__[pattern_type]) + if pattern_type == "do_all": + expected = ["1:22", "1:27"] + for pattern in test_output.patterns.__dict__[pattern_type]: + self.assertTrue(pattern.start_line in expected, "False positive: Pattern at " + pattern.start_line + " not in expected result: " + str(expected)) + self.assertTrue(len(test_output.patterns.__dict__[pattern_type]) == 2, "False negative: Missed pattern. \nFound: " + " ".join([p.start_line for p in test_output.patterns.__dict__[pattern_type]])+"\nExpected: " + " ".join(expected)) + else: + self.assertEqual(amount_of_identified_patterns, 0) \ No newline at end of file diff --git a/test/unit_tests/CMakeLists.txt b/test/unit_tests/CMakeLists.txt index e76c48326..af3be329c 100644 --- a/test/unit_tests/CMakeLists.txt +++ b/test/unit_tests/CMakeLists.txt @@ -38,7 +38,10 @@ target_sources( memory/memory_region_tree/test_memory_region_tree.cpp memory/mrtnode/test_mrtnode.cpp memory/perfect_shadow/test_perfect_shadow.cpp - memory/scope/test_scope.cpp) + memory/scope/test_scope.cpp + calltree/call_tree/test_call_tree.cpp + calltree/call_tree_node/test_call_tree_node.cpp + calltree/metadata_queue/test_metadata_queue.cpp) target_include_directories(DiscoPoP_UT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(DiscoPoP_UT PRIVATE DiscoPoP_RT) diff --git a/test/unit_tests/calltree/call_tree/test_call_tree.cpp b/test/unit_tests/calltree/call_tree/test_call_tree.cpp new file mode 100644 index 000000000..f53102162 --- /dev/null +++ b/test/unit_tests/calltree/call_tree/test_call_tree.cpp @@ -0,0 +1,162 @@ +#include + +#include "../../../../rtlib/calltree/CallTree.hpp" + +#include +#include + +// Tests for old version (i.e., capturing functionality) + +class CallTreeTest : public ::testing::Test {}; + +TEST_F(CallTreeTest, testConstructor) { + auto ct = __dp::CallTree(); + + ASSERT_EQ(call_tree_node_count.load(), 1); + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Root); +} + +TEST_F(CallTreeTest, testIncreasingNodeCount) { + auto ct = __dp::CallTree(); + + ASSERT_EQ(ct.get_node_count(), 1); + ct.enter_function(42); + ASSERT_EQ(ct.get_node_count(), 2); + ct.enter_function(43); + ASSERT_EQ(ct.get_node_count(), 3); +} + +TEST_F(CallTreeTest, testDecreasingNodeCount) { + auto ct = __dp::CallTree(); + + ASSERT_EQ(ct.get_node_count(), 1); + ct.enter_function(42); + ASSERT_EQ(ct.get_node_count(), 2); + ct.enter_function(43); + ASSERT_EQ(ct.get_node_count(), 3); + ct.exit_function(); + ASSERT_EQ(ct.get_node_count(), 2); + ct.exit_function(); + ASSERT_EQ(ct.get_node_count(), 1); +} + +TEST_F(CallTreeTest, testEnterLoop) { + auto ct = __dp::CallTree(); + ct.enter_loop(42); + + ASSERT_EQ(ct.get_node_count(), 2); + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Loop); + ASSERT_EQ(ct.get_current_node_ptr()->get_loop_or_function_id(), 42); +} + +TEST_F(CallTreeTest, testExitLoop){ + auto ct = __dp::CallTree(); + ct.enter_function(42); + ct.enter_loop(43); + ct.enter_iteration(1); + ct.enter_iteration(2); + ct.exit_loop(); + + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Function); + ASSERT_EQ(ct.get_current_node_ptr()->get_loop_or_function_id(), 42) ; + ASSERT_EQ(ct.get_node_count(), 2); +} + +TEST_F(CallTreeTest, testEnterFunction) { + auto ct = __dp::CallTree(); + ct.enter_function(42); + + ASSERT_EQ(ct.get_node_count(), 2); + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Function); + ASSERT_EQ(ct.get_current_node_ptr()->get_loop_or_function_id(), 42); +} + +TEST_F(CallTreeTest, testExitFunctionSimple){ + auto ct = __dp::CallTree(); + ct.enter_function(42); + ct.enter_loop(43); + ct.enter_iteration(1); + ct.enter_iteration(2); + ct.exit_function(); + + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Root); + ASSERT_EQ(ct.get_node_count(), 1); +} + +TEST_F(CallTreeTest, testExitFunction){ + auto ct = __dp::CallTree(); + ct.enter_function(42); + ct.enter_loop(43); + ct.enter_iteration(1); + ct.enter_iteration(2); + ct.exit_loop(); + ct.enter_loop(44); + ct.enter_iteration(1); + // ct.exit_loop(); missing on purpose + ct.exit_function(); + + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Root); + ASSERT_EQ(ct.get_node_count(), 1); +} + + +TEST_F(CallTreeTest, testEnterIterations) { + auto ct = __dp::CallTree(); + ct.enter_loop(42); + + ASSERT_EQ(ct.get_node_count(), 2); + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Loop); + ASSERT_EQ(ct.get_current_node_ptr()->get_loop_or_function_id(), 42); + + ct.enter_iteration(1); + ASSERT_EQ(ct.get_node_count(), 3); + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Iteration); + ASSERT_EQ(ct.get_current_node_ptr()->get_loop_or_function_id(), 42); + ASSERT_EQ(ct.get_current_node_ptr()->get_iteration_id(), 1); + + ct.enter_iteration(2); + // node of iteration 1 will be deleted, as ct.current pointer is redirected + ASSERT_EQ(ct.get_node_count(), 3); + ASSERT_EQ(ct.get_current_node_ptr()->get_node_type(), __dp::CallTreeNodeType::Iteration); + ASSERT_EQ(ct.get_current_node_ptr()->get_loop_or_function_id(), 42); + ASSERT_EQ(ct.get_current_node_ptr()->get_iteration_id(), 2); +} + +TEST_F(CallTreeTest, testAutomaticCleanup){ + auto ct = __dp::CallTree(); + ct.enter_function(42); + ct.enter_loop(43); + ct.enter_iteration(1); + ASSERT_EQ(ct.get_node_count(), 4); + + ct.enter_iteration(2); + // iteration node 1 shall be deleted, since it is not referenced anymore + ASSERT_EQ(ct.get_node_count(), 4); +} + +TEST_F(CallTreeTest, testPreventAutomaticCleanup){ + auto ct = __dp::CallTree(); + ct.enter_function(42); + ct.enter_loop(43); + ct.enter_iteration(1); + ASSERT_EQ(ct.get_node_count(), 4); + + { + // save ptr to iteration 1 to prevent cleanup + std::shared_ptr<__dp::CallTreeNode> dummy_ptr = ct.get_current_node_ptr(); + + ct.enter_iteration(2); + // iteration node 1 shall NOT be deleted, since a referenced still exists + ASSERT_EQ(ct.get_node_count(), 5); + } + + // dummy_ptr shall now be deleted. Check for automatic cleanup of iteration node 1 + ASSERT_EQ(ct.get_node_count(), 4); +} + +TEST_F(CallTreeTest, testImmediateFuncExit){ + auto ct = __dp::CallTree(); + ct.exit_function(); + // check for segfaults + ASSERT_TRUE(true); +} diff --git a/test/unit_tests/calltree/call_tree_node/test_call_tree_node.cpp b/test/unit_tests/calltree/call_tree_node/test_call_tree_node.cpp new file mode 100644 index 000000000..aa118eb88 --- /dev/null +++ b/test/unit_tests/calltree/call_tree_node/test_call_tree_node.cpp @@ -0,0 +1,74 @@ +#include + +#include "../../../../rtlib/calltree/CallTreeNode.hpp" +#include "../../../../rtlib/calltree/CallTreeNodeType.hpp" + +class CallTreeNodeTest : public ::testing::Test {}; + +TEST_F(CallTreeNodeTest, testConstructor) { + auto ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Function, 1, 0); + + ASSERT_EQ(ctn.get_loop_or_function_id(), 1); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Function); +} + +TEST_F(CallTreeNodeTest, testDefaultConstructor) { + auto ctn = __dp::CallTreeNode(); + + ASSERT_EQ(ctn.get_loop_or_function_id(), 0); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Root); + ASSERT_EQ(ctn.get_iteration_id(), 0); + ASSERT_EQ(ctn.get_parent_ptr(), nullptr); +} + +TEST_F(CallTreeNodeTest, testGetIterationId) { + auto ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Root, 0, 0); + ASSERT_EQ(ctn.get_loop_or_function_id(), 0); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Root); + ASSERT_EQ(ctn.get_iteration_id(), 0); + + ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Root, 0, 1); + ASSERT_EQ(ctn.get_loop_or_function_id(), 0); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Root); + ASSERT_EQ(ctn.get_iteration_id(), 0); + + ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Function, 1, 0); + ASSERT_EQ(ctn.get_loop_or_function_id(), 1); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Function); + ASSERT_EQ(ctn.get_iteration_id(), 0); + + ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Function, 1, 1); + ASSERT_EQ(ctn.get_loop_or_function_id(), 1); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Function); + ASSERT_EQ(ctn.get_iteration_id(), 0); + + ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Loop, 2, 0); + ASSERT_EQ(ctn.get_loop_or_function_id(), 2); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Loop); + ASSERT_EQ(ctn.get_iteration_id(), 0); + + ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Loop, 2, 1); + ASSERT_EQ(ctn.get_loop_or_function_id(), 2); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Loop); + ASSERT_EQ(ctn.get_iteration_id(), 0); + + ctn = __dp::CallTreeNode( nullptr, __dp::CallTreeNodeType::Iteration, 2, 1); + ASSERT_EQ(ctn.get_loop_or_function_id(), 2); + ASSERT_EQ(ctn.get_node_type(), __dp::CallTreeNodeType::Iteration); + ASSERT_EQ(ctn.get_iteration_id(), 1); +} + +TEST_F(CallTreeNodeTest, testGetParentPtr){ + auto root = __dp::CallTreeNode(); + auto function = __dp::CallTreeNode( make_shared<__dp::CallTreeNode>(root), __dp::CallTreeNodeType::Function, 1, 0); + auto loop = __dp::CallTreeNode( make_shared<__dp::CallTreeNode>(function), __dp::CallTreeNodeType::Loop, 2, 0); + auto iteration = __dp::CallTreeNode( make_shared<__dp::CallTreeNode>(loop), __dp::CallTreeNodeType::Iteration, 2, 1); + + ASSERT_EQ(iteration.get_parent_ptr()->get_node_type(), __dp::CallTreeNodeType::Loop); + ASSERT_EQ(iteration.get_parent_ptr()->get_loop_or_function_id(), 2); + ASSERT_EQ(iteration.get_parent_ptr()->get_iteration_id(), 0); + + ASSERT_EQ(iteration.get_parent_ptr()->get_parent_ptr()->get_node_type(), __dp::CallTreeNodeType::Function); + ASSERT_EQ(iteration.get_parent_ptr()->get_parent_ptr()->get_loop_or_function_id(), 1); + ASSERT_EQ(iteration.get_parent_ptr()->get_parent_ptr()->get_iteration_id(), 0); +} \ No newline at end of file diff --git a/test/unit_tests/calltree/metadata_queue/test_metadata_queue.cpp b/test/unit_tests/calltree/metadata_queue/test_metadata_queue.cpp new file mode 100644 index 000000000..1fd46b5ee --- /dev/null +++ b/test/unit_tests/calltree/metadata_queue/test_metadata_queue.cpp @@ -0,0 +1,8 @@ +#include + +#include "../../../../rtlib/calltree/MetaDataQueue.hpp" + +class MetaDataQueueTest : public ::testing::Test {}; + +TEST_F(MetaDataQueueTest, dummy) { +} \ No newline at end of file