diff --git a/src/c/CMakeLists.txt b/src/c/CMakeLists.txt index 77564d01..cdc41acf 100644 --- a/src/c/CMakeLists.txt +++ b/src/c/CMakeLists.txt @@ -34,14 +34,13 @@ if("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") endif("${isSystemDir}" STREQUAL "-1") -add_subdirectory(parser) add_subdirectory(runtime) add_subdirectory(weaver) add_subdirectory(test) add_subdirectory(examples) add_library(perfflowaspect INTERFACE) -target_link_libraries(perfflowaspect INTERFACE perfflow_runtime perfflow_parser WeavePassPlugin) +target_link_libraries(perfflowaspect INTERFACE perfflow_runtime WeavePassPlugin) install(TARGETS perfflowaspect EXPORT perfflow_export LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/src/c/README.md b/src/c/README.md index 26ce01ac..3492a144 100644 --- a/src/c/README.md +++ b/src/c/README.md @@ -27,9 +27,6 @@ as required by the AOP paradigm. PerfFlowAspect requires Clang and LLVM development packages as well as a jansson-devel package for JSON manipulation. -It additionally requires the dependencies of -our annotation parser code: i.e., -`flex` and `bison`. Note that LLVM_DIR must be set to the corresponding LLVM cmake directory which may differ across different Linux distributions. @@ -41,19 +38,17 @@ llvm-devel | llvm-dev | >= 9.0 jansson-devel | libjansson-dev | >= 2.6 openssl-devel | libssl-dev | >= 1.0.2 cmake | cmake | >= 3.10 -flex | flex | >= 2.5.37 -bison | bison | >= 3.0.4 make | make | >= 3.82 ##### Installing RedHat/CentOS Packages ``` -yum install clang llvm-devel gcc gcc-c++ jansson-devel openssl-devel make make flex bison +yum install clang llvm-devel gcc gcc-c++ jansson-devel openssl-devel cmake make ``` ##### Installing Ubuntu Packages ``` apt-get update -apt install clang llvm-dev libjansson-dev libssl-dev bison flex make cmake make flex bison +apt install clang llvm-dev libjansson-dev libssl-dev cmake make ``` ##### Building PerfFlowAspect diff --git a/src/c/parser/CMakeLists.txt b/src/c/parser/CMakeLists.txt deleted file mode 100644 index 2cbc769b..00000000 --- a/src/c/parser/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -set(LEX_FLAGS "--nodefault") - -set(YACC_FLAGS "-t --report=none") - -# builds lex.yy.cpp -flex_target(lexer lex.l ${CMAKE_CURRENT_BINARY_DIR}/lex.yy.cpp - COMPILE_FLAGS ${LEX_FLAGS} -) - -# builds parser.tab.cpp and parser.tab.h -bison_target(parser parser.y ${CMAKE_CURRENT_BINARY_DIR}/parser.tab.cpp - COMPILE_FLAGS ${YACC_FLAGS} - DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/parser.tab.h -) - -ADD_FLEX_BISON_DEPENDENCY(lexer parser) - -set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS} "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libperfflow_parser.map") - -set(perfflow_parser_sources - perfflow_parser.cpp -) - -set_source_files_properties(${perfflow_parser_sources} PROPERTIES COMPILE_FLAGS -std=c++11) - -set_source_files_properties(${FLEX_lexer_OUTPUTS} PROPERTIES COMPILE_FLAGS "-Wno-deprecated-register") - -add_library(perfflow_parser SHARED - ${perfflow_parser_sources} - ${BISON_parser_OUTPUTS} - ${FLEX_lexer_OUTPUTS} -) - -target_include_directories(perfflow_parser PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) - -install(TARGETS perfflow_parser - EXPORT perfflow_export - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} -) diff --git a/src/c/parser/lex.l b/src/c/parser/lex.l deleted file mode 100644 index 50d38bfc..00000000 --- a/src/c/parser/lex.l +++ /dev/null @@ -1,54 +0,0 @@ -/************************************************************\ - * Copyright 2021 Lawrence Livermore National Security, LLC - * (c.f. AUTHORS, NOTICE.LLNS, COPYING) - * - * This file is part of the Flux resource manager framework. - * For details, see https://github.com/flux-framework. - * - * SPDX-License-Identifier: LGPL-3.0 -\************************************************************/ - -%option noyywrap - -%{ -#include -#include -#define YY_DECL int yylex () -#include "parser.tab.h" -%} - -flowtype ['](in|out|inout|outin)['] -label [']([^'\\\n]|\\(.|\n))*['] -pcutop ['](around|before|after)(_async)?['] - -%% - -[ \t] ; // ignore all whitespace -"@perfflowaspect.aspect.critical_path" { return T_CPA; } -"@critical_path" { return T_CPA; } -"pointcut" { snprintf (yylval.str, 4096, "%s", yytext); return T_PCUT; } -{pcutop} { snprintf (yylval.str, 4096, "%s", yytext); return T_PC_OP; } -"flow" { snprintf (yylval.str, 4096, "%s", yytext); return T_FLOW; } -{flowtype} { snprintf (yylval.str, 4096, "%s", yytext); return T_FW_TY; } -"scope" { snprintf (yylval.str, 4096, "%s", yytext); return T_SCOPE; } -{label} { snprintf (yylval.str, 4096, "%s", yytext); return T_LABEL; } -\n { return T_NEWLINE; } -"," { return T_COMMA; } -"=" { return T_ASSIGN; } -"(" { return T_LEFT; } -")" { return T_RIGHT; } -. {} - -%% - -void set_input_string (const char *in) { - yy_scan_string (in); -} - -void end_lexical_scan () { - yy_delete_buffer(YY_CURRENT_BUFFER); -} - -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */ diff --git a/src/c/parser/libperfflow_parser.map b/src/c/parser/libperfflow_parser.map deleted file mode 100644 index 79c43985..00000000 --- a/src/c/parser/libperfflow_parser.map +++ /dev/null @@ -1,7 +0,0 @@ -LIBPERFFLOW_PARSER { - global: - extern "C++" { - perfflow_parser_*; - }; - local: *; -}; diff --git a/src/c/parser/parser.y b/src/c/parser/parser.y deleted file mode 100644 index e5f983fb..00000000 --- a/src/c/parser/parser.y +++ /dev/null @@ -1,141 +0,0 @@ -/************************************************************\ - * Copyright 2021 Lawrence Livermore National Security, LLC - * (c.f. AUTHORS, NOTICE.LLNS, COPYING) - * - * This file is part of the Flux resource manager framework. - * For details, see https://github.com/flux-framework. - * - * SPDX-License-Identifier: LGPL-3.0 -\************************************************************/ - -%{ - -#include -#include -#include -#include -#include -#include - -std::map params; -extern void set_input_string (const char *in); -extern void end_lexical_scan (); -extern int yylex(); -extern int yyparse (); -extern void yyerror (const char* s); -%} - -%union { - char str[4096]; -} - -%token T_NEWLINE -%token T_CPA -%token T_PCUT -%token T_PC_OP -%token T_FLOW -%token T_FW_TY -%token T_SCOPE -%token T_LABEL -%token T_COMMA -%token T_ASSIGN -%token T_LEFT -%token T_RIGHT - -%start annotation - -%% - -annotation: - | annotation line -; - -line: T_NEWLINE - | T_CPA T_LEFT args T_RIGHT -; - -args: - | assignments -; - -assignments: assignment - | assignment T_COMMA assignments -; - -assignment: pointcut_assignment - | scope_assignment - | flow_assignment -; - -pointcut_assignment: T_PCUT T_ASSIGN T_PC_OP { params[$1] = $3; } -; - -scope_assignment: T_SCOPE T_ASSIGN T_LABEL { params[$1] = $3; } -; - -flow_assignment: T_FLOW T_ASSIGN T_FW_TY { params[$1] = $3; } -; - -%% - -void yyerror (const char *s) -{ -} - -int validate (const char *in) -{ - int rc = -1; - if (!in) { - errno = EINVAL; - return rc; - } - set_input_string (in); - rc = yyparse (); - end_lexical_scan (); - params.clear (); - return rc; -} - -int parse (const char *in, std::string &pointcut, - std::string &scope, std::string &flow) -{ - int rc = -1; - if (!in) { - errno = EINVAL; - return rc; - } - set_input_string (in); - rc = yyparse (); - end_lexical_scan (); - - auto p_iter = params.find ("pointcut"); - if (p_iter != params.end ()) { - pointcut = p_iter->second; - pointcut = pointcut.substr (1, pointcut.size () - 2); - } else { - pointcut = "around"; - } - - auto p_iter2 = params.find ("scope"); - if (p_iter2 != params.end ()) { - scope = p_iter2->second; - scope = scope.substr (1, scope.size () - 2); - } else { - scope = "NA"; - } - - auto p_iter3 = params.find ("flow"); - if (p_iter3 != params.end ()) { - flow = p_iter3->second; - flow = flow.substr (1, flow.size () - 2); - } else { - flow = "NA"; - } - - params.clear (); - return (rc != 0) ? -1 : 0; -} - -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */ diff --git a/src/c/parser/perfflow_parser.cpp b/src/c/parser/perfflow_parser.cpp deleted file mode 100644 index 53731ca6..00000000 --- a/src/c/parser/perfflow_parser.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/************************************************************\ - * Copyright 2021 Lawrence Livermore National Security, LLC - * (c.f. AUTHORS, NOTICE.LLNS, COPYING) - * - * This file is part of the Flux resource manager framework. - * For details, see https://github.com/flux-framework. - * - * SPDX-License-Identifier: LGPL-3.0 -\************************************************************/ - -#include -#include "parser.tab.h" -#include "perfflow_parser.hpp" - -extern int validate(const char *); -extern int parse(const char *, std::string &, std::string &, std::string &); - -int perfflow_parser_validate(const char *anno) -{ - if (!anno) - { - errno = EINVAL; - return -1; - } - return validate(anno); -} - -int perfflow_parser_parse(const char *anno, - std::string &pointcut, - std::string &scope, - std::string &flow) -{ - if (!anno) - { - errno = EINVAL; - return -1; - } - return parse(anno, pointcut, scope, flow); -} - -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */ diff --git a/src/c/parser/perfflow_parser.hpp b/src/c/parser/perfflow_parser.hpp deleted file mode 100644 index 21bd2ff9..00000000 --- a/src/c/parser/perfflow_parser.hpp +++ /dev/null @@ -1,27 +0,0 @@ -/************************************************************\ - * Copyright 2021 Lawrence Livermore National Security, LLC - * (c.f. AUTHORS, NOTICE.LLNS, COPYING) - * - * This file is part of the Flux resource manager framework. - * For details, see https://github.com/flux-framework. - * - * SPDX-License-Identifier: LGPL-3.0 -\************************************************************/ - -#ifndef PERFFLOW_PARSER_HPP -#define PERFFLOW_PARSER_HPP - -#include - -int perfflow_parser_validate (const char *anno); - -int perfflow_parser_parse (const char *anno, - std::string &pointcut, - std::string &scope, - std::string &flow); - -#endif // PERFFLOW_PARSER_HPP - -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */ diff --git a/src/c/parser/test/Makefile b/src/c/parser/test/Makefile deleted file mode 100644 index a4f7e4f2..00000000 --- a/src/c/parser/test/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -CXX := g++ -CXXFLAGS := -I../../../common/libtap -I../ -LDFLAGS := -L../../../common/libtap -L../ - -all: anno_validate_test anno_parse_test - -anno_validate_test: anno_validate_test.o - $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) -lperfflow_parser -ltap -anno_parse_test: anno_parse_test.o - $(CXX) $(CXXFLAGS) $^ -o $@ $(LDFLAGS) -lperfflow_parser -ltap - -%.o: %.cpp - $(CXX) $(CXXFLAGS) $^ -c -o $@ - -.PHONY: clean - -clean: - rm -f *.o anno_validate_test anno_parse_test diff --git a/src/c/parser/test/anno_parse_test.cpp b/src/c/parser/test/anno_parse_test.cpp deleted file mode 100644 index b4fbb2a0..00000000 --- a/src/c/parser/test/anno_parse_test.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/************************************************************\ - * Copyright 2021 Lawrence Livermore National Security, LLC - * (c.f. AUTHORS, NOTICE.LLNS, COPYING) - * - * This file is part of the Flux resource manager framework. - * For details, see https://github.com/flux-framework. - * - * SPDX-License-Identifier: LGPL-3.0 -\************************************************************/ - -#include -#include -#include "tap.h" -#include "perfflow_parser.hpp" - -void parse_valid_annotations() -{ - int rc = -1; - std::string pointcut, scope, flow; - std::string anno1("@critical_path()"); - rc = perfflow_parser_parse(anno1.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "around" && scope == "NA" && flow == "NA", - "%s is correctly parsed", anno1.c_str()); - - pointcut = ""; scope = ""; flow = ""; - std::string anno2("@critical_path ( )"); - rc = perfflow_parser_parse(anno2.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "around" && scope == "NA" && flow == "NA", - "%s is correctly parsed", anno2.c_str()); - - pointcut = ""; scope = ""; flow = ""; - std::string anno3("@perfflowaspect.aspect.critical_path ()"); - rc = perfflow_parser_parse(anno3.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "around" && scope == "NA" && flow == "NA", - "%s is correctly parsed", anno3.c_str()); - - pointcut = ""; scope = ""; flow = ""; - std::string anno4("@critical_path (pointcut='around')"); - rc = perfflow_parser_parse(anno4.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "around" && scope == "NA" && flow == "NA", - "%s is correctly parsed", anno4.c_str()); - - pointcut = ""; scope = ""; flow = ""; - std::string anno5("@critical_path (pointcut ='around')"); - rc = perfflow_parser_parse(anno5.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "around" && scope == "NA" && flow == "NA", - "%s is correctly parsed", anno5.c_str()); - - pointcut = ""; scope = ""; flow = ""; - std::string anno6("@critical_path (pointcut='before', scope='foo')"); - rc = perfflow_parser_parse(anno6.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "before" && scope == "foo" && flow == "NA", - "%s is correctly parsed", anno6.c_str()); - - pointcut = ""; scope = ""; flow = ""; - std::string anno7("@critical_path (scope='foo', pointcut='before', flow='in')"); - rc = perfflow_parser_parse(anno7.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "before" && scope == "foo" && flow == "in", - "%s is correctly parsed", anno7.c_str()); - - pointcut = ""; scope = ""; - std::string anno8("@critical_path (scope='', pointcut='before')"); - rc = perfflow_parser_parse(anno8.c_str(), pointcut, scope, flow); - ok(rc == 0 && pointcut == "before" && scope == "" && flow == "NA", - "%s is correctly parsed", anno8.c_str()); -} - -int main(int argc, char *argv[]) -{ - int rc = -1; - plan(8); - parse_valid_annotations(); - done_testing(); - return EXIT_SUCCESS; -} - -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */ diff --git a/src/c/parser/test/anno_validate_test.cpp b/src/c/parser/test/anno_validate_test.cpp deleted file mode 100644 index 68031574..00000000 --- a/src/c/parser/test/anno_validate_test.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/************************************************************\ - * Copyright 2021 Lawrence Livermore National Security, LLC - * (c.f. AUTHORS, NOTICE.LLNS, COPYING) - * - * This file is part of the Flux resource manager framework. - * For details, see https://github.com/flux-framework. - * - * SPDX-License-Identifier: LGPL-3.0 -\************************************************************/ - -#include -#include -#include "tap.h" -#include "perfflow_parser.hpp" - -void test_valid_annotations() -{ - int rc = -1; - std::string anno1("@critical_path()"); - rc = perfflow_parser_validate(anno1.c_str()); - ok(rc == 0, "%s is valid", anno1.c_str()); - - std::string anno2("@critical_path ( )"); - rc = perfflow_parser_validate(anno2.c_str()); - ok(rc == 0, "%s is valid", anno2.c_str()); - - std::string anno3("@perfflowaspect.aspect.critical_path ()"); - rc = perfflow_parser_validate(anno3.c_str()); - ok(rc == 0, "%s is valid", anno3.c_str()); - - std::string anno4("@critical_path (pointcut='around')"); - rc = perfflow_parser_validate(anno4.c_str()); - ok(rc == 0, "%s is valid", anno4.c_str()); - - std::string anno5("@critical_path (pointcut ='around')"); - rc = perfflow_parser_validate(anno5.c_str()); - ok(rc == 0, "%s is valid", anno5.c_str()); - - std::string anno6("@critical_path (pointcut='before', scope='foo')"); - rc = perfflow_parser_validate(anno6.c_str()); - ok(rc == 0, "%s is valid", anno6.c_str()); - - std::string anno7("@critical_path (scope='foo', pointcut='before')"); - rc = perfflow_parser_validate(anno7.c_str()); - ok(rc == 0, "%s is valid", anno7.c_str()); - - std::string anno8("@critical_path (scope='', flow='out', pointcut='before')"); - rc = perfflow_parser_validate(anno8.c_str()); - ok(rc == 0, "%s is valid", anno8.c_str()); -} - -void test_invalid_annotations() -{ - int rc = -1; - std::string anno1("@critical _path()"); - rc = perfflow_parser_validate(anno1.c_str()); - ok(rc != 0, "%s is not valid", anno1.c_str()); - - std::string anno2("@critcal_path ( )"); - rc = perfflow_parser_validate(anno2.c_str()); - ok(rc != 0, "%s is not valid", anno2.c_str()); - - std::string anno3("@aspect.critical_path ()"); - rc = perfflow_parser_validate(anno3.c_str()); - ok(rc != 0, "%s is not valid", anno3.c_str()); - - std::string anno4("@critical_path (pointcut='arond')"); - rc = perfflow_parser_validate(anno4.c_str()); - ok(rc != 0, "%s is not valid", anno4.c_str()); - - std::string anno5("@critical_path (point_cut='around')"); - rc = perfflow_parser_validate(anno5.c_str()); - ok(rc != 0, "%s is not valid", anno5.c_str()); - - std::string anno6("@critical_path (pointcut='before' scope='foo')"); - rc = perfflow_parser_validate(anno6.c_str()); - ok(rc != 0, "%s is not valid", anno6.c_str()); - - std::string anno7("@critical_path (scope='bar', pointcut='')"); - rc = perfflow_parser_validate(anno7.c_str()); - ok(rc != 0, "%s is not valid", anno7.c_str()); -} - -int main(int argc, char *argv[]) -{ - int rc = -1; - plan(15); - test_valid_annotations(); - test_invalid_annotations(); - done_testing(); - return EXIT_SUCCESS; -} - -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */ diff --git a/src/c/weaver/CMakeLists.txt b/src/c/weaver/CMakeLists.txt index f04f215f..da50832d 100644 --- a/src/c/weaver/CMakeLists.txt +++ b/src/c/weaver/CMakeLists.txt @@ -1,5 +1,5 @@ # support C++14 features used by LLVM 10.0.0 -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) if(LLVM_FOUND) add_definitions(${LLVM_DEFINITIONS}) diff --git a/src/c/weaver/weave/CMakeLists.txt b/src/c/weaver/weave/CMakeLists.txt index 1702a662..b92af3fa 100644 --- a/src/c/weaver/weave/CMakeLists.txt +++ b/src/c/weaver/weave/CMakeLists.txt @@ -12,8 +12,6 @@ set_target_properties(WeavePass PROPERTIES COMPILE_FLAGS "-fno-rtti" ) -target_link_libraries(WeavePass perfflow_parser ${JANSSON_LIB}) - add_library(WeavePassPlugin INTERFACE) target_compile_options(WeavePassPlugin INTERFACE "SHELL:$>$>" diff --git a/src/c/weaver/weave/perfflow_weave.cpp b/src/c/weaver/weave/perfflow_weave.cpp index 4e81003c..f161fad7 100644 --- a/src/c/weaver/weave/perfflow_weave.cpp +++ b/src/c/weaver/weave/perfflow_weave.cpp @@ -8,225 +8,258 @@ * SPDX-License-Identifier: LGPL-3.0 \************************************************************/ -#include "llvm/IR/Value.h" -#include "llvm/IR/Attributes.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/llvm-config.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/GlobalVariable.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/Passes/OptimizationLevel.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" + +// TODO: Unsure about the version numbers here and below. +#if LLVM_VERSION_MAJOR > 12 +#include "llvm/IR/PassManager.h" +#else #include "llvm/IR/LegacyPassManager.h" -#ifdef PERFFLOWASPECT_CLANG_11_NEWER -#include "llvm/IR/AbstractCallSite.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/IPO/PassManagerBuilder.h" +#endif + +using namespace llvm; + +#if LLVM_VERSION_MAJOR > 14 +bool stringRefStartsWith(StringRef S, StringRef P) { return S.starts_with(P); } +bool stringRefEndsWith(StringRef S, StringRef P) { return S.ends_with(P); } #else -#include "llvm/IR/CallSite.h" +bool stringRefStartsWith(StringRef S, StringRef P) { return S.startswith(P); } +bool stringRefEndsWith(StringRef S, StringRef P) { return S.endswith(P); } #endif -#include "llvm/IR/Module.h" -#include "llvm/IR/Argument.h" -#include "llvm/IR/IRBuilder.h" -#include "llvm/Transforms/IPO/PassManagerBuilder.h" -#include "llvm/Support/raw_ostream.h" -#include "../../parser/perfflow_parser.hpp" -#include "perfflow_weave.hpp" +static constexpr auto WEAVER_ANNOTATION_PREFIX = "@critical_path("; +static constexpr auto WEAVER_ANNOTATION_SUFFIX = ")"; -using namespace llvm; +struct WeavingPassImpl { + WeavingPassImpl(Module &M) : M(M) {} + Module &M; + LLVMContext &Ctx = M.getContext(); + Type *VoidTy = Type::getVoidTy(Ctx); + Type *Int32Ty = Type::getInt32Ty(Ctx); + Type *VoidPtrTy = PointerType::get(Type::getInt8Ty(Ctx), 0); -/****************************************************************************** - * * - * Private Methods of WeavingPass Class * - * * - ******************************************************************************/ + FunctionType *WeaveFuncTy = FunctionType::get( + VoidTy, {Int32Ty, VoidPtrTy, VoidPtrTy, VoidPtrTy, VoidPtrTy, VoidPtrTy}, + false); -bool WeavingPass::insertAfter(Module &m, Function &f, StringRef &a, - int async, std::string &scope, std::string &flow, std::string pcut) -{ - if (m.empty() || f.empty()) - { - return false; - } + void createCall(StringRef RuntimeFunctionName, Instruction &I, int Async, + StringRef Scope, StringRef Flow, StringRef Pcut) { + auto FC = M.getOrInsertFunction(RuntimeFunctionName, WeaveFuncTy); - auto &context = m.getContext(); - Type *voidType = Type::getVoidTy(context); - Type *int32Type = Type::getInt32Ty(context); - Type *int8PtrType = Type::getInt8PtrTy(context); - std::vector params; - params.push_back(int32Type); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - - // voidType is return type, params are parameters and no variable length args - FunctionType *weaveFuncTy = FunctionType::get(voidType, params, false); - // Note: Use FunctionCallee after for Clang 9 or higher - FunctionCallee after = m.getOrInsertFunction("perfflow_weave_after", - weaveFuncTy); - // Constant *after = m.getOrInsertFunction("perfflow_weave_after", - // weaveFuncTy); - - // iterate through blocks - for (BasicBlock &bb : f) - { - Instruction *inst = bb.getTerminator(); - if (isa(inst) || isa(inst)) - { - IRBuilder<> builder(inst); - Value *v1 = builder.CreateGlobalStringPtr(m.getName(), "str"); - Value *v2 = builder.CreateGlobalStringPtr(f.getName(), "str"); - Value *v3 = builder.CreateGlobalStringPtr(StringRef(scope), "str"); - Value *v4 = builder.CreateGlobalStringPtr(StringRef(flow), "str"); - Value *v5 = builder.CreateGlobalStringPtr(StringRef(pcut), "str"); - std::vector args; - args.push_back(ConstantInt::get(Type::getInt32Ty(context), async)); - args.push_back(v1); - args.push_back(v2); - args.push_back(v3); - args.push_back(v4); - args.push_back(v5); - builder.CreateCall(after, args); - } + IRBuilder<> IRB(&I); + auto *ModuleName = IRB.CreateGlobalString(M.getName()); + auto *FunctionName = IRB.CreateGlobalString(I.getFunction()->getName()); + auto *ScopeName = IRB.CreateGlobalString(Scope); + auto *FlowName = IRB.CreateGlobalString(Flow); + auto *PcutName = IRB.CreateGlobalString(Pcut); + IRB.CreateCall(FC, {ConstantInt::get(Int32Ty, Async), ModuleName, + FunctionName, ScopeName, FlowName, PcutName}); + } + + bool insertAfter(Function &F, int Async, StringRef Scope, StringRef Flow, + StringRef Pcut) { + for (auto &BB : F) { + auto *TI = BB.getTerminator(); + if (isa(TI) || isa(TI)) + createCall("perfflow_weave_after", *TI, Async, Scope, Flow, Pcut); } return true; -} + } -bool WeavingPass::insertBefore(Module &m, Function &f, StringRef &a, - int async, std::string &scope, std::string &flow, std::string pcut) -{ - if (m.empty() || f.empty()) - { + bool insertBefore(Function &F, int Async, StringRef Scope, StringRef Flow, + StringRef Pcut) { + createCall("perfflow_weave_before", F.getEntryBlock().front(), Async, Scope, + Flow, Pcut); + return true; + } + + bool parseAnnotation(StringRef AnnotationString, StringRef &Pointcut, + StringRef &Scope, StringRef &Flow, bool &Async, + bool &Before, bool &After) { + auto Consume = [&](StringRef Tokens, StringRef Warning) { + AnnotationString = AnnotationString.trim(); + if (AnnotationString.consume_front(Tokens)) + return true; + if (!Warning.empty()) + outs() << "WeavePass[WARN]: " << Warning << "\n"; + return false; + }; + + std::array Values = {&Pointcut, &Scope, &Flow}; + std::array Options = {"pointcut", "scope", "flow"}; + bool Progress = true; + while (Progress) { + Progress = false; + for (int Idx = 0; Idx < Options.size(); ++Idx) { + auto Option = Options[Idx]; + AnnotationString = AnnotationString.trim(); + if (!AnnotationString.consume_front(Option)) + continue; + Progress = true; + if (!Consume("=", "expected \"=\" after option")) + return false; + if (!Consume("'", "expected \"'\" before value")) + return false; + AnnotationString = AnnotationString.trim(); + *Values[Idx] = + AnnotationString.take_until([](char C) { return C == '\''; }); + Consume(*Values[Idx], ""); + if (!Consume("'", "expected \"'\" after value")) + return false; + if (!Consume(",", "")) + break; + } + if (!Progress && !AnnotationString.empty()) { + outs() << "Leftover annotation '" << AnnotationString + << "', expected (X='value', Y='value', ...) with X,Y in {" + << join(Options, ",") << "}\n"; return false; + } + } + + // TODO: verify the values + std::array ValidValues = {"around", "before", + "after", "around_async", + "before_async", "after_async"}; + if (!any_of(ValidValues, + [&](auto ValidValue) { return Pointcut.equals(ValidValue); })) { + outs() << "pointcut value was " << Pointcut << " but expected any of {" + << join(ValidValues, ",") << "}\n"; + return false; } - auto &context = m.getContext(); - Type *voidType = Type::getVoidTy(context); - Type *int32Type = Type::getInt32Ty(context); - Type *int8PtrType = Type::getInt8PtrTy(context); - std::vector params; - params.push_back(int32Type); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - params.push_back(int8PtrType); - - // voidType is return type, params are parameters and no variable length args - FunctionType *weaveFuncTy = FunctionType::get(voidType, params, false); - // Note: User FunctionCallee before for Clang >= 9.0 - FunctionCallee before = m.getOrInsertFunction("perfflow_weave_before", - weaveFuncTy); - //Constant *before = m.getOrInsertFunction ("perfflow_weave_before", - // weaveFuncTy); - auto &entry = f.getEntryBlock(); - IRBuilder<> builder(&entry); - Value *v1 = builder.CreateGlobalStringPtr(m.getName(), "str"); - Value *v2 = builder.CreateGlobalStringPtr(f.getName(), "str"); - Value *v3 = builder.CreateGlobalStringPtr(StringRef(scope), "str"); - Value *v4 = builder.CreateGlobalStringPtr(StringRef(flow), "str"); - Value *v5 = builder.CreateGlobalStringPtr(StringRef(pcut), "str"); - builder.SetInsertPoint(&entry, entry.begin()); - std::vector args; - args.push_back(ConstantInt::get(Type::getInt32Ty(context), async)); - args.push_back(v1); - args.push_back(v2); - args.push_back(v3); - args.push_back(v4); - args.push_back(v5); - builder.CreateCall(before, args); + if (stringRefStartsWith(Pointcut, "around")) { + Before = After = true; + } else if (stringRefStartsWith(Pointcut, "before")) { + Before = true; + } else if (stringRefStartsWith(Pointcut, "after")) { + After = true; + } + if (stringRefEndsWith(Pointcut, "_async")) + Async = true; return true; -} + } + bool run() { + bool Changed = false; + outs() << "WeavePass loaded successfully. \n"; -/****************************************************************************** - * * - * Public Methods of WeavingPass Class * - * * - ******************************************************************************/ + auto *AnnotationsGV = M.getNamedGlobal("llvm.global.annotations"); + if (!AnnotationsGV) + return false; -bool WeavingPass::doInitialization(Module &m) -{ - outs() << "WeavePass loaded successfully. \n"; + // Get the array of annotations. + auto AnnotationsArray = AnnotationsGV->getInitializer(); - auto annotations = m.getNamedGlobal("llvm.global.annotations"); - if (!annotations) - { - return false; - } + for (auto &OpU : AnnotationsArray->operands()) { + // Check each element for function annotations. + auto AnnotationStruct = cast(OpU); + auto *Fn = dyn_cast( + AnnotationStruct->getOperand(0)->stripPointerCasts()); + if (!Fn) + continue; + + // We expect global string annotations + auto *AnnotationStringGV = dyn_cast( + AnnotationStruct->getOperand(1)->stripPointerCasts()); + if (!AnnotationStringGV || !AnnotationStringGV->getInitializer()) + continue; + StringRef AnnotationString = + cast(AnnotationStringGV->getInitializer()) + ->getAsCString(); + AnnotationString = AnnotationString.trim(); + + // Verify the annotation is meant for us. + if (!stringRefStartsWith(AnnotationString, WEAVER_ANNOTATION_PREFIX) || + !stringRefEndsWith(AnnotationString, WEAVER_ANNOTATION_SUFFIX)) + continue; + AnnotationString.consume_front(WEAVER_ANNOTATION_PREFIX); + AnnotationString.consume_back(WEAVER_ANNOTATION_SUFFIX); + + // Parse the values. + StringRef Pointcut = "around", Scope = "NA", Flow = "NA"; + bool Before = false, After = false, Async = false; + if (!parseAnnotation(AnnotationString, Pointcut, Scope, Flow, Async, + Before, After)) { + errs() << "WeavePass[WARN]: Ignoring " << AnnotationString << "\n"; + continue; + } + outs() << "Pointcut " << Pointcut << " Scope " << Scope + << " Flow: " << Flow << " Before " << Before << " After: " << After + << " Async " << Async << "\n"; - bool changed = false; - auto a = cast (annotations->getOperand(0)); - for (unsigned int i = 0; i < a->getNumOperands(); i++) - { - auto e = cast (a->getOperand(i)); - if (auto *fn = dyn_cast (e->getOperand(0)->getOperand(0))) - { - auto anno = cast( - cast(e->getOperand(1)->getOperand(0)) - ->getOperand(0)) - ->getAsCString(); - std::string pcut, scope, flow; - if (perfflow_parser_parse(anno.data(), pcut, scope, flow) == 0) - { - if (pcut == "around" || pcut == "before") - changed = insertBefore(m, *fn, - anno, 0, scope, flow, pcut) || changed; - else if (pcut == "around_async" || pcut == "before_async") - { - changed = insertBefore(m, *fn, - anno, 1, scope, flow, pcut) || changed; - } - if (pcut == "around" || pcut == "after") - { - if (pcut == "around") - { - if (flow == "in" || flow == "out") - { - flow = "NA"; - } - } - changed = insertAfter(m, *fn, - anno, 0, scope, flow, pcut) || changed; - } - else if (pcut == "around_async" || pcut == "after_async") - { - if (pcut == "around") - { - if (flow == "in" || flow == "out") - { - flow = "NA"; - } - } - changed = insertAfter(m, *fn, - anno, 1, scope, flow, pcut) || changed; - } - } - else - { - errs() << "WeavePass[WARN]: Ignoring " << anno << "\n"; - } - } + // Insert runtime calls. + if (Before) + Changed |= insertBefore(*Fn, Async, Scope, Flow, Pointcut); + if (After) + Changed |= insertAfter( + *Fn, Async, Scope, + Before && (Flow.equals("in") || Flow.equals("out")) ? "NA" : Flow, + Pointcut); } - return changed; -} + return Changed; + } +}; + +namespace { + +#if LLVM_VERSION_MAJOR > 12 -bool WeavingPass::runOnFunction(Function &F) -{ - return false; +struct WeavingPass : public PassInfoMixin { + PreservedAnalyses run(Module &M, ModuleAnalysisManager &) { + return WeavingPassImpl(M).run() ? PreservedAnalyses::none() + : PreservedAnalyses::all(); + } + static bool isRequired() { return true; } + static void registerPass(PassBuilder &PB) { + PB.registerPipelineStartEPCallback( + [](ModulePassManager &MPM, auto) { MPM.addPass(WeavingPass()); }); + } +}; + +extern "C" PassPluginLibraryInfo LLVM_ATTRIBUTE_WEAK llvmGetPassPluginInfo() { + return {LLVM_PLUGIN_API_VERSION, "weaver-pass", LLVM_VERSION_STRING, + WeavingPass::registerPass}; } -char WeavingPass::ID = 0; +#else -// Automatically enable the pass. -// http://adriansampson.net/blog/clangpass.html -static void registerWeavingPass(const PassManagerBuilder &, - legacy::PassManagerBase &PM) -{ - PM.add(new WeavingPass()); +struct WeavingLegacyPass : public ModulePass { + static char ID; + WeavingLegacyPass() : ModulePass(ID) {} + virtual bool runOnModule(Module &M) override { + outs() << __PRETTY_FUNCTION__ << "\n"; + return WeavingPassImpl(M).run(); + } +}; + +char WeavingLegacyPass::ID = 0; + +static void registerWeavingLegacyPass(const PassManagerBuilder &, + legacy::PassManagerBase &PM) { + outs() << __PRETTY_FUNCTION__ << "\n"; + PM.add(new WeavingLegacyPass()); } static RegisterStandardPasses -RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, - registerWeavingPass); + RegisterMyPass(PassManagerBuilder::EP_EarlyAsPossible, + registerWeavingLegacyPass); + +#endif -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */ +} // namespace diff --git a/src/c/weaver/weave/perfflow_weave.hpp b/src/c/weaver/weave/perfflow_weave.hpp deleted file mode 100644 index 3db17445..00000000 --- a/src/c/weaver/weave/perfflow_weave.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/************************************************************\ - * Copyright 2021 Lawrence Livermore National Security, LLC - * (c.f. AUTHORS, NOTICE.LLNS, COPYING) - * - * This file is part of the Flux resource manager framework. - * For details, see https://github.com/flux-framework. - * - * SPDX-License-Identifier: LGPL-3.0 -\************************************************************/ - -#ifndef PERFFLOW_WEAVE_H -#define PERFFLOW_WEAVE_H - -#include "llvm/IR/Function.h" -#include "llvm/IR/Module.h" -#include "llvm/Pass.h" - -#include - -using namespace llvm; - -namespace { - -class WeavingPass : public FunctionPass -{ -public: - static char ID; - WeavingPass () : FunctionPass (ID) {} - virtual bool doInitialization (Module &m); - virtual bool runOnFunction (Function &F); - -private: - bool insertAfter (Module &m, Function &f, StringRef &a, - int async, std::string &scope, std::string &flow, std::string pcut); - bool insertBefore (Module &m, Function &f, StringRef &a, - int async, std::string &scope, std::string &flow, std::string pcut); -}; - -} - -#endif // PERFFLOW_WEAVE_H - - -/* - * vi:tabstop=4 shiftwidth=4 expandtab - */