diff --git a/CMakeLists.txt b/CMakeLists.txt index c87e6fa..bd9a9b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -53,6 +53,9 @@ option(CILKTOOLS_BUILD_CILKSAN "Build Cilksan" ON) mark_as_advanced(CILKTOOLS_BUILD_CILKSAN) option(CILKTOOLS_BUILD_CILKSCALE "Build Cilkscale" ON) mark_as_advanced(CILKTOOLS_BUILD_CILKSCALE) +option(CILKTOOLS_BUILD_CILKGRAPH "Build Cilkgraph" ON) +mark_as_advanced(CILKTOOLS_BUILD_CILKGRAPH) + if (CILKTOOLS_STANDALONE_BUILD) set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to conform to") @@ -345,7 +348,7 @@ pythonize_bool(CILKTOOLS_TEST_USE_LLD) include(AddCilktools) -set(ALL_CILKTOOLS csi;cilksan;cilkscale) +set(ALL_CILKTOOLS csi;cilksan;cilkscale;cilkgraph) set(CILKTOOLS_TO_BUILD all CACHE STRING "cilktools to build if supported on the target (all;${ALL_CILKTOOLS})") list_replace(CILKTOOLS_TO_BUILD all "${ALL_CILKTOOLS}") diff --git a/cilkgraph/CMakeLists.txt b/cilkgraph/CMakeLists.txt new file mode 100644 index 0000000..cb47954 --- /dev/null +++ b/cilkgraph/CMakeLists.txt @@ -0,0 +1,77 @@ +# Build for the Cilkgraph runtime support library. + +set(CILKGRAPH_SOURCES + cilkgraph.cpp + csirt.cpp + ) + +include_directories(${CILKTOOLS_SOURCE_DIR}/include) + +set(CILKGRAPH_CFLAGS ${SANITIZER_COMMON_CFLAGS}) +append_list_if(CILKTOOLS_HAS_CILK -fopencilk CILKGRAPH_CFLAGS) +append_list_if(CILKTOOLS_HAS_FDEBUG_DEFAULT_VERSION_EQ_4_FLAG + -fdebug-default-version=4 CILKGRAPH_CFLAGS) +append_rtti_flag(OFF CILKGRAPH_CFLAGS) + +set(CILKGRAPH_COMMON_DEFINITIONS) +append_list_if(CILKTOOLS_HAS_CILK SERIAL_TOOL=0 CILKGRAPH_COMMON_DEFINITIONS) + +set(CILKGRAPH_DYNAMIC_LINK_FLAGS) +append_list_if(CILKTOOLS_HAS_CILK -fopencilk CILKGRAPH_DYNAMIC_LINK_FLAGS) + +set(CILKGRAPH_DYNAMIC_CFLAGS ${CILKGRAPH_CFLAGS}) +set(CILKGRAPH_DYNAMIC_DEFINITIONS ${CILKGRAPH_COMMON_DEFINITIONS}) + +set(CILKGRAPH_COMMON_LIBS ${SANITIZER_CXX_ABI_LIBRARY} ${SANITIZER_COMMON_LINK_LIBS}) + +set(CILKGRAPH_DYNAMIC_LIBS ${CILKGRAPH_COMMON_LIBS}) + +# Build Cilkgraph runtimes shipped with Clang. +add_cilktools_component(cilkgraph) + +if (APPLE) + add_cilktools_runtime(clang_rt.cilkgraph + STATIC + OS ${CILKTOOL_SUPPORTED_OS} + ARCHS ${CILKGRAPH_SUPPORTED_ARCH} + SOURCES ${CILKGRAPH_SOURCES} + CFLAGS ${CILKGRAPH_CFLAGS} + DEFS ${CILKGRAPH_COMMON_DEFINITIONS} + PARENT_TARGET cilkgraph) + + add_cilktools_runtime(clang_rt.cilkgraph + SHARED + OS ${CILKTOOL_SUPPORTED_OS} + ARCHS ${CILKGRAPH_SUPPORTED_ARCH} + SOURCES ${CILKGRAPH_SOURCES} + CFLAGS ${CILKGRAPH_DYNAMIC_CFLAGS} + LINK_FLAGS ${CILKGRAPH_DYNAMIC_LINK_FLAGS} + LINK_LIBS ${CILKGRAPH_DYNAMIC_LIBS} + DEFS ${CILKGRAPH_DYNAMIC_DEFINITIONS} + PARENT_TARGET cilkgraph) + +else() + foreach (arch ${CILKGRAPH_SUPPORTED_ARCH}) + add_cilktools_runtime(clang_rt.cilkgraph + STATIC + ARCHS ${arch} + SOURCES ${CILKGRAPH_SOURCES} + CFLAGS ${CILKGRAPH_CFLAGS} + DEFS ${CILKGRAPH_COMMON_DEFINITIONS} + PARENT_TARGET cilkgraph) + + add_cilktools_runtime(clang_rt.cilkgraph + SHARED + ARCHS ${arch} + SOURCES ${CILKGRAPH_SOURCES} + CFLAGS ${CILKGRAPH_DYNAMIC_CFLAGS} + LINK_FLAGS ${CILKGRAPH_DYNAMIC_LINK_FLAGS} + LINK_LIBS ${CILKGRAPH_DYNAMIC_LIBS} + DEFS ${CILKGRAPH_DYNAMIC_DEFINITIONS} + PARENT_TARGET cilkgraph) + endforeach() +endif() + +if (CILKTOOLS_INCLUDE_TESTS) + # TODO: add tests +endif() diff --git a/cilkgraph/calldag.h b/cilkgraph/calldag.h new file mode 100644 index 0000000..0036a8e --- /dev/null +++ b/cilkgraph/calldag.h @@ -0,0 +1,242 @@ +#ifndef INCLUDED_CALLGRAPH_H +#define INCLUDED_CALLGRAPH_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace calldag { + +enum class call_type { + Root, + Func, + Task, + Cont, + Sync, +}; + +template +OS& operator<<(OS& os, const call_type& type) { + switch(type) { + case call_type::Root: + os << "R"; + break; + case call_type::Func: + os << "F"; + break; + case call_type::Task: + os << "T"; + break; + case call_type::Cont: + os << "C"; + break; + case call_type::Sync: + os << "S"; + break; + } + return os; +} + +struct dot_diedge { + std::string a, b; +}; + +struct dot_node { + std::string name; + std::unordered_map attrs; +}; + +template +OS& operator<<(OS& os, dot_diedge e) { + os << "\"" << e.a << "\" -> \"" << e.b << "\" "; + return os; +} + +template +OS& operator<<(OS& os, dot_node node) { + os << "\"" << node.name << "\" ["; + for (auto& [k, v] : node.attrs) { + os << "\"" << k << "\"=\"" << v << "\","; + } + os << "] "; + return os; +} + +struct node_t { + call_type type; + unsigned head_worker_id; + unsigned tail_worker_id; + std::vector children; + + node_t(call_type type, unsigned worker_id) : + type(type), head_worker_id(worker_id) { + } + + ~node_t() { + for (auto& child : children) { + delete child; + } + } + + static std::string worker_id_to_color(unsigned worker_id) { + if (worker_id == 0) return "0 0.6 1"; + unsigned mssb = worker_id & -worker_id; + unsigned numer = 2 * (worker_id ^ mssb) + 1; + unsigned denom = 2 * mssb; + std::ostringstream ss; + ss << std::setprecision(std::numeric_limits::digits10); + ss << std::fixed << 1.0 * numer / denom; + ss << " 0.6 1"; + return ss.str(); + } + + template + void print_dot(OS& os, std::string& prefix) const { + std::vector active_nodes; + std::string prev_node = prefix + "head"; + os << dot_node{prev_node, {{"style", "filled"}, {"fillcolor", worker_id_to_color(head_worker_id)}}}; + os << dot_node{prefix + "tail", {{"style", "filled"}, {"fillcolor", worker_id_to_color(tail_worker_id)}}}; + int nc = children.size(); + for (int i = 0; i < nc; ++i) { + if (children[i]->type == call_type::Sync) { + std::string sync_node = prefix + std::to_string(i) + ".sync"; + os << dot_node{sync_node, {{"style", "filled"}, {"fillcolor", worker_id_to_color(children[i]->head_worker_id)}}}; + + os << dot_diedge{prev_node, sync_node}; + for (auto& node : active_nodes) { + os << dot_diedge{node, sync_node}; + } + active_nodes.clear(); + + prev_node = std::move(sync_node); + continue; + } else if (children[i]->type == call_type::Cont) { + active_nodes.push_back(prev_node); + prev_node = prefix + std::to_string(i) + ".cont"; + os << dot_node{prev_node, {{"style", "filled"}, {"fillcolor", worker_id_to_color(children[i]->head_worker_id)}}}; + + os << dot_diedge{prefix + "head", prev_node}; + continue; + } + + int init_prefix_len = prefix.size(); + prefix += std::to_string(i); + prefix.push_back('.'); + + os << dot_diedge{prev_node, prefix + "head"}; + prev_node = prefix + "tail"; + + children[i]->print_dot(os, prefix); + prefix.resize(init_prefix_len); + } + os << dot_diedge{prev_node, prefix + "tail"}; + } + + template + void dump(OS& os) const { + os << type << "[W" << head_worker_id << "]("; + for (auto& c : children) { + c->dump(os); + } + os << ")"; + } + + template + friend OS& operator<<(OS& os, const node_t& node); +}; + +template +OS& operator<<(OS& os, const node_t& node) { +#ifdef TRACE_CALLS + node.dump(os); + os << std::endl; +#endif + std::string prefix; + os << "digraph {"; + node.print_dot(os, prefix); + os << "}"; + return os; +} + +class dag_t { + node_t root{call_type::Root, -1u}; + std::vector stack = {&root}; + +public: + dag_t() {} + dag_t(dag_t&& other) : + root(std::move(other.root)), stack(std::move(other.stack)) + { + stack[0] = &root; + } + + void push(call_type type, unsigned worker_id) { + stack.push_back( + stack.back()->children.emplace_back(new node_t(type, worker_id))); + } + + void pop(unsigned worker_id) { + stack.back()->tail_worker_id = worker_id; + stack.pop_back(); + } + + void pop() { + stack.pop_back(); + } + + static void reduce(void* left_v, void* right_v) { + auto left = static_cast(left_v); + auto right = static_cast(right_v); + +#ifdef TRACE_CALLS + std::ostringstream ss; + ss << "rhs: " << right->stack.size() << std::endl; + std::cout << ss.str(); +#endif + + auto& lchildren = left->stack.back()->children; + auto& rchildren = right->root.children; + + lchildren.insert( + lchildren.end(), + std::make_move_iterator(rchildren.begin()), + std::make_move_iterator(rchildren.end())); + rchildren.clear(); + + auto& lstack = left->stack; + auto& rstack = right->stack; + lstack.insert( + lstack.end(), + std::make_move_iterator(std::next(rstack.begin())), + std::make_move_iterator(rstack.end())); + rstack.clear(); + + right->~dag_t(); + } + + static void identity(void* view) { + new (view) dag_t(); + } + + template + friend OS& operator<<(OS& os, const dag_t& g); +}; + +template +OS& operator<<(OS& os, const dag_t& g) { + os << g.root; + return os; +} + +using dag_reducer = dag_t + _Hyperobject(&dag_t::identity, &dag_t::reduce); + +} //namespace calldag + +#endif // INCLUDED_CALLGRAPH_H diff --git a/cilkgraph/cilkgraph.cpp b/cilkgraph/cilkgraph.cpp new file mode 100644 index 0000000..ea41eb7 --- /dev/null +++ b/cilkgraph/cilkgraph.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include +#include + +#include "calldag.h" + +using calldag::call_type; + +#define CILKTOOL_API extern "C" __attribute__((visibility("default"))) + +// TODO: correctly handle numbered syncregions + +class CilkgraphImpl_t { + std::unique_ptr outf; +public: + cilk::ostream_reducer outs_red; + calldag::dag_reducer callg; + +private: + // Need to manually register reducer + // + // > warning: reducer callbacks not implemented for structure members + // > [-Wcilk-ignored] + struct { + template + static void reducer_register(T& red) { + __cilkrts_reducer_register(&red, sizeof(red), + &std::decay_t::identity, + &std::decay_t::reduce); + } + + template + static void reducer_unregister(T& red) { + __cilkrts_reducer_unregister(&red); + } + + struct RAII { + CilkgraphImpl_t& this_; + + RAII(decltype(this_) this_) : this_(this_) { + reducer_register(this_.outs_red); + reducer_register(this_.callg); + } + + ~RAII() { + reducer_unregister(this_.outs_red); + reducer_unregister(this_.callg); + } + } raii; + } register_reducers = {.raii{*this}}; + +public: + CilkgraphImpl_t() : + outs_red([this]() -> std::basic_ostream& { + const char* envstr = getenv("CILKSCALE_OUT"); + if (envstr) + return *(outf = std::make_unique(envstr)); + return std::cout; + }()), + callg() // Not only are reducer callbacks not implemented, the hyperobject + // is not even default constructed unless explicitly constructed. + {} + + ~CilkgraphImpl_t() { + outs_red << callg << std::endl; + } +}; + +static std::unique_ptr tool = + std::make_unique(); + +static unsigned worker_number() { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + return __cilkrts_get_worker_number(); +#pragma clang diagnostic pop +} + +CILKTOOL_API void __csi_init() { +} + +CILKTOOL_API void __csi_unit_init(const char* const file_name, + const instrumentation_counts_t counts) { +} + +CILKTOOL_API + void __csi_bb_entry(const csi_id_t bb_id, const bb_prop_t prop) { + return; +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] bb_entry(bbid=" << bb_id << ")" + << std::endl; +#endif +} + +CILKTOOL_API +void __csi_bb_exit(const csi_id_t bb_id, const bb_prop_t prop) { + return; +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] bb_exit(bbid=" << bb_id << ")" + << std::endl; +#endif +} + +CILKTOOL_API +void __csi_func_entry(const csi_id_t func_id, const func_prop_t prop) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] func(fid=" << func_id << ", nsr=" + << prop.num_sync_reg << ")" << std::endl; +#endif + tool->callg.push(call_type::Func, worker_number()); +} + +CILKTOOL_API +void __csi_func_exit(const csi_id_t func_exit_id, const csi_id_t func_id, + const func_exit_prop_t prop) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] func_exit(feid=" << func_exit_id + << ", fid=" << func_id << ")" << std::endl; +#endif + tool->callg.pop(); +} + +CILKTOOL_API void __csi_task(const csi_id_t task_id, const csi_id_t detach_id, + const task_prop_t prop) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] task(tid=" << task_id << ", did=" + << detach_id << ", nsr=" << prop.num_sync_reg << ")" << std::endl; +#endif + tool->callg.push(call_type::Task, worker_number()); +} + +CILKTOOL_API +void __csi_task_exit(const csi_id_t task_exit_id, const csi_id_t task_id, + const csi_id_t detach_id, const unsigned sync_reg, + const task_exit_prop_t prop) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] task_exit(teid=" << task_exit_id + << ", tid=" << task_id << ", did=" << detach_id << ", sr=" + << sync_reg << ")" << std::endl; +#endif + tool->callg.pop(worker_number()); +} + +CILKTOOL_API +void __csi_detach(const csi_id_t detach_id, const unsigned sync_reg, + const detach_prop_t prop) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] detach(did=" << detach_id << ", sr=" + << sync_reg << ")" << std::endl; +#endif +} + +CILKTOOL_API +void __csi_detach_continue(const csi_id_t detach_continue_id, + const csi_id_t detach_id, const unsigned sync_reg, + const detach_continue_prop_t prop) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] detach_continue(dcid=" + << detach_continue_id << ", did=" << detach_id << ", sr=" << sync_reg + << ")" << std::endl; +#endif + tool->callg.push(call_type::Cont, worker_number()); + tool->callg.pop(); +} + +CILKTOOL_API +void __csi_before_sync(const csi_id_t sync_id, const unsigned sync_reg) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] before_sync(sid=" << sync_id << ", sr=" + << sync_reg << ")" << std::endl; +#endif + tool->callg.push(call_type::Sync, worker_number()); + tool->callg.pop(); +} + +CILKTOOL_API +void __csi_after_sync(const csi_id_t sync_id, const unsigned sync_reg) { +#ifdef TRACE_CALLS + tool->outs_red + << "[W" << worker_number() << "] after_sync(sid=" << sync_id << ", sr=" + << sync_reg << ")" << std::endl; +#endif +} diff --git a/cilkgraph/csirt.cpp b/cilkgraph/csirt.cpp new file mode 100644 index 0000000..f7db71f --- /dev/null +++ b/cilkgraph/csirt.cpp @@ -0,0 +1,28 @@ +#include +#include + +#define CSIRT_API __attribute__((visibility("default"))) + + +EXTERN_C + +typedef struct { + int64_t num_entries; + csi_id_t *id_base; + const source_loc_t *entries; +} unit_fed_table_t; + +typedef struct { + int64_t num_entries; + const sizeinfo_t *entries; +} unit_sizeinfo_table_t; + +typedef void (*__csi_init_callsite_to_functions)(); + +CSIRT_API +void __csirt_unit_init(const char *const name, + unit_fed_table_t *unit_fed_tables, + unit_sizeinfo_table_t *unit_sizeinfo_tables, + __csi_init_callsite_to_functions callsite_to_func_init) { +} +EXTERN_C_END diff --git a/cmake/Modules/AllSupportedArchDefs.cmake b/cmake/Modules/AllSupportedArchDefs.cmake index 6b1c1d4..c173067 100644 --- a/cmake/Modules/AllSupportedArchDefs.cmake +++ b/cmake/Modules/AllSupportedArchDefs.cmake @@ -28,3 +28,4 @@ set(OS_NAME "${CMAKE_SYSTEM_NAME}") set(ALL_CSI_SUPPORTED_ARCH ${X86_64} ${ARM64}) set(ALL_CILKSAN_SUPPORTED_ARCH ${X86_64} ${ARM64}) set(ALL_CILKSCALE_SUPPORTED_ARCH ${X86_64} ${ARM64}) +set(ALL_CILKGRAPH_SUPPORTED_ARCH ${X86_64} ${ARM64}) diff --git a/cmake/config-ix.cmake b/cmake/config-ix.cmake index 1f9ce9c..a9cc783 100644 --- a/cmake/config-ix.cmake +++ b/cmake/config-ix.cmake @@ -405,11 +405,15 @@ if(APPLE) list_intersect(CILKSCALE_SUPPORTED_ARCH ALL_CILKSCALE_SUPPORTED_ARCH CILKTOOLS_COMMON_SUPPORTED_ARCH) + list_intersect(CILKGRAPH_SUPPORTED_ARCH + ALL_CILKGRAPH_SUPPORTED_ARCH + CILKTOOLS_COMMON_SUPPORTED_ARCH) else() filter_available_targets(CSI_SUPPORTED_ARCH ${ALL_CSI_SUPPORTED_ARCH}) filter_available_targets(CILKSAN_SUPPORTED_ARCH ${ALL_CILKSAN_SUPPORTED_ARCH}) filter_available_targets(CILKSCALE_SUPPORTED_ARCH ${ALL_CILKSCALE_SUPPORTED_ARCH}) + filter_available_targets(CILKGRAPH_SUPPORTED_ARCH ${ALL_CILKGRAPH_SUPPORTED_ARCH}) endif() if (MSVC) @@ -462,3 +466,10 @@ if (CILKSCALE_SUPPORTED_ARCH AND else() set(CILKTOOLS_HAS_CILKSCALE FALSE) endif() + +if (CILKGRAPH_SUPPORTED_ARCH AND + OS_NAME MATCHES "Linux|Darwin|FreeBSD") + set(CILKTOOLS_HAS_CILKGRAPH TRUE) +else() + set(CILKTOOLS_HAS_CILKGRAPH FALSE) +endif()