From f1d3ddbde52b630ca66574b5e01b08be715c459a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:37 +0100 Subject: [PATCH 001/130] add/modified cmakefiles to add cgfcollector subproject --- CMakeLists.txt | 12 ++++++++++++ cgfcollector/CMakeLists.txt | 28 ++++++++++++++++++++++++++++ cmake/ClangLLVM.cmake | 5 +++++ 3 files changed, 45 insertions(+) create mode 100644 cgfcollector/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index cf535882..f8364e6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,3 +213,15 @@ if(METACG_BUILD_PYMETACG) endif() add_subdirectory(utils) + +# Build fortran collector +option( + METACG_BUILD_CGFCOLLECTOR + "On or off" + OFF +) + +if(METACG_BUILD_CGFCOLLECTOR) + include(ClangLLVM) + add_subdirectory(cgfcollector) +endif() diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt new file mode 100644 index 00000000..a675fc3b --- /dev/null +++ b/cgfcollector/CMakeLists.txt @@ -0,0 +1,28 @@ +set(PROJECT_NAME fcollector) +set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-target) + +file( + GLOB + FCOLLECTOR_SOURCES + src/*.cpp +) + +add_library(${PROJECT_NAME} SHARED ${FCOLLECTOR_SOURCES}) +add_flang(${PROJECT_NAME}) +add_metacg(${PROJECT_NAME}) +add_spdlog_libraries(${PROJECT_NAME}) +# add_json(${PROJECT_NAME}) + +install( + TARGETS ${PROJECT_NAME} + EXPORT ${TARGETS_EXPORT_NAME} + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) + +configure_package_config_file( + ${METACG_Directory}/cmake/Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + INSTALL_DESTINATION lib/cmake +) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION lib/cmake) diff --git a/cmake/ClangLLVM.cmake b/cmake/ClangLLVM.cmake index 36ef0850..e7e84ff1 100644 --- a/cmake/ClangLLVM.cmake +++ b/cmake/ClangLLVM.cmake @@ -79,3 +79,8 @@ function(add_clang target) endif() endfunction() + +function(add_flang target) + target_compile_definitions(${target} PRIVATE FLANG_LITTLE_ENDIAN) + target_link_libraries(${target} PUBLIC flangFrontendTool) +endfunction() From 828ac1ffb769d5b828fc78614b97d1d91f023db3 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:38 +0100 Subject: [PATCH 002/130] WIP: add simple function parsing and integrated graphlib --- cgfcollector/src/main.cpp | 116 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 cgfcollector/src/main.cpp diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp new file mode 100644 index 00000000..5a1ab38f --- /dev/null +++ b/cgfcollector/src/main.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction { + struct ParseTreeVisitor { + std::unordered_map> functionCalls; + std::vector functionNames; + + template + void handleFuncSubStmt(const T& stmt) { + if (auto* sym = std::get(stmt.t).symbol) { + functionNames.emplace_back(Fortran::lower::mangle::mangleName(*sym)); + functionCalls.emplace(functionNames.back(), std::vector()); + } + } + void handleEndFuncSubStmt() { + if (!functionNames.empty()) { + functionNames.pop_back(); + } + } + + template + bool Pre(const A&) { + return true; + } + template + void Post(const A&) {} + + bool Pre(const Fortran::parser::MainProgram& p) { + if (const auto& maybeStmt = std::get<0>(p.t)) { + if (maybeStmt->statement.v.symbol) { + functionNames.emplace_back(Fortran::lower::mangle::mangleName(*maybeStmt->statement.v.symbol)); + functionCalls.emplace(functionNames.back(), std::vector()); + } + } + return true; + } + + void Post(const Fortran::parser::MainProgram&) { + if (!functionNames.empty()) { + functionNames.pop_back(); + } + } + + void Post(const Fortran::parser::FunctionStmt& f) { handleFuncSubStmt(f); } + void Post(const Fortran::parser::EndFunctionStmt&) { handleEndFuncSubStmt(); } + void Post(const Fortran::parser::SubroutineStmt& s) { handleFuncSubStmt(s); } + void Post(const Fortran::parser::EndSubroutineStmt&) { handleEndFuncSubStmt(); } + + void Post(const Fortran::parser::ProcedureDesignator& p) { + if (auto* name = std::get_if(&p.u)) { + if (name->symbol) { + std::string callee = Fortran::lower::mangle::mangleName(*name->symbol); + // TODO + if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) { + callee = "intrinsic" + callee; + } + if (!functionNames.empty()) { + functionCalls[functionNames.back()].emplace_back(callee); + } + } + } + } + }; + + void executeAction() override { + ParseTreeVisitor visitor; + Fortran::parser::Walk(getParsing().parseTree(), visitor); + + auto cg = std::make_unique(); + for (const auto& [functionName, functionCalls] : visitor.functionCalls) { + auto* node = cg->getOrInsertNode(functionName); + for (const auto& call : functionCalls) { + auto* calleeNode = cg->getOrInsertNode(call); + cg->addEdge(node, calleeNode); + } + } + + auto& mcgManager = metacg::graph::MCGManager::get(); + + // mcgManager.resetManager(); + mcgManager.addToManagedGraphs("test", std::move(cg), true); + mcgManager.mergeIntoActiveGraph(); + + auto mcgWriter = std::make_unique(metacg::getVersionTwoFileInfo({ + std::string("CGCollector"), + MetaCG_VERSION_MAJOR, + MetaCG_VERSION_MINOR, + })); + if (!mcgWriter) { + llvm::errs() << "Unable to create a writer\n"; + return; + }; + + metacg::io::JsonSink jsonSink; + mcgWriter->writeActiveGraph(jsonSink); + + auto file = createOutputFile("json"); + file->write(jsonSink.getJson().dump().c_str(), jsonSink.getJson().dump().size()); + } +}; + +static Fortran::frontend::FrontendPluginRegistry::Add X("genCG", "Generate Callgraph"); From 11718890bf5db2d5ee48d73963230940b551bed3 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:38 +0100 Subject: [PATCH 003/130] add wrapper and simple test script --- cgfcollector/CMakeLists.txt | 38 +++++++++++++++++++++++++ cgfcollector/cgfcollector_wrapper.sh.in | 12 ++++++++ cgfcollector/test/test_runner.sh.in | 33 +++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100755 cgfcollector/cgfcollector_wrapper.sh.in create mode 100755 cgfcollector/test/test_runner.sh.in diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index a675fc3b..a5af2c45 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -26,3 +26,41 @@ configure_package_config_file( ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION lib/cmake) + +# generate wrapper script with build dir for easy of use during development +set(FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/cgfcollector_wrapper.sh.in") +set(FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh.in") + +option( + FCOLLECTOR_ADD_BUILD_TO_LIB_PATH + "Add build dir to LD_LIBRARY_PATH in wrapper script" + OFF +) + +set(FCOLLECTOR_WRAPPER_EXPORT_LINE "") +if(FCOLLECTOR_ADD_BUILD_TO_LIB_PATH) + set(FCOLLECTOR_WRAPPER_EXPORT_LINE "export LD_LIBRARY_PATH=\"${CMAKE_CURRENT_BINARY_DIR}:\$LD_LIBRARY_PATH\"") +endif() + +set(FCOLLECTOR_FILE_NAME "lib${PROJECT_NAME}.so") + +configure_file( + ${FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE} + ${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT} + @ONLY +) +install(PROGRAMS ${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT} DESTINATION bin) + +set(FCOLLECTOR_TEST_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/test/test_runner.sh.in") +set(FCOLLECTOR_TEST_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test_runner.sh") + +set(FCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") +set(FCOLLECTOR_WRAPPER "${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT}") + +configure_file( + ${FCOLLECTOR_TEST_SCRIPT_TEMPLATE} + ${FCOLLECTOR_TEST_SCRIPT_OUTPUT} + @ONLY +) + +install(PROGRAMS ${FCOLLECTOR_TEST_SCRIPT_OUTPUT} DESTINATION bin) diff --git a/cgfcollector/cgfcollector_wrapper.sh.in b/cgfcollector/cgfcollector_wrapper.sh.in new file mode 100755 index 00000000..9fbac358 --- /dev/null +++ b/cgfcollector/cgfcollector_wrapper.sh.in @@ -0,0 +1,12 @@ +#!/bin/bash + +flang_bin="flang-new" + +@FCOLLECTOR_WRAPPER_EXPORT_LINE@ + +if ! command -v "$flang_bin" &>/dev/null; then + echo "Error: $flang_bin not found in PATH." + exit 1 +fi + +$flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" "$@" diff --git a/cgfcollector/test/test_runner.sh.in b/cgfcollector/test/test_runner.sh.in new file mode 100755 index 00000000..82e27e1b --- /dev/null +++ b/cgfcollector/test/test_runner.sh.in @@ -0,0 +1,33 @@ +#!/bin/bash + +test_case_dir="@FCOLLECTOR_TEST_CASES_DIR@" +out_dir="out" + +if [[ ! -d "$out_dir" ]]; then + mkdir "$out_dir" +fi + +find "$test_case_dir" -type d | while read -r dir; do + mapfile -t input_files < <(find "$dir" -maxdepth 1 -name '*.f90') + output_file="$dir/output.json" + + if [[ ${#input_files[@]} -gt 0 && -f "$output_file" ]]; then + # generate test case name from dir path. test/simple/01 becomes simple_01 + test_case_name="$(basename "$(dirname "$dir")")_$(basename "$dir")" + + echo "Test case: $test_case_name" + + if ! @FCOLLECTOR_WRAPPER@ -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then + echo "Error: failed to generate callgraph" + continue + fi + + if ! diff -q <(tr -d '[:space:]' <"$out_dir/$test_case_name.json") <(tr -d '[:space:]' <"$output_file") >/dev/null; then + echo "Error: Output mismatch" + echo "Expected: $(cat "$output_file")" + echo "Got: $(cat "$out_dir/$test_case_name.json")" + else + echo "Test case passed" + fi + fi +done From 7a75d16cb26c601beeee6b114283c99a5bef7b7f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:38 +0100 Subject: [PATCH 004/130] add main function mangling for fortran to correctly identify main function --- graph/src/Callgraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graph/src/Callgraph.cpp b/graph/src/Callgraph.cpp index 8dcd4ee1..1cbbe25a 100644 --- a/graph/src/Callgraph.cpp +++ b/graph/src/Callgraph.cpp @@ -33,7 +33,7 @@ CgNode* Callgraph::getMain(bool forceRecompute) const { // Otherwise, try to find by name. if ((mainNode = getFirstNode("main")) || (mainNode = getFirstNode("_Z4main")) || - (mainNode = getFirstNode("_ZSt4mainiPPc"))) { + (mainNode = getFirstNode("_ZSt4mainiPPc")) || (mainNode = getFirstNode("_QQmain"))) { return mainNode; } From ea1f6b5b1e11eb503323fe3af11d17d0a85b4522 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 005/130] add simple test case 1 --- cgfcollector/test/simple/01/input.f90 | 15 ++++++++++++ cgfcollector/test/simple/01/output.json | 32 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 cgfcollector/test/simple/01/input.f90 create mode 100644 cgfcollector/test/simple/01/output.json diff --git a/cgfcollector/test/simple/01/input.f90 b/cgfcollector/test/simple/01/input.f90 new file mode 100644 index 00000000..8d049bea --- /dev/null +++ b/cgfcollector/test/simple/01/input.f90 @@ -0,0 +1,15 @@ +program main + implicit none + + call print_stars(5) +contains + subroutine print_stars(n) + implicit none + integer, intent(in) :: n + integer :: i + + do i = 1, n + write (*, *) '*' + end do + end subroutine print_stars +end program main diff --git a/cgfcollector/test/simple/01/output.json b/cgfcollector/test/simple/01/output.json new file mode 100644 index 00000000..7803fc83 --- /dev/null +++ b/cgfcollector/test/simple/01/output.json @@ -0,0 +1,32 @@ +{ + "_CG": { + "_QFPprint_stars": { + "callees": [], + "callers": ["_QQmain"], + "doesOverride": false, + "hasBody": false, + "isVirtual": false, + "meta": null, + "overriddenBy": [], + "overrides": [] + }, + "_QQmain": { + "callees": ["_QFPprint_stars"], + "callers": [], + "doesOverride": false, + "hasBody": false, + "isVirtual": false, + "meta": null, + "overriddenBy": [], + "overrides": [] + } + }, + "_MetaCG": { + "generator": { + "name": "CGCollector", + "sha": "", + "version": "0.7" + }, + "version": "2.0" + } +} From dfd778932811743a2d8a4f457b04428ec425e3c4 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 006/130] added test case using a module --- cgfcollector/test/simple/02/main.f90 | 9 +++++++++ cgfcollector/test/simple/02/module.f90 | 13 +++++++++++++ cgfcollector/test/simple/02/output.json | 1 + 3 files changed, 23 insertions(+) create mode 100644 cgfcollector/test/simple/02/main.f90 create mode 100644 cgfcollector/test/simple/02/module.f90 create mode 100644 cgfcollector/test/simple/02/output.json diff --git a/cgfcollector/test/simple/02/main.f90 b/cgfcollector/test/simple/02/main.f90 new file mode 100644 index 00000000..410c3260 --- /dev/null +++ b/cgfcollector/test/simple/02/main.f90 @@ -0,0 +1,9 @@ +program main + use my_module, only: my_subroutine + + implicit none + + call my_subroutine(5) + +end program main + diff --git a/cgfcollector/test/simple/02/module.f90 b/cgfcollector/test/simple/02/module.f90 new file mode 100644 index 00000000..3962f1c6 --- /dev/null +++ b/cgfcollector/test/simple/02/module.f90 @@ -0,0 +1,13 @@ +module my_module + + implicit none + +contains + subroutine my_subroutine(n) + integer, intent(in) :: n + + write (*, *) n + + end subroutine my_subroutine +end module my_module + diff --git a/cgfcollector/test/simple/02/output.json b/cgfcollector/test/simple/02/output.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/cgfcollector/test/simple/02/output.json @@ -0,0 +1 @@ +{} From c96cab8cf7968e52754b36de1dd0e16da94aec95 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 007/130] fix typo in CMakeLists.txt and add precompiled headers --- cgfcollector/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index a5af2c45..2a099f08 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -13,6 +13,14 @@ add_metacg(${PROJECT_NAME}) add_spdlog_libraries(${PROJECT_NAME}) # add_json(${PROJECT_NAME}) +target_precompile_headers( + ${PROJECT_NAME} + PRIVATE + include/headers.h +) + +target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) + install( TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} @@ -29,7 +37,7 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATIO # generate wrapper script with build dir for easy of use during development set(FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/cgfcollector_wrapper.sh.in") -set(FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh.in") +set(FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") option( FCOLLECTOR_ADD_BUILD_TO_LIB_PATH From 0a6f06a585bd07279b4112f8b4f0b483e62d5161 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 008/130] rework main to construct CG and set hasBody property accordingly --- cgfcollector/include/headers.h | 17 +++++ cgfcollector/src/main.cpp | 127 +++++++++++++++++++++------------ 2 files changed, 98 insertions(+), 46 deletions(-) create mode 100644 cgfcollector/include/headers.h diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h new file mode 100644 index 00000000..8b8ce06b --- /dev/null +++ b/cgfcollector/include/headers.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 5a1ab38f..31344ee4 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -1,29 +1,15 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "headers.h" class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction { - struct ParseTreeVisitor { - std::unordered_map> functionCalls; - std::vector functionNames; + class ParseTreeVisitor { + public: + ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; template void handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { functionNames.emplace_back(Fortran::lower::mangle::mangleName(*sym)); - functionCalls.emplace(functionNames.back(), std::vector()); + cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); } } void handleEndFuncSubStmt() { @@ -32,6 +18,8 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction } } + std::vector> getEdges() const { return edges; } + template bool Pre(const A&) { return true; @@ -40,21 +28,55 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction void Post(const A&) {} bool Pre(const Fortran::parser::MainProgram& p) { + inMainProgram = true; + if (const auto& maybeStmt = std::get<0>(p.t)) { - if (maybeStmt->statement.v.symbol) { - functionNames.emplace_back(Fortran::lower::mangle::mangleName(*maybeStmt->statement.v.symbol)); - functionCalls.emplace(functionNames.back(), std::vector()); - } + if (!maybeStmt->statement.v.symbol) + return true; + + functionNames.emplace_back(Fortran::lower::mangle::mangleName(*maybeStmt->statement.v.symbol)); + cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); } return true; } void Post(const Fortran::parser::MainProgram&) { + inMainProgram = false; + if (!functionNames.empty()) { functionNames.pop_back(); } } + bool Pre(const Fortran::parser::FunctionSubprogram&) { + inFunctionOrSubroutineSubProgram = true; + return true; + } + bool Post(const Fortran::parser::FunctionSubprogram&) { + inFunctionOrSubroutineSubProgram = false; + return true; + } + bool Pre(const Fortran::parser::SubroutineSubprogram&) { + inFunctionOrSubroutineSubProgram = true; + return true; + } + bool Post(const Fortran::parser::SubroutineSubprogram&) { + inFunctionOrSubroutineSubProgram = false; + return true; + } + + void Post(const Fortran::parser::ExecutionPart& e) { + if (!inFunctionOrSubroutineSubProgram && !inMainProgram) + return; + + auto* node = cg->getNode(functionNames.back()); + if (!node) { + return; + } + + node->setHasBody(true); + } + void Post(const Fortran::parser::FunctionStmt& f) { handleFuncSubStmt(f); } void Post(const Fortran::parser::EndFunctionStmt&) { handleEndFuncSubStmt(); } void Post(const Fortran::parser::SubroutineStmt& s) { handleFuncSubStmt(s); } @@ -62,44 +84,57 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction void Post(const Fortran::parser::ProcedureDesignator& p) { if (auto* name = std::get_if(&p.u)) { - if (name->symbol) { - std::string callee = Fortran::lower::mangle::mangleName(*name->symbol); - // TODO - if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) { - callee = "intrinsic" + callee; - } - if (!functionNames.empty()) { - functionCalls[functionNames.back()].emplace_back(callee); - } - } + if (!name->symbol) + return; + + std::string callee = Fortran::lower::mangle::mangleName(*name->symbol); + + // ignore intrinsic functions + if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) + return; + + if (functionNames.empty()) + return; + + edges.emplace_back(functionNames.back(), callee); } } + + private: + metacg::Callgraph* cg; + std::vector> edges; // (caller, callee) + std::string currentFileName; + + std::vector functionNames; + bool inFunctionOrSubroutineSubProgram = false; + bool inMainProgram = false; }; void executeAction() override { - ParseTreeVisitor visitor; + auto cg = std::make_unique(); + + ParseTreeVisitor visitor(cg.get(), getCurrentFile().str()); Fortran::parser::Walk(getParsing().parseTree(), visitor); - auto cg = std::make_unique(); - for (const auto& [functionName, functionCalls] : visitor.functionCalls) { - auto* node = cg->getOrInsertNode(functionName); - for (const auto& call : functionCalls) { - auto* calleeNode = cg->getOrInsertNode(call); - cg->addEdge(node, calleeNode); + // add edges + for (auto edge : visitor.getEdges()) { + auto* callerNode = cg->getNode(edge.first); + auto* calleeNode = cg->getNode(edge.second); + if (!calleeNode || !callerNode) { + llvm::outs() << "No nodes found for edge: " << edge.first << " -> " << edge.second << "\n"; + continue; } + + cg->addEdge(callerNode, calleeNode); } auto& mcgManager = metacg::graph::MCGManager::get(); - // mcgManager.resetManager(); + mcgManager.resetManager(); mcgManager.addToManagedGraphs("test", std::move(cg), true); mcgManager.mergeIntoActiveGraph(); - auto mcgWriter = std::make_unique(metacg::getVersionTwoFileInfo({ - std::string("CGCollector"), - MetaCG_VERSION_MAJOR, - MetaCG_VERSION_MINOR, - })); + auto mcgWriter = metacg::io::createWriter(3); if (!mcgWriter) { llvm::errs() << "Unable to create a writer\n"; return; From adbfaf6e46562bb89aa4476716430d36e9d1d544 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:40 +0100 Subject: [PATCH 009/130] tests now ignore _MetaCG part in json file --- cgfcollector/test/test_runner.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgfcollector/test/test_runner.sh.in b/cgfcollector/test/test_runner.sh.in index 82e27e1b..caf7cae2 100755 --- a/cgfcollector/test/test_runner.sh.in +++ b/cgfcollector/test/test_runner.sh.in @@ -22,7 +22,7 @@ find "$test_case_dir" -type d | while read -r dir; do continue fi - if ! diff -q <(tr -d '[:space:]' <"$out_dir/$test_case_name.json") <(tr -d '[:space:]' <"$output_file") >/dev/null; then + if ! diff -q <(jq 'del(._MetaCG)' "$out_dir/$test_case_name.json") <(jq 'del(._MetaCG)' "$output_file") >/dev/null; then echo "Error: Output mismatch" echo "Expected: $(cat "$output_file")" echo "Got: $(cat "$out_dir/$test_case_name.json")" From 7cc5b806f02955157b69fed39d21f33dd5f1cedb Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:40 +0100 Subject: [PATCH 010/130] update simple_01 test for MetaCG format version 3 --- cgfcollector/test/simple/01/output.json | 47 +++++++++++++------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/cgfcollector/test/simple/01/output.json b/cgfcollector/test/simple/01/output.json index 7803fc83..faf566b7 100644 --- a/cgfcollector/test/simple/01/output.json +++ b/cgfcollector/test/simple/01/output.json @@ -1,32 +1,33 @@ { "_CG": { - "_QFPprint_stars": { - "callees": [], - "callers": ["_QQmain"], - "doesOverride": false, - "hasBody": false, - "isVirtual": false, - "meta": null, - "overriddenBy": [], - "overrides": [] - }, - "_QQmain": { - "callees": ["_QFPprint_stars"], - "callers": [], - "doesOverride": false, - "hasBody": false, - "isVirtual": false, - "meta": null, - "overriddenBy": [], - "overrides": [] - } + "edges": [[[386379624964062775, 12232342110680008091], null]], + "nodes": [ + [ + 12232342110680008091, + { + "functionName": "_QFPprint_stars", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 386379624964062775, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ] + ] }, "_MetaCG": { "generator": { - "name": "CGCollector", - "sha": "", + "name": "MetaCG", + "sha": "e8fbd9caa11ea3da8a149732eeb7b9c88ba5249f", "version": "0.7" }, - "version": "2.0" + "version": "3.0" } } From 8806ae2432dd85ba905d6a477b16435e17c63053 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:40 +0100 Subject: [PATCH 011/130] add some tests --- cgfcollector/test/simple/03/input.f90 | 54 ++++++++++++++++ cgfcollector/test/simple/03/output.json | 1 + cgfcollector/test/simple/04/main.f90 | 51 +++++++++++++++ cgfcollector/test/simple/04/output.json | 1 + cgfcollector/test/simple/05/main.f90 | 59 +++++++++++++++++ cgfcollector/test/simple/05/output.json | 1 + cgfcollector/test/simple/06/main.f90 | 66 ++++++++++++++++++++ cgfcollector/test/simple/06/output.json | 1 + cgfcollector/test/simple/no_body/main.f90 | 39 ++++++++++++ cgfcollector/test/simple/no_body/output.json | 64 +++++++++++++++++++ 10 files changed, 337 insertions(+) create mode 100644 cgfcollector/test/simple/03/input.f90 create mode 100644 cgfcollector/test/simple/03/output.json create mode 100644 cgfcollector/test/simple/04/main.f90 create mode 100644 cgfcollector/test/simple/04/output.json create mode 100644 cgfcollector/test/simple/05/main.f90 create mode 100644 cgfcollector/test/simple/05/output.json create mode 100644 cgfcollector/test/simple/06/main.f90 create mode 100644 cgfcollector/test/simple/06/output.json create mode 100644 cgfcollector/test/simple/no_body/main.f90 create mode 100644 cgfcollector/test/simple/no_body/output.json diff --git a/cgfcollector/test/simple/03/input.f90 b/cgfcollector/test/simple/03/input.f90 new file mode 100644 index 00000000..1aa9fa8d --- /dev/null +++ b/cgfcollector/test/simple/03/input.f90 @@ -0,0 +1,54 @@ +module mod + implicit none + + type :: polynomial + private + real, allocatable :: a(:) + contains + procedure :: print_polynomial + final :: finalize_polynomial + end type polynomial + + interface polynomial + module procedure create_polynomial + end interface + +contains + + type(polynomial) function create_polynomial(a) + real, intent(in) :: a(0:) + integer :: degree(1) + + degree = findloc(a /= 0.0, value=.true., back=.true.) - 1 + allocate (create_polynomial%a(0:degree(1))) + create_polynomial%a(0:) = a(0:degree(1)) + end function create_polynomial + + subroutine print_polynomial(this) + class(polynomial), intent(in) :: this + write (*, *) 'Polynomial:', this%a + end subroutine print_polynomial + + subroutine finalize_polynomial(this) + type(polynomial), intent(inout) :: this + if (allocated(this%a)) then + deallocate (this%a) + end if + write (*, *) 'Finalizing polynomial' + end subroutine finalize_polynomial + +end module mod + +program main + use mod + implicit none + + type(polynomial) :: q + + work: block + q = polynomial([2., 3., 1., 0., 0.]) + + call q%print_polynomial + end block work + +end program main diff --git a/cgfcollector/test/simple/03/output.json b/cgfcollector/test/simple/03/output.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/cgfcollector/test/simple/03/output.json @@ -0,0 +1 @@ +{} diff --git a/cgfcollector/test/simple/04/main.f90 b/cgfcollector/test/simple/04/main.f90 new file mode 100644 index 00000000..34842f48 --- /dev/null +++ b/cgfcollector/test/simple/04/main.f90 @@ -0,0 +1,51 @@ +module mod + + implicit none + + type :: body + private + real :: mass + real :: pos(3), vel(3) + contains + procedure :: set_mass => set_mass_body + end type body + + type, extends(body) :: charged_body + real :: charge + contains + procedure :: set_mass => set_mass_charged_body + end type charged_body + + class(body), allocatable :: polymorphic_body + +contains + subroutine set_mass_body(this, a) + class(body), intent(inout) :: this + real, intent(in) :: a + + write (*, *) 'Setting mass in body' + + this%mass = a + end subroutine set_mass_body + + subroutine set_mass_charged_body(this, a) + class(charged_body), intent(inout) :: this + real, intent(in) :: a + + write (*, *) 'Setting mass in charged body' + + this%mass = a + end subroutine set_mass_charged_body + +end module mod + +program main + use mod + + implicit none + + allocate (charged_body :: polymorphic_body) + call polymorphic_body%set_mass(5.0) + +end program main + diff --git a/cgfcollector/test/simple/04/output.json b/cgfcollector/test/simple/04/output.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/cgfcollector/test/simple/04/output.json @@ -0,0 +1 @@ +{} diff --git a/cgfcollector/test/simple/05/main.f90 b/cgfcollector/test/simple/05/main.f90 new file mode 100644 index 00000000..6f2dc900 --- /dev/null +++ b/cgfcollector/test/simple/05/main.f90 @@ -0,0 +1,59 @@ +module shapes + implicit none + + type, abstract :: Shape + contains + procedure(draw), deferred :: draw_shape + end type Shape + + abstract interface + subroutine draw(self) + import :: Shape + class(Shape), intent(in) :: self + end subroutine draw + end interface + + type, extends(Shape) :: Circle + real :: radius + contains + procedure :: draw_shape => draw_circle + end type Circle + + type, extends(Shape) :: Rectangle + real :: width, height + contains + procedure :: draw_shape => draw_rectangle + end type Rectangle + +contains + + subroutine draw_circle(self) + class(Circle), intent(in) :: self + print *, "Drawing a circle with radius:", self%radius + end subroutine draw_circle + + subroutine draw_rectangle(self) + class(Rectangle), intent(in) :: self + print *, "Drawing a rectangle with width:", self%width, "and height:", self%height + end subroutine draw_rectangle + +end module shapes + +program main + use shapes + implicit none + + class(Shape), allocatable :: s + type(Circle) :: c + type(Rectangle) :: r + + c%radius = 5.0 + s = c + call s%draw_shape() + + r%width = 10.0 + r%height = 20.0 + s = r + call s%draw_shape() + +end program main diff --git a/cgfcollector/test/simple/05/output.json b/cgfcollector/test/simple/05/output.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/cgfcollector/test/simple/05/output.json @@ -0,0 +1 @@ +{} diff --git a/cgfcollector/test/simple/06/main.f90 b/cgfcollector/test/simple/06/main.f90 new file mode 100644 index 00000000..9d667081 --- /dev/null +++ b/cgfcollector/test/simple/06/main.f90 @@ -0,0 +1,66 @@ +module mod + + implicit none + + type, abstract :: sortable + contains + procedure(compare), deferred :: less_then + generic :: operator(<) => less_then + end type sortable + + interface + pure logical function compare(this, other) + import :: sortable + class(sortable), intent(in) :: this, other + end function compare + end interface + + type, extends(sortable) :: integer_sortable + integer :: value + contains + procedure :: less_then => less_than_integer + end type integer_sortable + +contains + pure logical function less_than_integer(this, other) + class(integer_sortable), intent(in) :: this + class(sortable), intent(in) :: other + + select type (other) + type is (integer_sortable) + less_than_integer = this%value < other%value + class default + error stop "Type mismatch in comparison" + end select + + ! unsafe + ! type(integer_sortable), allocatable :: other_int + ! other_int = transfer(other, this) + ! less_than_integer = this%value < other_int%value + + end function less_than_integer +end module mod + +program main + use mod + + implicit none + + class(sortable), allocatable :: a, b + + type(integer_sortable) :: c, d + + c%value = 5 + d%value = 10 + + allocate (a, source=c) + allocate (b, source=d) + + if (a < b) then + print *, "a is less than b" + else + print *, "a is not less than b" + end if + +end program main + diff --git a/cgfcollector/test/simple/06/output.json b/cgfcollector/test/simple/06/output.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/cgfcollector/test/simple/06/output.json @@ -0,0 +1 @@ +{} diff --git a/cgfcollector/test/simple/no_body/main.f90 b/cgfcollector/test/simple/no_body/main.f90 new file mode 100644 index 00000000..ea37b6b0 --- /dev/null +++ b/cgfcollector/test/simple/no_body/main.f90 @@ -0,0 +1,39 @@ +program main + implicit none + + interface + subroutine print_stuff(n) + implicit none + integer, intent(in)::n + end subroutine print_stuff + end interface + + call print_stuff(1) + + call print_stars(5) +contains + subroutine print_stars(n) + implicit none + integer, intent(in) :: n + integer :: i + + interface + subroutine print_stuff2(f) + implicit none + integer, intent(in)::f + end subroutine print_stuff2 + end interface + + call print_stuff(1) + + do i = 1, n + write (*, *) '*' + end do + + contains + subroutine subsub(d) + integer, intent(in) :: d + end subroutine subsub + + end subroutine print_stars +end program main diff --git a/cgfcollector/test/simple/no_body/output.json b/cgfcollector/test/simple/no_body/output.json new file mode 100644 index 00000000..2106561e --- /dev/null +++ b/cgfcollector/test/simple/no_body/output.json @@ -0,0 +1,64 @@ +{ + "_CG": { + "edges": [ + [[13116615762761984989, 4495794879652727597], null], + [[5993333203045879924, 13116615762761984989], null], + [[5993333203045879924, 4495794879652727597], null] + ], + "nodes": [ + [ + 6918181246387806493, + { + "functionName": "_QFFprint_starsPsubsub", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 4073897767569630127, + { + "functionName": "_QPprint_stuff2", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ], + [ + 13116615762761984989, + { + "functionName": "_QFPprint_stars", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 4495794879652727597, + { + "functionName": "_QPprint_stuff", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ], + [ + 5993333203045879924, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "e8fbd9caa11ea3da8a149732eeb7b9c88ba5249f", + "version": "0.7" + }, + "version": "3.0" + } +} From d29fb75510fd42ab73a3fb38aa5325c4fd2b3304 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:41 +0100 Subject: [PATCH 012/130] tests ignore the origin field as this differ from machines --- cgfcollector/test/simple/03/input.f90 | 7 +++---- cgfcollector/test/simple/03/output.json | 2 +- cgfcollector/test/test_runner.sh.in | 13 ++++++++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/cgfcollector/test/simple/03/input.f90 b/cgfcollector/test/simple/03/input.f90 index 1aa9fa8d..46351b21 100644 --- a/cgfcollector/test/simple/03/input.f90 +++ b/cgfcollector/test/simple/03/input.f90 @@ -43,12 +43,11 @@ program main use mod implicit none - type(polynomial) :: q - work: block - q = polynomial([2., 3., 1., 0., 0.]) + type(polynomial), allocatable :: q - call q%print_polynomial + q = polynomial([2., 3., 1., 0., 0.]) + call q%print_polynomial() end block work end program main diff --git a/cgfcollector/test/simple/03/output.json b/cgfcollector/test/simple/03/output.json index 0967ef42..7715fe43 100644 --- a/cgfcollector/test/simple/03/output.json +++ b/cgfcollector/test/simple/03/output.json @@ -1 +1 @@ -{} +{ "dummy": "da" } diff --git a/cgfcollector/test/test_runner.sh.in b/cgfcollector/test/test_runner.sh.in index caf7cae2..42203f14 100755 --- a/cgfcollector/test/test_runner.sh.in +++ b/cgfcollector/test/test_runner.sh.in @@ -7,22 +7,29 @@ if [[ ! -d "$out_dir" ]]; then mkdir "$out_dir" fi -find "$test_case_dir" -type d | while read -r dir; do +find "$test_case_dir" . -mindepth 1 -type d | while read -r dir; do mapfile -t input_files < <(find "$dir" -maxdepth 1 -name '*.f90') output_file="$dir/output.json" - if [[ ${#input_files[@]} -gt 0 && -f "$output_file" ]]; then + if [[ ${#input_files[@]} -gt 0 ]]; then # generate test case name from dir path. test/simple/01 becomes simple_01 test_case_name="$(basename "$(dirname "$dir")")_$(basename "$dir")" echo "Test case: $test_case_name" + if [ ! -f "$output_file" ] || [ ! -s "$output_file" ] || grep -q '{}' "$output_file"; then + echo "Skipping empty or missing output file: $output_file" + continue + fi + if ! @FCOLLECTOR_WRAPPER@ -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then echo "Error: failed to generate callgraph" continue fi - if ! diff -q <(jq 'del(._MetaCG)' "$out_dir/$test_case_name.json") <(jq 'del(._MetaCG)' "$output_file") >/dev/null; then + jq_string='del(._MetaCG) | ._CG.nodes |= map([.[0], (.[1] | del(.origin))])' + + if ! diff -q <(jq "$jq_string" "$out_dir/$test_case_name.json") <(jq "$jq_string" "$output_file") >/dev/null; then echo "Error: Output mismatch" echo "Expected: $(cat "$output_file")" echo "Got: $(cat "$out_dir/$test_case_name.json")" From 4067750fd8e9633e5bbc55af038ae8b6e4dc51b3 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:41 +0100 Subject: [PATCH 013/130] nesting test for calls with % --- cgfcollector/test/simple/nesting/main.f90 | 29 ++++++++++++ cgfcollector/test/simple/nesting/output.json | 33 ++++++++++++++ .../test/simple/nesting_calls/main.f90 | 38 ++++++++++++++++ .../test/simple/nesting_calls/output.json | 45 +++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 cgfcollector/test/simple/nesting/main.f90 create mode 100644 cgfcollector/test/simple/nesting/output.json create mode 100644 cgfcollector/test/simple/nesting_calls/main.f90 create mode 100644 cgfcollector/test/simple/nesting_calls/output.json diff --git a/cgfcollector/test/simple/nesting/main.f90 b/cgfcollector/test/simple/nesting/main.f90 new file mode 100644 index 00000000..85b73c69 --- /dev/null +++ b/cgfcollector/test/simple/nesting/main.f90 @@ -0,0 +1,29 @@ +module m + implicit none + + type :: inner + contains + procedure :: say + end type inner + + type :: outer + type(inner) :: b + end type outer + +contains + + subroutine say(this) + class(inner), intent(in) :: this + print *, "Hello from inner" + end subroutine say + +end module m + +program main + use m + implicit none + + type(outer) :: a + + call a%b%say() +end program main diff --git a/cgfcollector/test/simple/nesting/output.json b/cgfcollector/test/simple/nesting/output.json new file mode 100644 index 00000000..a15b0dcd --- /dev/null +++ b/cgfcollector/test/simple/nesting/output.json @@ -0,0 +1,33 @@ +{ + "_CG": { + "edges": [[[16400581539384321784, 12281366146811553951], null]], + "nodes": [ + [ + 16400581539384321784, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 12281366146811553951, + { + "functionName": "_QMmPsay", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "e7db04da1660956f8319a3d2fa80d30dad332800", + "version": "0.7" + }, + "version": "3.0" + } +} diff --git a/cgfcollector/test/simple/nesting_calls/main.f90 b/cgfcollector/test/simple/nesting_calls/main.f90 new file mode 100644 index 00000000..5d715a89 --- /dev/null +++ b/cgfcollector/test/simple/nesting_calls/main.f90 @@ -0,0 +1,38 @@ +module m + implicit none + + type :: T + contains + procedure :: func => return_ptr + procedure :: say + end type T + +contains + + function return_ptr(this) result(p) + class(T), intent(in) :: this + class(T), pointer :: p + allocate (T :: p) + select type (p) + type is (T) + p = this + end select + end function return_ptr + + subroutine say(this) + class(T), intent(in) :: this + print *, 'Hello from say' + end subroutine say + +end module m + +program main + use m + implicit none + + type(T) :: a + class(T), pointer :: tmp + + tmp => a%func() + call tmp%say() +end program main diff --git a/cgfcollector/test/simple/nesting_calls/output.json b/cgfcollector/test/simple/nesting_calls/output.json new file mode 100644 index 00000000..ab77bc70 --- /dev/null +++ b/cgfcollector/test/simple/nesting_calls/output.json @@ -0,0 +1,45 @@ +{ + "_CG": { + "edges": [ + [[3467742329716980950, 17909528412272728826], null], + [[3467742329716980950, 3828209433738255338], null] + ], + "nodes": [ + [ + 3467742329716980950, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 17909528412272728826, + { + "functionName": "_QMmPsay", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 3828209433738255338, + { + "functionName": "_QMmPreturn_ptr", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "e7db04da1660956f8319a3d2fa80d30dad332800", + "version": "0.7" + }, + "version": "3.0" + } +} From c67d045686bb7114538b2be56b53ee7ae6e31852 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:41 +0100 Subject: [PATCH 014/130] function calls with % are now correctly parsed and generate dot files for debugging graph edges --- cgfcollector/include/headers.h | 3 +++ cgfcollector/src/main.cpp | 40 ++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h index 8b8ce06b..83f3ca05 100644 --- a/cgfcollector/include/headers.h +++ b/cgfcollector/include/headers.h @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include #include #include @@ -13,5 +15,6 @@ #include #include #include +#include #include #include diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 31344ee4..d51261e9 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -83,20 +83,38 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction void Post(const Fortran::parser::EndSubroutineStmt&) { handleEndFuncSubStmt(); } void Post(const Fortran::parser::ProcedureDesignator& p) { + std::string callee = ""; + + // if just the name is called. (as subroutine with call and as function without call) if (auto* name = std::get_if(&p.u)) { if (!name->symbol) return; - std::string callee = Fortran::lower::mangle::mangleName(*name->symbol); - // ignore intrinsic functions if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) return; - if (functionNames.empty()) + callee = Fortran::lower::mangle::mangleName(*name->symbol); + } + + // if called from a object with % + if (auto* compRef = std::get_if(&p.u)) { + if (!compRef->v.thing.component.symbol) return; - edges.emplace_back(functionNames.back(), callee); + // ignore intrinsic functions TODO check + // if (compRef->v.thing.component.symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) + // return; + + callee = Fortran::lower::mangle::mangleName(*compRef->v.thing.component.symbol); + } + + if (functionNames.empty()) + return; + + edges.emplace_back(functionNames.back(), callee); + } + } } @@ -145,6 +163,20 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction auto file = createOutputFile("json"); file->write(jsonSink.getJson().dump().c_str(), jsonSink.getJson().dump().size()); + + // TODO: only debug ? + metacg::io::dot::DotGenerator dotGen(mcgManager.getCallgraph("test")); + dotGen.generate(); + + llvm::SmallString<128> outputPath(getInstance().getFrontendOpts().outputFile); + llvm::sys::path::replace_extension(outputPath, "dot"); + std::error_code ec; + llvm::raw_fd_ostream out(outputPath.str(), ec); + if (ec) { + llvm::errs() << "Error opening output file: " << ec.message() << "\n"; + return; + } + out << dotGen.getDotString(); } }; From 25cfa2177f59b2187d10b1df3e6dbf8f417e3cca Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:42 +0100 Subject: [PATCH 015/130] WIP: impl parsing for function overriding in derived types. And broke some tests. --- cgfcollector/src/main.cpp | 147 +++++++++++++++++++++--- cgfcollector/test/simple/03/output.json | 2 +- cgfcollector/test/simple/04/output.json | 46 +++++++- cgfcollector/test/test_runner.sh.in | 1 + 4 files changed, 181 insertions(+), 15 deletions(-) diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index d51261e9..ef77bd9d 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -52,18 +52,12 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction inFunctionOrSubroutineSubProgram = true; return true; } - bool Post(const Fortran::parser::FunctionSubprogram&) { - inFunctionOrSubroutineSubProgram = false; - return true; - } + void Post(const Fortran::parser::FunctionSubprogram&) { inFunctionOrSubroutineSubProgram = false; } bool Pre(const Fortran::parser::SubroutineSubprogram&) { inFunctionOrSubroutineSubProgram = true; return true; } - bool Post(const Fortran::parser::SubroutineSubprogram&) { - inFunctionOrSubroutineSubProgram = false; - return true; - } + void Post(const Fortran::parser::SubroutineSubprogram&) { inFunctionOrSubroutineSubProgram = false; } void Post(const Fortran::parser::ExecutionPart& e) { if (!inFunctionOrSubroutineSubProgram && !inMainProgram) @@ -99,14 +93,53 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction // if called from a object with % if (auto* compRef = std::get_if(&p.u)) { - if (!compRef->v.thing.component.symbol) + auto* symbolComp = compRef->v.thing.component.symbol; + if (!symbolComp) return; - // ignore intrinsic functions TODO check - // if (compRef->v.thing.component.symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) - // return; - callee = Fortran::lower::mangle::mangleName(*compRef->v.thing.component.symbol); + + auto* baseName = std::get_if(&compRef->v.thing.base.u); + if (!baseName || !baseName->symbol) + return; + auto* symbolBase = baseName->symbol; + + // handle derived types edges TODO test + if (auto* type = symbolBase->GetType()) { + if (auto* derived = type->AsDerived()) { + auto& typeSymbol = derived->typeSymbol(); + + auto findTypeIt = std::find_if(types.begin(), types.end(), + [&typeSymbol](const type_t& t) { return t.type == &typeSymbol; }); + if (findTypeIt == types.end()) + return; // TODO would i want to return here? yes this breaks the nesting tests + + // handle base type + auto& baseProcs = findTypeIt->procedures; + + auto baseProcIt = std::find_if(baseProcs.begin(), baseProcs.end(), [&symbolComp](const auto& p) { + return p.first->name() == symbolComp->name(); + }); + if (baseProcIt == baseProcs.end()) + return; // also here + + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); + + // handle derived types + for (const auto& t : types) { + if (t.extendsFrom != &typeSymbol) + continue; + + auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&symbolComp](const auto& p) { + return p.first->name() == symbolComp->name(); + }); + if (dProcIt == t.procedures.end()) + continue; + + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); + } + } + } } if (functionNames.empty()) @@ -115,7 +148,84 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction edges.emplace_back(functionNames.back(), callee); } + // TODO: handle destructors (finalizers) + void Post(const Fortran::parser::TypeDeclarationStmt& t) { + // TODO they dont need to hold intent + // const auto& attrs = std::get>(t.t); + // for (const auto& attr : attrs) { + // if (std::holds_alternative(attr.u)) { + // return; + // } + // } + + // const auto& entityDecls = std::get>(t.t); + // for (const auto& entity : entityDecls) { + // const auto& name = std::get(entity.t); + // if (!name.symbol) + // continue; + + // // TODO they dont need to hold intent + // if (name.symbol->attrs().test(Fortran::semantics::Attr::INTENT_IN)) + // continue; // skip intent in + + // llvm::outs() << "Found type declaration: " << name.symbol->name().ToString() << "\n"; + // if (auto* type = name.symbol->GetType()) { + // if (auto* derived = type->AsDerived()) { + // if (derived->HasDestruction()) { + // llvm::outs() << "Found derived type with destruction: " << name.symbol->name().ToString() << "\n"; + // } + // } + // } + // } + } + + // type def + bool Pre(const Fortran::parser::DerivedTypeDef&) { + inDerivedTypeDef = true; + types.emplace_back(); + + return true; + } + void Post(const Fortran::parser::DerivedTypeDef&) { inDerivedTypeDef = false; } + + // type stmt like type [, extends(...)] :: body (not exhaustive and not extends) + void Post(const Fortran::parser::DerivedTypeStmt& t) { + if (!inDerivedTypeDef) + return; + + auto& currentType = types.back(); + const auto& name = std::get(t.t); + currentType.type = name.symbol; + } + + // type attrs like extends + void Post(const Fortran::parser::TypeAttrSpec& a) { + if (!inDerivedTypeDef) + return; + + auto& currentType = types.back(); + if (std::holds_alternative(a.u)) { + const auto& extends = std::get(a.u); + currentType.extendsFrom = extends.v.symbol; + } + } + + // procedures in type defs + void Post(const Fortran::parser::TypeBoundProcDecl& d) { + if (!inDerivedTypeDef) + return; + + auto& name = std::get(d.t); + if (!name.symbol) + return; + + auto& optname = std::get>(d.t); + if (!optname || !optname->symbol) { + return; } + + auto& currentType = types.back(); + currentType.procedures.emplace_back(name.symbol, optname->symbol); } private: @@ -126,6 +236,14 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction std::vector functionNames; bool inFunctionOrSubroutineSubProgram = false; bool inMainProgram = false; + bool inDerivedTypeDef = false; + + typedef struct type { + Fortran::semantics::Symbol* type; + Fortran::semantics::Symbol* extendsFrom; + std::vector> procedures; + } type_t; + std::vector types; }; void executeAction() override { @@ -169,6 +287,9 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction dotGen.generate(); llvm::SmallString<128> outputPath(getInstance().getFrontendOpts().outputFile); + if (outputPath.empty()) { + outputPath = getCurrentFile(); + } llvm::sys::path::replace_extension(outputPath, "dot"); std::error_code ec; llvm::raw_fd_ostream out(outputPath.str(), ec); diff --git a/cgfcollector/test/simple/03/output.json b/cgfcollector/test/simple/03/output.json index 7715fe43..0967ef42 100644 --- a/cgfcollector/test/simple/03/output.json +++ b/cgfcollector/test/simple/03/output.json @@ -1 +1 @@ -{ "dummy": "da" } +{} diff --git a/cgfcollector/test/simple/04/output.json b/cgfcollector/test/simple/04/output.json index 0967ef42..19e90cf5 100644 --- a/cgfcollector/test/simple/04/output.json +++ b/cgfcollector/test/simple/04/output.json @@ -1 +1,45 @@ -{} +{ + "_CG": { + "edges": [ + [[9367282678967586139, 5793715532416084499], null], + [[9367282678967586139, 7473845567962644369], null] + ], + "nodes": [ + [ + 9367282678967586139, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 5793715532416084499, + { + "functionName": "_QMmodPset_mass_charged_body", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 7473845567962644369, + { + "functionName": "_QMmodPset_mass_body", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "78d1dc4e07062712037ce37336f919faec44b327", + "version": "0.7" + }, + "version": "3.0" + } +} diff --git a/cgfcollector/test/test_runner.sh.in b/cgfcollector/test/test_runner.sh.in index 42203f14..552905ad 100755 --- a/cgfcollector/test/test_runner.sh.in +++ b/cgfcollector/test/test_runner.sh.in @@ -27,6 +27,7 @@ find "$test_case_dir" . -mindepth 1 -type d | while read -r dir; do continue fi + # TODO: check origin jq_string='del(._MetaCG) | ._CG.nodes |= map([.[0], (.[1] | del(.origin))])' if ! diff -q <(jq "$jq_string" "$out_dir/$test_case_name.json") <(jq "$jq_string" "$output_file") >/dev/null; then From b0ac1f15f58d523785c4d59624e797cb066ab18b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:42 +0100 Subject: [PATCH 016/130] fix and cleanup the ProcedureDesignator parsing --- cgfcollector/src/main.cpp | 98 +++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 50 deletions(-) diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index ef77bd9d..0cf08a25 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -77,7 +77,8 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction void Post(const Fortran::parser::EndSubroutineStmt&) { handleEndFuncSubStmt(); } void Post(const Fortran::parser::ProcedureDesignator& p) { - std::string callee = ""; + if (functionNames.empty()) + return; // if just the name is called. (as subroutine with call and as function without call) if (auto* name = std::get_if(&p.u)) { @@ -88,64 +89,61 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) return; - callee = Fortran::lower::mangle::mangleName(*name->symbol); - } + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*name->symbol)); - // if called from a object with % - if (auto* compRef = std::get_if(&p.u)) { - auto* symbolComp = compRef->v.thing.component.symbol; + // if called from a object with %. (base % component) + } else if (auto* procCompRef = std::get_if(&p.u)) { + auto* symbolComp = procCompRef->v.thing.component.symbol; if (!symbolComp) return; - callee = Fortran::lower::mangle::mangleName(*compRef->v.thing.component.symbol); + edges.emplace_back(functionNames.back(), + Fortran::lower::mangle::mangleName(*procCompRef->v.thing.component.symbol)); - auto* baseName = std::get_if(&compRef->v.thing.base.u); + auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) return; auto* symbolBase = baseName->symbol; - // handle derived types edges TODO test - if (auto* type = symbolBase->GetType()) { - if (auto* derived = type->AsDerived()) { - auto& typeSymbol = derived->typeSymbol(); - - auto findTypeIt = std::find_if(types.begin(), types.end(), - [&typeSymbol](const type_t& t) { return t.type == &typeSymbol; }); - if (findTypeIt == types.end()) - return; // TODO would i want to return here? yes this breaks the nesting tests - - // handle base type - auto& baseProcs = findTypeIt->procedures; - - auto baseProcIt = std::find_if(baseProcs.begin(), baseProcs.end(), [&symbolComp](const auto& p) { - return p.first->name() == symbolComp->name(); - }); - if (baseProcIt == baseProcs.end()) - return; // also here - - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); - - // handle derived types - for (const auto& t : types) { - if (t.extendsFrom != &typeSymbol) - continue; - - auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&symbolComp](const auto& p) { - return p.first->name() == symbolComp->name(); - }); - if (dProcIt == t.procedures.end()) - continue; - - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); - } - } - } - } + // handle derived types edges - if (functionNames.empty()) - return; + auto* type = symbolBase->GetType(); + if (!type) + return; + auto* derived = type->AsDerived(); + if (!derived) + return; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + return; - edges.emplace_back(functionNames.back(), callee); + auto findTypeIt = + std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); + if (findTypeIt == types.end()) + return; + + // handle base type + auto baseProcIt = std::find_if(findTypeIt->procedures.begin(), findTypeIt->procedures.end(), + [&symbolComp](const auto& p) { return p.first->name() == symbolComp->name(); }); + if (baseProcIt == findTypeIt->procedures.end()) + return; + + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); + + // handle derived types + for (const auto& t : types) { + if (t.extendsFrom != typeSymbol) + continue; + + auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&symbolComp](const auto& p) { + return p.first->name() == symbolComp->name(); + }); // TODO: use statement + if (dProcIt == t.procedures.end()) + continue; + + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); + } + } } // TODO: handle destructors (finalizers) @@ -254,8 +252,8 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction // add edges for (auto edge : visitor.getEdges()) { - auto* callerNode = cg->getNode(edge.first); - auto* calleeNode = cg->getNode(edge.second); + auto* callerNode = cg->getOrInsertNode(edge.first); + auto* calleeNode = cg->getOrInsertNode(edge.second); if (!calleeNode || !callerNode) { llvm::outs() << "No nodes found for edge: " << edge.first << " -> " << edge.second << "\n"; continue; From 2df288f86ce74fc7b11018290db8c44b98159198 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:42 +0100 Subject: [PATCH 017/130] add -dot option to configure .dot generation --- cgfcollector/cgfcollector_wrapper.sh.in | 11 +- cgfcollector/src/main.cpp | 422 ++++++++++++------------ 2 files changed, 226 insertions(+), 207 deletions(-) diff --git a/cgfcollector/cgfcollector_wrapper.sh.in b/cgfcollector/cgfcollector_wrapper.sh.in index 9fbac358..7632fec8 100755 --- a/cgfcollector/cgfcollector_wrapper.sh.in +++ b/cgfcollector/cgfcollector_wrapper.sh.in @@ -4,9 +4,18 @@ flang_bin="flang-new" @FCOLLECTOR_WRAPPER_EXPORT_LINE@ +flang_args=("$@") +plugin_name="genCG" + +if [[ "$1" == "-dot" ]]; then + plugin_name="genCGwithDot" + shift + flang_args=("$@") +fi + if ! command -v "$flang_bin" &>/dev/null; then echo "Error: $flang_bin not found in PATH." exit 1 fi -$flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" "$@" +$flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "$plugin_name" "${flang_args[@]}" diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 0cf08a25..8ab3a226 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -1,249 +1,250 @@ #include "headers.h" -class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction { - class ParseTreeVisitor { - public: - ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; - - template - void handleFuncSubStmt(const T& stmt) { - if (auto* sym = std::get(stmt.t).symbol) { - functionNames.emplace_back(Fortran::lower::mangle::mangleName(*sym)); - cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); - } +class ParseTreeVisitor { + public: + ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; + + template + void handleFuncSubStmt(const T& stmt) { + if (auto* sym = std::get(stmt.t).symbol) { + functionNames.emplace_back(Fortran::lower::mangle::mangleName(*sym)); + cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); } - void handleEndFuncSubStmt() { - if (!functionNames.empty()) { - functionNames.pop_back(); - } + } + void handleEndFuncSubStmt() { + if (!functionNames.empty()) { + functionNames.pop_back(); } + } - std::vector> getEdges() const { return edges; } + std::vector> getEdges() const { return edges; } - template - bool Pre(const A&) { - return true; - } - template - void Post(const A&) {} + template + bool Pre(const A&) { + return true; + } + template + void Post(const A&) {} - bool Pre(const Fortran::parser::MainProgram& p) { - inMainProgram = true; + bool Pre(const Fortran::parser::MainProgram& p) { + inMainProgram = true; - if (const auto& maybeStmt = std::get<0>(p.t)) { - if (!maybeStmt->statement.v.symbol) - return true; + if (const auto& maybeStmt = std::get<0>(p.t)) { + if (!maybeStmt->statement.v.symbol) + return true; - functionNames.emplace_back(Fortran::lower::mangle::mangleName(*maybeStmt->statement.v.symbol)); - cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); - } - return true; + functionNames.emplace_back(Fortran::lower::mangle::mangleName(*maybeStmt->statement.v.symbol)); + cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); } + return true; + } - void Post(const Fortran::parser::MainProgram&) { - inMainProgram = false; + void Post(const Fortran::parser::MainProgram&) { + inMainProgram = false; - if (!functionNames.empty()) { - functionNames.pop_back(); - } + if (!functionNames.empty()) { + functionNames.pop_back(); } + } - bool Pre(const Fortran::parser::FunctionSubprogram&) { - inFunctionOrSubroutineSubProgram = true; - return true; - } - void Post(const Fortran::parser::FunctionSubprogram&) { inFunctionOrSubroutineSubProgram = false; } - bool Pre(const Fortran::parser::SubroutineSubprogram&) { - inFunctionOrSubroutineSubProgram = true; - return true; - } - void Post(const Fortran::parser::SubroutineSubprogram&) { inFunctionOrSubroutineSubProgram = false; } - - void Post(const Fortran::parser::ExecutionPart& e) { - if (!inFunctionOrSubroutineSubProgram && !inMainProgram) - return; + bool Pre(const Fortran::parser::FunctionSubprogram&) { + inFunctionOrSubroutineSubProgram = true; + return true; + } + void Post(const Fortran::parser::FunctionSubprogram&) { inFunctionOrSubroutineSubProgram = false; } + bool Pre(const Fortran::parser::SubroutineSubprogram&) { + inFunctionOrSubroutineSubProgram = true; + return true; + } + void Post(const Fortran::parser::SubroutineSubprogram&) { inFunctionOrSubroutineSubProgram = false; } - auto* node = cg->getNode(functionNames.back()); - if (!node) { - return; - } + void Post(const Fortran::parser::ExecutionPart& e) { + if (!inFunctionOrSubroutineSubProgram && !inMainProgram) + return; - node->setHasBody(true); + auto* node = cg->getNode(functionNames.back()); + if (!node) { + return; } - void Post(const Fortran::parser::FunctionStmt& f) { handleFuncSubStmt(f); } - void Post(const Fortran::parser::EndFunctionStmt&) { handleEndFuncSubStmt(); } - void Post(const Fortran::parser::SubroutineStmt& s) { handleFuncSubStmt(s); } - void Post(const Fortran::parser::EndSubroutineStmt&) { handleEndFuncSubStmt(); } + node->setHasBody(true); + } - void Post(const Fortran::parser::ProcedureDesignator& p) { - if (functionNames.empty()) - return; + void Post(const Fortran::parser::FunctionStmt& f) { handleFuncSubStmt(f); } + void Post(const Fortran::parser::EndFunctionStmt&) { handleEndFuncSubStmt(); } + void Post(const Fortran::parser::SubroutineStmt& s) { handleFuncSubStmt(s); } + void Post(const Fortran::parser::EndSubroutineStmt&) { handleEndFuncSubStmt(); } - // if just the name is called. (as subroutine with call and as function without call) - if (auto* name = std::get_if(&p.u)) { - if (!name->symbol) - return; - - // ignore intrinsic functions - if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) - return; - - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*name->symbol)); - - // if called from a object with %. (base % component) - } else if (auto* procCompRef = std::get_if(&p.u)) { - auto* symbolComp = procCompRef->v.thing.component.symbol; - if (!symbolComp) - return; - - edges.emplace_back(functionNames.back(), - Fortran::lower::mangle::mangleName(*procCompRef->v.thing.component.symbol)); - - auto* baseName = std::get_if(&procCompRef->v.thing.base.u); - if (!baseName || !baseName->symbol) - return; - auto* symbolBase = baseName->symbol; - - // handle derived types edges - - auto* type = symbolBase->GetType(); - if (!type) - return; - auto* derived = type->AsDerived(); - if (!derived) - return; - auto* typeSymbol = &derived->typeSymbol(); - if (!typeSymbol) - return; - - auto findTypeIt = - std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); - if (findTypeIt == types.end()) - return; - - // handle base type - auto baseProcIt = std::find_if(findTypeIt->procedures.begin(), findTypeIt->procedures.end(), - [&symbolComp](const auto& p) { return p.first->name() == symbolComp->name(); }); - if (baseProcIt == findTypeIt->procedures.end()) - return; - - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); - - // handle derived types - for (const auto& t : types) { - if (t.extendsFrom != typeSymbol) - continue; - - auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&symbolComp](const auto& p) { - return p.first->name() == symbolComp->name(); - }); // TODO: use statement - if (dProcIt == t.procedures.end()) - continue; - - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); - } - } - } + void Post(const Fortran::parser::ProcedureDesignator& p) { + if (functionNames.empty()) + return; - // TODO: handle destructors (finalizers) - void Post(const Fortran::parser::TypeDeclarationStmt& t) { - // TODO they dont need to hold intent - // const auto& attrs = std::get>(t.t); - // for (const auto& attr : attrs) { - // if (std::holds_alternative(attr.u)) { - // return; - // } - // } - - // const auto& entityDecls = std::get>(t.t); - // for (const auto& entity : entityDecls) { - // const auto& name = std::get(entity.t); - // if (!name.symbol) - // continue; - - // // TODO they dont need to hold intent - // if (name.symbol->attrs().test(Fortran::semantics::Attr::INTENT_IN)) - // continue; // skip intent in - - // llvm::outs() << "Found type declaration: " << name.symbol->name().ToString() << "\n"; - // if (auto* type = name.symbol->GetType()) { - // if (auto* derived = type->AsDerived()) { - // if (derived->HasDestruction()) { - // llvm::outs() << "Found derived type with destruction: " << name.symbol->name().ToString() << "\n"; - // } - // } - // } - // } - } + // if just the name is called. (as subroutine with call and as function without call) + if (auto* name = std::get_if(&p.u)) { + if (!name->symbol) + return; - // type def - bool Pre(const Fortran::parser::DerivedTypeDef&) { - inDerivedTypeDef = true; - types.emplace_back(); + // ignore intrinsic functions + if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) + return; - return true; - } - void Post(const Fortran::parser::DerivedTypeDef&) { inDerivedTypeDef = false; } + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*name->symbol)); - // type stmt like type [, extends(...)] :: body (not exhaustive and not extends) - void Post(const Fortran::parser::DerivedTypeStmt& t) { - if (!inDerivedTypeDef) + // if called from a object with %. (base % component) + } else if (auto* procCompRef = std::get_if(&p.u)) { + auto* symbolComp = procCompRef->v.thing.component.symbol; + if (!symbolComp) return; - auto& currentType = types.back(); - const auto& name = std::get(t.t); - currentType.type = name.symbol; - } + edges.emplace_back(functionNames.back(), + Fortran::lower::mangle::mangleName(*procCompRef->v.thing.component.symbol)); - // type attrs like extends - void Post(const Fortran::parser::TypeAttrSpec& a) { - if (!inDerivedTypeDef) + auto* baseName = std::get_if(&procCompRef->v.thing.base.u); + if (!baseName || !baseName->symbol) return; + auto* symbolBase = baseName->symbol; - auto& currentType = types.back(); - if (std::holds_alternative(a.u)) { - const auto& extends = std::get(a.u); - currentType.extendsFrom = extends.v.symbol; - } - } + // handle derived types edges - // procedures in type defs - void Post(const Fortran::parser::TypeBoundProcDecl& d) { - if (!inDerivedTypeDef) + auto* type = symbolBase->GetType(); + if (!type) + return; + auto* derived = type->AsDerived(); + if (!derived) + return; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) return; - auto& name = std::get(d.t); - if (!name.symbol) + auto findTypeIt = + std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); + if (findTypeIt == types.end()) return; - auto& optname = std::get>(d.t); - if (!optname || !optname->symbol) { + // handle base type + auto baseProcIt = std::find_if(findTypeIt->procedures.begin(), findTypeIt->procedures.end(), + [&symbolComp](const auto& p) { return p.first->name() == symbolComp->name(); }); + if (baseProcIt == findTypeIt->procedures.end()) return; + + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); + + // handle derived types + for (const auto& t : types) { + if (t.extendsFrom != typeSymbol) + continue; + + auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&symbolComp](const auto& p) { + return p.first->name() == symbolComp->name(); + }); // TODO: use statement + if (dProcIt == t.procedures.end()) + continue; + + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); } + } + } + + // TODO: handle destructors (finalizers) + void Post(const Fortran::parser::TypeDeclarationStmt& t) { + // TODO they dont need to hold intent + // const auto& attrs = std::get>(t.t); + // for (const auto& attr : attrs) { + // if (std::holds_alternative(attr.u)) { + // return; + // } + // } + + // const auto& entityDecls = std::get>(t.t); + // for (const auto& entity : entityDecls) { + // const auto& name = std::get(entity.t); + // if (!name.symbol) + // continue; + + // // TODO they dont need to hold intent + // if (name.symbol->attrs().test(Fortran::semantics::Attr::INTENT_IN)) + // continue; // skip intent in + + // llvm::outs() << "Found type declaration: " << name.symbol->name().ToString() << "\n"; + // if (auto* type = name.symbol->GetType()) { + // if (auto* derived = type->AsDerived()) { + // if (derived->HasDestruction()) { + // llvm::outs() << "Found derived type with destruction: " << name.symbol->name().ToString() << "\n"; + // } + // } + // } + // } + } - auto& currentType = types.back(); - currentType.procedures.emplace_back(name.symbol, optname->symbol); + // type def + bool Pre(const Fortran::parser::DerivedTypeDef&) { + inDerivedTypeDef = true; + types.emplace_back(); + + return true; + } + void Post(const Fortran::parser::DerivedTypeDef&) { inDerivedTypeDef = false; } + + // type stmt like type [, extends(...)] :: body (not exhaustive and not extends) + void Post(const Fortran::parser::DerivedTypeStmt& t) { + if (!inDerivedTypeDef) + return; + + auto& currentType = types.back(); + const auto& name = std::get(t.t); + currentType.type = name.symbol; + } + + // type attrs like extends + void Post(const Fortran::parser::TypeAttrSpec& a) { + if (!inDerivedTypeDef) + return; + + auto& currentType = types.back(); + if (std::holds_alternative(a.u)) { + const auto& extends = std::get(a.u); + currentType.extendsFrom = extends.v.symbol; } + } + + // procedures in type defs + void Post(const Fortran::parser::TypeBoundProcDecl& d) { + if (!inDerivedTypeDef) + return; - private: - metacg::Callgraph* cg; - std::vector> edges; // (caller, callee) - std::string currentFileName; + auto& name = std::get(d.t); + if (!name.symbol) + return; + + auto& optname = std::get>(d.t); + if (!optname || !optname->symbol) { + return; + } - std::vector functionNames; - bool inFunctionOrSubroutineSubProgram = false; - bool inMainProgram = false; - bool inDerivedTypeDef = false; + auto& currentType = types.back(); + currentType.procedures.emplace_back(name.symbol, optname->symbol); + } - typedef struct type { - Fortran::semantics::Symbol* type; - Fortran::semantics::Symbol* extendsFrom; - std::vector> procedures; - } type_t; - std::vector types; - }; + private: + metacg::Callgraph* cg; + std::vector> edges; // (caller, callee) + std::string currentFileName; + + std::vector functionNames; + bool inFunctionOrSubroutineSubProgram = false; + bool inMainProgram = false; + bool inDerivedTypeDef = false; + + typedef struct type { + Fortran::semantics::Symbol* type; + Fortran::semantics::Symbol* extendsFrom; + std::vector> procedures; + } type_t; + std::vector types; +}; +class CollectCG : public Fortran::frontend::PluginParseTreeAction { + public: void executeAction() override { auto cg = std::make_unique(); @@ -279,8 +280,16 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction auto file = createOutputFile("json"); file->write(jsonSink.getJson().dump().c_str(), jsonSink.getJson().dump().size()); + } +}; + +class CollectCGwithDot : public CollectCG { + public: + void executeAction() override { + CollectCG::executeAction(); + + auto& mcgManager = metacg::graph::MCGManager::get(); - // TODO: only debug ? metacg::io::dot::DotGenerator dotGen(mcgManager.getCallgraph("test")); dotGen.generate(); @@ -299,4 +308,5 @@ class CollectCallersAndCallees : public Fortran::frontend::PluginParseTreeAction } }; -static Fortran::frontend::FrontendPluginRegistry::Add X("genCG", "Generate Callgraph"); +static Fortran::frontend::FrontendPluginRegistry::Add X("genCG", "Generate Callgraph"); +static Fortran::frontend::FrontendPluginRegistry::Add Y("genCGwithDot", "Generate Callgraph"); From 7d80405cb08b59292f83033a281f2ac26e600e05 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:43 +0100 Subject: [PATCH 018/130] more tests --- cgfcollector/test/simple/02/module.f90 | 4 ++ cgfcollector/test/simple/use/main.f90 | 48 ++++++++++++++++++++++++ cgfcollector/test/simple/use/output.json | 45 ++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 cgfcollector/test/simple/use/main.f90 create mode 100644 cgfcollector/test/simple/use/output.json diff --git a/cgfcollector/test/simple/02/module.f90 b/cgfcollector/test/simple/02/module.f90 index 3962f1c6..24a89b12 100644 --- a/cgfcollector/test/simple/02/module.f90 +++ b/cgfcollector/test/simple/02/module.f90 @@ -3,6 +3,10 @@ module my_module implicit none contains + subroutine subsub() + integer :: n + end subroutine subsub + subroutine my_subroutine(n) integer, intent(in) :: n diff --git a/cgfcollector/test/simple/use/main.f90 b/cgfcollector/test/simple/use/main.f90 new file mode 100644 index 00000000..0a298573 --- /dev/null +++ b/cgfcollector/test/simple/use/main.f90 @@ -0,0 +1,48 @@ +module mod + + implicit none + + type :: base + private + real ::var + contains + procedure :: set_var => set_var_base + end type base + + type, extends(base) :: derived + contains + procedure :: set_var => set_var_derived + end type derived + +contains + subroutine set_var_base(this, a) + class(base), intent(inout) :: this + real, intent(in) :: a + + print *, 'Setting var from base' + + this%var = a + end subroutine set_var_base + + subroutine set_var_derived(this, a) + class(derived), intent(inout) :: this + real, intent(in) :: a + + print *, 'Setting var from derived' + + this%var = a + end subroutine set_var_derived +end module mod + +program main + use mod, only: base_rename => base, derived_rename => derived + + implicit none + + type(base_rename), allocatable :: b + + allocate (base_rename :: b) + call b%set_var(3.14) + +end program main + diff --git a/cgfcollector/test/simple/use/output.json b/cgfcollector/test/simple/use/output.json new file mode 100644 index 00000000..12a47827 --- /dev/null +++ b/cgfcollector/test/simple/use/output.json @@ -0,0 +1,45 @@ +{ + "_CG": { + "edges": [ + [[156073635694004542, 15462925951592547380], null], + [[156073635694004542, 8395601259825836172], null] + ], + "nodes": [ + [ + 156073635694004542, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 15462925951592547380, + { + "functionName": "_QMmodPset_var_derived", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 8395601259825836172, + { + "functionName": "_QMmodPset_var_base", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "bcce43c481a129077e674aaa2aa736946bcf925a", + "version": "0.7" + }, + "version": "3.0" + } +} From 7e59c702f52209a271cb131de930b540417a14f7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:43 +0100 Subject: [PATCH 019/130] WIP: impl finalizers parsing. Still overestimates --- cgfcollector/src/main.cpp | 95 ++++++++++++++++++++++++++++----------- 1 file changed, 68 insertions(+), 27 deletions(-) diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 8ab3a226..51875bb6 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -8,6 +8,7 @@ class ParseTreeVisitor { void handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { functionNames.emplace_back(Fortran::lower::mangle::mangleName(*sym)); + functionDummyArgs.emplace_back(std::vector()); cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); } } @@ -15,6 +16,9 @@ class ParseTreeVisitor { if (!functionNames.empty()) { functionNames.pop_back(); } + if (!functionDummyArgs.empty()) { + functionDummyArgs.pop_back(); + } } std::vector> getEdges() const { return edges; } @@ -70,9 +74,26 @@ class ParseTreeVisitor { node->setHasBody(true); } - void Post(const Fortran::parser::FunctionStmt& f) { handleFuncSubStmt(f); } + void Post(const Fortran::parser::FunctionStmt& f) { + handleFuncSubStmt(f); + + // collect function arguments + const auto& name_list = std::get>(f.t); + for (auto name : name_list) { + functionDummyArgs.back().push_back(&name); + } + } void Post(const Fortran::parser::EndFunctionStmt&) { handleEndFuncSubStmt(); } - void Post(const Fortran::parser::SubroutineStmt& s) { handleFuncSubStmt(s); } + void Post(const Fortran::parser::SubroutineStmt& s) { + handleFuncSubStmt(s); + + // collect subroutine arguments (dummy args) + const auto* dummyArg_list = &std::get>(s.t); + for (const auto& dummyArg : *dummyArg_list) { + const auto* name = std::get_if(&dummyArg.u); + functionDummyArgs.back().push_back(name); + } + } void Post(const Fortran::parser::EndSubroutineStmt&) { handleEndFuncSubStmt(); } void Post(const Fortran::parser::ProcedureDesignator& p) { @@ -145,37 +166,56 @@ class ParseTreeVisitor { } } - // TODO: handle destructors (finalizers) + // handle destructors (finalizers) TODO: test i definitely missed some edges cases void Post(const Fortran::parser::TypeDeclarationStmt& t) { - // TODO they dont need to hold intent - // const auto& attrs = std::get>(t.t); - // for (const auto& attr : attrs) { - // if (std::holds_alternative(attr.u)) { - // return; + // TODO: allocatable case + // const auto& attrSpec = std::get>(t.t); + // for (const auto& attr : attrSpec) { + // if (std::holds_alternative(attr.u)) { + // return; // skip allocatable because no finalizer called // } // } - // const auto& entityDecls = std::get>(t.t); - // for (const auto& entity : entityDecls) { - // const auto& name = std::get(entity.t); - // if (!name.symbol) - // continue; - - // // TODO they dont need to hold intent - // if (name.symbol->attrs().test(Fortran::semantics::Attr::INTENT_IN)) - // continue; // skip intent in - - // llvm::outs() << "Found type declaration: " << name.symbol->name().ToString() << "\n"; - // if (auto* type = name.symbol->GetType()) { - // if (auto* derived = type->AsDerived()) { - // if (derived->HasDestruction()) { - // llvm::outs() << "Found derived type with destruction: " << name.symbol->name().ToString() << "\n"; - // } - // } - // } - // } + for (const auto& entity : std::get>(t.t)) { + const auto& name = std::get(entity.t); + if (!name.symbol) + continue; + + // skip if name is an argument to a function or subroutine + if (!functionDummyArgs.empty()) { + auto it = + std::find_if(functionDummyArgs.back().begin(), functionDummyArgs.back().end(), + [&name](const Fortran::parser::Name* dummyArg) { return dummyArg->symbol == name.symbol; }); + + if (it != functionDummyArgs.back().end()) + continue; + } + + auto* type = name.symbol->GetType(); + if (!type) + continue; + auto* derived = type->AsDerived(); + if (!derived) + continue; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + continue; + + const auto* details = std::get_if(&typeSymbol->details()); + if (!details) + continue; + + // derived->HasDefaultInitialization(); + + // add edges for finalizers + for (auto final : details->finals()) { + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*final.second)); + } + } } + // following 4 methods are for collecting types and their procedures. see type struct and vector. + // type def bool Pre(const Fortran::parser::DerivedTypeDef&) { inDerivedTypeDef = true; @@ -231,6 +271,7 @@ class ParseTreeVisitor { std::string currentFileName; std::vector functionNames; + std::vector> functionDummyArgs; bool inFunctionOrSubroutineSubProgram = false; bool inMainProgram = false; bool inDerivedTypeDef = false; From 43df1b3ac2fdcb17bca1926c4fa69711bb3b0678 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:43 +0100 Subject: [PATCH 020/130] fix: problem with deferred keyword in abstract type where edges where not inserted into cg --- cgfcollector/src/main.cpp | 43 +++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 51875bb6..ec7bb1c1 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -155,9 +155,8 @@ class ParseTreeVisitor { if (t.extendsFrom != typeSymbol) continue; - auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&symbolComp](const auto& p) { - return p.first->name() == symbolComp->name(); - }); // TODO: use statement + auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), + [&symbolComp](const auto& p) { return p.first->name() == symbolComp->name(); }); if (dProcIt == t.procedures.end()) continue; @@ -214,7 +213,7 @@ class ParseTreeVisitor { } } - // following 4 methods are for collecting types and their procedures. see type struct and vector. + // following 5 methods are for collecting types and their procedures. see type struct and vector. // type def bool Pre(const Fortran::parser::DerivedTypeDef&) { @@ -248,21 +247,35 @@ class ParseTreeVisitor { } // procedures in type defs - void Post(const Fortran::parser::TypeBoundProcDecl& d) { + void Post(const Fortran::parser::TypeBoundProcedureStmt& s) { if (!inDerivedTypeDef) return; - auto& name = std::get(d.t); - if (!name.symbol) - return; + if (auto* withoutInterface = std::get_if(&s.u)) { + for (const auto& d : withoutInterface->declarations) { + auto& name = std::get(d.t); + if (!name.symbol) + return; - auto& optname = std::get>(d.t); - if (!optname || !optname->symbol) { - return; - } + auto& optname = std::get>(d.t); + if (!optname || !optname->symbol) { + return; + } - auto& currentType = types.back(); - currentType.procedures.emplace_back(name.symbol, optname->symbol); + auto& currentType = types.back(); + currentType.procedures.emplace_back(name.symbol, optname->symbol); + } + + // only for abstract types, with deferred in binding attr list + } else if (auto* withInterface = std::get_if(&s.u)) { + for (const auto& n : withInterface->bindingNames) { + if (!n.symbol) + return; + + auto& currentType = types.back(); + currentType.procedures.emplace_back(n.symbol, n.symbol); + } + } } private: @@ -279,7 +292,7 @@ class ParseTreeVisitor { typedef struct type { Fortran::semantics::Symbol* type; Fortran::semantics::Symbol* extendsFrom; - std::vector> procedures; + std::vector> procedures; // name [=> optname] } type_t; std::vector types; }; From 555a4d1912152609b23779b6bee7486cb49a85ce Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:44 +0100 Subject: [PATCH 021/130] simple 05 add output.json --- cgfcollector/test/simple/05/output.json | 56 ++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/cgfcollector/test/simple/05/output.json b/cgfcollector/test/simple/05/output.json index 0967ef42..95e81226 100644 --- a/cgfcollector/test/simple/05/output.json +++ b/cgfcollector/test/simple/05/output.json @@ -1 +1,55 @@ -{} +{ + "_CG": { + "edges": [ + [[10447073260129445604, 5161265265095615880], null], + [[10447073260129445604, 12495205984704274385], null], + [[10447073260129445604, 9011580163634161557], null] + ], + "nodes": [ + [ + 5161265265095615880, + { + "functionName": "_QMshapesPdraw_rectangle", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 12495205984704274385, + { + "functionName": "_QMshapesPdraw_circle", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 10447073260129445604, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 9011580163634161557, + { + "functionName": "_QMshapesPdraw", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "38e3c2db60ec72712056eabd89642e3a6fb631cc", + "version": "0.7" + }, + "version": "3.0" + } +} From 55188a844227e7a10b9df6f38038de1c71713020 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:44 +0100 Subject: [PATCH 022/130] impl operator overloading parsing, WIP: defined operator parsing --- cgfcollector/include/headers.h | 3 + cgfcollector/src/main.cpp | 122 ++++++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h index 83f3ca05..3931981d 100644 --- a/cgfcollector/include/headers.h +++ b/cgfcollector/include/headers.h @@ -17,4 +17,7 @@ #include #include #include +#include +#include +#include #include diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index ec7bb1c1..82e51a7f 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -278,6 +278,123 @@ class ParseTreeVisitor { } } + // collect defined operators in a type def (operator overloading) + void Post(const Fortran::parser::TypeBoundGenericStmt& s) { + if (!inDerivedTypeDef) + return; + + const auto& genericSpec = std::get>(s.t); + if (auto* definedOperator = std::get_if(&genericSpec.value().u)) { + if (auto* intrinsicOp = std::get_if(&definedOperator->u)) { + const auto& names = std::get>(s.t); + + auto& currentType = types.back(); + + for (auto name : names) { + if (!name.symbol) + continue; + + currentType.operators.emplace_back(intrinsicOp, name.symbol); + } + } + } + } + + template + bool holds_any_of(const Variant& v) { + return (std::holds_alternative(v) || ...); + } + + void Post(const Fortran::parser::Expr& e) { + /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) + Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, + LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, + DefinedUnary, DefinedBinary + */ + using PE = Fortran::parser::Expr; + + if (holds_any_of(e.u)) { + if (const auto* definedUnary = std::get_if(&e.u)) { + const auto& opname = std::get(definedUnary->t); + if (!opname.v.symbol) + return; + } + if (const auto* definedBinary = std::get_if(&e.u)) { + const auto& opname = std::get(definedBinary->t); + if (!opname.v.symbol) + return; + } + } + + if (holds_any_of( + e.u)) { + std::visit( + [&](auto&& arg) { + using T = std::decay_t; + const Fortran::common::Indirection* expr = nullptr; + if constexpr (std::is_base_of_v) { + expr = &std::get<0>(arg.t); + } else if constexpr (std::is_base_of_v) { + expr = &arg.v; + } else { + return; // not a unary or binary operator + } + + auto* designator = std::get_if>(&expr->value().u); + if (!designator) + return; + + auto* dataRef = std::get_if(&designator->value().u); + if (!dataRef) + return; + + auto* name = std::get_if(&dataRef->u); + if (!name || !name->symbol) + return; + + auto* type = name->symbol->GetType(); + if (!type) + return; + + auto* derived = type->AsDerived(); + if (!derived) + return; + + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + return; + + auto findTypeIt = + std::find_if(types.begin(), types.end(), [=](const type_t& t) { return t.type == typeSymbol; }); + if (findTypeIt == types.end()) + return; + + for (const auto& ops : findTypeIt->operators) { + auto* op = ops.first; + auto* sym = ops.second; + + auto baseProcIt = std::find_if(findTypeIt->procedures.begin(), findTypeIt->procedures.end(), + [&](const auto& p) { return p.first->name() == sym->name(); }); + if (baseProcIt != findTypeIt->procedures.end()) { + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); + } + + for (const auto& t : types) { + if (t.extendsFrom != typeSymbol) + continue; + auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), + [&](const auto& p) { return p.first->name() == sym->name(); }); + if (dProcIt != t.procedures.end()) { + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); + } + } + } + }, + e.u); + } + } + private: metacg::Callgraph* cg; std::vector> edges; // (caller, callee) @@ -292,7 +409,10 @@ class ParseTreeVisitor { typedef struct type { Fortran::semantics::Symbol* type; Fortran::semantics::Symbol* extendsFrom; - std::vector> procedures; // name [=> optname] + std::vector> + procedures; // name(symbol) => optname(symbol) + std::vector> + operators; // operator => name(symbol) TODO: do i need IntrinsicOperator } type_t; std::vector types; }; From 8eeaab0e7a02ce4381fbedf4bf53429ab71e4d61 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:44 +0100 Subject: [PATCH 023/130] better test case for operator overloading and defined operators --- cgfcollector/test/simple/06/main.f90 | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/cgfcollector/test/simple/06/main.f90 b/cgfcollector/test/simple/06/main.f90 index 9d667081..2642985b 100644 --- a/cgfcollector/test/simple/06/main.f90 +++ b/cgfcollector/test/simple/06/main.f90 @@ -5,22 +5,39 @@ module mod type, abstract :: sortable contains procedure(compare), deferred :: less_then + procedure(not), deferred :: not_impl generic :: operator(<) => less_then + generic :: operator(.NOT.) => not_impl end type sortable interface pure logical function compare(this, other) import :: sortable + implicit none class(sortable), intent(in) :: this, other end function compare + pure logical function not(this) + import :: sortable + implicit none + class(sortable), intent(in) :: this + end function not end interface type, extends(sortable) :: integer_sortable integer :: value contains procedure :: less_then => less_than_integer + procedure :: not_impl => not_impl_integer end type integer_sortable + interface operator(.NEGX.) + module procedure negx + end interface + + interface operator(+) + module procedure add_stuff + end interface + contains pure logical function less_than_integer(this, other) class(integer_sortable), intent(in) :: this @@ -39,6 +56,28 @@ pure logical function less_than_integer(this, other) ! less_than_integer = this%value < other_int%value end function less_than_integer + + pure logical function not_impl(this) + class(sortable), intent(in) :: this + not_impl = .NOT. this%less_then(this) + end function not_impl + + pure logical function not_impl_integer(this) + class(integer_sortable), intent(in) :: this + not_impl_integer = .NOT. this < this + end function not_impl_integer + + function negx(x) result(res) + real, intent(in) :: x + real :: res + res = -x + end function negx + + function add_stuff(x, y) result(res) + type(integer_sortable), intent(in) :: x, y + type(integer_sortable) :: res + res%value = x%value + y%value + end function add_stuff end module mod program main @@ -49,6 +88,9 @@ program main class(sortable), allocatable :: a, b type(integer_sortable) :: c, d + type(integer_sortable) :: res + + real :: e = 5.0, f c%value = 5 d%value = 10 @@ -62,5 +104,11 @@ program main print *, "a is not less than b" end if + f = .NEGX.e + print *, f + + res = c + d + print *, "Result of addition: ", res%value + end program main From 7d4bcef8a8296bcebe6221b6b2c3057de7ff8681 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:45 +0100 Subject: [PATCH 024/130] better test case for finalizers --- cgfcollector/test/simple/03/input.f90 | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cgfcollector/test/simple/03/input.f90 b/cgfcollector/test/simple/03/input.f90 index 46351b21..8e57634d 100644 --- a/cgfcollector/test/simple/03/input.f90 +++ b/cgfcollector/test/simple/03/input.f90 @@ -39,6 +39,26 @@ end subroutine finalize_polynomial end module mod +module mod_use + use mod + + implicit none + +contains + + subroutine func_calls_final() + type(polynomial), allocatable :: q + + q = polynomial([2., 3., 1., 0., 0.]) + call q%print_polynomial() + end subroutine func_calls_final + + subroutine func_does_not_call_final() + type(polynomial), allocatable :: q + end subroutine func_does_not_call_final + +end module mod_use + program main use mod implicit none @@ -50,4 +70,6 @@ program main call q%print_polynomial() end block work + ! sould not call final because main. see 7.5.6.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) and (https://j3-fortran.org/doc/year/10/10-158r1.txt) + end program main From a8cd7ab29d1aaf13022c613b72bda1d9df974518 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:45 +0100 Subject: [PATCH 025/130] tests: impl dedicated CG compare tool using node names to compare CGs instead of node ids --- cgfcollector/CMakeLists.txt | 13 ++++- cgfcollector/test/cgCompare.cpp | 80 +++++++++++++++++++++++++++++ cgfcollector/test/test_runner.sh.in | 13 ++--- 3 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 cgfcollector/test/cgCompare.cpp diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 2a099f08..4ecbb26a 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -35,16 +35,26 @@ configure_package_config_file( install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION lib/cmake) +# cgCompare program +add_executable(${PROJECT_NAME}-cgCompare test/cgCompare.cpp) +add_metacg(${PROJECT_NAME}-cgCompare) +add_spdlog_libraries(${PROJECT_NAME}-cgCompare) +install( + TARGETS ${PROJECT_NAME}-cgCompare + LIBRARY DESTINATION bin + ARCHIVE DESTINATION bin +) + # generate wrapper script with build dir for easy of use during development set(FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/cgfcollector_wrapper.sh.in") set(FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") +# FCOLLECTOR_WRAPPER_EXPORT_LINE for debuging option( FCOLLECTOR_ADD_BUILD_TO_LIB_PATH "Add build dir to LD_LIBRARY_PATH in wrapper script" OFF ) - set(FCOLLECTOR_WRAPPER_EXPORT_LINE "") if(FCOLLECTOR_ADD_BUILD_TO_LIB_PATH) set(FCOLLECTOR_WRAPPER_EXPORT_LINE "export LD_LIBRARY_PATH=\"${CMAKE_CURRENT_BINARY_DIR}:\$LD_LIBRARY_PATH\"") @@ -64,6 +74,7 @@ set(FCOLLECTOR_TEST_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test_runner.sh") set(FCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") set(FCOLLECTOR_WRAPPER "${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT}") +set(FCOLLECTOR_CG_COMPARE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-cgCompare") configure_file( ${FCOLLECTOR_TEST_SCRIPT_TEMPLATE} diff --git a/cgfcollector/test/cgCompare.cpp b/cgfcollector/test/cgCompare.cpp new file mode 100644 index 00000000..c8ad1113 --- /dev/null +++ b/cgfcollector/test/cgCompare.cpp @@ -0,0 +1,80 @@ +#include +#include +#include + +static auto console = metacg::MCGLogger::instance().getConsole(); +static auto errConsole = metacg::MCGLogger::instance().getErrConsole(); + +bool endsMatch(const std::string& a, const std::string& b) { + if (a.size() >= b.size()) { + return a.compare(a.size() - b.size(), b.size(), b) == 0; + } else { + return b.compare(b.size() - a.size(), a.size(), a) == 0; + } +} + +static bool compareNodesAndEdges(const metacg::Callgraph* cg1, const metacg::Callgraph* cg2) { + bool equal = true; + + for (const auto& [id, node] : cg1->getNodes()) { + if (!cg2->hasNode(node->getFunctionName())) { + errConsole->error("Node {} is missing in the other call graph.", node->getFunctionName()); + equal = false; + continue; + } + + const auto& node2 = cg2->getNode(node->getFunctionName()); + if (node2->getHasBody() != node->getHasBody()) { + errConsole->error("Node {} has different hasBody flags: expected {} got {}", node->getFunctionName(), + node2->getHasBody(), node->getHasBody()); + equal = false; + } + + if (!endsMatch(node->getOrigin(), node2->getOrigin())) { + errConsole->error("Node {} has different origins: expected '{}' got '{}'", node->getFunctionName(), + node2->getOrigin(), node->getOrigin()); + equal = false; + } + } + + for (const auto& [id, edge] : cg1->getEdges()) { + auto name1 = cg1->getNode(id.first)->getFunctionName(); + auto name2 = cg1->getNode(id.second)->getFunctionName(); + + if (!cg2->existEdgeFromTo(name1, name2)) { + errConsole->error("Edge from {} to {} is missing in the other call graph.", name1, name2); + equal = false; + } + } + + return equal; +} + +int main(int argc, char* argv[]) { + if (argc != 3) { + errConsole->error("Usage: cgCompare "); + return EXIT_FAILURE; + } + + metacg::io::FileSource fs1(argv[1]); + metacg::io::FileSource fs2(argv[2]); + + auto mcgReader1 = metacg::io::createReader(fs1); + auto mcgReader2 = metacg::io::createReader(fs2); + if (!mcgReader1 || !mcgReader2) { + return EXIT_FAILURE; + } + + auto cg1 = mcgReader1->read(); + auto cg2 = mcgReader2->read(); + if (!cg1 || !cg2) { + errConsole->error("Error reading call graphs."); + return EXIT_FAILURE; + } + + if (!compareNodesAndEdges(cg1.get(), cg2.get()) || !compareNodesAndEdges(cg2.get(), cg1.get())) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/cgfcollector/test/test_runner.sh.in b/cgfcollector/test/test_runner.sh.in index 552905ad..05d52466 100755 --- a/cgfcollector/test/test_runner.sh.in +++ b/cgfcollector/test/test_runner.sh.in @@ -22,20 +22,15 @@ find "$test_case_dir" . -mindepth 1 -type d | while read -r dir; do continue fi - if ! @FCOLLECTOR_WRAPPER@ -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then + if ! @FCOLLECTOR_WRAPPER@ -dot -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then echo "Error: failed to generate callgraph" continue fi - # TODO: check origin - jq_string='del(._MetaCG) | ._CG.nodes |= map([.[0], (.[1] | del(.origin))])' - - if ! diff -q <(jq "$jq_string" "$out_dir/$test_case_name.json") <(jq "$jq_string" "$output_file") >/dev/null; then - echo "Error: Output mismatch" - echo "Expected: $(cat "$output_file")" - echo "Got: $(cat "$out_dir/$test_case_name.json")" - else + if @FCOLLECTOR_CG_COMPARE@ "$output_file" "$out_dir/$test_case_name.json"; then echo "Test case passed" + else + echo "Error: Output mismatch" fi fi done From 208bb6d437b443d95c36bd2fc02c04e7d3481e3a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:45 +0100 Subject: [PATCH 026/130] add cmake managed tests for multi file tests --- cgfcollector/CMakeLists.txt | 14 +++++ cgfcollector/test/multi/02/CMakeLists.txt | 8 +++ .../test/{simple => multi}/02/main.f90 | 0 .../test/{simple => multi}/02/module.f90 | 0 cgfcollector/test/multi/02/output.json | 42 +++++++++++++++ cgfcollector/test/multi/CMakeLists.txt | 7 +++ cgfcollector/test/simple/02/output.json | 1 - cgfcollector/test/test_runner.sh.in | 52 ++++++++++++++++++- 8 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 cgfcollector/test/multi/02/CMakeLists.txt rename cgfcollector/test/{simple => multi}/02/main.f90 (100%) rename cgfcollector/test/{simple => multi}/02/module.f90 (100%) create mode 100644 cgfcollector/test/multi/02/output.json create mode 100644 cgfcollector/test/multi/CMakeLists.txt delete mode 100644 cgfcollector/test/simple/02/output.json diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 4ecbb26a..f797e922 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -83,3 +83,17 @@ configure_file( ) install(PROGRAMS ${FCOLLECTOR_TEST_SCRIPT_OUTPUT} DESTINATION bin) + +file( + GENERATE + OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/test/cmake_base.txt" + CONTENT + "\ +# This file is generated by CMake. +set(CMAKE_Fortran_COMPILER \"${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT}\") +set(CMAKE_Fortran_FLAGS \"\") +set(CMAKE_Fortran_COMPILE_OBJECT \" -dot -o \") +set(CMAKE_Fortran_LINK_EXECUTABLE \"$ \") +set(CMAKE_EXECUTABLE_SUFFIX .json) +" +) diff --git a/cgfcollector/test/multi/02/CMakeLists.txt b/cgfcollector/test/multi/02/CMakeLists.txt new file mode 100644 index 00000000..d9f4917c --- /dev/null +++ b/cgfcollector/test/multi/02/CMakeLists.txt @@ -0,0 +1,8 @@ +file( + GLOB + SOURCES + "*.f90" +) +add_executable(02 ${SOURCES}) + +set_target_properties(02 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/cgfcollector/test/simple/02/main.f90 b/cgfcollector/test/multi/02/main.f90 similarity index 100% rename from cgfcollector/test/simple/02/main.f90 rename to cgfcollector/test/multi/02/main.f90 diff --git a/cgfcollector/test/simple/02/module.f90 b/cgfcollector/test/multi/02/module.f90 similarity index 100% rename from cgfcollector/test/simple/02/module.f90 rename to cgfcollector/test/multi/02/module.f90 diff --git a/cgfcollector/test/multi/02/output.json b/cgfcollector/test/multi/02/output.json new file mode 100644 index 00000000..2cfcbb0d --- /dev/null +++ b/cgfcollector/test/multi/02/output.json @@ -0,0 +1,42 @@ +{ + "_CG": { + "edges": [[[2308780131476637806, 15299685676102505225], null]], + "nodes": [ + [ + 2308780131476637806, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 9603846864033343388, + { + "functionName": "_QMmy_modulePsubsub", + "hasBody": true, + "meta": null, + "origin": "module.f90" + } + ], + [ + 15299685676102505225, + { + "functionName": "_QMmy_modulePmy_subroutine", + "hasBody": true, + "meta": null, + "origin": "module.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "51dac91ac3ddf708d8cd319588ace9650576ea1a", + "version": "0.7" + }, + "version": "3.0" + } +} diff --git a/cgfcollector/test/multi/CMakeLists.txt b/cgfcollector/test/multi/CMakeLists.txt new file mode 100644 index 00000000..414ea07f --- /dev/null +++ b/cgfcollector/test/multi/CMakeLists.txt @@ -0,0 +1,7 @@ +cmake_minimum_required(VERSION 3.20) + +project(cgfctest LANGUAGES Fortran) + +include(../cmake_base.txt) + +add_subdirectory(02) diff --git a/cgfcollector/test/simple/02/output.json b/cgfcollector/test/simple/02/output.json deleted file mode 100644 index 0967ef42..00000000 --- a/cgfcollector/test/simple/02/output.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/cgfcollector/test/test_runner.sh.in b/cgfcollector/test/test_runner.sh.in index 05d52466..5b9fc806 100755 --- a/cgfcollector/test/test_runner.sh.in +++ b/cgfcollector/test/test_runner.sh.in @@ -7,7 +7,57 @@ if [[ ! -d "$out_dir" ]]; then mkdir "$out_dir" fi -find "$test_case_dir" . -mindepth 1 -type d | while read -r dir; do +# cmake managed test dirs +find "$test_case_dir" -mindepth 1 -type d \! -exec test -e "{}/output.json" \; \! -exec test -e "{}/*.f90" \; -exec find {} -maxdepth 1 -name 'CMakeLists.txt' \; | while read -r dir; do + dir="$(dirname "$dir")" + + tmp_dir="$(mktemp -d)" + + copy_to="$(pwd)/$out_dir" + if [[ ! -d "$copy_to" ]]; then + echo "Error: $copy_to does not exist" + fi + + echo "Running cmake managed tests from dir: $(basename "$dir")" + + { + cd "$dir" + cmake -S . -B "$tmp_dir" + cd "$tmp_dir" + make + find . -maxdepth 1 -name '*.json' | while read -r file; do + cp "$file" "$copy_to/$(basename "$dir")_$(basename "$file")" + + test_case_name="$(basename "$dir")_$(basename -s .json "$file")" + + echo "Test case: $test_case_name" + + output_file="$dir/$(basename -s .json "$file")/output.json" + + if [[ -f "$output_file" ]] && [[ -s "$output_file" ]] && grep -q '{}' "$output_file"; then + echo "Skipping empty or missing output file: $output_file" + continue + fi + + if @FCOLLECTOR_CG_COMPARE@ "$output_file" "$copy_to/$(basename "$dir")_$(basename "$file")"; then + echo "Test case passed" + else + echo "Error: Output mismatch" + fi + + done + } || { + echo "Error: could not generate CG" + rm -rf "$tmp_dir" + continue + } + + rm -rf "$tmp_dir" +done + +# no cmake managed test dirs +find "$test_case_dir" -mindepth 1 -type d \! -exec test -e "{}/CMakeLists.txt" \; -exec find {} -maxdepth 1 -name "output.json" \; | while read -r dir; do + dir="$(dirname "$dir")" mapfile -t input_files < <(find "$dir" -maxdepth 1 -name '*.f90') output_file="$dir/output.json" From 1d91598e9d313e4d3a8cf592feb082b8049c805c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:46 +0100 Subject: [PATCH 027/130] renamed test case 6 to operator --- cgfcollector/test/simple/{06 => operator}/main.f90 | 0 cgfcollector/test/simple/{06 => operator}/output.json | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename cgfcollector/test/simple/{06 => operator}/main.f90 (100%) rename cgfcollector/test/simple/{06 => operator}/output.json (100%) diff --git a/cgfcollector/test/simple/06/main.f90 b/cgfcollector/test/simple/operator/main.f90 similarity index 100% rename from cgfcollector/test/simple/06/main.f90 rename to cgfcollector/test/simple/operator/main.f90 diff --git a/cgfcollector/test/simple/06/output.json b/cgfcollector/test/simple/operator/output.json similarity index 100% rename from cgfcollector/test/simple/06/output.json rename to cgfcollector/test/simple/operator/output.json From b88ca446ad3e8e88e6070fe1e24b3d8af54477f6 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:46 +0100 Subject: [PATCH 028/130] add basic README.md for cgfcollector --- cgfcollector/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 cgfcollector/README.md diff --git a/cgfcollector/README.md b/cgfcollector/README.md new file mode 100644 index 00000000..86e9e53a --- /dev/null +++ b/cgfcollector/README.md @@ -0,0 +1,36 @@ +# CG fortran collector + +## Usage + +`cgfcollector_wrapper.sh` convenience wrapper to run parse plugin. + +### Generate a callgraph from a CMake project + +Paste this into your CMakeLists.txt. + +``` +set(CMAKE_Fortran_COMPILER ) +set(CMAKE_Fortran_FLAGS "") +set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") +set(CMAKE_Fortran_LINK_EXECUTABLE " ") +set(CMAKE_EXECUTABLE_SUFFIX .json) +``` + +This will hook into the cmake build process and generate a callgraph instead of +an executable. + +## Running test + +run `test_runner.sh` + +## Debug + +### print parse tree + +```sh +flang-new -fc1 -fdebug-dump-parse-tree file.f90 +``` + +### Grammar + +[Grammar](https://flang.llvm.org/docs/f2018-grammar.html) From 087feb00ce86776d4a26a2fe1014e21a028b611e Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:46 +0100 Subject: [PATCH 029/130] move some files in the tools directory --- cgfcollector/CMakeLists.txt | 16 +++++-- cgfcollector/test/cmake_base.txt | 6 +++ cgfcollector/{test => tools}/cgCompare.cpp | 0 .../{ => tools}/cgfcollector_wrapper.sh.in | 0 .../{test => tools}/test_runner.sh.in | 0 cgfcollector/tools/visuel.cpp | 42 +++++++++++++++++++ 6 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 cgfcollector/test/cmake_base.txt rename cgfcollector/{test => tools}/cgCompare.cpp (100%) rename cgfcollector/{ => tools}/cgfcollector_wrapper.sh.in (100%) rename cgfcollector/{test => tools}/test_runner.sh.in (100%) create mode 100644 cgfcollector/tools/visuel.cpp diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index f797e922..756c7016 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -36,7 +36,7 @@ configure_package_config_file( install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION lib/cmake) # cgCompare program -add_executable(${PROJECT_NAME}-cgCompare test/cgCompare.cpp) +add_executable(${PROJECT_NAME}-cgCompare ${CMAKE_CURRENT_SOURCE_DIR}/tools/cgCompare.cpp) add_metacg(${PROJECT_NAME}-cgCompare) add_spdlog_libraries(${PROJECT_NAME}-cgCompare) install( @@ -45,8 +45,18 @@ install( ARCHIVE DESTINATION bin ) +# visuel program to generate dot file from graph +add_executable(${PROJECT_NAME}-visuel ${CMAKE_CURRENT_SOURCE_DIR}/tools/visuel.cpp) +add_metacg(${PROJECT_NAME}-visuel) +add_spdlog_libraries(${PROJECT_NAME}-visuel) +install( + TARGETS ${PROJECT_NAME}-visuel + LIBRARY DESTINATION bin + ARCHIVE DESTINATION bin +) + # generate wrapper script with build dir for easy of use during development -set(FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/cgfcollector_wrapper.sh.in") +set(FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/tools/cgfcollector_wrapper.sh.in") set(FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") # FCOLLECTOR_WRAPPER_EXPORT_LINE for debuging @@ -69,7 +79,7 @@ configure_file( ) install(PROGRAMS ${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT} DESTINATION bin) -set(FCOLLECTOR_TEST_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/test/test_runner.sh.in") +set(FCOLLECTOR_TEST_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/tools/test_runner.sh.in") set(FCOLLECTOR_TEST_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test_runner.sh") set(FCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") diff --git a/cgfcollector/test/cmake_base.txt b/cgfcollector/test/cmake_base.txt new file mode 100644 index 00000000..21108e4a --- /dev/null +++ b/cgfcollector/test/cmake_base.txt @@ -0,0 +1,6 @@ +# This file is generated by CMake. +set(CMAKE_Fortran_COMPILER "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/cgfcollector/cgfcollector_wrapper.sh") +set(CMAKE_Fortran_FLAGS "") +set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") +set(CMAKE_Fortran_LINK_EXECUTABLE "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/tools/cgmerge2/cgmerge2 ") +set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/test/cgCompare.cpp b/cgfcollector/tools/cgCompare.cpp similarity index 100% rename from cgfcollector/test/cgCompare.cpp rename to cgfcollector/tools/cgCompare.cpp diff --git a/cgfcollector/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in similarity index 100% rename from cgfcollector/cgfcollector_wrapper.sh.in rename to cgfcollector/tools/cgfcollector_wrapper.sh.in diff --git a/cgfcollector/test/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in similarity index 100% rename from cgfcollector/test/test_runner.sh.in rename to cgfcollector/tools/test_runner.sh.in diff --git a/cgfcollector/tools/visuel.cpp b/cgfcollector/tools/visuel.cpp new file mode 100644 index 00000000..01bc2c46 --- /dev/null +++ b/cgfcollector/tools/visuel.cpp @@ -0,0 +1,42 @@ +#include +#include +#include +#include +#include + +static auto console = metacg::MCGLogger::instance().getConsole(); +static auto errConsole = metacg::MCGLogger::instance().getErrConsole(); + +int main(int argc, char* argv[]) { + if (argc != 2) { + errConsole->error("Usage: visuel "); + return EXIT_FAILURE; + } + + metacg::io::FileSource fs1(argv[1]); + + auto mcgReader1 = metacg::io::createReader(fs1); + if (!mcgReader1) { + return EXIT_FAILURE; + } + + auto cg = mcgReader1->read(); + if (!cg) { + errConsole->error("Error reading call graphs."); + return EXIT_FAILURE; + } + + metacg::io::dot::DotGenerator dotGen(cg.get()); + dotGen.generate(); + + std::ofstream outFile("callgraph.dot"); + if (!outFile.is_open()) { + errConsole->error("Error opening output file for writing."); + return EXIT_FAILURE; + } + outFile << dotGen.getDotString(); + + outFile.close(); + + return EXIT_SUCCESS; +} From 055cfdb88e0c146341a9612cc9b95bc7773804f1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:47 +0100 Subject: [PATCH 030/130] more tests (function_test and math_demo) --- .../test/simple/function_test/main.f90 | 92 +++++++++++++++++++ .../test/simple/function_test/output.json | 84 +++++++++++++++++ cgfcollector/test/simple/math_demo/main.f90 | 73 +++++++++++++++ .../test/simple/math_demo/output.json | 66 +++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 cgfcollector/test/simple/function_test/main.f90 create mode 100644 cgfcollector/test/simple/function_test/output.json create mode 100644 cgfcollector/test/simple/math_demo/main.f90 create mode 100644 cgfcollector/test/simple/math_demo/output.json diff --git a/cgfcollector/test/simple/function_test/main.f90 b/cgfcollector/test/simple/function_test/main.f90 new file mode 100644 index 00000000..e9485175 --- /dev/null +++ b/cgfcollector/test/simple/function_test/main.f90 @@ -0,0 +1,92 @@ +module vector_operations +contains + function vector_add(a, b) result(result) + implicit none + real, dimension(:), intent(in) :: a, b + real, dimension(size(a)) :: result + integer :: i + + if (size(a) /= size(b)) then + print *, "Error: Vectors must be of the same size." + stop + end if + + do i = 1, size(a) + result(i) = a(i) + b(i) + end do + end function vector_add + function vector_norm(n, vec) result(norm) + implicit none + integer, intent(in) :: n + real, intent(in) :: vec(n) + real :: norm + + norm = sqrt(sum(vec**2)) + + end function vector_norm +end module vector_operations + +function vector_norm2(n, vec) result(norm) + implicit none + integer, intent(in) :: n + real, intent(in) :: vec(n) + real :: norm + + norm = sqrt(sum(vec**2)) + +end function vector_norm2 + +function size(arr) result(s) + implicit none + real, dimension(:), intent(in) :: arr + integer :: s + + s = 10 + +contains + function func1(arr) result(s) + implicit none + real, dimension(:), intent(in) :: arr + integer :: s + + s = size(arr) + end function func1 + subroutine func2(arr) + implicit none + real, dimension(:), intent(in) :: arr + integer :: s + + s = size(arr) + end subroutine func2 +end function size + +program main + use vector_operations, only: vector_add, vector_norm2 => vector_norm + implicit none + + real, dimension(3) :: a, b, result + integer :: i + + interface + function size(arr) result(s) + implicit none + real, dimension(:), intent(in) :: arr + integer :: s + end function size + end interface + + a = [1.0, 2.0, 3.0] + b = [4.0, 5.0, 6.0] + result = vector_add(a, b) + + print *, "Result of vector addition:" + do i = 1, size(result) + print *, result(i) + end do + + i = size(a) + print *, "i: ", i + + print *, "Norm of vector a:" + print *, vector_norm2(size(a), a) +end program main diff --git a/cgfcollector/test/simple/function_test/output.json b/cgfcollector/test/simple/function_test/output.json new file mode 100644 index 00000000..d3ff935b --- /dev/null +++ b/cgfcollector/test/simple/function_test/output.json @@ -0,0 +1,84 @@ +{ + "_CG": { + "edges": [ + [[14247392955135821084, 10265585301451757595], null], + [[14247392955135821084, 9264498473018160840], null], + [[14247392955135821084, 8180353735812950427], null], + [[3560107481050335624, 8180353735812950427], null], + [[18176357673246278819, 8180353735812950427], null] + ], + "nodes": [ + [ + 18176357673246278819, + { + "functionName": "_QFsizePfunc1", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 8180353735812950427, + { + "functionName": "_QPsize", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 4612314267457897323, + { + "functionName": "_QPvector_norm2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 14247392955135821084, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 3560107481050335624, + { + "functionName": "_QFsizePfunc2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 10265585301451757595, + { + "functionName": "_QMvector_operationsPvector_norm", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 9264498473018160840, + { + "functionName": "_QMvector_operationsPvector_add", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "e484641c4a9cc749eef4bfa01fca86020321e52d", + "version": "0.7" + }, + "version": "3.0" + } +} diff --git a/cgfcollector/test/simple/math_demo/main.f90 b/cgfcollector/test/simple/math_demo/main.f90 new file mode 100644 index 00000000..e73ba738 --- /dev/null +++ b/cgfcollector/test/simple/math_demo/main.f90 @@ -0,0 +1,73 @@ +module math_utils + implicit none + private + public :: factorial, array_stats, dot_product_custom, normalize_vector + +contains + recursive function factorial(n) result(fact) + integer, intent(in) :: n + integer :: fact + if (n <= 1) then + fact = 1 + else + fact = n*factorial(n - 1) + end if + end function factorial + + subroutine array_stats(arr, mean, std_dev) + real, intent(in) :: arr(:) + real, intent(out) :: mean, std_dev + real :: sum_arr, variance + integer :: n + n = size(arr) + sum_arr = sum(arr) + mean = sum_arr/n + variance = sum((arr - mean)**2)/n + std_dev = sqrt(variance) + end subroutine array_stats + + function dot_product_custom(a, b) result(dp) + real, intent(in) :: a(:), b(:) + real :: dp + dp = sum(a*b) + end function dot_product_custom + + subroutine normalize_vector(vec, norm_vec) + real, intent(in) :: vec(:) + real, intent(out) :: norm_vec(size(vec)) + real :: magnitude + magnitude = sqrt(sum(vec**2)) + if (magnitude /= 0.0) then + norm_vec = vec/magnitude + else + norm_vec = 0.0 + end if + end subroutine normalize_vector + +end module math_utils + +program complex_demo + use math_utils, only: factorial, array_stats, dot_product_custom, normalize_vector + implicit none + + real :: x(5), mean, std_dev, dp + real :: vec1(3), vec2(3), norm_vec(3) + integer :: i, fact + + x = [1.0, 2.0, 3.0, 4.0, 5.0] + call array_stats(x, mean, std_dev) + print *, 'Mean:', mean, 'Std Dev:', std_dev + + vec1 = [1.0, 0.0, 0.0] + vec2 = [0.0, 1.0, 0.0] + dp = dot_product_custom(vec1, vec2) + print *, 'Dot Product:', dp + + call normalize_vector(vec1, norm_vec) + print *, 'Normalized Vector:', norm_vec + + do i = 1, 5 + fact = factorial(i) + print *, 'Factorial(', i, ') = ', fact + end do +end program complex_demo diff --git a/cgfcollector/test/simple/math_demo/output.json b/cgfcollector/test/simple/math_demo/output.json new file mode 100644 index 00000000..c5cd76ae --- /dev/null +++ b/cgfcollector/test/simple/math_demo/output.json @@ -0,0 +1,66 @@ +{ + "_CG": { + "edges": [ + [[5017978461990509046, 2963930559116137426], null], + [[5017978461990509046, 14284706976400344361], null], + [[5017978461990509046, 17311429370828588933], null], + [[5017978461990509046, 3724766418702407261], null], + [[2963930559116137426, 2963930559116137426], null] + ], + "nodes": [ + [ + 5017978461990509046, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 17311429370828588933, + { + "functionName": "_QMmath_utilsPnormalize_vector", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 14284706976400344361, + { + "functionName": "_QMmath_utilsPdot_product_custom", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 3724766418702407261, + { + "functionName": "_QMmath_utilsParray_stats", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 2963930559116137426, + { + "functionName": "_QMmath_utilsPfactorial", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "e484641c4a9cc749eef4bfa01fca86020321e52d", + "version": "0.7" + }, + "version": "3.0" + } +} From d700acde53155135f18a43d376f5bbb374649dc5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:47 +0100 Subject: [PATCH 031/130] WIP: commit --- cgfcollector/src/main.cpp | 270 +++++++++++++++++++++++++++++--------- 1 file changed, 207 insertions(+), 63 deletions(-) diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 82e51a7f..6d0d2837 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -1,5 +1,14 @@ #include "headers.h" +typedef struct type { + Fortran::semantics::Symbol* type; + Fortran::semantics::Symbol* extendsFrom; + std::vector> + procedures; // name(symbol) => optname(symbol) + std::vector> + operators; // operator => name(symbol) +} type_t; + class ParseTreeVisitor { public: ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; @@ -10,6 +19,8 @@ class ParseTreeVisitor { functionNames.emplace_back(Fortran::lower::mangle::mangleName(*sym)); functionDummyArgs.emplace_back(std::vector()); cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); + + llvm::outs() << "Add node: " << Fortran::lower::mangle::mangleName(*sym) << "\n"; } } void handleEndFuncSubStmt() { @@ -23,6 +34,45 @@ class ParseTreeVisitor { std::vector> getEdges() const { return edges; } + // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. + std::vector find_type_with_derived_types(const Fortran::semantics::Symbol* typeSymbol) { + std::vector typeWithDerived; + + auto findTypeIt = + std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); + if (findTypeIt == types.end()) + return typeWithDerived; + + typeWithDerived.push_back(*findTypeIt); + + for (auto t : types) { + if (t.extendsFrom != typeSymbol) + continue; + + typeWithDerived.push_back(t); + } + + return typeWithDerived; + } + + // this function searches with typeSymbol for a type in types vector and adds edges for procedures that matches + // procedureSymbol. And also adds edges from types that extends from typeSymbol. + void add_edges_for_produces_and_derived_types(std::vector typeWithDerived, + const Fortran::semantics::Symbol* procedureSymbol) { + for (type_t t : typeWithDerived) { + auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&procedureSymbol](const auto& p) { + return p.first->name() == procedureSymbol->name(); + }); + if (procIt == t.procedures.end()) + continue; + + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*procIt->second)); + + llvm::outs() << "Add edge: " << functionNames.back() << " -> " + << Fortran::lower::mangle::mangleName(*procIt->second) << "\n"; + } + } + template bool Pre(const A&) { return true; @@ -75,6 +125,9 @@ class ParseTreeVisitor { } void Post(const Fortran::parser::FunctionStmt& f) { + llvm::outs() << "In function: " << Fortran::lower::mangle::mangleName(*std::get(f.t).symbol) + << "\n"; + handleFuncSubStmt(f); // collect function arguments @@ -83,8 +136,17 @@ class ParseTreeVisitor { functionDummyArgs.back().push_back(&name); } } - void Post(const Fortran::parser::EndFunctionStmt&) { handleEndFuncSubStmt(); } + void Post(const Fortran::parser::EndFunctionStmt&) { + if (!functionNames.empty()) { + llvm::outs() << "End function: " << functionNames.back() << "\n"; + } + + handleEndFuncSubStmt(); + } void Post(const Fortran::parser::SubroutineStmt& s) { + llvm::outs() << "In subroutine: " + << Fortran::lower::mangle::mangleName(*std::get(s.t).symbol) << "\n"; + handleFuncSubStmt(s); // collect subroutine arguments (dummy args) @@ -94,7 +156,13 @@ class ParseTreeVisitor { functionDummyArgs.back().push_back(name); } } - void Post(const Fortran::parser::EndSubroutineStmt&) { handleEndFuncSubStmt(); } + void Post(const Fortran::parser::EndSubroutineStmt&) { + if (!functionNames.empty()) { + llvm::outs() << "End subroutine: " << functionNames.back() << "\n"; + } + + handleEndFuncSubStmt(); + } void Post(const Fortran::parser::ProcedureDesignator& p) { if (functionNames.empty()) @@ -111,14 +179,19 @@ class ParseTreeVisitor { edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*name->symbol)); + llvm::outs() << "Add edge: " << functionNames.back() << " -> " + << Fortran::lower::mangle::mangleName(*name->symbol) << "\n"; + // if called from a object with %. (base % component) } else if (auto* procCompRef = std::get_if(&p.u)) { auto* symbolComp = procCompRef->v.thing.component.symbol; if (!symbolComp) return; - edges.emplace_back(functionNames.back(), - Fortran::lower::mangle::mangleName(*procCompRef->v.thing.component.symbol)); + edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*symbolComp)); + + llvm::outs() << "Add edge: " << functionNames.back() << " -> " << Fortran::lower::mangle::mangleName(*symbolComp) + << "\n"; auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) @@ -137,31 +210,7 @@ class ParseTreeVisitor { if (!typeSymbol) return; - auto findTypeIt = - std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); - if (findTypeIt == types.end()) - return; - - // handle base type - auto baseProcIt = std::find_if(findTypeIt->procedures.begin(), findTypeIt->procedures.end(), - [&symbolComp](const auto& p) { return p.first->name() == symbolComp->name(); }); - if (baseProcIt == findTypeIt->procedures.end()) - return; - - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); - - // handle derived types - for (const auto& t : types) { - if (t.extendsFrom != typeSymbol) - continue; - - auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), - [&symbolComp](const auto& p) { return p.first->name() == symbolComp->name(); }); - if (dProcIt == t.procedures.end()) - continue; - - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); - } + add_edges_for_produces_and_derived_types(find_type_with_derived_types(typeSymbol), symbolComp); } } @@ -209,6 +258,9 @@ class ParseTreeVisitor { // add edges for finalizers for (auto final : details->finals()) { edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*final.second)); + + llvm::outs() << "Add edge: " << functionNames.back() << " -> " + << Fortran::lower::mangle::mangleName(*final.second) << "\n"; } } } @@ -300,11 +352,99 @@ class ParseTreeVisitor { } } + // the following 4 methods are for collecting defined operators in interface statements + bool Pre(const Fortran::parser::InterfaceStmt&) { + inInterfaceStmt = true; + return true; + } + bool Pre(const Fortran::parser::EndInterfaceStmt&) { + inInterfaceStmt = false; + inInterfaceStmtDefinedOperator = false; + return true; + } + void Post(const Fortran::parser::DefinedOperator& op) { + if (!inInterfaceStmt) + return; + + inInterfaceStmtDefinedOperator = true; + + interfaceOperators.emplace_back(&op.u, std::vector()); + } + void Post(const Fortran::parser::ProcedureStmt& p) { + if (!inInterfaceStmtDefinedOperator) + return; + + auto name = std::get>(p.t); + for (const auto& n : name) { + if (!n.symbol) + continue; + + if (interfaceOperators.empty()) { + llvm::errs() << "This should no happen. Likely there is a bug with parsing DefinedOperator's\n"; + continue; + } + + interfaceOperators.back().second.push_back(n.symbol); + } + } + template bool holds_any_of(const Variant& v) { return (std::holds_alternative(v) || ...); } + bool compare_expr_IntrinsicOperator(const Fortran::parser::Expr* expr, + const Fortran::parser::DefinedOperator::IntrinsicOperator* op) { + if (!expr || !op) + return false; + + using namespace Fortran::parser; + using IO = DefinedOperator::IntrinsicOperator; + + switch (*op) { + // case IO::UnaryPlus: + // return std::get_if(&expr->u) != nullptr; + // case IO::Negate: + // return std::get_if(&expr->u) != nullptr; + case IO::NOT: + return std::get_if(&expr->u) != nullptr; + case IO::Power: + return std::get_if(&expr->u) != nullptr; + case IO::Multiply: + return std::get_if(&expr->u) != nullptr; + case IO::Divide: + return std::get_if(&expr->u) != nullptr; + case IO::Add: + return std::get_if(&expr->u) != nullptr; + case IO::Subtract: + return std::get_if(&expr->u) != nullptr; + case IO::Concat: + return std::get_if(&expr->u) != nullptr; + case IO::LT: + return std::get_if(&expr->u) != nullptr; + case IO::LE: + return std::get_if(&expr->u) != nullptr; + case IO::EQ: + return std::get_if(&expr->u) != nullptr; + case IO::NE: + return std::get_if(&expr->u) != nullptr; + case IO::GE: + return std::get_if(&expr->u) != nullptr; + case IO::GT: + return std::get_if(&expr->u) != nullptr; + case IO::AND: + return std::get_if(&expr->u) != nullptr; + case IO::OR: + return std::get_if(&expr->u) != nullptr; + case IO::EQV: + return std::get_if(&expr->u) != nullptr; + case IO::NEQV: + return std::get_if(&expr->u) != nullptr; + default: + return false; + } + } + void Post(const Fortran::parser::Expr& e) { /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, @@ -326,6 +466,8 @@ class ParseTreeVisitor { } } + // UnaryPlus and Negate are not in IntrinsicOperator whyyyyyyyyy ??????? Can they be overridden? + if (holds_any_of( e.u)) { @@ -341,6 +483,8 @@ class ParseTreeVisitor { return; // not a unary or binary operator } + llvm::outs() << "Expr: " << expr->value().source.ToString() << "\n"; + auto* designator = std::get_if>(&expr->value().u); if (!designator) return; @@ -365,31 +509,30 @@ class ParseTreeVisitor { if (!typeSymbol) return; - auto findTypeIt = - std::find_if(types.begin(), types.end(), [=](const type_t& t) { return t.type == typeSymbol; }); - if (findTypeIt == types.end()) + auto typeWithDerived = find_type_with_derived_types(typeSymbol); + + auto findOpIt = std::find_if(typeWithDerived.begin(), typeWithDerived.end(), [&](const type_t& t) { + return std::any_of(t.operators.begin(), t.operators.end(), [&](const auto& op) { + llvm::outs() << "Checking operator: " << Fortran::parser::DefinedOperator::EnumToString(*op.first) + << " with expr: " << expr->value().source.ToString() << "\n"; + + return compare_expr_IntrinsicOperator(&expr->value(), op.first); + }); + }); + if (findOpIt == typeWithDerived.end()) return; - for (const auto& ops : findTypeIt->operators) { - auto* op = ops.first; - auto* sym = ops.second; - - auto baseProcIt = std::find_if(findTypeIt->procedures.begin(), findTypeIt->procedures.end(), - [&](const auto& p) { return p.first->name() == sym->name(); }); - if (baseProcIt != findTypeIt->procedures.end()) { - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*baseProcIt->second)); - } - - for (const auto& t : types) { - if (t.extendsFrom != typeSymbol) - continue; - auto dProcIt = std::find_if(t.procedures.begin(), t.procedures.end(), - [&](const auto& p) { return p.first->name() == sym->name(); }); - if (dProcIt != t.procedures.end()) { - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*dProcIt->second)); - } - } - } + llvm::outs() << "Found operator: " << findOpIt->type->name() << "\n"; + + auto operatorIt = std::find_if(findOpIt->operators.begin(), findOpIt->operators.end(), [&](const auto& op) { + return compare_expr_IntrinsicOperator(&expr->value(), op.first); + }); + if (operatorIt == findOpIt->operators.end()) + return; + + auto* procedureSymbol = operatorIt->second; + + add_edges_for_produces_and_derived_types(typeWithDerived, procedureSymbol); }, e.u); } @@ -400,21 +543,22 @@ class ParseTreeVisitor { std::vector> edges; // (caller, callee) std::string currentFileName; - std::vector functionNames; - std::vector> functionDummyArgs; bool inFunctionOrSubroutineSubProgram = false; bool inMainProgram = false; bool inDerivedTypeDef = false; + bool inInterfaceStmt = false; + bool inInterfaceStmtDefinedOperator = false; + bool inInterfaceSpecification = false; + + std::vector functionNames; + std::vector> functionDummyArgs; + + std::vector types; - typedef struct type { - Fortran::semantics::Symbol* type; - Fortran::semantics::Symbol* extendsFrom; - std::vector> - procedures; // name(symbol) => optname(symbol) - std::vector> - operators; // operator => name(symbol) TODO: do i need IntrinsicOperator - } type_t; - std::vector types; + std::vector*, + std::vector>> + interfaceOperators; // operator name (symbol) => [procedure names (symbols)] }; class CollectCG : public Fortran::frontend::PluginParseTreeAction { From 3aa2ed59999d1cb59e145cb327cf22eef3978699 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:47 +0100 Subject: [PATCH 032/130] WIP: normal operator parsing now working, operator in interfaces are still vastly overestimated and custom operators do not get parsed --- cgfcollector/include/headers.h | 1 + cgfcollector/src/main.cpp | 270 +++++++++++++++++++++------------ 2 files changed, 171 insertions(+), 100 deletions(-) diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h index 3931981d..0db95650 100644 --- a/cgfcollector/include/headers.h +++ b/cgfcollector/include/headers.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 6d0d2837..6db21449 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -16,16 +16,17 @@ class ParseTreeVisitor { template void handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { - functionNames.emplace_back(Fortran::lower::mangle::mangleName(*sym)); + functionSymbols.emplace_back(sym); functionDummyArgs.emplace_back(std::vector()); - cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); + cg->insert( + std::make_unique(Fortran::lower::mangle::mangleName(*sym), currentFileName, false, false)); llvm::outs() << "Add node: " << Fortran::lower::mangle::mangleName(*sym) << "\n"; } } void handleEndFuncSubStmt() { - if (!functionNames.empty()) { - functionNames.pop_back(); + if (!functionSymbols.empty()) { + functionSymbols.pop_back(); } if (!functionDummyArgs.empty()) { functionDummyArgs.pop_back(); @@ -45,11 +46,34 @@ class ParseTreeVisitor { typeWithDerived.push_back(*findTypeIt); - for (auto t : types) { - if (t.extendsFrom != typeSymbol) - continue; + // base type + if ((*findTypeIt).extendsFrom == nullptr) { + for (auto t : types) { + if (t.extendsFrom != typeSymbol) + continue; + + typeWithDerived.push_back(t); + } + // not a base type, go back recursively to find all "base" types + } else { + auto* currentExtendsFrom = (*findTypeIt).extendsFrom; + while (currentExtendsFrom) { + auto currentType = std::find_if(types.begin(), types.end(), [¤tExtendsFrom](const type_t& t) { + return t.type == currentExtendsFrom; + }); + if (currentType == types.end()) { + llvm::errs() << "Error: Types array (extendsFrom) field entry missing."; + return typeWithDerived; + } + + typeWithDerived.push_back(*currentType); - typeWithDerived.push_back(t); + if ((*currentType).extendsFrom != nullptr) { + currentExtendsFrom = (*currentType).extendsFrom; + } else { + currentExtendsFrom = nullptr; + } + } } return typeWithDerived; @@ -66,9 +90,10 @@ class ParseTreeVisitor { if (procIt == t.procedures.end()) continue; - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*procIt->second)); + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*procIt->second)); - llvm::outs() << "Add edge: " << functionNames.back() << " -> " + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " << Fortran::lower::mangle::mangleName(*procIt->second) << "\n"; } } @@ -87,8 +112,9 @@ class ParseTreeVisitor { if (!maybeStmt->statement.v.symbol) return true; - functionNames.emplace_back(Fortran::lower::mangle::mangleName(*maybeStmt->statement.v.symbol)); - cg->insert(std::make_unique(functionNames.back(), currentFileName, false, false)); + functionSymbols.emplace_back(maybeStmt->statement.v.symbol); + cg->insert(std::make_unique(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + currentFileName, false, false)); } return true; } @@ -96,8 +122,8 @@ class ParseTreeVisitor { void Post(const Fortran::parser::MainProgram&) { inMainProgram = false; - if (!functionNames.empty()) { - functionNames.pop_back(); + if (!functionSymbols.empty()) { + functionSymbols.pop_back(); } } @@ -116,7 +142,7 @@ class ParseTreeVisitor { if (!inFunctionOrSubroutineSubProgram && !inMainProgram) return; - auto* node = cg->getNode(functionNames.back()); + auto* node = cg->getNode(Fortran::lower::mangle::mangleName(*functionSymbols.back())); if (!node) { return; } @@ -137,8 +163,8 @@ class ParseTreeVisitor { } } void Post(const Fortran::parser::EndFunctionStmt&) { - if (!functionNames.empty()) { - llvm::outs() << "End function: " << functionNames.back() << "\n"; + if (!functionSymbols.empty()) { + llvm::outs() << "End function: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; } handleEndFuncSubStmt(); @@ -157,15 +183,15 @@ class ParseTreeVisitor { } } void Post(const Fortran::parser::EndSubroutineStmt&) { - if (!functionNames.empty()) { - llvm::outs() << "End subroutine: " << functionNames.back() << "\n"; + if (!functionSymbols.empty()) { + llvm::outs() << "End subroutine: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; } handleEndFuncSubStmt(); } void Post(const Fortran::parser::ProcedureDesignator& p) { - if (functionNames.empty()) + if (functionSymbols.empty()) return; // if just the name is called. (as subroutine with call and as function without call) @@ -177,9 +203,10 @@ class ParseTreeVisitor { if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) return; - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*name->symbol)); + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*name->symbol)); - llvm::outs() << "Add edge: " << functionNames.back() << " -> " + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " << Fortran::lower::mangle::mangleName(*name->symbol) << "\n"; // if called from a object with %. (base % component) @@ -188,10 +215,11 @@ class ParseTreeVisitor { if (!symbolComp) return; - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*symbolComp)); + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*symbolComp)); - llvm::outs() << "Add edge: " << functionNames.back() << " -> " << Fortran::lower::mangle::mangleName(*symbolComp) - << "\n"; + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*symbolComp) << "\n"; auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) @@ -257,9 +285,10 @@ class ParseTreeVisitor { // add edges for finalizers for (auto final : details->finals()) { - edges.emplace_back(functionNames.back(), Fortran::lower::mangle::mangleName(*final.second)); + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*final.second)); - llvm::outs() << "Add edge: " << functionNames.back() << " -> " + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " << Fortran::lower::mangle::mangleName(*final.second) << "\n"; } } @@ -393,6 +422,13 @@ class ParseTreeVisitor { return (std::holds_alternative(v) || ...); } + bool isOperator(const Fortran::parser::Expr* e) { + using PE = Fortran::parser::Expr; + return holds_any_ofu), PE::UnaryPlus, PE::Negate, PE::NOT, PE::Power, PE::Multiply, PE::Divide, + PE::Add, PE::Subtract, PE::Concat, PE::LT, PE::LE, PE::EQ, PE::NE, PE::GE, PE::GT, PE::AND, + PE::OR, PE::EQV, PE::NEQV, PE::DefinedBinary, PE::DefinedUnary>(e->u); + } + bool compare_expr_IntrinsicOperator(const Fortran::parser::Expr* expr, const Fortran::parser::DefinedOperator::IntrinsicOperator* op) { if (!expr || !op) @@ -445,96 +481,128 @@ class ParseTreeVisitor { } } - void Post(const Fortran::parser::Expr& e) { + bool Pre(const Fortran::parser::Expr& e) { /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) - Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, - LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, - DefinedUnary, DefinedBinary - */ + Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, + LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, + DefinedUnary, DefinedBinary + */ using PE = Fortran::parser::Expr; - if (holds_any_of(e.u)) { - if (const auto* definedUnary = std::get_if(&e.u)) { - const auto& opname = std::get(definedUnary->t); - if (!opname.v.symbol) - return; - } - if (const auto* definedBinary = std::get_if(&e.u)) { - const auto& opname = std::get(definedBinary->t); - if (!opname.v.symbol) - return; - } - } + auto* designator = std::get_if>(&e.u); + if (designator && !exprStmtWithOps.empty()) { + auto* dataRef = std::get_if(&designator->value().u); + if (!dataRef) + return true; - // UnaryPlus and Negate are not in IntrinsicOperator whyyyyyyyyy ??????? Can they be overridden? - - if (holds_any_of( - e.u)) { - std::visit( - [&](auto&& arg) { - using T = std::decay_t; - const Fortran::common::Indirection* expr = nullptr; - if constexpr (std::is_base_of_v) { - expr = &std::get<0>(arg.t); - } else if constexpr (std::is_base_of_v) { - expr = &arg.v; - } else { - return; // not a unary or binary operator - } + auto* name = std::get_if(&dataRef->u); + if (!name || !name->symbol) + return true; + + auto* type = name->symbol->GetType(); + if (!type) + return true; - llvm::outs() << "Expr: " << expr->value().source.ToString() << "\n"; + auto* derived = type->AsDerived(); + if (!derived) + return true; - auto* designator = std::get_if>(&expr->value().u); - if (!designator) - return; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + return true; - auto* dataRef = std::get_if(&designator->value().u); - if (!dataRef) - return; + auto typeWithDerived = find_type_with_derived_types(typeSymbol); - auto* name = std::get_if(&dataRef->u); - if (!name || !name->symbol) - return; + for (auto e : exprStmtWithOps) { + // search in derived types + for (const auto& t : typeWithDerived) { + auto opIt = std::find_if(t.operators.begin(), t.operators.end(), + [&](const auto& p) { return compare_expr_IntrinsicOperator(e, p.first); }); + if (opIt == t.operators.end()) + continue; - auto* type = name->symbol->GetType(); - if (!type) - return; + auto funcSymbol = opIt->second; - auto* derived = type->AsDerived(); - if (!derived) - return; + bool skipSelfCall = false; + for (type_t t : typeWithDerived) { + auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), + [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); + if (procIt == t.procedures.end()) + continue; - auto* typeSymbol = &derived->typeSymbol(); - if (!typeSymbol) - return; + if (procIt->second->name() == functionSymbols.back()->name()) { + skipSelfCall = true; + break; + } + } + + if (!skipSelfCall) + add_edges_for_produces_and_derived_types(typeWithDerived, funcSymbol); + } - auto typeWithDerived = find_type_with_derived_types(typeSymbol); + // search in interface operators TODO improve this just add everything in the interface instead of comparing + // types + auto it = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& p) { + if (auto* intrinsicOp = std::get_if(p.first)) { + return compare_expr_IntrinsicOperator(e, intrinsicOp); + } + if (auto* definedOpName = std::get_if(p.first)) { + llvm::outs() << "definedOpName: " << definedOpName->v.symbol->name() << "\n"; + + // TODO: Designator is in another expression + + // auto* designator = std::get_if>(&e->u); + // if (!designator) + // return false; + // auto* dataRef = std::get_if(&designator->value().u); + // if (!dataRef) + // return false; + // auto* name = std::get_if(&dataRef->u); + // if (!name || !name->symbol) + // return false; + + // llvm::outs() << "defineOpName: " << definedOpName->v.symbol->name() << " vs " << name->symbol->name() + // << "\n"; + + // return definedOpName->v.symbol->name() == name->symbol->name(); // doesnt work + return false; + } + return false; + }); + if (it != interfaceOperators.end()) { + for (auto* sym : it->second) { + // skip self calls + if (sym->name() == functionSymbols.back()->name()) + continue; + + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*sym)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*sym) << "\n"; + } + } + } - auto findOpIt = std::find_if(typeWithDerived.begin(), typeWithDerived.end(), [&](const type_t& t) { - return std::any_of(t.operators.begin(), t.operators.end(), [&](const auto& op) { - llvm::outs() << "Checking operator: " << Fortran::parser::DefinedOperator::EnumToString(*op.first) - << " with expr: " << expr->value().source.ToString() << "\n"; + return true; + } - return compare_expr_IntrinsicOperator(&expr->value(), op.first); - }); - }); - if (findOpIt == typeWithDerived.end()) - return; + if (isOperator(&e)) { + exprStmtWithOps.emplace_back(&e); + } - llvm::outs() << "Found operator: " << findOpIt->type->name() << "\n"; + return true; + } - auto operatorIt = std::find_if(findOpIt->operators.begin(), findOpIt->operators.end(), [&](const auto& op) { - return compare_expr_IntrinsicOperator(&expr->value(), op.first); - }); - if (operatorIt == findOpIt->operators.end()) - return; + void Post(const Fortran::parser::Expr& e) { + using PE = Fortran::parser::Expr; - auto* procedureSymbol = operatorIt->second; + if (!isOperator(&e)) { + return; + } - add_edges_for_produces_and_derived_types(typeWithDerived, procedureSymbol); - }, - e.u); + if (!exprStmtWithOps.empty()) { + exprStmtWithOps.pop_back(); } } @@ -550,7 +618,7 @@ class ParseTreeVisitor { bool inInterfaceStmtDefinedOperator = false; bool inInterfaceSpecification = false; - std::vector functionNames; + std::vector functionSymbols; std::vector> functionDummyArgs; std::vector types; @@ -559,6 +627,8 @@ class ParseTreeVisitor { const std::variant*, std::vector>> interfaceOperators; // operator name (symbol) => [procedure names (symbols)] + + std::vector exprStmtWithOps; }; class CollectCG : public Fortran::frontend::PluginParseTreeAction { From a72078cd9677660bc71f03a7bb372792b46e4c49 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:48 +0100 Subject: [PATCH 033/130] refactor: move ParseTreeVisitor into its own file --- cgfcollector/include/ParseTreeVisitor.h | 117 +++++ cgfcollector/include/headers.h | 3 - cgfcollector/src/ParseTreeVisitor.cpp | 576 +++++++++++++++++++++ cgfcollector/src/main.cpp | 633 +----------------------- 4 files changed, 695 insertions(+), 634 deletions(-) create mode 100644 cgfcollector/include/ParseTreeVisitor.h create mode 100644 cgfcollector/src/ParseTreeVisitor.cpp diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h new file mode 100644 index 00000000..eb9fd054 --- /dev/null +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -0,0 +1,117 @@ +#pragma once + +#include "headers.h" + +using namespace Fortran::parser; +using namespace Fortran::semantics; + +typedef struct type { + Symbol* type; + Symbol* extendsFrom; + std::vector> procedures; // name(symbol) => optname(symbol) + std::vector> operators; // operator => name(symbol) +} type_t; + +class ParseTreeVisitor { + public: + ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; + + std::vector> getEdges() const { return edges; } + + template + void handleFuncSubStmt(const T& stmt); + void handleEndFuncSubStmt(); + + // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. + std::vector find_type_with_derived_types(const Symbol* typeSymbol); + + // this function searches with typeSymbol for a type in types vector and adds edges for procedures that matches + // procedureSymbol. And also adds edges from types that extends from typeSymbol. + void add_edges_for_produces_and_derived_types(std::vector typeWithDerived, const Symbol* procedureSymbol); + + template + bool holds_any_of(const Variant& v) { + return (std::holds_alternative(v) || ...); + } + + bool isOperator(const Expr* e); + + bool compare_expr_IntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op); + + template + bool Pre(const A&) { + return true; + } + template + void Post(const A&) {} + + bool Pre(const MainProgram& p); + void Post(const MainProgram&); + + bool Pre(const FunctionSubprogram&); + void Post(const FunctionSubprogram&); + bool Pre(const SubroutineSubprogram&); + void Post(const SubroutineSubprogram&); + + void Post(const ExecutionPart& e); + + void Post(const FunctionStmt& f); + void Post(const EndFunctionStmt&); + void Post(const SubroutineStmt& s); + void Post(const EndSubroutineStmt&); + + void Post(const ProcedureDesignator& p); + + // handle destructors (finalizers) TODO: test i definitely missed some edges cases + void Post(const TypeDeclarationStmt& t); + + // following 5 methods are for collecting types and their procedures. see type struct and vector. + + // type def + bool Pre(const DerivedTypeDef&); + void Post(const DerivedTypeDef&); + + // type stmt like type [, extends(...)] :: body (not exhaustive and not extends) + void Post(const DerivedTypeStmt& t); + + // type attrs like extends + void Post(const TypeAttrSpec& a); + + // procedures in type defs + void Post(const TypeBoundProcedureStmt& s); + + // collect defined operators in a type def (operator overloading) + void Post(const TypeBoundGenericStmt& s); + + // the following 4 methods are for collecting defined operators in interface statements + bool Pre(const InterfaceStmt&); + bool Pre(const EndInterfaceStmt&); + void Post(const DefinedOperator& op); + void Post(const ProcedureStmt& p); + + bool Pre(const Expr& e); + void Post(const Expr& e); + + private: + metacg::Callgraph* cg; + std::vector> edges; // (caller, callee) + std::string currentFileName; + + bool inFunctionOrSubroutineSubProgram = false; + bool inMainProgram = false; + bool inDerivedTypeDef = false; + bool inInterfaceStmt = false; + bool inInterfaceStmtDefinedOperator = false; + bool inInterfaceSpecification = false; + + std::vector functionSymbols; + std::vector> functionDummyArgs; + + std::vector types; + + std::vector*, + std::vector>> + interfaceOperators; // operator name (symbol) => [procedure names (symbols)] + + std::vector exprStmtWithOps; +}; diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h index 0db95650..951f69bb 100644 --- a/cgfcollector/include/headers.h +++ b/cgfcollector/include/headers.h @@ -1,7 +1,4 @@ -#pragma once - #include -#include #include #include #include diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp new file mode 100644 index 00000000..cef8f823 --- /dev/null +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -0,0 +1,576 @@ +#include "ParseTreeVisitor.h" + +// util functions + +template +void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { + if (auto* sym = std::get(stmt.t).symbol) { + functionSymbols.emplace_back(sym); + functionDummyArgs.emplace_back(std::vector()); + cg->insert( + std::make_unique(Fortran::lower::mangle::mangleName(*sym), currentFileName, false, false)); + + llvm::outs() << "Add node: " << Fortran::lower::mangle::mangleName(*sym) << "\n"; + } +} + +template void ParseTreeVisitor::handleFuncSubStmt(const FunctionStmt&); +template void ParseTreeVisitor::handleFuncSubStmt(const SubroutineStmt&); + +void ParseTreeVisitor::handleEndFuncSubStmt() { + if (!functionSymbols.empty()) { + functionSymbols.pop_back(); + } + if (!functionDummyArgs.empty()) { + functionDummyArgs.pop_back(); + } +} + +std::vector ParseTreeVisitor::find_type_with_derived_types(const Symbol* typeSymbol) { + std::vector typeWithDerived; + + auto findTypeIt = + std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); + if (findTypeIt == types.end()) + return typeWithDerived; + + typeWithDerived.push_back(*findTypeIt); + + // base type + if ((*findTypeIt).extendsFrom == nullptr) { + for (auto t : types) { + if (t.extendsFrom != typeSymbol) + continue; + + typeWithDerived.push_back(t); + } + // not a base type, go back recursively to find all "base" types + } else { + auto* currentExtendsFrom = (*findTypeIt).extendsFrom; + while (currentExtendsFrom) { + auto currentType = std::find_if(types.begin(), types.end(), + [¤tExtendsFrom](const type_t& t) { return t.type == currentExtendsFrom; }); + if (currentType == types.end()) { + llvm::errs() << "Error: Types array (extendsFrom) field entry missing."; + return typeWithDerived; + } + + typeWithDerived.push_back(*currentType); + + if ((*currentType).extendsFrom != nullptr) { + currentExtendsFrom = (*currentType).extendsFrom; + } else { + currentExtendsFrom = nullptr; + } + } + } + + return typeWithDerived; +} + +void ParseTreeVisitor::add_edges_for_produces_and_derived_types(std::vector typeWithDerived, + const Symbol* procedureSymbol) { + for (type_t t : typeWithDerived) { + auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&procedureSymbol](const auto& p) { + return p.first->name() == procedureSymbol->name(); + }); + if (procIt == t.procedures.end()) + continue; + + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*procIt->second)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*procIt->second) << "\n"; + } +} + +bool ParseTreeVisitor::isOperator(const Expr* e) { + using PE = Expr; + return holds_any_ofu), PE::UnaryPlus, PE::Negate, PE::NOT, PE::Power, PE::Multiply, PE::Divide, PE::Add, + PE::Subtract, PE::Concat, PE::LT, PE::LE, PE::EQ, PE::NE, PE::GE, PE::GT, PE::AND, PE::OR, + PE::EQV, PE::NEQV, PE::DefinedBinary, PE::DefinedUnary>(e->u); +} + +bool ParseTreeVisitor::compare_expr_IntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op) { + if (!expr || !op) + return false; + + using IO = DefinedOperator::IntrinsicOperator; + + switch (*op) { + // case IO::UnaryPlus: + // return std::get_if(&expr->u) != nullptr; + // case IO::Negate: + // return std::get_if(&expr->u) != nullptr; + case IO::NOT: + return std::get_if(&expr->u) != nullptr; + case IO::Power: + return std::get_if(&expr->u) != nullptr; + case IO::Multiply: + return std::get_if(&expr->u) != nullptr; + case IO::Divide: + return std::get_if(&expr->u) != nullptr; + case IO::Add: + return std::get_if(&expr->u) != nullptr; + case IO::Subtract: + return std::get_if(&expr->u) != nullptr; + case IO::Concat: + return std::get_if(&expr->u) != nullptr; + case IO::LT: + return std::get_if(&expr->u) != nullptr; + case IO::LE: + return std::get_if(&expr->u) != nullptr; + case IO::EQ: + return std::get_if(&expr->u) != nullptr; + case IO::NE: + return std::get_if(&expr->u) != nullptr; + case IO::GE: + return std::get_if(&expr->u) != nullptr; + case IO::GT: + return std::get_if(&expr->u) != nullptr; + case IO::AND: + return std::get_if(&expr->u) != nullptr; + case IO::OR: + return std::get_if(&expr->u) != nullptr; + case IO::EQV: + return std::get_if(&expr->u) != nullptr; + case IO::NEQV: + return std::get_if(&expr->u) != nullptr; + default: + return false; + } +} + +// Visitor implementations + +bool ParseTreeVisitor::Pre(const MainProgram& p) { + inMainProgram = true; + + if (const auto& maybeStmt = std::get<0>(p.t)) { + if (!maybeStmt->statement.v.symbol) + return true; + + functionSymbols.emplace_back(maybeStmt->statement.v.symbol); + cg->insert(std::make_unique(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + currentFileName, false, false)); + } + return true; +} + +void ParseTreeVisitor::Post(const MainProgram&) { + inMainProgram = false; + + if (!functionSymbols.empty()) { + functionSymbols.pop_back(); + } +} + +bool ParseTreeVisitor::Pre(const FunctionSubprogram&) { + inFunctionOrSubroutineSubProgram = true; + return true; +} + +void ParseTreeVisitor::Post(const FunctionSubprogram&) { inFunctionOrSubroutineSubProgram = false; } + +bool ParseTreeVisitor::Pre(const SubroutineSubprogram&) { + inFunctionOrSubroutineSubProgram = true; + return true; +} + +void ParseTreeVisitor::Post(const SubroutineSubprogram&) { inFunctionOrSubroutineSubProgram = false; } + +void ParseTreeVisitor::Post(const ExecutionPart& e) { + if (!inFunctionOrSubroutineSubProgram && !inMainProgram) + return; + + auto* node = cg->getNode(Fortran::lower::mangle::mangleName(*functionSymbols.back())); + if (!node) { + return; + } + + node->setHasBody(true); +} + +void ParseTreeVisitor::Post(const FunctionStmt& f) { + llvm::outs() << "In function: " << Fortran::lower::mangle::mangleName(*std::get(f.t).symbol) << "\n"; + + handleFuncSubStmt(f); + + // collect function arguments + const auto& name_list = std::get>(f.t); + for (auto name : name_list) { + functionDummyArgs.back().push_back(&name); + } +} + +void ParseTreeVisitor::Post(const EndFunctionStmt&) { + if (!functionSymbols.empty()) { + llvm::outs() << "End function: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; + } + + handleEndFuncSubStmt(); +} + +void ParseTreeVisitor::Post(const SubroutineStmt& s) { + llvm::outs() << "In subroutine: " << Fortran::lower::mangle::mangleName(*std::get(s.t).symbol) << "\n"; + + handleFuncSubStmt(s); + + // collect subroutine arguments (dummy args) + const auto* dummyArg_list = &std::get>(s.t); + for (const auto& dummyArg : *dummyArg_list) { + const auto* name = std::get_if(&dummyArg.u); + functionDummyArgs.back().push_back(name); + } +} + +void ParseTreeVisitor::Post(const EndSubroutineStmt&) { + if (!functionSymbols.empty()) { + llvm::outs() << "End subroutine: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; + } + + handleEndFuncSubStmt(); +} + +void ParseTreeVisitor::Post(const ProcedureDesignator& p) { + if (functionSymbols.empty()) + return; + + // if just the name is called. (as subroutine with call and as function without call) + if (auto* name = std::get_if(&p.u)) { + if (!name->symbol) + return; + + // ignore intrinsic functions + if (name->symbol->attrs().test(Attr::INTRINSIC)) + return; + + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*name->symbol)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*name->symbol) << "\n"; + + // if called from a object with %. (base % component) + } else if (auto* procCompRef = std::get_if(&p.u)) { + auto* symbolComp = procCompRef->v.thing.component.symbol; + if (!symbolComp) + return; + + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*symbolComp)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*symbolComp) << "\n"; + + auto* baseName = std::get_if(&procCompRef->v.thing.base.u); + if (!baseName || !baseName->symbol) + return; + auto* symbolBase = baseName->symbol; + + // handle derived types edges + + auto* type = symbolBase->GetType(); + if (!type) + return; + auto* derived = type->AsDerived(); + if (!derived) + return; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + return; + + add_edges_for_produces_and_derived_types(find_type_with_derived_types(typeSymbol), symbolComp); + } +} + +void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { + // TODO: allocatable case + // const auto& attrSpec = std::get>(t.t); + // for (const auto& attr : attrSpec) { + // if (std::holds_alternative(attr.u)) { + // return; // skip allocatable because no finalizer called + // } + // } + + for (const auto& entity : std::get>(t.t)) { + const auto& name = std::get(entity.t); + if (!name.symbol) + continue; + + // skip if name is an argument to a function or subroutine + if (!functionDummyArgs.empty()) { + auto it = std::find_if(functionDummyArgs.back().begin(), functionDummyArgs.back().end(), + [&name](const Name* dummyArg) { return dummyArg->symbol == name.symbol; }); + + if (it != functionDummyArgs.back().end()) + continue; + } + + auto* type = name.symbol->GetType(); + if (!type) + continue; + auto* derived = type->AsDerived(); + if (!derived) + continue; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + continue; + + const auto* details = std::get_if(&typeSymbol->details()); + if (!details) + continue; + + // derived->HasDefaultInitialization(); + + // add edges for finalizers + for (auto final : details->finals()) { + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*final.second)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*final.second) << "\n"; + } + } +} + +bool ParseTreeVisitor::Pre(const DerivedTypeDef&) { + inDerivedTypeDef = true; + types.emplace_back(); + + return true; +} + +void ParseTreeVisitor::Post(const DerivedTypeDef&) { inDerivedTypeDef = false; } + +void ParseTreeVisitor::Post(const DerivedTypeStmt& t) { + if (!inDerivedTypeDef) + return; + + auto& currentType = types.back(); + const auto& name = std::get(t.t); + currentType.type = name.symbol; +} + +void ParseTreeVisitor::Post(const TypeAttrSpec& a) { + if (!inDerivedTypeDef) + return; + + auto& currentType = types.back(); + if (std::holds_alternative(a.u)) { + const auto& extends = std::get(a.u); + currentType.extendsFrom = extends.v.symbol; + } +} + +void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { + if (!inDerivedTypeDef) + return; + + if (auto* withoutInterface = std::get_if(&s.u)) { + for (const auto& d : withoutInterface->declarations) { + auto& name = std::get(d.t); + if (!name.symbol) + return; + + auto& optname = std::get>(d.t); + if (!optname || !optname->symbol) { + return; + } + + auto& currentType = types.back(); + currentType.procedures.emplace_back(name.symbol, optname->symbol); + } + + // only for abstract types, with deferred in binding attr list + } else if (auto* withInterface = std::get_if(&s.u)) { + for (const auto& n : withInterface->bindingNames) { + if (!n.symbol) + return; + + auto& currentType = types.back(); + currentType.procedures.emplace_back(n.symbol, n.symbol); + } + } +} + +void ParseTreeVisitor::Post(const TypeBoundGenericStmt& s) { + if (!inDerivedTypeDef) + return; + + const auto& genericSpec = std::get>(s.t); + if (auto* definedOperator = std::get_if(&genericSpec.value().u)) { + if (auto* intrinsicOp = std::get_if(&definedOperator->u)) { + const auto& names = std::get>(s.t); + + auto& currentType = types.back(); + + for (auto name : names) { + if (!name.symbol) + continue; + + currentType.operators.emplace_back(intrinsicOp, name.symbol); + } + } + } +} + +bool ParseTreeVisitor::Pre(const InterfaceStmt&) { + inInterfaceStmt = true; + return true; +} + +bool ParseTreeVisitor::Pre(const EndInterfaceStmt&) { + inInterfaceStmt = false; + inInterfaceStmtDefinedOperator = false; + return true; +} + +void ParseTreeVisitor::Post(const DefinedOperator& op) { + if (!inInterfaceStmt) + return; + + inInterfaceStmtDefinedOperator = true; + + interfaceOperators.emplace_back(&op.u, std::vector()); +} + +void ParseTreeVisitor::Post(const ProcedureStmt& p) { + if (!inInterfaceStmtDefinedOperator) + return; + + auto name = std::get>(p.t); + for (const auto& n : name) { + if (!n.symbol) + continue; + + if (interfaceOperators.empty()) { + llvm::errs() << "This should no happen. Likely there is a bug with parsing DefinedOperator's\n"; + continue; + } + + interfaceOperators.back().second.push_back(n.symbol); + } +} + +bool ParseTreeVisitor::Pre(const Expr& e) { + /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) + Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, + LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, + DefinedUnary, DefinedBinary + */ + auto* designator = std::get_if>(&e.u); + if (designator && !exprStmtWithOps.empty()) { + auto* dataRef = std::get_if(&designator->value().u); + if (!dataRef) + return true; + + auto* name = std::get_if(&dataRef->u); + if (!name || !name->symbol) + return true; + + auto* type = name->symbol->GetType(); + if (!type) + return true; + + auto* derived = type->AsDerived(); + if (!derived) + return true; + + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + return true; + + auto typeWithDerived = find_type_with_derived_types(typeSymbol); + + for (auto e : exprStmtWithOps) { + // search in derived types + for (const auto& t : typeWithDerived) { + auto opIt = std::find_if(t.operators.begin(), t.operators.end(), + [&](const auto& p) { return compare_expr_IntrinsicOperator(e, p.first); }); + if (opIt == t.operators.end()) + continue; + + auto funcSymbol = opIt->second; + + bool skipSelfCall = false; + for (type_t t : typeWithDerived) { + auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), + [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); + if (procIt == t.procedures.end()) + continue; + + if (procIt->second->name() == functionSymbols.back()->name()) { + skipSelfCall = true; + break; + } + } + + if (!skipSelfCall) + add_edges_for_produces_and_derived_types(typeWithDerived, funcSymbol); + } + + // search in interface operators TODO improve this just add everything in the interface instead of comparing + // types + auto it = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& p) { + if (auto* intrinsicOp = std::get_if(p.first)) { + return compare_expr_IntrinsicOperator(e, intrinsicOp); + } + if (auto* definedOpName = std::get_if(p.first)) { + llvm::outs() << "definedOpName: " << definedOpName->v.symbol->name() << "\n"; + + // TODO: Designator is in another expression + + // auto* designator = std::get_if>(&e->u); + // if (!designator) + // return false; + // auto* dataRef = std::get_if(&designator->value().u); + // if (!dataRef) + // return false; + // auto* name = std::get_if(&dataRef->u); + // if (!name || !name->symbol) + // return false; + + // llvm::outs() << "defineOpName: " << definedOpName->v.symbol->name() << " vs " << name->symbol->name() + // << "\n"; + + // return definedOpName->v.symbol->name() == name->symbol->name(); // doesnt work + return false; + } + return false; + }); + if (it != interfaceOperators.end()) { + for (auto* sym : it->second) { + // skip self calls + if (sym->name() == functionSymbols.back()->name()) + continue; + + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*sym)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*sym) << "\n"; + } + } + } + + return true; + } + + if (isOperator(&e)) { + exprStmtWithOps.emplace_back(&e); + } + + return true; +} + +void ParseTreeVisitor::Post(const Expr& e) { + if (!isOperator(&e)) { + return; + } + + if (!exprStmtWithOps.empty()) { + exprStmtWithOps.pop_back(); + } +} diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 6db21449..e2538910 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -1,635 +1,6 @@ -#include "headers.h" +#include "ParseTreeVisitor.h" -typedef struct type { - Fortran::semantics::Symbol* type; - Fortran::semantics::Symbol* extendsFrom; - std::vector> - procedures; // name(symbol) => optname(symbol) - std::vector> - operators; // operator => name(symbol) -} type_t; - -class ParseTreeVisitor { - public: - ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; - - template - void handleFuncSubStmt(const T& stmt) { - if (auto* sym = std::get(stmt.t).symbol) { - functionSymbols.emplace_back(sym); - functionDummyArgs.emplace_back(std::vector()); - cg->insert( - std::make_unique(Fortran::lower::mangle::mangleName(*sym), currentFileName, false, false)); - - llvm::outs() << "Add node: " << Fortran::lower::mangle::mangleName(*sym) << "\n"; - } - } - void handleEndFuncSubStmt() { - if (!functionSymbols.empty()) { - functionSymbols.pop_back(); - } - if (!functionDummyArgs.empty()) { - functionDummyArgs.pop_back(); - } - } - - std::vector> getEdges() const { return edges; } - - // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. - std::vector find_type_with_derived_types(const Fortran::semantics::Symbol* typeSymbol) { - std::vector typeWithDerived; - - auto findTypeIt = - std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); - if (findTypeIt == types.end()) - return typeWithDerived; - - typeWithDerived.push_back(*findTypeIt); - - // base type - if ((*findTypeIt).extendsFrom == nullptr) { - for (auto t : types) { - if (t.extendsFrom != typeSymbol) - continue; - - typeWithDerived.push_back(t); - } - // not a base type, go back recursively to find all "base" types - } else { - auto* currentExtendsFrom = (*findTypeIt).extendsFrom; - while (currentExtendsFrom) { - auto currentType = std::find_if(types.begin(), types.end(), [¤tExtendsFrom](const type_t& t) { - return t.type == currentExtendsFrom; - }); - if (currentType == types.end()) { - llvm::errs() << "Error: Types array (extendsFrom) field entry missing."; - return typeWithDerived; - } - - typeWithDerived.push_back(*currentType); - - if ((*currentType).extendsFrom != nullptr) { - currentExtendsFrom = (*currentType).extendsFrom; - } else { - currentExtendsFrom = nullptr; - } - } - } - - return typeWithDerived; - } - - // this function searches with typeSymbol for a type in types vector and adds edges for procedures that matches - // procedureSymbol. And also adds edges from types that extends from typeSymbol. - void add_edges_for_produces_and_derived_types(std::vector typeWithDerived, - const Fortran::semantics::Symbol* procedureSymbol) { - for (type_t t : typeWithDerived) { - auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&procedureSymbol](const auto& p) { - return p.first->name() == procedureSymbol->name(); - }); - if (procIt == t.procedures.end()) - continue; - - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*procIt->second)); - - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*procIt->second) << "\n"; - } - } - - template - bool Pre(const A&) { - return true; - } - template - void Post(const A&) {} - - bool Pre(const Fortran::parser::MainProgram& p) { - inMainProgram = true; - - if (const auto& maybeStmt = std::get<0>(p.t)) { - if (!maybeStmt->statement.v.symbol) - return true; - - functionSymbols.emplace_back(maybeStmt->statement.v.symbol); - cg->insert(std::make_unique(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - currentFileName, false, false)); - } - return true; - } - - void Post(const Fortran::parser::MainProgram&) { - inMainProgram = false; - - if (!functionSymbols.empty()) { - functionSymbols.pop_back(); - } - } - - bool Pre(const Fortran::parser::FunctionSubprogram&) { - inFunctionOrSubroutineSubProgram = true; - return true; - } - void Post(const Fortran::parser::FunctionSubprogram&) { inFunctionOrSubroutineSubProgram = false; } - bool Pre(const Fortran::parser::SubroutineSubprogram&) { - inFunctionOrSubroutineSubProgram = true; - return true; - } - void Post(const Fortran::parser::SubroutineSubprogram&) { inFunctionOrSubroutineSubProgram = false; } - - void Post(const Fortran::parser::ExecutionPart& e) { - if (!inFunctionOrSubroutineSubProgram && !inMainProgram) - return; - - auto* node = cg->getNode(Fortran::lower::mangle::mangleName(*functionSymbols.back())); - if (!node) { - return; - } - - node->setHasBody(true); - } - - void Post(const Fortran::parser::FunctionStmt& f) { - llvm::outs() << "In function: " << Fortran::lower::mangle::mangleName(*std::get(f.t).symbol) - << "\n"; - - handleFuncSubStmt(f); - - // collect function arguments - const auto& name_list = std::get>(f.t); - for (auto name : name_list) { - functionDummyArgs.back().push_back(&name); - } - } - void Post(const Fortran::parser::EndFunctionStmt&) { - if (!functionSymbols.empty()) { - llvm::outs() << "End function: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; - } - - handleEndFuncSubStmt(); - } - void Post(const Fortran::parser::SubroutineStmt& s) { - llvm::outs() << "In subroutine: " - << Fortran::lower::mangle::mangleName(*std::get(s.t).symbol) << "\n"; - - handleFuncSubStmt(s); - - // collect subroutine arguments (dummy args) - const auto* dummyArg_list = &std::get>(s.t); - for (const auto& dummyArg : *dummyArg_list) { - const auto* name = std::get_if(&dummyArg.u); - functionDummyArgs.back().push_back(name); - } - } - void Post(const Fortran::parser::EndSubroutineStmt&) { - if (!functionSymbols.empty()) { - llvm::outs() << "End subroutine: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; - } - - handleEndFuncSubStmt(); - } - - void Post(const Fortran::parser::ProcedureDesignator& p) { - if (functionSymbols.empty()) - return; - - // if just the name is called. (as subroutine with call and as function without call) - if (auto* name = std::get_if(&p.u)) { - if (!name->symbol) - return; - - // ignore intrinsic functions - if (name->symbol->attrs().test(Fortran::semantics::Attr::INTRINSIC)) - return; - - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*name->symbol)); - - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*name->symbol) << "\n"; - - // if called from a object with %. (base % component) - } else if (auto* procCompRef = std::get_if(&p.u)) { - auto* symbolComp = procCompRef->v.thing.component.symbol; - if (!symbolComp) - return; - - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*symbolComp)); - - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*symbolComp) << "\n"; - - auto* baseName = std::get_if(&procCompRef->v.thing.base.u); - if (!baseName || !baseName->symbol) - return; - auto* symbolBase = baseName->symbol; - - // handle derived types edges - - auto* type = symbolBase->GetType(); - if (!type) - return; - auto* derived = type->AsDerived(); - if (!derived) - return; - auto* typeSymbol = &derived->typeSymbol(); - if (!typeSymbol) - return; - - add_edges_for_produces_and_derived_types(find_type_with_derived_types(typeSymbol), symbolComp); - } - } - - // handle destructors (finalizers) TODO: test i definitely missed some edges cases - void Post(const Fortran::parser::TypeDeclarationStmt& t) { - // TODO: allocatable case - // const auto& attrSpec = std::get>(t.t); - // for (const auto& attr : attrSpec) { - // if (std::holds_alternative(attr.u)) { - // return; // skip allocatable because no finalizer called - // } - // } - - for (const auto& entity : std::get>(t.t)) { - const auto& name = std::get(entity.t); - if (!name.symbol) - continue; - - // skip if name is an argument to a function or subroutine - if (!functionDummyArgs.empty()) { - auto it = - std::find_if(functionDummyArgs.back().begin(), functionDummyArgs.back().end(), - [&name](const Fortran::parser::Name* dummyArg) { return dummyArg->symbol == name.symbol; }); - - if (it != functionDummyArgs.back().end()) - continue; - } - - auto* type = name.symbol->GetType(); - if (!type) - continue; - auto* derived = type->AsDerived(); - if (!derived) - continue; - auto* typeSymbol = &derived->typeSymbol(); - if (!typeSymbol) - continue; - - const auto* details = std::get_if(&typeSymbol->details()); - if (!details) - continue; - - // derived->HasDefaultInitialization(); - - // add edges for finalizers - for (auto final : details->finals()) { - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*final.second)); - - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*final.second) << "\n"; - } - } - } - - // following 5 methods are for collecting types and their procedures. see type struct and vector. - - // type def - bool Pre(const Fortran::parser::DerivedTypeDef&) { - inDerivedTypeDef = true; - types.emplace_back(); - - return true; - } - void Post(const Fortran::parser::DerivedTypeDef&) { inDerivedTypeDef = false; } - - // type stmt like type [, extends(...)] :: body (not exhaustive and not extends) - void Post(const Fortran::parser::DerivedTypeStmt& t) { - if (!inDerivedTypeDef) - return; - - auto& currentType = types.back(); - const auto& name = std::get(t.t); - currentType.type = name.symbol; - } - - // type attrs like extends - void Post(const Fortran::parser::TypeAttrSpec& a) { - if (!inDerivedTypeDef) - return; - - auto& currentType = types.back(); - if (std::holds_alternative(a.u)) { - const auto& extends = std::get(a.u); - currentType.extendsFrom = extends.v.symbol; - } - } - - // procedures in type defs - void Post(const Fortran::parser::TypeBoundProcedureStmt& s) { - if (!inDerivedTypeDef) - return; - - if (auto* withoutInterface = std::get_if(&s.u)) { - for (const auto& d : withoutInterface->declarations) { - auto& name = std::get(d.t); - if (!name.symbol) - return; - - auto& optname = std::get>(d.t); - if (!optname || !optname->symbol) { - return; - } - - auto& currentType = types.back(); - currentType.procedures.emplace_back(name.symbol, optname->symbol); - } - - // only for abstract types, with deferred in binding attr list - } else if (auto* withInterface = std::get_if(&s.u)) { - for (const auto& n : withInterface->bindingNames) { - if (!n.symbol) - return; - - auto& currentType = types.back(); - currentType.procedures.emplace_back(n.symbol, n.symbol); - } - } - } - - // collect defined operators in a type def (operator overloading) - void Post(const Fortran::parser::TypeBoundGenericStmt& s) { - if (!inDerivedTypeDef) - return; - - const auto& genericSpec = std::get>(s.t); - if (auto* definedOperator = std::get_if(&genericSpec.value().u)) { - if (auto* intrinsicOp = std::get_if(&definedOperator->u)) { - const auto& names = std::get>(s.t); - - auto& currentType = types.back(); - - for (auto name : names) { - if (!name.symbol) - continue; - - currentType.operators.emplace_back(intrinsicOp, name.symbol); - } - } - } - } - - // the following 4 methods are for collecting defined operators in interface statements - bool Pre(const Fortran::parser::InterfaceStmt&) { - inInterfaceStmt = true; - return true; - } - bool Pre(const Fortran::parser::EndInterfaceStmt&) { - inInterfaceStmt = false; - inInterfaceStmtDefinedOperator = false; - return true; - } - void Post(const Fortran::parser::DefinedOperator& op) { - if (!inInterfaceStmt) - return; - - inInterfaceStmtDefinedOperator = true; - - interfaceOperators.emplace_back(&op.u, std::vector()); - } - void Post(const Fortran::parser::ProcedureStmt& p) { - if (!inInterfaceStmtDefinedOperator) - return; - - auto name = std::get>(p.t); - for (const auto& n : name) { - if (!n.symbol) - continue; - - if (interfaceOperators.empty()) { - llvm::errs() << "This should no happen. Likely there is a bug with parsing DefinedOperator's\n"; - continue; - } - - interfaceOperators.back().second.push_back(n.symbol); - } - } - - template - bool holds_any_of(const Variant& v) { - return (std::holds_alternative(v) || ...); - } - - bool isOperator(const Fortran::parser::Expr* e) { - using PE = Fortran::parser::Expr; - return holds_any_ofu), PE::UnaryPlus, PE::Negate, PE::NOT, PE::Power, PE::Multiply, PE::Divide, - PE::Add, PE::Subtract, PE::Concat, PE::LT, PE::LE, PE::EQ, PE::NE, PE::GE, PE::GT, PE::AND, - PE::OR, PE::EQV, PE::NEQV, PE::DefinedBinary, PE::DefinedUnary>(e->u); - } - - bool compare_expr_IntrinsicOperator(const Fortran::parser::Expr* expr, - const Fortran::parser::DefinedOperator::IntrinsicOperator* op) { - if (!expr || !op) - return false; - - using namespace Fortran::parser; - using IO = DefinedOperator::IntrinsicOperator; - - switch (*op) { - // case IO::UnaryPlus: - // return std::get_if(&expr->u) != nullptr; - // case IO::Negate: - // return std::get_if(&expr->u) != nullptr; - case IO::NOT: - return std::get_if(&expr->u) != nullptr; - case IO::Power: - return std::get_if(&expr->u) != nullptr; - case IO::Multiply: - return std::get_if(&expr->u) != nullptr; - case IO::Divide: - return std::get_if(&expr->u) != nullptr; - case IO::Add: - return std::get_if(&expr->u) != nullptr; - case IO::Subtract: - return std::get_if(&expr->u) != nullptr; - case IO::Concat: - return std::get_if(&expr->u) != nullptr; - case IO::LT: - return std::get_if(&expr->u) != nullptr; - case IO::LE: - return std::get_if(&expr->u) != nullptr; - case IO::EQ: - return std::get_if(&expr->u) != nullptr; - case IO::NE: - return std::get_if(&expr->u) != nullptr; - case IO::GE: - return std::get_if(&expr->u) != nullptr; - case IO::GT: - return std::get_if(&expr->u) != nullptr; - case IO::AND: - return std::get_if(&expr->u) != nullptr; - case IO::OR: - return std::get_if(&expr->u) != nullptr; - case IO::EQV: - return std::get_if(&expr->u) != nullptr; - case IO::NEQV: - return std::get_if(&expr->u) != nullptr; - default: - return false; - } - } - - bool Pre(const Fortran::parser::Expr& e) { - /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) - Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, - LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, - DefinedUnary, DefinedBinary - */ - using PE = Fortran::parser::Expr; - - auto* designator = std::get_if>(&e.u); - if (designator && !exprStmtWithOps.empty()) { - auto* dataRef = std::get_if(&designator->value().u); - if (!dataRef) - return true; - - auto* name = std::get_if(&dataRef->u); - if (!name || !name->symbol) - return true; - - auto* type = name->symbol->GetType(); - if (!type) - return true; - - auto* derived = type->AsDerived(); - if (!derived) - return true; - - auto* typeSymbol = &derived->typeSymbol(); - if (!typeSymbol) - return true; - - auto typeWithDerived = find_type_with_derived_types(typeSymbol); - - for (auto e : exprStmtWithOps) { - // search in derived types - for (const auto& t : typeWithDerived) { - auto opIt = std::find_if(t.operators.begin(), t.operators.end(), - [&](const auto& p) { return compare_expr_IntrinsicOperator(e, p.first); }); - if (opIt == t.operators.end()) - continue; - - auto funcSymbol = opIt->second; - - bool skipSelfCall = false; - for (type_t t : typeWithDerived) { - auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), - [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); - if (procIt == t.procedures.end()) - continue; - - if (procIt->second->name() == functionSymbols.back()->name()) { - skipSelfCall = true; - break; - } - } - - if (!skipSelfCall) - add_edges_for_produces_and_derived_types(typeWithDerived, funcSymbol); - } - - // search in interface operators TODO improve this just add everything in the interface instead of comparing - // types - auto it = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& p) { - if (auto* intrinsicOp = std::get_if(p.first)) { - return compare_expr_IntrinsicOperator(e, intrinsicOp); - } - if (auto* definedOpName = std::get_if(p.first)) { - llvm::outs() << "definedOpName: " << definedOpName->v.symbol->name() << "\n"; - - // TODO: Designator is in another expression - - // auto* designator = std::get_if>(&e->u); - // if (!designator) - // return false; - // auto* dataRef = std::get_if(&designator->value().u); - // if (!dataRef) - // return false; - // auto* name = std::get_if(&dataRef->u); - // if (!name || !name->symbol) - // return false; - - // llvm::outs() << "defineOpName: " << definedOpName->v.symbol->name() << " vs " << name->symbol->name() - // << "\n"; - - // return definedOpName->v.symbol->name() == name->symbol->name(); // doesnt work - return false; - } - return false; - }); - if (it != interfaceOperators.end()) { - for (auto* sym : it->second) { - // skip self calls - if (sym->name() == functionSymbols.back()->name()) - continue; - - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*sym)); - - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*sym) << "\n"; - } - } - } - - return true; - } - - if (isOperator(&e)) { - exprStmtWithOps.emplace_back(&e); - } - - return true; - } - - void Post(const Fortran::parser::Expr& e) { - using PE = Fortran::parser::Expr; - - if (!isOperator(&e)) { - return; - } - - if (!exprStmtWithOps.empty()) { - exprStmtWithOps.pop_back(); - } - } - - private: - metacg::Callgraph* cg; - std::vector> edges; // (caller, callee) - std::string currentFileName; - - bool inFunctionOrSubroutineSubProgram = false; - bool inMainProgram = false; - bool inDerivedTypeDef = false; - bool inInterfaceStmt = false; - bool inInterfaceStmtDefinedOperator = false; - bool inInterfaceSpecification = false; - - std::vector functionSymbols; - std::vector> functionDummyArgs; - - std::vector types; - - std::vector*, - std::vector>> - interfaceOperators; // operator name (symbol) => [procedure names (symbols)] - - std::vector exprStmtWithOps; -}; +#include class CollectCG : public Fortran::frontend::PluginParseTreeAction { public: From 2f727a05e4637923e45ef38dad8ce03e13c50ee2 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:48 +0100 Subject: [PATCH 034/130] fix operator and improved operator test --- cgfcollector/include/ParseTreeVisitor.h | 13 ++ cgfcollector/src/ParseTreeVisitor.cpp | 166 +++++++------- cgfcollector/test/simple/operator/main.f90 | 150 ++++++++++--- cgfcollector/test/simple/operator/output.json | 204 +++++++++++++++++- 4 files changed, 408 insertions(+), 125 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index eb9fd054..c8cf5cdc 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -4,6 +4,7 @@ using namespace Fortran::parser; using namespace Fortran::semantics; +using namespace Fortran::common; typedef struct type { Symbol* type; @@ -38,6 +39,18 @@ class ParseTreeVisitor { bool compare_expr_IntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op); + template + const Name* getNameFromClassWithDesignator(const T& t) { + if (const auto* designator = std::get_if>(&t.u)) { + if (const auto* dataRef = std::get_if(&designator->value().u)) { + if (const auto* name = std::get_if(&dataRef->u)) { + return name; + } + } + } + return nullptr; + } + template bool Pre(const A&) { return true; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index cef8f823..a34ac0af 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -86,10 +86,16 @@ void ParseTreeVisitor::add_edges_for_produces_and_derived_types(std::vectoru), PE::UnaryPlus, PE::Negate, PE::NOT, PE::Power, PE::Multiply, PE::Divide, PE::Add, - PE::Subtract, PE::Concat, PE::LT, PE::LE, PE::EQ, PE::NE, PE::GE, PE::GT, PE::AND, PE::OR, - PE::EQV, PE::NEQV, PE::DefinedBinary, PE::DefinedUnary>(e->u); + /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) + Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, + LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, + DefinedUnary, DefinedBinary + */ + + return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::Power, Expr::Multiply, + Expr::Divide, Expr::Add, Expr::Subtract, Expr::Concat, Expr::LT, Expr::LE, Expr::EQ, Expr::NE, + Expr::GE, Expr::GT, Expr::AND, Expr::OR, Expr::EQV, Expr::NEQV, Expr::DefinedUnary, + Expr::DefinedBinary>(e->u); } bool ParseTreeVisitor::compare_expr_IntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op) { @@ -399,7 +405,7 @@ void ParseTreeVisitor::Post(const TypeBoundGenericStmt& s) { if (!inDerivedTypeDef) return; - const auto& genericSpec = std::get>(s.t); + const auto& genericSpec = std::get>(s.t); if (auto* definedOperator = std::get_if(&genericSpec.value().u)) { if (auto* intrinsicOp = std::get_if(&definedOperator->u)) { const auto& names = std::get>(s.t); @@ -455,25 +461,57 @@ void ParseTreeVisitor::Post(const ProcedureStmt& p) { } bool ParseTreeVisitor::Pre(const Expr& e) { - /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) - Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, - LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, - DefinedUnary, DefinedBinary - */ - auto* designator = std::get_if>(&e.u); - if (designator && !exprStmtWithOps.empty()) { - auto* dataRef = std::get_if(&designator->value().u); - if (!dataRef) - return true; + const auto* name = getNameFromClassWithDesignator(e); + // true if not in a designator expr + if (!name || !name->symbol || exprStmtWithOps.empty()) { + if (isOperator(&e)) { + exprStmtWithOps.emplace_back(&e); + } - auto* name = std::get_if(&dataRef->u); - if (!name || !name->symbol) - return true; + return true; + } - auto* type = name->symbol->GetType(); - if (!type) - return true; + auto* type = name->symbol->GetType(); + if (!type) + return true; + + for (auto e : exprStmtWithOps) { + // search in interface operators TODO improve this just add everything in the interface instead of comparing + // types + auto it = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& p) { + if (auto* intrinsicOp = std::get_if(p.first)) { + return compare_expr_IntrinsicOperator(e, intrinsicOp); + } + if (auto* definedOpName = std::get_if(p.first)) { + if (auto* definedUnary = std::get_if(&e->u)) { + auto* exprOpName = &std::get(definedUnary->t); + + return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); + } + if (auto* definedBinary = std::get_if(&e->u)) { + llvm::outs() << "definedbinary" << "\n"; + } + return false; + } + return false; + }); + if (it != interfaceOperators.end()) { + // iterate over all procedures in interface (this vastly overestimates the calls). TODO: look into procdure + // params to identify only the onces that could be called. + for (auto* sym : it->second) { + // skip self calls + if (sym->name() == functionSymbols.back()->name()) + continue; + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*sym)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*sym) << "\n"; + } + } + + // search in derived types auto* derived = type->AsDerived(); if (!derived) return true; @@ -484,82 +522,30 @@ bool ParseTreeVisitor::Pre(const Expr& e) { auto typeWithDerived = find_type_with_derived_types(typeSymbol); - for (auto e : exprStmtWithOps) { - // search in derived types - for (const auto& t : typeWithDerived) { - auto opIt = std::find_if(t.operators.begin(), t.operators.end(), - [&](const auto& p) { return compare_expr_IntrinsicOperator(e, p.first); }); - if (opIt == t.operators.end()) - continue; + for (const auto& t : typeWithDerived) { + auto opIt = std::find_if(t.operators.begin(), t.operators.end(), + [&](const auto& p) { return compare_expr_IntrinsicOperator(e, p.first); }); + if (opIt == t.operators.end()) + continue; - auto funcSymbol = opIt->second; + auto funcSymbol = opIt->second; - bool skipSelfCall = false; - for (type_t t : typeWithDerived) { - auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), - [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); - if (procIt == t.procedures.end()) - continue; + bool skipSelfCall = false; + for (type_t t : typeWithDerived) { + auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), + [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); + if (procIt == t.procedures.end()) + continue; - if (procIt->second->name() == functionSymbols.back()->name()) { - skipSelfCall = true; - break; - } + if (procIt->second->name() == functionSymbols.back()->name()) { + skipSelfCall = true; + break; } - - if (!skipSelfCall) - add_edges_for_produces_and_derived_types(typeWithDerived, funcSymbol); } - // search in interface operators TODO improve this just add everything in the interface instead of comparing - // types - auto it = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& p) { - if (auto* intrinsicOp = std::get_if(p.first)) { - return compare_expr_IntrinsicOperator(e, intrinsicOp); - } - if (auto* definedOpName = std::get_if(p.first)) { - llvm::outs() << "definedOpName: " << definedOpName->v.symbol->name() << "\n"; - - // TODO: Designator is in another expression - - // auto* designator = std::get_if>(&e->u); - // if (!designator) - // return false; - // auto* dataRef = std::get_if(&designator->value().u); - // if (!dataRef) - // return false; - // auto* name = std::get_if(&dataRef->u); - // if (!name || !name->symbol) - // return false; - - // llvm::outs() << "defineOpName: " << definedOpName->v.symbol->name() << " vs " << name->symbol->name() - // << "\n"; - - // return definedOpName->v.symbol->name() == name->symbol->name(); // doesnt work - return false; - } - return false; - }); - if (it != interfaceOperators.end()) { - for (auto* sym : it->second) { - // skip self calls - if (sym->name() == functionSymbols.back()->name()) - continue; - - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*sym)); - - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*sym) << "\n"; - } - } + if (!skipSelfCall) + add_edges_for_produces_and_derived_types(typeWithDerived, funcSymbol); } - - return true; - } - - if (isOperator(&e)) { - exprStmtWithOps.emplace_back(&e); } return true; diff --git a/cgfcollector/test/simple/operator/main.f90 b/cgfcollector/test/simple/operator/main.f90 index 2642985b..5983d6a1 100644 --- a/cgfcollector/test/simple/operator/main.f90 +++ b/cgfcollector/test/simple/operator/main.f90 @@ -11,12 +11,12 @@ module mod end type sortable interface - pure logical function compare(this, other) + logical function compare(this, other) import :: sortable implicit none class(sortable), intent(in) :: this, other end function compare - pure logical function not(this) + logical function not(this) import :: sortable implicit none class(sortable), intent(in) :: this @@ -30,54 +30,73 @@ end function not procedure :: not_impl => not_impl_integer end type integer_sortable + type :: integer_wrapper + integer :: value + end type integer_wrapper + interface operator(.NEGX.) module procedure negx end interface interface operator(+) - module procedure add_stuff + procedure add_stuff, add_stuff2 + module procedure unary_plus end interface contains - pure logical function less_than_integer(this, other) + logical function less_than_integer(this, other) class(integer_sortable), intent(in) :: this class(sortable), intent(in) :: other select type (other) type is (integer_sortable) less_than_integer = this%value < other%value + print *, "Comparing integer_sortable: ", this%value, " < ", other%value class default error stop "Type mismatch in comparison" end select - - ! unsafe - ! type(integer_sortable), allocatable :: other_int - ! other_int = transfer(other, this) - ! less_than_integer = this%value < other_int%value - end function less_than_integer - pure logical function not_impl(this) + logical function not_impl(this) class(sortable), intent(in) :: this not_impl = .NOT. this%less_then(this) + print *, "Hello from not_impl" end function not_impl - pure logical function not_impl_integer(this) + logical function not_impl_integer(this) class(integer_sortable), intent(in) :: this not_impl_integer = .NOT. this < this + print *, "Hello from not_impl_integer" end function not_impl_integer function negx(x) result(res) real, intent(in) :: x real :: res res = -x + print *, "Hello from negx" end function negx function add_stuff(x, y) result(res) type(integer_sortable), intent(in) :: x, y type(integer_sortable) :: res res%value = x%value + y%value + print *, "Hello from add_stuff" end function add_stuff + + function add_stuff2(x, y) result(res) + type(integer_wrapper), intent(in) :: x, y + type(integer_wrapper) :: res + res%value = x%value + y%value + print *, "Hello from add_stuff2" + end function add_stuff2 + + function unary_plus(x) result(res) + class(integer_sortable), intent(in) :: x + type(integer_sortable) :: res + + res%value = x%value + print *, "Hello from unary_plus" + end function unary_plus end module mod program main @@ -85,30 +104,93 @@ program main implicit none - class(sortable), allocatable :: a, b + call test_compare() + call test_compare2() + call test_not() + call test_negx() + call test_add() + call test_add2() + call test_unary_plus() + call test_expr() - type(integer_sortable) :: c, d - type(integer_sortable) :: res - - real :: e = 5.0, f - - c%value = 5 - d%value = 10 - - allocate (a, source=c) - allocate (b, source=d) - - if (a < b) then - print *, "a is less than b" - else - print *, "a is not less than b" - end if +contains + subroutine test_compare() + class(sortable), allocatable :: a, b + type(integer_sortable) :: c, d + type(integer_sortable) :: res - f = .NEGX.e - print *, f + c%value = 5 + d%value = 10 + allocate (a, source=c) + allocate (b, source=d) + + if (a < b) then + print *, "a is less than b" + else + print *, "a is nat less than b" + end if + end subroutine test_compare + + subroutine test_compare2() + class(sortable), allocatable :: a, b + type(integer_sortable) :: c, d + type(integer_sortable) :: res - res = c + d - print *, "Result of addition: ", res%value + c%value = 5 + d%value = 10 + allocate (a, source=c) + allocate (b, source=d) + + if (c < d) then + print *, "c is less than d" + else + print *, "c is not less than d" + end if + end subroutine test_compare2 + + subroutine test_not() + type(integer_sortable) :: c + + if (.NOT. c) then + print *, "c is not true" + else + print *, "c is true" + end if + end subroutine test_not + + subroutine test_negx() + real :: e = 5.0, f + f = .NEGX.e + print *, "Negated value: ", f + end subroutine test_negx + + subroutine test_add() + type(integer_sortable) :: c, d, res + c%value = 5 + d%value = 10 + res = c + d + print *, "Result of addition: ", res%value + end subroutine test_add + + subroutine test_add2() + type(integer_wrapper) :: c, d, res + c%value = 5 + d%value = 10 + res = c + d + c + print *, "Result of addition: ", res%value + end subroutine test_add2 + + subroutine test_unary_plus() + type(integer_sortable) :: c, res + c%value = 5 + res = +c + print *, "Result of unary plus: ", res%value + end subroutine test_unary_plus + + subroutine test_expr() + logical :: ad + + ad = (.NOT. 324 < 2) .EQV. .true. + end subroutine test_expr end program main - diff --git a/cgfcollector/test/simple/operator/output.json b/cgfcollector/test/simple/operator/output.json index 0967ef42..67b0bae1 100644 --- a/cgfcollector/test/simple/operator/output.json +++ b/cgfcollector/test/simple/operator/output.json @@ -1 +1,203 @@ -{} +{ + "_CG": { + "edges": [ + [[7572781303902459746, 2925204865112094556], null], + [[7572781303902459746, 11442313694358728277], null], + [[10361574024262302863, 2925204865112094556], null], + [[10361574024262302863, 11442313694358728277], null], + [[11460580497354340053, 16784127278057253920], null], + [[1923978352858283650, 12300079433521964041], null], + [[11278138169644782137, 13832393172155657730], null], + [[479400711443056833, 15223348241347075203], null], + [[10361574024262302863, 390067326288520518], null], + [[73841721019936442, 15223348241347075203], null], + [[16784127278057253920, 15223348241347075203], null], + [[7572781303902459746, 390067326288520518], null], + [[16784127278057253920, 13832393172155657730], null], + [[124759220132753432, 479400711443056833], null], + [[11278138169644782137, 15223348241347075203], null], + [[124759220132753432, 10361574024262302863], null], + [[124759220132753432, 11278138169644782137], null], + [[124759220132753432, 11460580497354340053], null], + [[11460580497354340053, 16873906278150271435], null], + [[124759220132753432, 7572781303902459746], null], + [[124759220132753432, 16014592354400816154], null], + [[73841721019936442, 13832393172155657730], null], + [[124759220132753432, 1923978352858283650], null], + [[124759220132753432, 2818987135271915965], null], + [[479400711443056833, 13832393172155657730], null] + ], + "nodes": [ + [ + 2818987135271915965, + { + "functionName": "_QFPtest_expr", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 16014592354400816154, + { + "functionName": "_QFPtest_unary_plus", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 7572781303902459746, + { + "functionName": "_QFPtest_add2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 10361574024262302863, + { + "functionName": "_QFPtest_add", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 13832393172155657730, + { + "functionName": "_QPcompare", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ], + [ + 16873906278150271435, + { + "functionName": "_QPnot", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ], + [ + 15223348241347075203, + { + "functionName": "_QMmodPless_than_integer", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 73841721019936442, + { + "functionName": "_QMmodPnot_impl", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 390067326288520518, + { + "functionName": "_QMmodPadd_stuff2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 479400711443056833, + { + "functionName": "_QFPtest_compare", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 16784127278057253920, + { + "functionName": "_QMmodPnot_impl_integer", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 11460580497354340053, + { + "functionName": "_QFPtest_not", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 11278138169644782137, + { + "functionName": "_QFPtest_compare2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 12300079433521964041, + { + "functionName": "_QMmodPnegx", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 11442313694358728277, + { + "functionName": "_QMmodPadd_stuff", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 1923978352858283650, + { + "functionName": "_QFPtest_negx", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 124759220132753432, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 2925204865112094556, + { + "functionName": "_QMmodPunary_plus", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "d24b04d4dec70317adaeb660996546c13cf4a4d2", + "version": "0.7" + }, + "version": "3.0" + } +} From 417afc883e2afa976d2b57863cc386caf9ab7c21 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:48 +0100 Subject: [PATCH 035/130] destructor now handeling the allocatable case with assignments --- cgfcollector/include/ParseTreeVisitor.h | 15 +++ cgfcollector/src/ParseTreeVisitor.cpp | 116 +++++++++++++++++------- cgfcollector/test/simple/03/input.f90 | 43 +++++++++ 3 files changed, 140 insertions(+), 34 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index c8cf5cdc..2b728277 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -13,6 +13,12 @@ typedef struct type { std::vector> operators; // operator => name(symbol) } type_t; +typedef struct trackedVar { + Symbol* var; + Symbol* procedure; // procedure in which var was defined + bool hasBeenInitialized = false; +} trackedVar_t; + class ParseTreeVisitor { public: ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; @@ -23,6 +29,8 @@ class ParseTreeVisitor { void handleFuncSubStmt(const T& stmt); void handleEndFuncSubStmt(); + void handleTrackedVars(); + // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. std::vector find_type_with_derived_types(const Symbol* typeSymbol); @@ -51,6 +59,8 @@ class ParseTreeVisitor { return nullptr; } + const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); + template bool Pre(const A&) { return true; @@ -75,6 +85,8 @@ class ParseTreeVisitor { void Post(const ProcedureDesignator& p); + void Post(const AssignmentStmt& a); + // handle destructors (finalizers) TODO: test i definitely missed some edges cases void Post(const TypeDeclarationStmt& t); @@ -102,6 +114,7 @@ class ParseTreeVisitor { void Post(const DefinedOperator& op); void Post(const ProcedureStmt& p); + // parse operators in expressions bool Pre(const Expr& e); void Post(const Expr& e); @@ -127,4 +140,6 @@ class ParseTreeVisitor { interfaceOperators; // operator name (symbol) => [procedure names (symbols)] std::vector exprStmtWithOps; + + std::vector trackedVars; }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index a34ac0af..7a68f602 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -18,6 +18,8 @@ template void ParseTreeVisitor::handleFuncSubStmt(const FunctionSt template void ParseTreeVisitor::handleFuncSubStmt(const SubroutineStmt&); void ParseTreeVisitor::handleEndFuncSubStmt() { + handleTrackedVars(); + if (!functionSymbols.empty()) { functionSymbols.pop_back(); } @@ -26,6 +28,37 @@ void ParseTreeVisitor::handleEndFuncSubStmt() { } } +void ParseTreeVisitor::handleTrackedVars() { + for (auto& trackedVar : trackedVars) { + if (trackedVar.hasBeenInitialized) { + if (trackedVar.procedure != functionSymbols.back()) { + continue; + } + + // add edge for deconstruction (finalizer) + auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); + if (!typeSymbol) + continue; + auto* details = std::get_if(&typeSymbol->details()); + if (!details) + continue; + + for (const auto& final : details->finals()) { + edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), + Fortran::lower::mangle::mangleName(*final.second)); + + llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " + << Fortran::lower::mangle::mangleName(*final.second) << "\n"; + } + } + } + + // cleanup trackedVars + trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), + [&](const trackedVar_t& t) { return t.procedure == functionSymbols.back(); }), + trackedVars.end()); +} + std::vector ParseTreeVisitor::find_type_with_derived_types(const Symbol* typeSymbol) { std::vector typeWithDerived; @@ -148,6 +181,19 @@ bool ParseTreeVisitor::compare_expr_IntrinsicOperator(const Expr* expr, const De } } +const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { + auto* type = symbol->GetType(); + if (!type) + return nullptr; + auto* derived = type->AsDerived(); + if (!derived) + return nullptr; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + return nullptr; + return typeSymbol; +} + // Visitor implementations bool ParseTreeVisitor::Pre(const MainProgram& p) { @@ -167,6 +213,8 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { void ParseTreeVisitor::Post(const MainProgram&) { inMainProgram = false; + handleTrackedVars(); + if (!functionSymbols.empty()) { functionSymbols.pop_back(); } @@ -277,13 +325,7 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { // handle derived types edges - auto* type = symbolBase->GetType(); - if (!type) - return; - auto* derived = type->AsDerived(); - if (!derived) - return; - auto* typeSymbol = &derived->typeSymbol(); + auto* typeSymbol = getTypeSymbolFromSymbol(symbolBase); if (!typeSymbol) return; @@ -291,14 +333,27 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { } } +void ParseTreeVisitor::Post(const AssignmentStmt& a) { + const auto* var = &std::get(a.t); + + auto* name = getNameFromClassWithDesignator(*var); + if (!name || !name->symbol) + return; + + auto trackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), + [&name](const auto& t) { return t.var->name() == name->symbol->name(); }); + if (trackedVarIt == trackedVars.end()) { + return; + } + + trackedVarIt->hasBeenInitialized = true; +} + void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { - // TODO: allocatable case - // const auto& attrSpec = std::get>(t.t); - // for (const auto& attr : attrSpec) { - // if (std::holds_alternative(attr.u)) { - // return; // skip allocatable because no finalizer called - // } - // } + if (functionSymbols.empty()) { + // type declaration inside a module TODO: + return; + } for (const auto& entity : std::get>(t.t)) { const auto& name = std::get(entity.t); @@ -314,24 +369,25 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { continue; } - auto* type = name.symbol->GetType(); - if (!type) - continue; - auto* derived = type->AsDerived(); - if (!derived) - continue; - auto* typeSymbol = &derived->typeSymbol(); + auto* typeSymbol = getTypeSymbolFromSymbol(name.symbol); if (!typeSymbol) continue; + const auto& attrSpec = std::get>(t.t); + auto it = std::find_if(attrSpec.begin(), attrSpec.end(), + [](const AttrSpec& a) { return std::holds_alternative(a.u); }); + if (it != attrSpec.end()) { + trackedVars.push_back({name.symbol, functionSymbols.back(), false}); + llvm::outs() << "Track variable: " << name.symbol->name() << "\n"; + continue; // skip var with allocatable attr + } + const auto* details = std::get_if(&typeSymbol->details()); if (!details) continue; - // derived->HasDefaultInitialization(); - // add edges for finalizers - for (auto final : details->finals()) { + for (const auto& final : details->finals()) { edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), Fortran::lower::mangle::mangleName(*final.second)); @@ -471,10 +527,6 @@ bool ParseTreeVisitor::Pre(const Expr& e) { return true; } - auto* type = name->symbol->GetType(); - if (!type) - return true; - for (auto e : exprStmtWithOps) { // search in interface operators TODO improve this just add everything in the interface instead of comparing // types @@ -512,13 +564,9 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } // search in derived types - auto* derived = type->AsDerived(); - if (!derived) - return true; - - auto* typeSymbol = &derived->typeSymbol(); + auto* typeSymbol = getTypeSymbolFromSymbol(name->symbol); if (!typeSymbol) - return true; + continue; auto typeWithDerived = find_type_with_derived_types(typeSymbol); diff --git a/cgfcollector/test/simple/03/input.f90 b/cgfcollector/test/simple/03/input.f90 index 8e57634d..4dfd63da 100644 --- a/cgfcollector/test/simple/03/input.f90 +++ b/cgfcollector/test/simple/03/input.f90 @@ -13,6 +13,14 @@ module mod module procedure create_polynomial end interface + type dummy_type + integer :: i + contains + final :: finalize_dummy + end type dummy_type + + type(polynomial) :: polynomialInModule + contains type(polynomial) function create_polynomial(a) @@ -37,6 +45,11 @@ subroutine finalize_polynomial(this) write (*, *) 'Finalizing polynomial' end subroutine finalize_polynomial + subroutine finalize_dummy(this) + type(dummy_type), intent(inout) :: this + write (*, *) 'Finalizing dummy_type' + end subroutine finalize_dummy + end module mod module mod_use @@ -53,6 +66,23 @@ subroutine func_calls_final() call q%print_polynomial() end subroutine func_calls_final + subroutine func_calls_final2() + type(polynomial) :: q + end subroutine func_calls_final2 + + subroutine func_calls_final3() + type(polynomial), allocatable :: q + + call set_q() + + contains + subroutine set_q() + q = polynomial([2., 3., 1., 0., 0.]) + end subroutine set_q + subroutine set_a() + end subroutine set_a + end subroutine func_calls_final3 + subroutine func_does_not_call_final() type(polynomial), allocatable :: q end subroutine func_does_not_call_final @@ -61,8 +91,12 @@ end module mod_use program main use mod + use mod_use implicit none + type(dummy_type), allocatable :: dummy + dummy = dummy_type(1) + work: block type(polynomial), allocatable :: q @@ -70,6 +104,15 @@ program main call q%print_polynomial() end block work + print *, 'Calling func_calls_final' + call func_calls_final() + print *, 'Calling func_calls_final2' + call func_calls_final2() + print *, 'Calling func_does_not_call_final' + call func_does_not_call_final() + print *, 'Calling func_calls_final3' + call func_calls_final3() + ! sould not call final because main. see 7.5.6.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) and (https://j3-fortran.org/doc/year/10/10-158r1.txt) end program main From 21af6256269a4c00ee70331eb4b2dfe9ace22f9a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:49 +0100 Subject: [PATCH 036/130] extended parsing and test for destructors for fix assignment(=), allocate, move_alloc and add handling so destructors in main function are not called when exiting --- cgfcollector/include/ParseTreeVisitor.h | 5 ++ cgfcollector/src/ParseTreeVisitor.cpp | 77 +++++++++++++++++++++---- cgfcollector/test/simple/03/input.f90 | 76 ++++++++++++++++++------ 3 files changed, 129 insertions(+), 29 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 2b728277..691c2d86 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -61,6 +61,8 @@ class ParseTreeVisitor { const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); + void handleTrackedVarAssignment(SourceName sourceName); + template bool Pre(const A&) { return true; @@ -86,6 +88,8 @@ class ParseTreeVisitor { void Post(const ProcedureDesignator& p); void Post(const AssignmentStmt& a); + void Post(const AllocateStmt& a); + void Post(const Call& c); // handle destructors (finalizers) TODO: test i definitely missed some edges cases void Post(const TypeDeclarationStmt& t); @@ -141,5 +145,6 @@ class ParseTreeVisitor { std::vector exprStmtWithOps; + // mainly used for destructor handling std::vector trackedVars; }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 7a68f602..70a18df2 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -29,11 +29,12 @@ void ParseTreeVisitor::handleEndFuncSubStmt() { } void ParseTreeVisitor::handleTrackedVars() { - for (auto& trackedVar : trackedVars) { - if (trackedVar.hasBeenInitialized) { - if (trackedVar.procedure != functionSymbols.back()) { + if (!inMainProgram) { + for (auto& trackedVar : trackedVars) { + if (!trackedVar.hasBeenInitialized) + continue; + if (trackedVar.procedure != functionSymbols.back()) continue; - } // add edge for deconstruction (finalizer) auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); @@ -194,6 +195,24 @@ const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { return typeSymbol; } +// search trackedVars for a canditate and set it as initialized. +// Prefers local variables when (shadowed) +void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { + auto anyTrackedVarIt = + std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { return t.var->name() == sourceName; }); + if (anyTrackedVarIt == trackedVars.end()) + return; + + // find local variable with the same name in the current function scope (shadowed) + auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { + return t.var->name() == sourceName && t.procedure == functionSymbols.back(); + }); + + // prefer local var if found + auto& trackedVar = (localVarIt != trackedVars.end()) ? *localVarIt : *anyTrackedVarIt; + trackedVar.hasBeenInitialized = true; +} + // Visitor implementations bool ParseTreeVisitor::Pre(const MainProgram& p) { @@ -211,13 +230,13 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { } void ParseTreeVisitor::Post(const MainProgram&) { - inMainProgram = false; - handleTrackedVars(); if (!functionSymbols.empty()) { functionSymbols.pop_back(); } + + inMainProgram = false; } bool ParseTreeVisitor::Pre(const FunctionSubprogram&) { @@ -340,13 +359,49 @@ void ParseTreeVisitor::Post(const AssignmentStmt& a) { if (!name || !name->symbol) return; - auto trackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), - [&name](const auto& t) { return t.var->name() == name->symbol->name(); }); - if (trackedVarIt == trackedVars.end()) { - return; + handleTrackedVarAssignment(name->symbol->name()); +} + +void ParseTreeVisitor::Post(const AllocateStmt& a) { + const auto* allocs = &std::get>(a.t); + + for (const auto& alloc : *allocs) { + const auto* allocObj = &std::get(alloc.t); + const auto* name = std::get_if(&allocObj->u); + if (!name || !name->symbol) { + continue; + } + + handleTrackedVarAssignment(name->symbol->name()); } +} - trackedVarIt->hasBeenInitialized = true; +void ParseTreeVisitor::Post(const Call& c) { + // handle move_alloc intrinsic for allocatable vars + const auto* designator = &std::get(c.t); + const auto* args = &std::get>(c.t); + + const auto* name = std::get_if(&designator->u); + if (!name || !name->symbol) + return; + if (!name->symbol->attrs().test(Attr::INTRINSIC) && name->symbol->name() != "move_alloc") + return; + + if (args->size() < 2) + return; + + for (const auto& arg : *args) { + const auto* actualArg = &std::get(arg.t); + const auto* expr = std::get_if>(&actualArg->u); + if (!expr) + return; + auto* name = getNameFromClassWithDesignator(expr->value()); + if (!name || !name->symbol) + return; + + llvm::outs() << "Tracked Var: " << name->symbol->name() << "\n"; + handleTrackedVarAssignment(name->symbol->name()); + } } void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { diff --git a/cgfcollector/test/simple/03/input.f90 b/cgfcollector/test/simple/03/input.f90 index 4dfd63da..a721842b 100644 --- a/cgfcollector/test/simple/03/input.f90 +++ b/cgfcollector/test/simple/03/input.f90 @@ -19,6 +19,10 @@ module mod final :: finalize_dummy end type dummy_type + interface dummy_type + module procedure create_dummy_type + end interface dummy_type + type(polynomial) :: polynomialInModule contains @@ -50,6 +54,12 @@ subroutine finalize_dummy(this) write (*, *) 'Finalizing dummy_type' end subroutine finalize_dummy + type(dummy_type) function create_dummy_type(i) + integer, intent(in) :: i + create_dummy_type%i = i + write (*, *) 'Creating dummy_type with i = ', create_dummy_type%i + end function create_dummy_type + end module mod module mod_use @@ -73,14 +83,33 @@ end subroutine func_calls_final2 subroutine func_calls_final3() type(polynomial), allocatable :: q - call set_q() + ! call set_q() + ! call set_q_alloc() + ! call set_q_alloc2() + call set_q_move_alloc() contains subroutine set_q() q = polynomial([2., 3., 1., 0., 0.]) end subroutine set_q - subroutine set_a() - end subroutine set_a + subroutine set_q_alloc() + type(polynomial), allocatable :: p + p = polynomial([2., 3., 1., 0., 0.]) + + allocate (q, source=p) + end subroutine set_q_alloc + subroutine set_q_alloc2() + type(polynomial), allocatable :: q + allocate (q) + end subroutine set_q_alloc2 + subroutine set_nothing() + end subroutine set_nothing + subroutine set_q_move_alloc() + type(polynomial), allocatable :: p + p = polynomial([2., 3., 1., 0., 0.]) + + call move_alloc(p, q) + end subroutine set_q_move_alloc end subroutine func_calls_final3 subroutine func_does_not_call_final() @@ -94,25 +123,36 @@ program main use mod_use implicit none + type :: implicit_constructor + integer :: i + end type implicit_constructor + type(implicit_constructor), allocatable :: t + type(dummy_type), allocatable :: dummy - dummy = dummy_type(1) - work: block - type(polynomial), allocatable :: q + ! sould not call final because main function. see 7.5.6.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) and (https://j3-fortran.org/doc/year/10/10-158r1.txt) + dummy = dummy_type(1) - q = polynomial([2., 3., 1., 0., 0.]) - call q%print_polynomial() - end block work + t = implicit_constructor(1) - print *, 'Calling func_calls_final' - call func_calls_final() - print *, 'Calling func_calls_final2' - call func_calls_final2() - print *, 'Calling func_does_not_call_final' - call func_does_not_call_final() - print *, 'Calling func_calls_final3' - call func_calls_final3() + call func() - ! sould not call final because main. see 7.5.6.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) and (https://j3-fortran.org/doc/year/10/10-158r1.txt) +contains + subroutine func() + work: block + type(polynomial), allocatable :: q + q = polynomial([2., 3., 1., 0., 0.]) + call q%print_polynomial() + end block work + + print *, 'Calling func_calls_final' + call func_calls_final() + print *, 'Calling func_calls_final2' + call func_calls_final2() + print *, 'Calling func_does_not_call_final' + call func_does_not_call_final() + print *, 'Calling func_calls_final3' + call func_calls_final3() + end subroutine func end program main From 0d232fd861c678e06e4e526fb614b179ef6709ba Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:49 +0100 Subject: [PATCH 037/130] renamed 03 test to final --- cgfcollector/test/simple/03/output.json | 1 - .../test/simple/{03 => final}/input.f90 | 0 cgfcollector/test/simple/final/output.json | 170 ++++++++++++++++++ 3 files changed, 170 insertions(+), 1 deletion(-) delete mode 100644 cgfcollector/test/simple/03/output.json rename cgfcollector/test/simple/{03 => final}/input.f90 (100%) create mode 100644 cgfcollector/test/simple/final/output.json diff --git a/cgfcollector/test/simple/03/output.json b/cgfcollector/test/simple/03/output.json deleted file mode 100644 index 0967ef42..00000000 --- a/cgfcollector/test/simple/03/output.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/cgfcollector/test/simple/03/input.f90 b/cgfcollector/test/simple/final/input.f90 similarity index 100% rename from cgfcollector/test/simple/03/input.f90 rename to cgfcollector/test/simple/final/input.f90 diff --git a/cgfcollector/test/simple/final/output.json b/cgfcollector/test/simple/final/output.json new file mode 100644 index 00000000..87789832 --- /dev/null +++ b/cgfcollector/test/simple/final/output.json @@ -0,0 +1,170 @@ +{ + "_CG": { + "edges": [ + [[3822379411845032233, 5680270675404457652], null], + [[3822379411845032233, 12508171564432771677], null], + [[3822379411845032233, 5535892002473160732], null], + [[3822379411845032233, 1652103244747807645], null], + [[12508171564432771677, 1652103244747807645], null], + [[12508171564432771677, 5535892002473160732], null], + [[14702235150400109274, 6799523544328452981], null], + [[12508171564432771677, 6799523544328452981], null], + [[11435300669595464177, 6799523544328452981], null], + [[5680270675404457652, 6799523544328452981], null], + [[5680270675404457652, 1069804627613295716], null], + [[1069804627613295716, 1652103244747807645], null], + [[3822379411845032233, 11435300669595464177], null], + [[2376654072098555825, 1652103244747807645], null], + [[15228000581015890548, 1652103244747807645], null], + [[15228000581015890548, 6799523544328452981], null], + [[3822379411845032233, 12485267842655417361], null], + [[1069804627613295716, 6799523544328452981], null], + [[4394530829600054380, 3822379411845032233], null] + ], + "nodes": [ + [ + 3822379411845032233, + { + "functionName": "_QFPfunc", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 1652103244747807645, + { + "functionName": "_QMmodPcreate_polynomial", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 5535892002473160732, + { + "functionName": "_QMmodPprint_polynomial", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 14702235150400109274, + { + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc2", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 6799523544328452981, + { + "functionName": "_QMmodPfinalize_polynomial", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 1197558351918936976, + { + "functionName": "_QMmodPfinalize_dummy", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 2376654072098555825, + { + "functionName": "_QMmod_useFfunc_calls_final3Pset_q", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 5680270675404457652, + { + "functionName": "_QMmod_usePfunc_calls_final3", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 11435300669595464177, + { + "functionName": "_QMmod_usePfunc_calls_final2", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 15228000581015890548, + { + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 7708430699293006602, + { + "functionName": "_QMmod_useFfunc_calls_final3Pset_nothing", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 12508171564432771677, + { + "functionName": "_QMmod_usePfunc_calls_final", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 1069804627613295716, + { + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_move_alloc", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 4394530829600054380, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 12485267842655417361, + { + "functionName": "_QMmod_usePfunc_does_not_call_final", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "b9ee508ff9d4dbb464f7cf2c723b43400d2d3709", + "version": "0.7" + }, + "version": "3.0" + } +} From 20af78cfcebe295d892720dd77d676be40d9937c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:49 +0100 Subject: [PATCH 038/130] improved logging for debugging and fixes for destructor handling --- cgfcollector/include/AL.h | 61 ++++++++++ cgfcollector/include/ParseTreeVisitor.h | 9 +- cgfcollector/src/ParseTreeVisitor.cpp | 146 +++++++++++++++--------- cgfcollector/src/main.cpp | 6 + cgfcollector/tools/test_runner.sh.in | 31 +++++ 5 files changed, 195 insertions(+), 58 deletions(-) create mode 100644 cgfcollector/include/AL.h diff --git a/cgfcollector/include/AL.h b/cgfcollector/include/AL.h new file mode 100644 index 00000000..74f25d6f --- /dev/null +++ b/cgfcollector/include/AL.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +template <> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const Fortran::parser::CharBlock& cb, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "{}", std::string_view(cb.begin(), cb.size())); + } +}; + +// aligned logger that formats debug messages with a : . To make it more readable. +class AL { + public: + static AL* getInstance() { + static AL instance; + return &instance; + } + + template + void debug(const std::string& fmt_str, Args&&... args) { + std::string formatted = fmt::format(fmt_str, std::forward(args)...); + size_t colon_pos = formatted.find(':'); + + if (colon_pos != std::string::npos) { + entries.emplace_back(formatted, colon_pos); + max_key_width = std::max(max_key_width, colon_pos); + } else { + entries.emplace_back(formatted, std::string::npos); + } + } + + void error(const std::string& fmt_str) { spdlog::error("{}", fmt_str); } + + void flush() { + for (const auto& [line, colon_pos] : entries) { + if (colon_pos == std::string::npos) { + spdlog::debug("{}", line); // no colon + } else { + std::string key = line.substr(0, colon_pos); + std::string rest = line.substr(colon_pos); // includes : + + std::string padded_key = fmt::format("{:<{}}", key, max_key_width); + spdlog::debug("{}{}", padded_key, rest); + } + } + entries.clear(); + max_key_width = 0; + } + + private: + AL() = default; + + std::vector> entries; + size_t max_key_width = 0; +}; diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 691c2d86..25d3ca8f 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -2,9 +2,12 @@ #include "headers.h" +#include "AL.h" + using namespace Fortran::parser; using namespace Fortran::semantics; using namespace Fortran::common; +using Fortran::lower::mangle::mangleName; typedef struct type { Symbol* type; @@ -38,6 +41,8 @@ class ParseTreeVisitor { // procedureSymbol. And also adds edges from types that extends from typeSymbol. void add_edges_for_produces_and_derived_types(std::vector typeWithDerived, const Symbol* procedureSymbol); + void add_edges_for_finalizers(const Symbol* typeSymbol); + template bool holds_any_of(const Variant& v) { return (std::holds_alternative(v) || ...); @@ -101,7 +106,7 @@ class ParseTreeVisitor { void Post(const DerivedTypeDef&); // type stmt like type [, extends(...)] :: body (not exhaustive and not extends) - void Post(const DerivedTypeStmt& t); + bool Pre(const DerivedTypeStmt& t); // type attrs like extends void Post(const TypeAttrSpec& a); @@ -147,4 +152,6 @@ class ParseTreeVisitor { // mainly used for destructor handling std::vector trackedVars; + + AL* al = AL::getInstance(); }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 70a18df2..0e175c0a 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -7,10 +7,9 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { functionSymbols.emplace_back(sym); functionDummyArgs.emplace_back(std::vector()); - cg->insert( - std::make_unique(Fortran::lower::mangle::mangleName(*sym), currentFileName, false, false)); + cg->insert(std::make_unique(mangleName(*sym), currentFileName, false, false)); - llvm::outs() << "Add node: " << Fortran::lower::mangle::mangleName(*sym) << "\n"; + al->debug("Add node: {} ({})", mangleName(*sym), fmt::ptr(sym)); } } @@ -29,7 +28,10 @@ void ParseTreeVisitor::handleEndFuncSubStmt() { } void ParseTreeVisitor::handleTrackedVars() { - if (!inMainProgram) { + if (mangleName(*functionSymbols.back()) != "_QQmain") { + if (!trackedVars.empty()) + al->debug("Handle tracked vars for function"); + for (auto& trackedVar : trackedVars) { if (!trackedVar.hasBeenInitialized) continue; @@ -40,17 +42,8 @@ void ParseTreeVisitor::handleTrackedVars() { auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); if (!typeSymbol) continue; - auto* details = std::get_if(&typeSymbol->details()); - if (!details) - continue; - - for (const auto& final : details->finals()) { - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*final.second)); - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*final.second) << "\n"; - } + add_edges_for_finalizers(typeSymbol); } } @@ -85,7 +78,7 @@ std::vector ParseTreeVisitor::find_type_with_derived_types(const Symbol* auto currentType = std::find_if(types.begin(), types.end(), [¤tExtendsFrom](const type_t& t) { return t.type == currentExtendsFrom; }); if (currentType == types.end()) { - llvm::errs() << "Error: Types array (extendsFrom) field entry missing."; + al->error("Error: Types array (extendsFrom) field entry missing."); return typeWithDerived; } @@ -111,11 +104,30 @@ void ParseTreeVisitor::add_edges_for_produces_and_derived_types(std::vectorsecond)); + edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*procIt->second)); + + al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleName(*procIt->second), fmt::ptr(procIt->second)); + } +} + +void ParseTreeVisitor::add_edges_for_finalizers(const Symbol* typeSymbol) { + std::vector typeSymbols = find_type_with_derived_types(typeSymbol); + + for (const auto& type : typeSymbols) { + const Symbol* typeSymbol = type.type; + + const auto* details = std::get_if(&typeSymbol->details()); + if (!details) + return; + + // add edges for finalizers + for (const auto& final : details->finals()) { + edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*final.second)); - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*procIt->second) << "\n"; + al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleName(*final.second), fmt::ptr(&final.second.get())); + } } } @@ -211,6 +223,8 @@ void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { // prefer local var if found auto& trackedVar = (localVarIt != trackedVars.end()) ? *localVarIt : *anyTrackedVarIt; trackedVar.hasBeenInitialized = true; + + al->debug("Tracked var assigned: {} ({})", trackedVar.var->name(), fmt::ptr(trackedVar.var)); } // Visitor implementations @@ -223,8 +237,9 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { return true; functionSymbols.emplace_back(maybeStmt->statement.v.symbol); - cg->insert(std::make_unique(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - currentFileName, false, false)); + cg->insert(std::make_unique(mangleName(*functionSymbols.back()), currentFileName, false, false)); + + al->debug("\nIn main program: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); } return true; } @@ -232,6 +247,8 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { void ParseTreeVisitor::Post(const MainProgram&) { handleTrackedVars(); + al->debug("End main program: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); + if (!functionSymbols.empty()) { functionSymbols.pop_back(); } @@ -257,7 +274,7 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { if (!inFunctionOrSubroutineSubProgram && !inMainProgram) return; - auto* node = cg->getNode(Fortran::lower::mangle::mangleName(*functionSymbols.back())); + auto* node = cg->getNode(mangleName(*functionSymbols.back())); if (!node) { return; } @@ -266,7 +283,7 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { } void ParseTreeVisitor::Post(const FunctionStmt& f) { - llvm::outs() << "In function: " << Fortran::lower::mangle::mangleName(*std::get(f.t).symbol) << "\n"; + al->debug("\nIn function: {} ({})", mangleName(*std::get(f.t).symbol), fmt::ptr(std::get(f.t).symbol)); handleFuncSubStmt(f); @@ -279,14 +296,14 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { void ParseTreeVisitor::Post(const EndFunctionStmt&) { if (!functionSymbols.empty()) { - llvm::outs() << "End function: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; + al->debug("End function: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); } handleEndFuncSubStmt(); } void ParseTreeVisitor::Post(const SubroutineStmt& s) { - llvm::outs() << "In subroutine: " << Fortran::lower::mangle::mangleName(*std::get(s.t).symbol) << "\n"; + al->debug("\nIn subroutine: {} ({})", mangleName(*std::get(s.t).symbol), fmt::ptr(std::get(s.t).symbol)); handleFuncSubStmt(s); @@ -300,7 +317,7 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { void ParseTreeVisitor::Post(const EndSubroutineStmt&) { if (!functionSymbols.empty()) { - llvm::outs() << "End subroutine: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << "\n"; + al->debug("End subroutine: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); } handleEndFuncSubStmt(); @@ -319,11 +336,10 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (name->symbol->attrs().test(Attr::INTRINSIC)) return; - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*name->symbol)); + edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*name->symbol)); - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*name->symbol) << "\n"; + al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleName(*name->symbol), fmt::ptr(name->symbol)); // if called from a object with %. (base % component) } else if (auto* procCompRef = std::get_if(&p.u)) { @@ -331,11 +347,10 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (!symbolComp) return; - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*symbolComp)); + edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*symbolComp)); - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*symbolComp) << "\n"; + al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleName(*symbolComp), fmt::ptr(symbolComp)); auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) @@ -399,7 +414,6 @@ void ParseTreeVisitor::Post(const Call& c) { if (!name || !name->symbol) return; - llvm::outs() << "Tracked Var: " << name->symbol->name() << "\n"; handleTrackedVarAssignment(name->symbol->name()); } } @@ -433,22 +447,14 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { [](const AttrSpec& a) { return std::holds_alternative(a.u); }); if (it != attrSpec.end()) { trackedVars.push_back({name.symbol, functionSymbols.back(), false}); - llvm::outs() << "Track variable: " << name.symbol->name() << "\n"; - continue; // skip var with allocatable attr - } + al->debug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - const auto* details = std::get_if(&typeSymbol->details()); - if (!details) continue; - - // add edges for finalizers - for (const auto& final : details->finals()) { - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*final.second)); - - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*final.second) << "\n"; + // skip var with allocatable attr. + // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. } + + add_edges_for_finalizers(typeSymbol); } } @@ -459,15 +465,22 @@ bool ParseTreeVisitor::Pre(const DerivedTypeDef&) { return true; } -void ParseTreeVisitor::Post(const DerivedTypeDef&) { inDerivedTypeDef = false; } +void ParseTreeVisitor::Post(const DerivedTypeDef&) { + inDerivedTypeDef = false; + al->debug("End derived type: {} ({})", types.back().type->name(), fmt::ptr(types.back().type)); +} -void ParseTreeVisitor::Post(const DerivedTypeStmt& t) { +bool ParseTreeVisitor::Pre(const DerivedTypeStmt& t) { if (!inDerivedTypeDef) - return; + return true; auto& currentType = types.back(); const auto& name = std::get(t.t); currentType.type = name.symbol; + + al->debug("\nIn derived type: {} ({})", currentType.type->name(), fmt::ptr(currentType.type)); + + return true; } void ParseTreeVisitor::Post(const TypeAttrSpec& a) { @@ -478,6 +491,8 @@ void ParseTreeVisitor::Post(const TypeAttrSpec& a) { if (std::holds_alternative(a.u)) { const auto& extends = std::get(a.u); currentType.extendsFrom = extends.v.symbol; + + al->debug("Extends from: {} ({})", currentType.extendsFrom->name(), fmt::ptr(currentType.extendsFrom)); } } @@ -498,6 +513,9 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { auto& currentType = types.back(); currentType.procedures.emplace_back(name.symbol, optname->symbol); + + al->debug("Add procedure: {} ({}) -> {} ({})", name.symbol->name(), fmt::ptr(name.symbol), + optname->symbol->name(), fmt::ptr(optname->symbol)); } // only for abstract types, with deferred in binding attr list @@ -508,6 +526,9 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { auto& currentType = types.back(); currentType.procedures.emplace_back(n.symbol, n.symbol); + + al->debug("Add procedure: {} ({}) -> {} ({})", n.symbol->name(), fmt::ptr(n.symbol), n.symbol->name(), + fmt::ptr(n.symbol)); } } } @@ -528,6 +549,9 @@ void ParseTreeVisitor::Post(const TypeBoundGenericStmt& s) { continue; currentType.operators.emplace_back(intrinsicOp, name.symbol); + + al->debug("Add operator: {} -> {} ({})", DefinedOperator::EnumToString(*intrinsicOp), name.symbol->name(), + fmt::ptr(name.symbol)); } } } @@ -563,7 +587,7 @@ void ParseTreeVisitor::Post(const ProcedureStmt& p) { continue; if (interfaceOperators.empty()) { - llvm::errs() << "This should no happen. Likely there is a bug with parsing DefinedOperator's\n"; + al->error("This should no happen. Likely there is a bug with parsing DefinedOperator's"); continue; } @@ -596,7 +620,6 @@ bool ParseTreeVisitor::Pre(const Expr& e) { return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); } if (auto* definedBinary = std::get_if(&e->u)) { - llvm::outs() << "definedbinary" << "\n"; } return false; } @@ -610,11 +633,10 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (sym->name() == functionSymbols.back()->name()) continue; - edges.emplace_back(Fortran::lower::mangle::mangleName(*functionSymbols.back()), - Fortran::lower::mangle::mangleName(*sym)); + edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*sym)); - llvm::outs() << "Add edge: " << Fortran::lower::mangle::mangleName(*functionSymbols.back()) << " -> " - << Fortran::lower::mangle::mangleName(*sym) << "\n"; + al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleName(*sym), fmt::ptr(sym)); } } @@ -655,6 +677,16 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } void ParseTreeVisitor::Post(const Expr& e) { + // find out if this is a constructor + // auto* functionRef = std::get_if>(&e.u); + // if (functionRef) { + // auto* designator = &std::get(functionRef->value().v.t); + // auto* name = std::get_if(&designator->u); + // if (!name || !name->symbol) { + // return; + // } + // } + if (!isOperator(&e)) { return; } diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index e2538910..99c43d98 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -7,6 +7,9 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { void executeAction() override { auto cg = std::make_unique(); + spdlog::set_pattern("%v"); + spdlog::set_level(spdlog::level::debug); + ParseTreeVisitor visitor(cg.get(), getCurrentFile().str()); Fortran::parser::Walk(getParsing().parseTree(), visitor); @@ -39,6 +42,9 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { auto file = createOutputFile("json"); file->write(jsonSink.getJson().dump().c_str(), jsonSink.getJson().dump().size()); + + AL* al = AL::getInstance(); + al->flush(); } }; diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 5b9fc806..75896073 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -7,6 +7,29 @@ if [[ ! -d "$out_dir" ]]; then mkdir "$out_dir" fi +run_tests_with_name=() +while [[ $# -gt 0 ]]; do + run_tests_with_name+=("$1") + shift +done + +function should_be_run() +{ + local test_case_name="$1" + + if [[ ${#run_tests_with_name[@]} -eq 0 ]]; then + return 0 + fi + + for name in "${run_tests_with_name[@]}"; do + if [[ "$test_case_name" == "$name" ]]; then + return 0 + fi + done + + return 1 +} + # cmake managed test dirs find "$test_case_dir" -mindepth 1 -type d \! -exec test -e "{}/output.json" \; \! -exec test -e "{}/*.f90" \; -exec find {} -maxdepth 1 -name 'CMakeLists.txt' \; | while read -r dir; do dir="$(dirname "$dir")" @@ -30,6 +53,10 @@ find "$test_case_dir" -mindepth 1 -type d \! -exec test -e "{}/output.json" \; \ test_case_name="$(basename "$dir")_$(basename -s .json "$file")" + if ! should_be_run "$test_case_name"; then + continue + fi + echo "Test case: $test_case_name" output_file="$dir/$(basename -s .json "$file")/output.json" @@ -65,6 +92,10 @@ find "$test_case_dir" -mindepth 1 -type d \! -exec test -e "{}/CMakeLists.txt" \ # generate test case name from dir path. test/simple/01 becomes simple_01 test_case_name="$(basename "$(dirname "$dir")")_$(basename "$dir")" + if ! should_be_run "$test_case_name"; then + continue + fi + echo "Test case: $test_case_name" if [ ! -f "$output_file" ] || [ ! -s "$output_file" ] || grep -q '{}' "$output_file"; then From 2cad8c9515e596991b897d8adc27e6565ed12e45 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:50 +0100 Subject: [PATCH 039/130] impl destructor handling for function arguments without allocatable attr --- cgfcollector/include/ParseTreeVisitor.h | 7 +++ cgfcollector/src/ParseTreeVisitor.cpp | 71 +++++++++++++++++-------- 2 files changed, 57 insertions(+), 21 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 25d3ca8f..5f7edee8 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -20,8 +20,15 @@ typedef struct trackedVar { Symbol* var; Symbol* procedure; // procedure in which var was defined bool hasBeenInitialized = false; + bool addFinalizers = false; } trackedVar_t; +typedef struct function { + Symbol* symbol; + std::vector dummyArgs; + std::vector dummyArgsHasBeenInitialized; +} function_t; + class ParseTreeVisitor { public: ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 0e175c0a..cf731a8b 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -33,17 +33,20 @@ void ParseTreeVisitor::handleTrackedVars() { al->debug("Handle tracked vars for function"); for (auto& trackedVar : trackedVars) { - if (!trackedVar.hasBeenInitialized) - continue; - if (trackedVar.procedure != functionSymbols.back()) - continue; + if (trackedVar.addFinalizers) { + if (!trackedVar.hasBeenInitialized) + continue; + if (trackedVar.procedure != functionSymbols.back()) + continue; - // add edge for deconstruction (finalizer) - auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); - if (!typeSymbol) - continue; + // add edge for deconstruction (finalizer) + auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); + if (!typeSymbol) + continue; - add_edges_for_finalizers(typeSymbol); + add_edges_for_finalizers(typeSymbol); + } else { + } } } @@ -430,31 +433,57 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { continue; // skip if name is an argument to a function or subroutine + bool isFunctionArg = false; if (!functionDummyArgs.empty()) { auto it = std::find_if(functionDummyArgs.back().begin(), functionDummyArgs.back().end(), [&name](const Name* dummyArg) { return dummyArg->symbol == name.symbol; }); if (it != functionDummyArgs.back().end()) - continue; + isFunctionArg = true; } auto* typeSymbol = getTypeSymbolFromSymbol(name.symbol); if (!typeSymbol) continue; - const auto& attrSpec = std::get>(t.t); - auto it = std::find_if(attrSpec.begin(), attrSpec.end(), - [](const AttrSpec& a) { return std::holds_alternative(a.u); }); - if (it != attrSpec.end()) { - trackedVars.push_back({name.symbol, functionSymbols.back(), false}); - al->debug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - - continue; - // skip var with allocatable attr. - // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. + bool holds_allocatable = false; + const IntentSpec* holds_intent = nullptr; + for (const auto& attr : std::get>(t.t)) { + if (std::holds_alternative(attr.u)) + holds_allocatable = true; + else if (std::holds_alternative(attr.u)) + holds_intent = &std::get(attr.u); } - add_edges_for_finalizers(typeSymbol); + if (isFunctionArg) { + if (!holds_allocatable) { + if (!holds_intent) { + // no intent attr, if not set does not call finalizer. Why? idk. + trackedVars.push_back({name.symbol, functionSymbols.back(), false, true}); + al->debug("Add tracking for function argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + } else { + if (holds_intent->v == IntentSpec::Intent::Out) { + // intent out, calls finalizer because (7.5.6.3 line 21 and onwards) + add_edges_for_finalizers(typeSymbol); + } else if (holds_intent->v == IntentSpec::Intent::InOut) { + // intent inout, calls finalizer when set. + trackedVars.push_back({name.symbol, functionSymbols.back(), false, true}); + al->debug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + } + } + } else { + // needs to be check at the end of prog TODO: allocatable attr as function argument + } + } else { + if (holds_allocatable) { + trackedVars.push_back({name.symbol, functionSymbols.back(), false, true}); + al->debug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + // skip var with allocatable attr. + // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. + } else { + add_edges_for_finalizers(typeSymbol); + } + } } } From 0f12aa94b32cd9b02fb7c346a61a039664f0edbf Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:50 +0100 Subject: [PATCH 040/130] test final simplified --- cgfcollector/test/simple/final/input.f90 | 39 +-------- cgfcollector/test/simple/final/output.json | 96 ++++++++++------------ 2 files changed, 46 insertions(+), 89 deletions(-) diff --git a/cgfcollector/test/simple/final/input.f90 b/cgfcollector/test/simple/final/input.f90 index a721842b..31d82da9 100644 --- a/cgfcollector/test/simple/final/input.f90 +++ b/cgfcollector/test/simple/final/input.f90 @@ -13,18 +13,6 @@ module mod module procedure create_polynomial end interface - type dummy_type - integer :: i - contains - final :: finalize_dummy - end type dummy_type - - interface dummy_type - module procedure create_dummy_type - end interface dummy_type - - type(polynomial) :: polynomialInModule - contains type(polynomial) function create_polynomial(a) @@ -49,17 +37,6 @@ subroutine finalize_polynomial(this) write (*, *) 'Finalizing polynomial' end subroutine finalize_polynomial - subroutine finalize_dummy(this) - type(dummy_type), intent(inout) :: this - write (*, *) 'Finalizing dummy_type' - end subroutine finalize_dummy - - type(dummy_type) function create_dummy_type(i) - integer, intent(in) :: i - create_dummy_type%i = i - write (*, *) 'Creating dummy_type with i = ', create_dummy_type%i - end function create_dummy_type - end module mod module mod_use @@ -83,10 +60,10 @@ end subroutine func_calls_final2 subroutine func_calls_final3() type(polynomial), allocatable :: q - ! call set_q() + call set_q() ! call set_q_alloc() ! call set_q_alloc2() - call set_q_move_alloc() + ! call set_q_move_alloc() contains subroutine set_q() @@ -123,18 +100,6 @@ program main use mod_use implicit none - type :: implicit_constructor - integer :: i - end type implicit_constructor - type(implicit_constructor), allocatable :: t - - type(dummy_type), allocatable :: dummy - - ! sould not call final because main function. see 7.5.6.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) and (https://j3-fortran.org/doc/year/10/10-158r1.txt) - dummy = dummy_type(1) - - t = implicit_constructor(1) - call func() contains diff --git a/cgfcollector/test/simple/final/output.json b/cgfcollector/test/simple/final/output.json index 87789832..e6a79d20 100644 --- a/cgfcollector/test/simple/final/output.json +++ b/cgfcollector/test/simple/final/output.json @@ -1,29 +1,30 @@ { "_CG": { "edges": [ - [[3822379411845032233, 5680270675404457652], null], - [[3822379411845032233, 12508171564432771677], null], - [[3822379411845032233, 5535892002473160732], null], - [[3822379411845032233, 1652103244747807645], null], - [[12508171564432771677, 1652103244747807645], null], - [[12508171564432771677, 5535892002473160732], null], - [[14702235150400109274, 6799523544328452981], null], - [[12508171564432771677, 6799523544328452981], null], - [[11435300669595464177, 6799523544328452981], null], - [[5680270675404457652, 6799523544328452981], null], - [[5680270675404457652, 1069804627613295716], null], - [[1069804627613295716, 1652103244747807645], null], - [[3822379411845032233, 11435300669595464177], null], - [[2376654072098555825, 1652103244747807645], null], - [[15228000581015890548, 1652103244747807645], null], - [[15228000581015890548, 6799523544328452981], null], - [[3822379411845032233, 12485267842655417361], null], - [[1069804627613295716, 6799523544328452981], null], - [[4394530829600054380, 3822379411845032233], null] + [[10431988963324364992, 6149613639027182890], null], + [[10431988963324364992, 5064320190928089604], null], + [[10431988963324364992, 13979031587664314780], null], + [[10431988963324364992, 16645470703084508464], null], + [[911036281617746681, 2052109649073365059], null], + [[10431988963324364992, 2052109649073365059], null], + [[6149613639027182890, 2052109649073365059], null], + [[911036281617746681, 13979031587664314780], null], + [[11433580247649436846, 2052109649073365059], null], + [[6149613639027182890, 5064320190928089604], null], + [[16645470703084508464, 4934351978831295596], null], + [[6149613639027182890, 13979031587664314780], null], + [[2915883528470775764, 2052109649073365059], null], + [[10431988963324364992, 16280562401692851033], null], + [[10552440586870006343, 2052109649073365059], null], + [[10431988963324364992, 11433580247649436846], null], + [[4934351978831295596, 13979031587664314780], null], + [[16645470703084508464, 2052109649073365059], null], + [[10552440586870006343, 13979031587664314780], null], + [[15456597401670502006, 10431988963324364992], null] ], "nodes": [ [ - 3822379411845032233, + 10431988963324364992, { "functionName": "_QFPfunc", "hasBody": true, @@ -32,7 +33,7 @@ } ], [ - 1652103244747807645, + 13979031587664314780, { "functionName": "_QMmodPcreate_polynomial", "hasBody": true, @@ -41,7 +42,7 @@ } ], [ - 5535892002473160732, + 5064320190928089604, { "functionName": "_QMmodPprint_polynomial", "hasBody": true, @@ -50,34 +51,34 @@ } ], [ - 14702235150400109274, + 2052109649073365059, { - "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc2", + "functionName": "_QMmodPfinalize_polynomial", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 6799523544328452981, + 6149613639027182890, { - "functionName": "_QMmodPfinalize_polynomial", + "functionName": "_QMmod_usePfunc_calls_final", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 1197558351918936976, + 11433580247649436846, { - "functionName": "_QMmodPfinalize_dummy", + "functionName": "_QMmod_usePfunc_calls_final2", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 2376654072098555825, + 4934351978831295596, { "functionName": "_QMmod_useFfunc_calls_final3Pset_q", "hasBody": true, @@ -86,25 +87,25 @@ } ], [ - 5680270675404457652, + 2915883528470775764, { - "functionName": "_QMmod_usePfunc_calls_final3", + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc2", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 11435300669595464177, + 16280562401692851033, { - "functionName": "_QMmod_usePfunc_calls_final2", + "functionName": "_QMmod_usePfunc_does_not_call_final", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 15228000581015890548, + 911036281617746681, { "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc", "hasBody": true, @@ -113,45 +114,36 @@ } ], [ - 7708430699293006602, + 15456597401670502006, { - "functionName": "_QMmod_useFfunc_calls_final3Pset_nothing", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 12508171564432771677, - { - "functionName": "_QMmod_usePfunc_calls_final", + "functionName": "_QQmain", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 1069804627613295716, + 16645470703084508464, { - "functionName": "_QMmod_useFfunc_calls_final3Pset_q_move_alloc", + "functionName": "_QMmod_usePfunc_calls_final3", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 4394530829600054380, + 1914061080208569539, { - "functionName": "_QQmain", + "functionName": "_QMmod_useFfunc_calls_final3Pset_nothing", "hasBody": true, "meta": null, "origin": "input.f90" } ], [ - 12485267842655417361, + 10552440586870006343, { - "functionName": "_QMmod_usePfunc_does_not_call_final", + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_move_alloc", "hasBody": true, "meta": null, "origin": "input.f90" @@ -162,7 +154,7 @@ "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "b9ee508ff9d4dbb464f7cf2c723b43400d2d3709", + "sha": "535f208aefbf510c4c3536f9bf9e6978e2adc2e5", "version": "0.7" }, "version": "3.0" From 31fd832a13fd623c4c65f8c50f2c561eb786dcb2 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:50 +0100 Subject: [PATCH 041/130] test final2 for finalizers with function arguments without allocatable attr --- cgfcollector/test/simple/final2/main.f90 | 94 +++++++++++++++++ cgfcollector/test/simple/final2/output.json | 111 ++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 cgfcollector/test/simple/final2/main.f90 create mode 100644 cgfcollector/test/simple/final2/output.json diff --git a/cgfcollector/test/simple/final2/main.f90 b/cgfcollector/test/simple/final2/main.f90 new file mode 100644 index 00000000..745761a1 --- /dev/null +++ b/cgfcollector/test/simple/final2/main.f90 @@ -0,0 +1,94 @@ +module mod + + implicit none + + type :: base + integer :: a + contains + final :: finalize_base + end type base + + type, extends(base) :: derived + integer :: b + contains + final :: finalize_derived + end type derived + +contains + + subroutine finalize_base(this) + type(base), intent(inout) :: this + print *, "Finalizing base" + end subroutine finalize_base + + subroutine finalize_derived(this) + type(derived), intent(inout) :: this + print *, "Finalizing derived" + end subroutine finalize_derived + + subroutine func_arg(this) + ! this does not call a finalize + type(derived) :: this + print *, "In func_arg" + end subroutine func_arg + + subroutine func_arg2(this) + ! this calls a finalize + type(derived) :: this + print *, "In func_arg2" + this = derived(1, 2) + end subroutine func_arg2 + + subroutine func_arg_out(this) + ! this calls a finalize (7.5.6.3 line 21 and onwards) + type(derived), intent(out) :: this + print *, "In func_arg_out" + end subroutine func_arg_out + + subroutine func_arg_inout(this) + ! does not call a finalize + type(derived), intent(inout) :: this + print *, "In func_arg_inout" + end subroutine func_arg_inout + + subroutine func_arg_inout2(this) + ! this calls a finalize + type(derived), intent(inout) :: this + print *, "In func_arg_inout2" + this = derived(1, 2) + end subroutine func_arg_inout2 + +end module mod + +program main + use mod + + implicit none + + call func() + +contains + + subroutine func() + type(derived) :: obj + + print *, "Before func_arg" + call func_arg(obj) + print *, "After func_arg" + print *, "Before func_arg2" + call func_arg2(obj) + print *, "After func_arg2" + print *, "Before func_arg_out" + call func_arg_out(obj) + print *, "After func_arg_out" + print *, "Before func_arg_inout" + call func_arg_inout(obj) + print *, "After func_arg_inout" + print *, "Before func_arg_inout2" + call func_arg_inout2(obj) + print *, "After func_arg_inout2" + + end subroutine func + +end program main + diff --git a/cgfcollector/test/simple/final2/output.json b/cgfcollector/test/simple/final2/output.json new file mode 100644 index 00000000..8cc2ee12 --- /dev/null +++ b/cgfcollector/test/simple/final2/output.json @@ -0,0 +1,111 @@ +{ + "_CG": { + "edges": [ + [[13845067782904566984, 12261923257537545416], null], + [[9132368188186752179, 12261923257537545416], null], + [[8812770886124818036, 9132368188186752179], null], + [[13845067782904566984, 9634951400506836789], null], + [[15997684825707339448, 9634951400506836789], null], + [[11592059056552374804, 8812770886124818036], null], + [[15997684825707339448, 12261923257537545416], null], + [[8812770886124818036, 9634951400506836789], null], + [[8812770886124818036, 15997684825707339448], null], + [[9132368188186752179, 9634951400506836789], null], + [[8812770886124818036, 17137297412558704458], null], + [[8812770886124818036, 12261923257537545416], null], + [[8812770886124818036, 6215837638387017975], null], + [[8812770886124818036, 13845067782904566984], null] + ], + "nodes": [ + [ + 11592059056552374804, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 15997684825707339448, + { + "functionName": "_QMmodPfunc_arg_inout2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 17137297412558704458, + { + "functionName": "_QMmodPfunc_arg_inout", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 13845067782904566984, + { + "functionName": "_QMmodPfunc_arg2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 6215837638387017975, + { + "functionName": "_QMmodPfunc_arg", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 8812770886124818036, + { + "functionName": "_QFPfunc", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 9132368188186752179, + { + "functionName": "_QMmodPfunc_arg_out", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 9634951400506836789, + { + "functionName": "_QMmodPfinalize_derived", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 12261923257537545416, + { + "functionName": "_QMmodPfinalize_base", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "535f208aefbf510c4c3536f9bf9e6978e2adc2e5", + "version": "0.7" + }, + "version": "3.0" + } +} From 96188ef3d50ac596dcae7c0b8adb3854fa18044b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:51 +0100 Subject: [PATCH 042/130] impl entry and add test case --- cgfcollector/include/ParseTreeVisitor.h | 2 + cgfcollector/src/ParseTreeVisitor.cpp | 10 ++++ cgfcollector/test/simple/entry/main.f90 | 31 ++++++++++++ cgfcollector/test/simple/entry/output.json | 55 ++++++++++++++++++++++ 4 files changed, 98 insertions(+) create mode 100644 cgfcollector/test/simple/entry/main.f90 create mode 100644 cgfcollector/test/simple/entry/output.json diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 5f7edee8..03e454c7 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -92,6 +92,8 @@ class ParseTreeVisitor { void Post(const ExecutionPart& e); + void Post(const EntryStmt& e); + void Post(const FunctionStmt& f); void Post(const EndFunctionStmt&); void Post(const SubroutineStmt& s); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index cf731a8b..181cddd1 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -285,6 +285,16 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { node->setHasBody(true); } +void ParseTreeVisitor::Post(const EntryStmt& e) { + auto* name = &std::get(e.t); + if (!name->symbol) + return; + + al->debug("Add Entry point: {} ({})", mangleName(*name->symbol), fmt::ptr(name->symbol)); + + cg->insert(std::make_unique(mangleName(*name->symbol), currentFileName, false, true)); +} + void ParseTreeVisitor::Post(const FunctionStmt& f) { al->debug("\nIn function: {} ({})", mangleName(*std::get(f.t).symbol), fmt::ptr(std::get(f.t).symbol)); diff --git a/cgfcollector/test/simple/entry/main.f90 b/cgfcollector/test/simple/entry/main.f90 new file mode 100644 index 00000000..46027443 --- /dev/null +++ b/cgfcollector/test/simple/entry/main.f90 @@ -0,0 +1,31 @@ +module mod + + implicit none + +contains + subroutine func(a, b, c) + integer :: a, b + character(4) :: c + print *, "entry func" + return + + entry entry1(a, b, c) + print *, "entry entry1" + return + + entry entry2 + print *, "entry entry2" + return + end +end module mod + +program main + use mod + implicit none + + call func(1, 2, "1234") + call entry1(1, 2, "1234") + call entry2() + +end program main + diff --git a/cgfcollector/test/simple/entry/output.json b/cgfcollector/test/simple/entry/output.json new file mode 100644 index 00000000..be456e51 --- /dev/null +++ b/cgfcollector/test/simple/entry/output.json @@ -0,0 +1,55 @@ +{ + "_CG": { + "edges": [ + [[3941336225589151125, 11585286096804977045], null], + [[3941336225589151125, 11158142119152065688], null], + [[3941336225589151125, 2512112026932646668], null] + ], + "nodes": [ + [ + 3941336225589151125, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 11585286096804977045, + { + "functionName": "_QMmodPentry2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 11158142119152065688, + { + "functionName": "_QMmodPentry1", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 2512112026932646668, + { + "functionName": "_QMmodPfunc", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "58079ff194dddcb21778f4c423db10fccc27ebae", + "version": "0.7" + }, + "version": "3.0" + } +} From ded841d0f60d01b0c8288be739d38c6493633450 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:51 +0100 Subject: [PATCH 043/130] renamed 01, 03 and 05 test cases to more descriptive names --- cgfcollector/test/simple/01/output.json | 33 ------------------- .../test/simple/{04 => polymorphism}/main.f90 | 0 .../simple/{04 => polymorphism}/output.json | 0 .../simple/{05 => polymorphism2}/main.f90 | 0 .../simple/{05 => polymorphism2}/output.json | 0 .../test/simple/{01 => simple}/input.f90 | 0 cgfcollector/test/simple/simple/output.json | 33 +++++++++++++++++++ 7 files changed, 33 insertions(+), 33 deletions(-) delete mode 100644 cgfcollector/test/simple/01/output.json rename cgfcollector/test/simple/{04 => polymorphism}/main.f90 (100%) rename cgfcollector/test/simple/{04 => polymorphism}/output.json (100%) rename cgfcollector/test/simple/{05 => polymorphism2}/main.f90 (100%) rename cgfcollector/test/simple/{05 => polymorphism2}/output.json (100%) rename cgfcollector/test/simple/{01 => simple}/input.f90 (100%) create mode 100644 cgfcollector/test/simple/simple/output.json diff --git a/cgfcollector/test/simple/01/output.json b/cgfcollector/test/simple/01/output.json deleted file mode 100644 index faf566b7..00000000 --- a/cgfcollector/test/simple/01/output.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "_CG": { - "edges": [[[386379624964062775, 12232342110680008091], null]], - "nodes": [ - [ - 12232342110680008091, - { - "functionName": "_QFPprint_stars", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 386379624964062775, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "e8fbd9caa11ea3da8a149732eeb7b9c88ba5249f", - "version": "0.7" - }, - "version": "3.0" - } -} diff --git a/cgfcollector/test/simple/04/main.f90 b/cgfcollector/test/simple/polymorphism/main.f90 similarity index 100% rename from cgfcollector/test/simple/04/main.f90 rename to cgfcollector/test/simple/polymorphism/main.f90 diff --git a/cgfcollector/test/simple/04/output.json b/cgfcollector/test/simple/polymorphism/output.json similarity index 100% rename from cgfcollector/test/simple/04/output.json rename to cgfcollector/test/simple/polymorphism/output.json diff --git a/cgfcollector/test/simple/05/main.f90 b/cgfcollector/test/simple/polymorphism2/main.f90 similarity index 100% rename from cgfcollector/test/simple/05/main.f90 rename to cgfcollector/test/simple/polymorphism2/main.f90 diff --git a/cgfcollector/test/simple/05/output.json b/cgfcollector/test/simple/polymorphism2/output.json similarity index 100% rename from cgfcollector/test/simple/05/output.json rename to cgfcollector/test/simple/polymorphism2/output.json diff --git a/cgfcollector/test/simple/01/input.f90 b/cgfcollector/test/simple/simple/input.f90 similarity index 100% rename from cgfcollector/test/simple/01/input.f90 rename to cgfcollector/test/simple/simple/input.f90 diff --git a/cgfcollector/test/simple/simple/output.json b/cgfcollector/test/simple/simple/output.json new file mode 100644 index 00000000..d7c1b198 --- /dev/null +++ b/cgfcollector/test/simple/simple/output.json @@ -0,0 +1,33 @@ +{ + "_CG": { + "edges": [[[386379624964062775, 12232342110680008091], null]], + "nodes": [ + [ + 12232342110680008091, + { + "functionName": "_QFPprint_stars", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ], + [ + 386379624964062775, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "input.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "e8fbd9caa11ea3da8a149732eeb7b9c88ba5249f", + "version": "0.7" + }, + "version": "3.0" + } +} From e2f6bb192ff425bd21805be6d2acda0d52d70146 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:51 +0100 Subject: [PATCH 044/130] add generic interface test --- .../test/simple/generic_interface/main.f90 | 44 ++++++++++++++++++ .../test/simple/generic_interface/output.json | 45 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 cgfcollector/test/simple/generic_interface/main.f90 create mode 100644 cgfcollector/test/simple/generic_interface/output.json diff --git a/cgfcollector/test/simple/generic_interface/main.f90 b/cgfcollector/test/simple/generic_interface/main.f90 new file mode 100644 index 00000000..325deb15 --- /dev/null +++ b/cgfcollector/test/simple/generic_interface/main.f90 @@ -0,0 +1,44 @@ +module mod + + implicit none + + interface add + module procedure add_int, add_real + end interface add + +contains + + subroutine add_int(x, y) + implicit none + integer, intent(inout) :: x, y + x = x + y + end subroutine add_int + + subroutine add_real(x, y) + implicit none + real, intent(inout) :: x, y + x = x + y + end subroutine add_real + +end module mod + +program main + use mod + + implicit none + + integer :: x, y + real :: r1, r2 + x = 1 + y = 2 + r1 = 3.3 + r2 = 4.4 + + call add(x, y) + call add(r1, r2) + + print *, "Integer addition result: ", x + print *, "Real addition result: ", r1 + +end program main + diff --git a/cgfcollector/test/simple/generic_interface/output.json b/cgfcollector/test/simple/generic_interface/output.json new file mode 100644 index 00000000..95dba5e2 --- /dev/null +++ b/cgfcollector/test/simple/generic_interface/output.json @@ -0,0 +1,45 @@ +{ + "_CG": { + "edges": [ + [[12261419144365086168, 12651824050695698132], null], + [[12261419144365086168, 1355953991221275603], null] + ], + "nodes": [ + [ + 12261419144365086168, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 12651824050695698132, + { + "functionName": "_QMmodPadd_real", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 1355953991221275603, + { + "functionName": "_QMmodPadd_int", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "535f208aefbf510c4c3536f9bf9e6978e2adc2e5", + "version": "0.7" + }, + "version": "3.0" + } +} From 8aa4ef76e3796cc044c2f735072b23cc2dc9c54c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:52 +0100 Subject: [PATCH 045/130] removed cmake_base.txt --- cgfcollector/test/cmake_base.txt | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 cgfcollector/test/cmake_base.txt diff --git a/cgfcollector/test/cmake_base.txt b/cgfcollector/test/cmake_base.txt deleted file mode 100644 index 21108e4a..00000000 --- a/cgfcollector/test/cmake_base.txt +++ /dev/null @@ -1,6 +0,0 @@ -# This file is generated by CMake. -set(CMAKE_Fortran_COMPILER "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/cgfcollector/cgfcollector_wrapper.sh") -set(CMAKE_Fortran_FLAGS "") -set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") -set(CMAKE_Fortran_LINK_EXECUTABLE "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/tools/cgmerge2/cgmerge2 ") -set(CMAKE_EXECUTABLE_SUFFIX .json) From bc22ca80831256b0d45ec127f437d31ef867b052 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:52 +0100 Subject: [PATCH 046/130] simplified cmake tests --- cgfcollector/test/multi/02/CMakeLists.txt | 9 +- cgfcollector/test/multi/02/main.f90 | 1 + cgfcollector/test/multi/02/module.f90 | 3 + cgfcollector/test/multi/02/module0_5.f90 | 13 +++ cgfcollector/test/multi/02/module2.f90 | 10 ++ cgfcollector/test/multi/02/module3.f90 | 13 +++ cgfcollector/test/multi/CMakeLists.txt | 7 -- cgfcollector/test/multi/cmake_base.txt | 6 ++ cgfcollector/tools/test_runner.sh.in | 115 +++++++++------------- 9 files changed, 97 insertions(+), 80 deletions(-) create mode 100644 cgfcollector/test/multi/02/module0_5.f90 create mode 100644 cgfcollector/test/multi/02/module2.f90 create mode 100644 cgfcollector/test/multi/02/module3.f90 delete mode 100644 cgfcollector/test/multi/CMakeLists.txt create mode 100644 cgfcollector/test/multi/cmake_base.txt diff --git a/cgfcollector/test/multi/02/CMakeLists.txt b/cgfcollector/test/multi/02/CMakeLists.txt index d9f4917c..8e9b4327 100644 --- a/cgfcollector/test/multi/02/CMakeLists.txt +++ b/cgfcollector/test/multi/02/CMakeLists.txt @@ -1,8 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project(02 LANGUAGES Fortran) + +include(../cmake_base.txt) + file( GLOB SOURCES "*.f90" ) -add_executable(02 ${SOURCES}) -set_target_properties(02 PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +add_executable(${CMAKE_PROJECT_NAME} ${SOURCES}) diff --git a/cgfcollector/test/multi/02/main.f90 b/cgfcollector/test/multi/02/main.f90 index 410c3260..def16537 100644 --- a/cgfcollector/test/multi/02/main.f90 +++ b/cgfcollector/test/multi/02/main.f90 @@ -1,5 +1,6 @@ program main use my_module, only: my_subroutine + use module3 implicit none diff --git a/cgfcollector/test/multi/02/module.f90 b/cgfcollector/test/multi/02/module.f90 index 24a89b12..2a1b0134 100644 --- a/cgfcollector/test/multi/02/module.f90 +++ b/cgfcollector/test/multi/02/module.f90 @@ -1,4 +1,5 @@ module my_module + use module0_5 implicit none @@ -12,6 +13,8 @@ subroutine my_subroutine(n) write (*, *) n + call func() + end subroutine my_subroutine end module my_module diff --git a/cgfcollector/test/multi/02/module0_5.f90 b/cgfcollector/test/multi/02/module0_5.f90 new file mode 100644 index 00000000..0ff134db --- /dev/null +++ b/cgfcollector/test/multi/02/module0_5.f90 @@ -0,0 +1,13 @@ +module module0_5 + use module2, only: func2 => func + + implicit none + +contains + subroutine func() + call func2() + print *, "This is module2" + end subroutine func + +end module module0_5 + diff --git a/cgfcollector/test/multi/02/module2.f90 b/cgfcollector/test/multi/02/module2.f90 new file mode 100644 index 00000000..9c7af856 --- /dev/null +++ b/cgfcollector/test/multi/02/module2.f90 @@ -0,0 +1,10 @@ +module module2 + + implicit none +contains + subroutine func() + print *, "This is module2" + end subroutine func + +end module module2 + diff --git a/cgfcollector/test/multi/02/module3.f90 b/cgfcollector/test/multi/02/module3.f90 new file mode 100644 index 00000000..1abff39f --- /dev/null +++ b/cgfcollector/test/multi/02/module3.f90 @@ -0,0 +1,13 @@ +module module3 + use module2, only: func2 => func + + implicit none + +contains + subroutine func() + call func2() + print *, "This is module3" + end subroutine func + +end module module3 + diff --git a/cgfcollector/test/multi/CMakeLists.txt b/cgfcollector/test/multi/CMakeLists.txt deleted file mode 100644 index 414ea07f..00000000 --- a/cgfcollector/test/multi/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -project(cgfctest LANGUAGES Fortran) - -include(../cmake_base.txt) - -add_subdirectory(02) diff --git a/cgfcollector/test/multi/cmake_base.txt b/cgfcollector/test/multi/cmake_base.txt new file mode 100644 index 00000000..21108e4a --- /dev/null +++ b/cgfcollector/test/multi/cmake_base.txt @@ -0,0 +1,6 @@ +# This file is generated by CMake. +set(CMAKE_Fortran_COMPILER "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/cgfcollector/cgfcollector_wrapper.sh") +set(CMAKE_Fortran_FLAGS "") +set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") +set(CMAKE_Fortran_LINK_EXECUTABLE "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/tools/cgmerge2/cgmerge2 ") +set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 75896073..caedc6cd 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -1,7 +1,9 @@ #!/bin/bash test_case_dir="@FCOLLECTOR_TEST_CASES_DIR@" -out_dir="out" + +scriptdir="$(cd "$(dirname "$0")" && pwd -P)" +out_dir="$scriptdir/out" if [[ ! -d "$out_dir" ]]; then mkdir "$out_dir" @@ -30,88 +32,59 @@ function should_be_run() return 1 } -# cmake managed test dirs -find "$test_case_dir" -mindepth 1 -type d \! -exec test -e "{}/output.json" \; \! -exec test -e "{}/*.f90" \; -exec find {} -maxdepth 1 -name 'CMakeLists.txt' \; | while read -r dir; do +find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "output.json" \; | while read -r dir; do dir="$(dirname "$dir")" + output_file="$dir/output.json" - tmp_dir="$(mktemp -d)" + # generate test case name from dir path. test/simple/01 becomes simple_01 + test_case_name="$(basename "$(dirname "$dir")")_$(basename "$dir")" - copy_to="$(pwd)/$out_dir" - if [[ ! -d "$copy_to" ]]; then - echo "Error: $copy_to does not exist" + if ! should_be_run "$test_case_name"; then + continue fi - echo "Running cmake managed tests from dir: $(basename "$dir")" - - { - cd "$dir" - cmake -S . -B "$tmp_dir" - cd "$tmp_dir" - make - find . -maxdepth 1 -name '*.json' | while read -r file; do - cp "$file" "$copy_to/$(basename "$dir")_$(basename "$file")" - - test_case_name="$(basename "$dir")_$(basename -s .json "$file")" - - if ! should_be_run "$test_case_name"; then - continue - fi - - echo "Test case: $test_case_name" + echo "Test case: $test_case_name" - output_file="$dir/$(basename -s .json "$file")/output.json" - - if [[ -f "$output_file" ]] && [[ -s "$output_file" ]] && grep -q '{}' "$output_file"; then - echo "Skipping empty or missing output file: $output_file" - continue - fi - - if @FCOLLECTOR_CG_COMPARE@ "$output_file" "$copy_to/$(basename "$dir")_$(basename "$file")"; then - echo "Test case passed" - else - echo "Error: Output mismatch" - fi - - done - } || { - echo "Error: could not generate CG" - rm -rf "$tmp_dir" + if [ ! -f "$output_file" ] || [ ! -s "$output_file" ] || grep -q '{}' "$output_file"; then + echo "Skipping empty or missing output file: $output_file" continue - } - - rm -rf "$tmp_dir" -done - -# no cmake managed test dirs -find "$test_case_dir" -mindepth 1 -type d \! -exec test -e "{}/CMakeLists.txt" \; -exec find {} -maxdepth 1 -name "output.json" \; | while read -r dir; do - dir="$(dirname "$dir")" - mapfile -t input_files < <(find "$dir" -maxdepth 1 -name '*.f90') - output_file="$dir/output.json" - - if [[ ${#input_files[@]} -gt 0 ]]; then - # generate test case name from dir path. test/simple/01 becomes simple_01 - test_case_name="$(basename "$(dirname "$dir")")_$(basename "$dir")" + fi - if ! should_be_run "$test_case_name"; then + if find "$dir" -maxdepth 1 -name 'CMakeLists.txt' | grep -q .; then + # cmake managed test + tmp_dir="$(mktemp -d)" + + { + cd "$dir" + cmake -S . -B "$tmp_dir" + cd "$tmp_dir" + make + find . -maxdepth 1 -name '*.json' | while read -r file; do + cp "$file" "$out_dir/$test_case_name.json" + done + } || { + echo "Error: could not generate CG" + rm -rf "$tmp_dir" continue - fi + } - echo "Test case: $test_case_name" - - if [ ! -f "$output_file" ] || [ ! -s "$output_file" ] || grep -q '{}' "$output_file"; then - echo "Skipping empty or missing output file: $output_file" - continue - fi + rm -rf "$tmp_dir" + else + # not cmake managed test + mapfile -t input_files < <(find "$dir" -maxdepth 1 -name '*.f90') + if [[ ${#input_files[@]} -gt 0 ]]; then - if ! @FCOLLECTOR_WRAPPER@ -dot -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then - echo "Error: failed to generate callgraph" - continue + if ! @FCOLLECTOR_WRAPPER@ -dot -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then + echo "Error: failed to generate callgraph" + continue + fi fi + fi - if @FCOLLECTOR_CG_COMPARE@ "$output_file" "$out_dir/$test_case_name.json"; then - echo "Test case passed" - else - echo "Error: Output mismatch" - fi + if @FCOLLECTOR_CG_COMPARE@ "$output_file" "$out_dir/$test_case_name.json"; then + echo "Test case passed" + else + echo "Error: Output mismatch" fi + done From 98b3f5a64ef9b47655505695f0233b42ffb8e848 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:52 +0100 Subject: [PATCH 047/130] add output.json for multi_02 test --- cgfcollector/test/multi/02/output.json | 48 +++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/cgfcollector/test/multi/02/output.json b/cgfcollector/test/multi/02/output.json index 2cfcbb0d..681188f1 100644 --- a/cgfcollector/test/multi/02/output.json +++ b/cgfcollector/test/multi/02/output.json @@ -1,7 +1,39 @@ { "_CG": { - "edges": [[[2308780131476637806, 15299685676102505225], null]], + "edges": [ + [[1083670836535463101, 11444295161927196599], null], + [[11444295161927196599, 9530012462595688619], null], + [[2308780131476637806, 1083670836535463101], null], + [[6753505644903624250, 9530012462595688619], null] + ], "nodes": [ + [ + 9603846864033343388, + { + "functionName": "_QMmy_modulePsubsub", + "hasBody": true, + "meta": null, + "origin": "module.f90" + } + ], + [ + 11444295161927196599, + { + "functionName": "_QMmodule0_5Pfunc", + "hasBody": true, + "meta": null, + "origin": "module0_5.f90" + } + ], + [ + 1083670836535463101, + { + "functionName": "_QMmy_modulePmy_subroutine", + "hasBody": true, + "meta": null, + "origin": "unknownOrigin" + } + ], [ 2308780131476637806, { @@ -12,21 +44,21 @@ } ], [ - 9603846864033343388, + 6753505644903624250, { - "functionName": "_QMmy_modulePsubsub", + "functionName": "_QMmodule3Pfunc", "hasBody": true, "meta": null, - "origin": "module.f90" + "origin": "module3.f90" } ], [ - 15299685676102505225, + 9530012462595688619, { - "functionName": "_QMmy_modulePmy_subroutine", + "functionName": "_QMmodule2Pfunc", "hasBody": true, "meta": null, - "origin": "module.f90" + "origin": "unknownOrigin" } ] ] @@ -34,7 +66,7 @@ "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "51dac91ac3ddf708d8cd319588ace9650576ea1a", + "sha": "d76681a62ca400b6d9ff4a24bde16f373326f998", "version": "0.7" }, "version": "3.0" From 68a1208c6bd980811fcf622e3447137a842d4133 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:53 +0100 Subject: [PATCH 048/130] renamed multi_02 test to multi_deps --- cgfcollector/src/main.cpp | 7 +++++-- cgfcollector/test/multi/{02 => deps}/CMakeLists.txt | 2 +- cgfcollector/test/multi/{02 => deps}/main.f90 | 0 cgfcollector/test/multi/{02 => deps}/module.f90 | 0 cgfcollector/test/multi/{02 => deps}/module0_5.f90 | 0 cgfcollector/test/multi/{02 => deps}/module2.f90 | 0 cgfcollector/test/multi/{02 => deps}/module3.f90 | 0 cgfcollector/test/multi/{02 => deps}/output.json | 0 8 files changed, 6 insertions(+), 3 deletions(-) rename cgfcollector/test/multi/{02 => deps}/CMakeLists.txt (82%) rename cgfcollector/test/multi/{02 => deps}/main.f90 (100%) rename cgfcollector/test/multi/{02 => deps}/module.f90 (100%) rename cgfcollector/test/multi/{02 => deps}/module0_5.f90 (100%) rename cgfcollector/test/multi/{02 => deps}/module2.f90 (100%) rename cgfcollector/test/multi/{02 => deps}/module3.f90 (100%) rename cgfcollector/test/multi/{02 => deps}/output.json (100%) diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 99c43d98..c1940b91 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -7,8 +7,11 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { void executeAction() override { auto cg = std::make_unique(); - spdlog::set_pattern("%v"); - spdlog::set_level(spdlog::level::debug); + // TODO: remove + if (std::getenv("CUSTOM_DEBUG")) { + spdlog::set_pattern("%v"); + spdlog::set_level(spdlog::level::debug); + } ParseTreeVisitor visitor(cg.get(), getCurrentFile().str()); Fortran::parser::Walk(getParsing().parseTree(), visitor); diff --git a/cgfcollector/test/multi/02/CMakeLists.txt b/cgfcollector/test/multi/deps/CMakeLists.txt similarity index 82% rename from cgfcollector/test/multi/02/CMakeLists.txt rename to cgfcollector/test/multi/deps/CMakeLists.txt index 8e9b4327..abf09466 100644 --- a/cgfcollector/test/multi/02/CMakeLists.txt +++ b/cgfcollector/test/multi/deps/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) -project(02 LANGUAGES Fortran) +project(deps LANGUAGES Fortran) include(../cmake_base.txt) diff --git a/cgfcollector/test/multi/02/main.f90 b/cgfcollector/test/multi/deps/main.f90 similarity index 100% rename from cgfcollector/test/multi/02/main.f90 rename to cgfcollector/test/multi/deps/main.f90 diff --git a/cgfcollector/test/multi/02/module.f90 b/cgfcollector/test/multi/deps/module.f90 similarity index 100% rename from cgfcollector/test/multi/02/module.f90 rename to cgfcollector/test/multi/deps/module.f90 diff --git a/cgfcollector/test/multi/02/module0_5.f90 b/cgfcollector/test/multi/deps/module0_5.f90 similarity index 100% rename from cgfcollector/test/multi/02/module0_5.f90 rename to cgfcollector/test/multi/deps/module0_5.f90 diff --git a/cgfcollector/test/multi/02/module2.f90 b/cgfcollector/test/multi/deps/module2.f90 similarity index 100% rename from cgfcollector/test/multi/02/module2.f90 rename to cgfcollector/test/multi/deps/module2.f90 diff --git a/cgfcollector/test/multi/02/module3.f90 b/cgfcollector/test/multi/deps/module3.f90 similarity index 100% rename from cgfcollector/test/multi/02/module3.f90 rename to cgfcollector/test/multi/deps/module3.f90 diff --git a/cgfcollector/test/multi/02/output.json b/cgfcollector/test/multi/deps/output.json similarity index 100% rename from cgfcollector/test/multi/02/output.json rename to cgfcollector/test/multi/deps/output.json From fcd93aed961734d56084f667c0b3df96dfd06b2e Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:53 +0100 Subject: [PATCH 049/130] add interop tests --- cgfcollector/test/simple/interop/func.c | 1 + cgfcollector/test/simple/interop/main.f90 | 24 ++++++++++++++ cgfcollector/test/simple/interop/output.json | 33 +++++++++++++++++++ .../test/simple/interop_external/func.c | 1 + .../test/simple/interop_external/main.f90 | 15 +++++++++ .../test/simple/interop_external/output.json | 33 +++++++++++++++++++ 6 files changed, 107 insertions(+) create mode 100644 cgfcollector/test/simple/interop/func.c create mode 100644 cgfcollector/test/simple/interop/main.f90 create mode 100644 cgfcollector/test/simple/interop/output.json create mode 100644 cgfcollector/test/simple/interop_external/func.c create mode 100644 cgfcollector/test/simple/interop_external/main.f90 create mode 100644 cgfcollector/test/simple/interop_external/output.json diff --git a/cgfcollector/test/simple/interop/func.c b/cgfcollector/test/simple/interop/func.c new file mode 100644 index 00000000..1f764483 --- /dev/null +++ b/cgfcollector/test/simple/interop/func.c @@ -0,0 +1 @@ +int add(int a, int b) { return a + b; } diff --git a/cgfcollector/test/simple/interop/main.f90 b/cgfcollector/test/simple/interop/main.f90 new file mode 100644 index 00000000..d8ecb4b9 --- /dev/null +++ b/cgfcollector/test/simple/interop/main.f90 @@ -0,0 +1,24 @@ +program main + use iso_c_binding + implicit none + + interface + function add(a, b) bind(C) result(res) + import :: C_INT + implicit none + integer(C_INT), value :: a, b + integer(C_INT) :: res + end function add + end interface + + integer(C_INT) :: result + integer(C_INT) :: var1 + integer(C_INT) :: var2 + var1 = 3 + var2 = 23 + + result = add(var1, var2) + print *, "Result from C add function:", result + +end program main + diff --git a/cgfcollector/test/simple/interop/output.json b/cgfcollector/test/simple/interop/output.json new file mode 100644 index 00000000..2a4e3540 --- /dev/null +++ b/cgfcollector/test/simple/interop/output.json @@ -0,0 +1,33 @@ +{ + "_CG": { + "edges": [[[10932468432584512985, 15390318080014301756], null]], + "nodes": [ + [ + 15390318080014301756, + { + "functionName": "add", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ], + [ + 10932468432584512985, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "16c60e49d2f6547d0493529a47951ca207bb5d3d", + "version": "0.7" + }, + "version": "3.0" + } +} diff --git a/cgfcollector/test/simple/interop_external/func.c b/cgfcollector/test/simple/interop_external/func.c new file mode 100644 index 00000000..ba2771bf --- /dev/null +++ b/cgfcollector/test/simple/interop_external/func.c @@ -0,0 +1 @@ +int add_(int* a, int* b) { return *a + *b; } diff --git a/cgfcollector/test/simple/interop_external/main.f90 b/cgfcollector/test/simple/interop_external/main.f90 new file mode 100644 index 00000000..1c34e2ed --- /dev/null +++ b/cgfcollector/test/simple/interop_external/main.f90 @@ -0,0 +1,15 @@ +program main + use iso_c_binding + implicit none + + integer(C_INT), external :: add + + integer(C_INT) :: result + integer(C_INT) :: var1 + integer(C_INT) :: var2 + var1 = 3 + var2 = 23 + + result = add(var1, var2) + print *, "Result from C add function:", result +end program main diff --git a/cgfcollector/test/simple/interop_external/output.json b/cgfcollector/test/simple/interop_external/output.json new file mode 100644 index 00000000..fc96ac79 --- /dev/null +++ b/cgfcollector/test/simple/interop_external/output.json @@ -0,0 +1,33 @@ +{ + "_CG": { + "edges": [[[13180814381945652224, 9267160619168015051], null]], + "nodes": [ + [ + 9267160619168015051, + { + "functionName": "_QPadd", + "hasBody": false, + "meta": null, + "origin": "unknownOrigin" + } + ], + [ + 13180814381945652224, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "16c60e49d2f6547d0493529a47951ca207bb5d3d", + "version": "0.7" + }, + "version": "3.0" + } +} From 2d0bfdff7dff0ac14e4b14c0662326c203ef829a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:53 +0100 Subject: [PATCH 050/130] destructor handling with static lifetimes and test --- cgfcollector/src/ParseTreeVisitor.cpp | 6 +++ cgfcollector/test/simple/final5/main.f90 | 46 +++++++++++++++++++ cgfcollector/test/simple/final5/output.json | 51 +++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 cgfcollector/test/simple/final5/main.f90 create mode 100644 cgfcollector/test/simple/final5/output.json diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 181cddd1..a250314f 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -458,13 +458,19 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { bool holds_allocatable = false; const IntentSpec* holds_intent = nullptr; + bool holds_save = false; for (const auto& attr : std::get>(t.t)) { if (std::holds_alternative(attr.u)) holds_allocatable = true; else if (std::holds_alternative(attr.u)) holds_intent = &std::get(attr.u); + else if (std::holds_alternative(attr.u)) + holds_save = true; } + if (holds_save) + continue; // vars with save attr are not destructed + if (isFunctionArg) { if (!holds_allocatable) { if (!holds_intent) { diff --git a/cgfcollector/test/simple/final5/main.f90 b/cgfcollector/test/simple/final5/main.f90 new file mode 100644 index 00000000..c8ea710a --- /dev/null +++ b/cgfcollector/test/simple/final5/main.f90 @@ -0,0 +1,46 @@ +module mod + + implicit none + + type :: base + integer :: a + contains + final :: finalize_base + end type base + + type, extends(base) :: derived + integer :: b + contains + final :: finalize_derived + end type derived + + type(derived) :: derivedInModule ! not destructed because static lifetime + +contains + + subroutine finalize_base(this) + type(base), intent(inout) :: this + print *, "Finalizing base" + end subroutine finalize_base + + subroutine finalize_derived(this) + type(derived), intent(inout) :: this + print *, "Finalizing derived" + end subroutine finalize_derived + +end module mod + +program main + + implicit none + +contains + subroutine func() + use mod + type(derived), save :: obj ! save = static lifetime + + obj = derived(1, 2) + end subroutine func + +end program main + diff --git a/cgfcollector/test/simple/final5/output.json b/cgfcollector/test/simple/final5/output.json new file mode 100644 index 00000000..ca9489ef --- /dev/null +++ b/cgfcollector/test/simple/final5/output.json @@ -0,0 +1,51 @@ +{ + "_CG": { + "edges": [], + "nodes": [ + [ + 16500006632140824326, + { + "functionName": "_QFPfunc", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 4938433408998471207, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 16172947494565568999, + { + "functionName": "_QMmodPfinalize_derived", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 1620398472718245726, + { + "functionName": "_QMmodPfinalize_base", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "cff904532489787c5f6ff332d161ed9e1dcb3d9f", + "version": "0.7" + }, + "version": "3.0" + } +} From 944b82fdbb2d0739f8c5ab818d59836ba08e3e32 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:54 +0100 Subject: [PATCH 051/130] additional finalizer test --- cgfcollector/test/simple/final4/main.f90 | 36 ++++++++++++++++++ cgfcollector/test/simple/final4/output.json | 42 +++++++++++++++++++++ 2 files changed, 78 insertions(+) create mode 100644 cgfcollector/test/simple/final4/main.f90 create mode 100644 cgfcollector/test/simple/final4/output.json diff --git a/cgfcollector/test/simple/final4/main.f90 b/cgfcollector/test/simple/final4/main.f90 new file mode 100644 index 00000000..0d7e2fbb --- /dev/null +++ b/cgfcollector/test/simple/final4/main.f90 @@ -0,0 +1,36 @@ +module mod + implicit none + + type dummy_type + integer :: i + contains + final :: finalize_dummy + end type dummy_type + + interface dummy_type + module procedure create_dummy_type + end interface dummy_type +contains + subroutine finalize_dummy(this) + type(dummy_type), intent(inout) :: this + write (*, *) 'Finalizing dummy_type' + end subroutine finalize_dummy + + type(dummy_type) function create_dummy_type(i) + integer, intent(in) :: i + create_dummy_type%i = i + write (*, *) 'Creating dummy_type with i = ', create_dummy_type%i + end function create_dummy_type +end module mod + +program main + use mod + + implicit none + + type(dummy_type), allocatable :: dummy + dummy = dummy_type(1) + + ! dummy finalizer is called here but this is not confrom with the fortran specification (7.5.6.4) +end program main + diff --git a/cgfcollector/test/simple/final4/output.json b/cgfcollector/test/simple/final4/output.json new file mode 100644 index 00000000..21562fee --- /dev/null +++ b/cgfcollector/test/simple/final4/output.json @@ -0,0 +1,42 @@ +{ + "_CG": { + "edges": [[[9628105010719225529, 1576245589781696893], null]], + "nodes": [ + [ + 9628105010719225529, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 1576245589781696893, + { + "functionName": "_QMmodPcreate_dummy_type", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 3690541955172721898, + { + "functionName": "_QMmodPfinalize_dummy", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "cff904532489787c5f6ff332d161ed9e1dcb3d9f", + "version": "0.7" + }, + "version": "3.0" + } +} From 54baf9a4e901d0cdd6ab18edb25f96df4a238918 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:54 +0100 Subject: [PATCH 052/130] CMakeLists -g commend --- cgfcollector/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 756c7016..7a4e66b3 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -21,6 +21,8 @@ target_precompile_headers( target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +# target_compile_options(${PROJECT_NAME} PRIVATE -g) + install( TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} From acba642238db3e6901d0aca1a45ff425e690f98d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:54 +0100 Subject: [PATCH 053/130] improved variable tracking, impl handling of allocatable trough function calls and some minor improvements --- cgfcollector/include/ParseTreeVisitor.h | 28 ++++- cgfcollector/src/ParseTreeVisitor.cpp | 136 ++++++++++++++++++------ cgfcollector/src/main.cpp | 22 +++- 3 files changed, 149 insertions(+), 37 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 03e454c7..cea2483c 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -24,16 +24,28 @@ typedef struct trackedVar { } trackedVar_t; typedef struct function { - Symbol* symbol; - std::vector dummyArgs; - std::vector dummyArgsHasBeenInitialized; + typedef struct dummyArg { + Symbol* symbol; + bool hasBeenInitialized = false; + } dummyArg_t; + + Symbol* symbol; // function + std::vector dummyArgs; } function_t; +typedef struct potentialFinalizer { + std::size_t argPos; + std::string procedureCalled; + std::vector> finalizerEdges; +} potentialFinalizer_t; + class ParseTreeVisitor { public: ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; - std::vector> getEdges() const { return edges; } + std::vector>& getEdges() { return edges; } + std::vector& getPotentialFinalizers() { return potentialFinalizers; } + std::vector& getFunctions() { return functions; } template void handleFuncSubStmt(const T& stmt); @@ -73,8 +85,12 @@ class ParseTreeVisitor { const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); + trackedVar_t* getTrackedVarFromSourceName(SourceName sourceName); void handleTrackedVarAssignment(SourceName sourceName); + void addTrackedVar(trackedVar_t var); + void removeTrackedVars(Symbol* procedureSymbol); + template bool Pre(const A&) { return true; @@ -163,4 +179,8 @@ class ParseTreeVisitor { std::vector trackedVars; AL* al = AL::getInstance(); + + std::vector functions; + + std::vector potentialFinalizers; }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index a250314f..f4fb005d 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -8,6 +8,7 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { functionSymbols.emplace_back(sym); functionDummyArgs.emplace_back(std::vector()); cg->insert(std::make_unique(mangleName(*sym), currentFileName, false, false)); + functions.push_back({sym, std::vector()}); al->debug("Add node: {} ({})", mangleName(*sym), fmt::ptr(sym)); } @@ -33,27 +34,34 @@ void ParseTreeVisitor::handleTrackedVars() { al->debug("Handle tracked vars for function"); for (auto& trackedVar : trackedVars) { - if (trackedVar.addFinalizers) { - if (!trackedVar.hasBeenInitialized) - continue; - if (trackedVar.procedure != functionSymbols.back()) - continue; + if (!trackedVar.hasBeenInitialized) + continue; + if (trackedVar.procedure != functionSymbols.back()) + continue; - // add edge for deconstruction (finalizer) + // add edge for deconstruction (finalizer) + if (trackedVar.addFinalizers) { auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); if (!typeSymbol) continue; - add_edges_for_finalizers(typeSymbol); - } else { + } + + // set init on dummy function args + auto functionIt = std::find_if(functions.begin(), functions.end(), + [&](const auto& f) { return f.symbol == functionSymbols.back(); }); + if (functionIt != functions.end()) { + auto dummyArgIt = std::find_if(functionIt->dummyArgs.begin(), functionIt->dummyArgs.end(), + [&](const auto& d) { return d.symbol == trackedVar.var; }); + if (dummyArgIt != functionIt->dummyArgs.end()) { + dummyArgIt->hasBeenInitialized = true; + } } } } // cleanup trackedVars - trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), - [&](const trackedVar_t& t) { return t.procedure == functionSymbols.back(); }), - trackedVars.end()); + removeTrackedVars(functionSymbols.back()); } std::vector ParseTreeVisitor::find_type_with_derived_types(const Symbol* typeSymbol) { @@ -210,13 +218,11 @@ const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { return typeSymbol; } -// search trackedVars for a canditate and set it as initialized. -// Prefers local variables when (shadowed) -void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { +trackedVar_t* ParseTreeVisitor::getTrackedVarFromSourceName(SourceName sourceName) { auto anyTrackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { return t.var->name() == sourceName; }); if (anyTrackedVarIt == trackedVars.end()) - return; + return nullptr; // find local variable with the same name in the current function scope (shadowed) auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { @@ -224,10 +230,40 @@ void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { }); // prefer local var if found - auto& trackedVar = (localVarIt != trackedVars.end()) ? *localVarIt : *anyTrackedVarIt; - trackedVar.hasBeenInitialized = true; + return (localVarIt != trackedVars.end()) ? &(*localVarIt) : &(*anyTrackedVarIt); +} + +// search trackedVars for a canditate and set it as initialized. +// Prefers local variables when (shadowed) +void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { + auto* trackedVar = getTrackedVarFromSourceName(sourceName); + if (!trackedVar) + return; + + trackedVar->hasBeenInitialized = true; - al->debug("Tracked var assigned: {} ({})", trackedVar.var->name(), fmt::ptr(trackedVar.var)); + al->debug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); +} + +void ParseTreeVisitor::addTrackedVar(trackedVar_t var) { + auto it = + std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar_t& t) { return t.var == var.var; }); + if (it != trackedVars.end()) { + // update info + it->addFinalizers = var.addFinalizers; + it->hasBeenInitialized = var.hasBeenInitialized; + al->debug("Update tracked variable: {} ({})", var.var->name(), fmt::ptr(var.var)); + return; + } + + trackedVars.push_back(var); + al->debug("Add tracking for variable: {} ({})", var.var->name(), fmt::ptr(var.var)); +} + +void ParseTreeVisitor::removeTrackedVars(Symbol* procedureSymbol) { + trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), + [&](const trackedVar_t& t) { return t.procedure == procedureSymbol; }), + trackedVars.end()); } // Visitor implementations @@ -300,10 +336,17 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { handleFuncSubStmt(f); + auto functionsIt = std::find_if(functions.begin(), functions.end(), + [&](const auto& func) { return func.symbol == functionSymbols.back(); }); + // collect function arguments const auto& name_list = std::get>(f.t); for (auto name : name_list) { functionDummyArgs.back().push_back(&name); + if (functionsIt != functions.end()) { + functionsIt->dummyArgs.push_back({name.symbol, false}); + addTrackedVar({name.symbol, functionSymbols.back(), false, false}); + } } } @@ -320,11 +363,18 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { handleFuncSubStmt(s); + auto functionsIt = std::find_if(functions.begin(), functions.end(), + [&](const auto& func) { return func.symbol == functionSymbols.back(); }); + // collect subroutine arguments (dummy args) const auto* dummyArg_list = &std::get>(s.t); for (const auto& dummyArg : *dummyArg_list) { const auto* name = std::get_if(&dummyArg.u); functionDummyArgs.back().push_back(name); + if (functionsIt != functions.end()) { + functionsIt->dummyArgs.push_back({name->symbol, false}); + addTrackedVar({name->symbol, functionSymbols.back(), false, false}); + } } } @@ -405,19 +455,14 @@ void ParseTreeVisitor::Post(const AllocateStmt& a) { } void ParseTreeVisitor::Post(const Call& c) { - // handle move_alloc intrinsic for allocatable vars const auto* designator = &std::get(c.t); const auto* args = &std::get>(c.t); - const auto* name = std::get_if(&designator->u); - if (!name || !name->symbol) - return; - if (!name->symbol->attrs().test(Attr::INTRINSIC) && name->symbol->name() != "move_alloc") - return; - - if (args->size() < 2) + const auto* procName = std::get_if(&designator->u); + if (!procName || !procName->symbol) return; + std::size_t argPos = 0; for (const auto& arg : *args) { const auto* actualArg = &std::get(arg.t); const auto* expr = std::get_if>(&actualArg->u); @@ -427,7 +472,36 @@ void ParseTreeVisitor::Post(const Call& c) { if (!name || !name->symbol) return; - handleTrackedVarAssignment(name->symbol->name()); + // handle move_alloc intrinsic for allocatable vars + if (procName->symbol->attrs().test(Attr::INTRINSIC) && procName->symbol->name() == "move_alloc") { + handleTrackedVarAssignment(name->symbol->name()); + } else { + auto* trackedVar = getTrackedVarFromSourceName(name->symbol->name()); + if (!trackedVar) + continue; + + auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar->var); + // TODO: rework + potentialFinalizer pf = {argPos, mangleName(*procName->symbol)}; + + std::vector typeSymbols = find_type_with_derived_types(typeSymbol); + + for (const auto& type : typeSymbols) { + const Symbol* typeSymbol = type.type; + + const auto* details = std::get_if(&typeSymbol->details()); + if (!details) + return; + + // add edges for finalizers + for (const auto& final : details->finals()) { + pf.finalizerEdges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*final.second)); + } + } + + potentialFinalizers.push_back(pf); + al->debug("Add potential finalizer for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); + } } } @@ -475,25 +549,23 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { if (!holds_allocatable) { if (!holds_intent) { // no intent attr, if not set does not call finalizer. Why? idk. - trackedVars.push_back({name.symbol, functionSymbols.back(), false, true}); al->debug("Add tracking for function argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + addTrackedVar({name.symbol, functionSymbols.back(), false, true}); } else { if (holds_intent->v == IntentSpec::Intent::Out) { // intent out, calls finalizer because (7.5.6.3 line 21 and onwards) add_edges_for_finalizers(typeSymbol); } else if (holds_intent->v == IntentSpec::Intent::InOut) { // intent inout, calls finalizer when set. - trackedVars.push_back({name.symbol, functionSymbols.back(), false, true}); al->debug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + addTrackedVar({name.symbol, functionSymbols.back(), false, true}); } } - } else { - // needs to be check at the end of prog TODO: allocatable attr as function argument } } else { if (holds_allocatable) { - trackedVars.push_back({name.symbol, functionSymbols.back(), false, true}); al->debug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + addTrackedVar({name.symbol, functionSymbols.back(), false, true}); // skip var with allocatable attr. // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. } else { diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index c1940b91..d25ac18a 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -5,6 +5,8 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { public: void executeAction() override { + AL* al = AL::getInstance(); + auto cg = std::make_unique(); // TODO: remove @@ -16,6 +18,25 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { ParseTreeVisitor visitor(cg.get(), getCurrentFile().str()); Fortran::parser::Walk(getParsing().parseTree(), visitor); + // handle potential finalizers from function calls + for (const auto pf : visitor.getPotentialFinalizers()) { + auto functions = visitor.getFunctions(); + auto calledIt = std::find_if(functions.begin(), functions.end(), + [&](const auto& f) { return mangleName(*f.symbol) == pf.procedureCalled; }); + if (calledIt == functions.end()) + continue; + + auto arg = calledIt->dummyArgs.begin() + pf.argPos; + + if (!arg->hasBeenInitialized) + continue; + + for (const auto& edge : pf.finalizerEdges) { + visitor.getEdges().emplace_back(edge.first, edge.second); + al->debug("Add edge for potential finalizer: {} -> {}", edge.first, edge.second); + } + } + // add edges for (auto edge : visitor.getEdges()) { auto* callerNode = cg->getOrInsertNode(edge.first); @@ -46,7 +67,6 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { auto file = createOutputFile("json"); file->write(jsonSink.getJson().dump().c_str(), jsonSink.getJson().dump().size()); - AL* al = AL::getInstance(); al->flush(); } }; From fe2afa31c95d77737bc658f87ebf1b38fc06a7e8 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:55 +0100 Subject: [PATCH 054/130] more consistent function naming scheme --- cgfcollector/include/ParseTreeVisitor.h | 8 +++---- cgfcollector/src/ParseTreeVisitor.cpp | 30 ++++++++++++------------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index cea2483c..e7169969 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -54,13 +54,13 @@ class ParseTreeVisitor { void handleTrackedVars(); // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. - std::vector find_type_with_derived_types(const Symbol* typeSymbol); + std::vector findTypeWithDerivedTypes(const Symbol* typeSymbol); // this function searches with typeSymbol for a type in types vector and adds edges for procedures that matches // procedureSymbol. And also adds edges from types that extends from typeSymbol. - void add_edges_for_produces_and_derived_types(std::vector typeWithDerived, const Symbol* procedureSymbol); + void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); - void add_edges_for_finalizers(const Symbol* typeSymbol); + void addEdgesForFinalizers(const Symbol* typeSymbol); template bool holds_any_of(const Variant& v) { @@ -69,7 +69,7 @@ class ParseTreeVisitor { bool isOperator(const Expr* e); - bool compare_expr_IntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op); + bool compareExprIntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op); template const Name* getNameFromClassWithDesignator(const T& t) { diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index f4fb005d..05726118 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -44,7 +44,7 @@ void ParseTreeVisitor::handleTrackedVars() { auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); if (!typeSymbol) continue; - add_edges_for_finalizers(typeSymbol); + addEdgesForFinalizers(typeSymbol); } // set init on dummy function args @@ -64,7 +64,7 @@ void ParseTreeVisitor::handleTrackedVars() { removeTrackedVars(functionSymbols.back()); } -std::vector ParseTreeVisitor::find_type_with_derived_types(const Symbol* typeSymbol) { +std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeSymbol) { std::vector typeWithDerived; auto findTypeIt = @@ -106,8 +106,8 @@ std::vector ParseTreeVisitor::find_type_with_derived_types(const Symbol* return typeWithDerived; } -void ParseTreeVisitor::add_edges_for_produces_and_derived_types(std::vector typeWithDerived, - const Symbol* procedureSymbol) { +void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, + const Symbol* procedureSymbol) { for (type_t t : typeWithDerived) { auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&procedureSymbol](const auto& p) { return p.first->name() == procedureSymbol->name(); @@ -122,8 +122,8 @@ void ParseTreeVisitor::add_edges_for_produces_and_derived_types(std::vector typeSymbols = find_type_with_derived_types(typeSymbol); +void ParseTreeVisitor::addEdgesForFinalizers(const Symbol* typeSymbol) { + std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); for (const auto& type : typeSymbols) { const Symbol* typeSymbol = type.type; @@ -155,7 +155,7 @@ bool ParseTreeVisitor::isOperator(const Expr* e) { Expr::DefinedBinary>(e->u); } -bool ParseTreeVisitor::compare_expr_IntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op) { +bool ParseTreeVisitor::compareExprIntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op) { if (!expr || !op) return false; @@ -426,7 +426,7 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (!typeSymbol) return; - add_edges_for_produces_and_derived_types(find_type_with_derived_types(typeSymbol), symbolComp); + addEdgesForProducesAndDerivedTypes(findTypeWithDerivedTypes(typeSymbol), symbolComp); } } @@ -484,7 +484,7 @@ void ParseTreeVisitor::Post(const Call& c) { // TODO: rework potentialFinalizer pf = {argPos, mangleName(*procName->symbol)}; - std::vector typeSymbols = find_type_with_derived_types(typeSymbol); + std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); for (const auto& type : typeSymbols) { const Symbol* typeSymbol = type.type; @@ -554,7 +554,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { } else { if (holds_intent->v == IntentSpec::Intent::Out) { // intent out, calls finalizer because (7.5.6.3 line 21 and onwards) - add_edges_for_finalizers(typeSymbol); + addEdgesForFinalizers(typeSymbol); } else if (holds_intent->v == IntentSpec::Intent::InOut) { // intent inout, calls finalizer when set. al->debug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); @@ -569,7 +569,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { // skip var with allocatable attr. // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. } else { - add_edges_for_finalizers(typeSymbol); + addEdgesForFinalizers(typeSymbol); } } } @@ -728,7 +728,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { // types auto it = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& p) { if (auto* intrinsicOp = std::get_if(p.first)) { - return compare_expr_IntrinsicOperator(e, intrinsicOp); + return compareExprIntrinsicOperator(e, intrinsicOp); } if (auto* definedOpName = std::get_if(p.first)) { if (auto* definedUnary = std::get_if(&e->u)) { @@ -762,11 +762,11 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (!typeSymbol) continue; - auto typeWithDerived = find_type_with_derived_types(typeSymbol); + auto typeWithDerived = findTypeWithDerivedTypes(typeSymbol); for (const auto& t : typeWithDerived) { auto opIt = std::find_if(t.operators.begin(), t.operators.end(), - [&](const auto& p) { return compare_expr_IntrinsicOperator(e, p.first); }); + [&](const auto& p) { return compareExprIntrinsicOperator(e, p.first); }); if (opIt == t.operators.end()) continue; @@ -786,7 +786,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } if (!skipSelfCall) - add_edges_for_produces_and_derived_types(typeWithDerived, funcSymbol); + addEdgesForProducesAndDerivedTypes(typeWithDerived, funcSymbol); } } From 1412446746368787a6f9196c96dbc7ccc9f7b50c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:55 +0100 Subject: [PATCH 055/130] some fixes for destructor with allocatable vars and test --- cgfcollector/include/ParseTreeVisitor.h | 10 +- cgfcollector/src/ParseTreeVisitor.cpp | 48 ++++---- cgfcollector/test/simple/final3/main.f90 | 126 ++++++++++++++++++++ cgfcollector/test/simple/final3/output.json | 125 +++++++++++++++++++ 4 files changed, 283 insertions(+), 26 deletions(-) create mode 100644 cgfcollector/test/simple/final3/main.f90 create mode 100644 cgfcollector/test/simple/final3/output.json diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index e7169969..3a064ead 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -9,6 +9,8 @@ using namespace Fortran::semantics; using namespace Fortran::common; using Fortran::lower::mangle::mangleName; +typedef std::pair edge; // (caller, callee) + typedef struct type { Symbol* type; Symbol* extendsFrom; @@ -36,14 +38,14 @@ typedef struct function { typedef struct potentialFinalizer { std::size_t argPos; std::string procedureCalled; - std::vector> finalizerEdges; + std::vector finalizerEdges; } potentialFinalizer_t; class ParseTreeVisitor { public: ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; - std::vector>& getEdges() { return edges; } + std::vector& getEdges() { return edges; } std::vector& getPotentialFinalizers() { return potentialFinalizers; } std::vector& getFunctions() { return functions; } @@ -61,6 +63,8 @@ class ParseTreeVisitor { void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); void addEdgesForFinalizers(const Symbol* typeSymbol); + void addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol); + std::vector> getEdgesForFinalizers(const Symbol* typeSymbol); template bool holds_any_of(const Variant& v) { @@ -154,7 +158,7 @@ class ParseTreeVisitor { private: metacg::Callgraph* cg; - std::vector> edges; // (caller, callee) + std::vector edges; std::string currentFileName; bool inFunctionOrSubroutineSubProgram = false; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 05726118..587ab891 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -123,6 +123,22 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector ty } void ParseTreeVisitor::addEdgesForFinalizers(const Symbol* typeSymbol) { + for (const auto& edge : getEdgesForFinalizers(typeSymbol)) { + edges.emplace_back(mangleName(*edge.first), mangleName(*edge.second)); + + al->debug("Add edge for finalizer: {} ({}) -> {} ({})", mangleName(*edge.first), fmt::ptr(edge.first), + mangleName(*edge.second), fmt::ptr(edge.second)); + } +} + +void ParseTreeVisitor::addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol) { + for (const auto& edge : getEdgesForFinalizers(typeSymbol)) { + edges->emplace_back(mangleName(*edge.first), mangleName(*edge.second)); + } +} + +std::vector> ParseTreeVisitor::getEdgesForFinalizers(const Symbol* typeSymbol) { + std::vector> edges; std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); for (const auto& type : typeSymbols) { @@ -130,16 +146,15 @@ void ParseTreeVisitor::addEdgesForFinalizers(const Symbol* typeSymbol) { const auto* details = std::get_if(&typeSymbol->details()); if (!details) - return; + continue; // add edges for finalizers for (const auto& final : details->finals()) { - edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*final.second)); - - al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleName(*final.second), fmt::ptr(&final.second.get())); + edges.emplace_back(functionSymbols.back(), &final.second.get()); } } + + return edges; } bool ParseTreeVisitor::isOperator(const Expr* e) { @@ -476,28 +491,15 @@ void ParseTreeVisitor::Post(const Call& c) { if (procName->symbol->attrs().test(Attr::INTRINSIC) && procName->symbol->name() == "move_alloc") { handleTrackedVarAssignment(name->symbol->name()); } else { + // handle finalizers for allocatable vars. + // This collects info from variables that are parse as arguments to functions. Function are defined below the + // execution part, so this need to be handled at the end of the parse tree traversal. auto* trackedVar = getTrackedVarFromSourceName(name->symbol->name()); if (!trackedVar) continue; - auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar->var); - // TODO: rework - potentialFinalizer pf = {argPos, mangleName(*procName->symbol)}; - - std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); - - for (const auto& type : typeSymbols) { - const Symbol* typeSymbol = type.type; - - const auto* details = std::get_if(&typeSymbol->details()); - if (!details) - return; - - // add edges for finalizers - for (const auto& final : details->finals()) { - pf.finalizerEdges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*final.second)); - } - } + potentialFinalizer pf = {argPos, mangleName(*procName->symbol), std::vector()}; + addEdgesForFinalizers(&pf.finalizerEdges, getTypeSymbolFromSymbol(trackedVar->var)); potentialFinalizers.push_back(pf); al->debug("Add potential finalizer for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); diff --git a/cgfcollector/test/simple/final3/main.f90 b/cgfcollector/test/simple/final3/main.f90 new file mode 100644 index 00000000..bcb7dae2 --- /dev/null +++ b/cgfcollector/test/simple/final3/main.f90 @@ -0,0 +1,126 @@ +module mod + + implicit none + + type :: base + integer :: a + contains + final :: finalize_base + end type base + + type, extends(base) :: derived + integer :: b + contains + final :: finalize_derived + end type derived + +contains + + subroutine finalize_base(this) + type(base), intent(inout) :: this + print *, "Finalizing base" + end subroutine finalize_base + + subroutine finalize_derived(this) + type(derived), intent(inout) :: this + print *, "Finalizing derived" + end subroutine finalize_derived + + subroutine func_arg(this) + type(derived), allocatable :: this + print *, "In func_arg" + allocate (this) + end subroutine func_arg + + subroutine func_arg_out(this) + type(derived), allocatable, intent(out) :: this + print *, "In func_arg_out" + allocate (this) + end subroutine func_arg_out + + subroutine func_arg_inout(this) + type(derived), allocatable, intent(inout) :: this + print *, "In func_arg_inout" + allocate (this) + end subroutine func_arg_inout + + subroutine func_arg2(this) + type(derived), allocatable :: this + print *, "In func_arg2" + end subroutine func_arg2 + + subroutine func_arg_out2(this) + type(derived), allocatable, intent(out) :: this + print *, "In func_arg_out2" + end subroutine func_arg_out2 + + subroutine func_arg_inout2(this) + type(derived), allocatable, intent(inout) :: this + print *, "In func_arg_inout2" + end subroutine func_arg_inout2 + +end module mod + +program main + use mod + + implicit none + + print *, "Calling func" + call func() + print *, "Calling func_no_finalize" + call func_no_finalize() + +contains + + subroutine func() + type(derived), allocatable :: obj + type(derived), allocatable :: obj2 + type(derived), allocatable :: obj3 + + print *, "Before func_arg" + call func_arg(obj) + print *, "After func_arg" + print *, "Before func_arg_out" + call func_arg_out(obj2) + print *, "After func_arg_out" + print *, "Before func_arg_inout" + call func_arg_inout(obj3) + print *, "After func_arg_inout" + + end subroutine func + + subroutine func_no_finalize() + type(derived), allocatable :: obj + type(derived), allocatable :: obj2 + type(derived), allocatable :: obj3 + + print *, "Before func_arg2" + call func_arg2(obj) + print *, "After func_arg2" + print *, "Before func_arg_out2" + call func_arg_out2(obj2) + print *, "After func_arg_out2" + print *, "Before func_arg_inout2" + call func_arg_inout2(obj3) + print *, "After func_arg_inout2" + + end subroutine func_no_finalize + + ! TODO: + ! subroutine func_more() + ! type(derived), allocatable :: obj + + ! call func_more_internal(obj) + + ! contains + ! subroutine func_more_internal(obj_internal) + ! type(derived), intent(out), allocatable :: obj_internal + + ! call func_arg_out(obj_internal) + + ! end subroutine func_more_internal + ! end subroutine func_more + +end program main + diff --git a/cgfcollector/test/simple/final3/output.json b/cgfcollector/test/simple/final3/output.json new file mode 100644 index 00000000..2377d1af --- /dev/null +++ b/cgfcollector/test/simple/final3/output.json @@ -0,0 +1,125 @@ +{ + "_CG": { + "edges": [ + [[13057133462220643391, 1454521131396630178], null], + [[14650538270171694253, 4796010356411637051], null], + [[14650538270171694253, 16491118580773965493], null], + [[14650538270171694253, 7396763912426530726], null], + [[13057133462220643391, 4051482389717363313], null], + [[13057133462220643391, 8008070197589690300], null], + [[13057133462220643391, 15912510891746475667], null], + [[13057133462220643391, 3412517584417964141], null], + [[16336895082892013384, 14650538270171694253], null], + [[16336895082892013384, 13057133462220643391], null] + ], + "nodes": [ + [ + 14650538270171694253, + { + "functionName": "_QFPfunc_no_finalize", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 4796010356411637051, + { + "functionName": "_QMmodPfunc_arg_inout2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 16336895082892013384, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 16491118580773965493, + { + "functionName": "_QMmodPfunc_arg_out2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 7396763912426530726, + { + "functionName": "_QMmodPfunc_arg2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 3412517584417964141, + { + "functionName": "_QMmodPfunc_arg", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 8008070197589690300, + { + "functionName": "_QMmodPfinalize_derived", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 13057133462220643391, + { + "functionName": "_QFPfunc", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 15912510891746475667, + { + "functionName": "_QMmodPfunc_arg_inout", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 4051482389717363313, + { + "functionName": "_QMmodPfunc_arg_out", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 1454521131396630178, + { + "functionName": "_QMmodPfinalize_base", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "65b007fe24367861c97fcadcc94cc1c6a165c074", + "version": "0.7" + }, + "version": "3.0" + } +} From 9a75d452b10ae7d5469c8f51b611e6b9185e0805 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:55 +0100 Subject: [PATCH 056/130] some code cleanup regarding operators --- cgfcollector/src/ParseTreeVisitor.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 587ab891..02b17ec8 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -735,10 +735,11 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (auto* definedOpName = std::get_if(p.first)) { if (auto* definedUnary = std::get_if(&e->u)) { auto* exprOpName = &std::get(definedUnary->t); - return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); } if (auto* definedBinary = std::get_if(&e->u)) { + auto* exprOpName = &std::get(definedBinary->t); + return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); } return false; } @@ -749,7 +750,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { // params to identify only the onces that could be called. for (auto* sym : it->second) { // skip self calls - if (sym->name() == functionSymbols.back()->name()) + if (mangleName(*sym) == mangleName(*functionSymbols.back())) continue; edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*sym)); @@ -796,16 +797,6 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } void ParseTreeVisitor::Post(const Expr& e) { - // find out if this is a constructor - // auto* functionRef = std::get_if>(&e.u); - // if (functionRef) { - // auto* designator = &std::get(functionRef->value().v.t); - // auto* name = std::get_if(&designator->u); - // if (!name || !name->symbol) { - // return; - // } - // } - if (!isOperator(&e)) { return; } From 89489c4110a6bc1b6de6565dabd39db47c25a42a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:56 +0100 Subject: [PATCH 057/130] add more operator tests --- cgfcollector/test/simple/operator2/main.f90 | 72 +++++++++++++ .../test/simple/operator2/output.json | 55 ++++++++++ cgfcollector/test/simple/operator3/main.f90 | 102 ++++++++++++++++++ .../test/simple/operator3/output.json | 75 +++++++++++++ 4 files changed, 304 insertions(+) create mode 100644 cgfcollector/test/simple/operator2/main.f90 create mode 100644 cgfcollector/test/simple/operator2/output.json create mode 100644 cgfcollector/test/simple/operator3/main.f90 create mode 100644 cgfcollector/test/simple/operator3/output.json diff --git a/cgfcollector/test/simple/operator2/main.f90 b/cgfcollector/test/simple/operator2/main.f90 new file mode 100644 index 00000000..9ea4245d --- /dev/null +++ b/cgfcollector/test/simple/operator2/main.f90 @@ -0,0 +1,72 @@ +module mod + + implicit none + + type, abstract :: base + integer :: value + contains + procedure(fbase), deferred :: function_base + end type base + + abstract interface + logical function fbase(this) + import :: base + implicit none + class(base), intent(in) :: this + end function fbase + end interface + + type, extends(base) :: derived + integer :: extra_value + contains + procedure :: function_base => function_base2 + end type derived + + interface operator(.NOT.) + module procedure not_base + end interface + +contains + + logical function not_base(this) + class(base), intent(in) :: this + + select type (this) + type is (derived) + print *, "Derived NOT called with value: ", this%value, " and extra value: ", this%extra_value + not_base = .true. + class default + print *, "Base NOT called with value: ", this%value + not_base = .false. + end select + end function not_base + + logical function function_base2(this) + class(derived), intent(in) :: this + print *, "Derived function called with value: ", this%value, " and extra value: ", this%extra_value + function_base2 = .true. + end function function_base2 +end module mod + +program main + use mod + implicit none + + class(base), allocatable :: obj1, obj2, result + + class(base), allocatable :: obj3 + logical :: obj3_result, obj3_result2 + + allocate (derived :: obj3) + obj3%value = 20 + select type (d => obj3) + type is (derived) + d%extra_value = 30 + end select + obj3_result = (.NOT. obj3) + obj3_result2 = obj3%function_base() + print *, "Base object NOT: ", obj3_result + print *, "Base function: ", obj3_result2 + +end program main + diff --git a/cgfcollector/test/simple/operator2/output.json b/cgfcollector/test/simple/operator2/output.json new file mode 100644 index 00000000..9ce32625 --- /dev/null +++ b/cgfcollector/test/simple/operator2/output.json @@ -0,0 +1,55 @@ +{ + "_CG": { + "edges": [ + [[6566338197382086444, 18183997041583555397], null], + [[6566338197382086444, 844415908090943580], null], + [[6566338197382086444, 16861331205106935383], null] + ], + "nodes": [ + [ + 6566338197382086444, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 18183997041583555397, + { + "functionName": "_QMmodPfunction_base2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 16861331205106935383, + { + "functionName": "_QMmodPnot_base", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 844415908090943580, + { + "functionName": "_QMmodPfbase", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "1c4c99974efba03080aa26f17f925be8d6f36f2c", + "version": "0.7" + }, + "version": "3.0" + } +} diff --git a/cgfcollector/test/simple/operator3/main.f90 b/cgfcollector/test/simple/operator3/main.f90 new file mode 100644 index 00000000..31475fec --- /dev/null +++ b/cgfcollector/test/simple/operator3/main.f90 @@ -0,0 +1,102 @@ +module mod + + implicit none + + type, abstract :: base + integer :: value + contains + procedure(s), deferred :: show + end type base + + abstract interface + subroutine s(this) + import :: base + implicit none + class(base), intent(in) :: this + end subroutine s + end interface + + type, extends(base) :: derived1 + contains + procedure :: show => show_derived1 + end type derived1 + + type, extends(base) :: derived2 + contains + procedure :: show => show_derived2 + end type derived2 + + interface operator(+) + module procedure add_base + end interface + + interface operator(-) + module procedure sub_derived1 + end interface + +contains + + function add_base(a, b) result(c) + class(base), intent(in) :: a, b + class(base), allocatable :: c + + select type (a) + type is (derived1) + allocate (derived1 :: c) + c%value = a%value + b%value + type is (derived2) + allocate (derived2 :: c) + c%value = a%value + b%value + class default + allocate (derived1 :: c) + c%value = a%value + b%value + end select + end function add_base + + function sub_derived1(a, b) result(c) + class(base), intent(in) :: a, b + class(base), allocatable :: c + + allocate (derived1 :: c) + c%value = a%value - b%value + end function sub_derived1 + + subroutine show_derived1(this) + class(derived1), intent(in) :: this + print *, "Derived1 with value: ", this%value + end subroutine show_derived1 + + subroutine show_derived2(this) + class(derived2), intent(in) :: this + print *, "Derived2 with value: ", this%value + end subroutine show_derived2 + +end module mod + +program main + use mod + + implicit none + + class(base), allocatable :: obj1, obj2, result + + class(derived1), allocatable :: obj_d1 + class(base), allocatable :: obj_d2 + + allocate (derived1 :: obj1) + obj1%value = 5 + allocate (derived2 :: obj2) + obj2%value = 7 + allocate (derived1 :: obj_d1) + obj_d1%value = 10 + allocate (derived1 :: obj_d2) + obj_d2%value = 10 + + result = (obj1 + obj2) - obj1 + call result%show() + + result = obj_d1 + obj_d2 + call result%show() + +end program main + diff --git a/cgfcollector/test/simple/operator3/output.json b/cgfcollector/test/simple/operator3/output.json new file mode 100644 index 00000000..23f95863 --- /dev/null +++ b/cgfcollector/test/simple/operator3/output.json @@ -0,0 +1,75 @@ +{ + "_CG": { + "edges": [ + [[3955331171395186654, 959250268827548101], null], + [[3955331171395186654, 12628145085038494357], null], + [[3955331171395186654, 15985040955282811027], null], + [[3955331171395186654, 8300338079480834402], null], + [[3955331171395186654, 5335457571921960532], null] + ], + "nodes": [ + [ + 3955331171395186654, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 959250268827548101, + { + "functionName": "_QMmodPshow_derived2", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 12628145085038494357, + { + "functionName": "_QMmodPshow_derived1", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 5335457571921960532, + { + "functionName": "_QMmodPsub_derived1", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 8300338079480834402, + { + "functionName": "_QMmodPadd_base", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 15985040955282811027, + { + "functionName": "_QMmodPs", + "hasBody": false, + "meta": null, + "origin": "main.f90" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "1c4c99974efba03080aa26f17f925be8d6f36f2c", + "version": "0.7" + }, + "version": "3.0" + } +} From 9955ae735e4f5d68892cb6248c03c82ff791c82d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:56 +0100 Subject: [PATCH 058/130] operator: improved interface operators and can now distinguish between unary and binary operators --- cgfcollector/include/ParseTreeVisitor.h | 2 + cgfcollector/src/ParseTreeVisitor.cpp | 58 +++++++++++++------ cgfcollector/test/simple/operator/output.json | 5 +- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 3a064ead..ff26d9aa 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -74,6 +74,8 @@ class ParseTreeVisitor { bool isOperator(const Expr* e); bool compareExprIntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op); + bool isBinaryOperator(const Expr* e); + bool isUnaryOperator(const Expr* e); template const Name* getNameFromClassWithDesignator(const T& t) { diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 02b17ec8..0f2ab451 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -177,10 +177,6 @@ bool ParseTreeVisitor::compareExprIntrinsicOperator(const Expr* expr, const Defi using IO = DefinedOperator::IntrinsicOperator; switch (*op) { - // case IO::UnaryPlus: - // return std::get_if(&expr->u) != nullptr; - // case IO::Negate: - // return std::get_if(&expr->u) != nullptr; case IO::NOT: return std::get_if(&expr->u) != nullptr; case IO::Power: @@ -190,9 +186,11 @@ bool ParseTreeVisitor::compareExprIntrinsicOperator(const Expr* expr, const Defi case IO::Divide: return std::get_if(&expr->u) != nullptr; case IO::Add: - return std::get_if(&expr->u) != nullptr; + return std::get_if(&expr->u) != nullptr || + std::get_if(&expr->u) != nullptr; // UnaryPlus also uses + case IO::Subtract: - return std::get_if(&expr->u) != nullptr; + return std::get_if(&expr->u) != nullptr || + std::get_if(&expr->u) != nullptr; // Negate also uses - case IO::Concat: return std::get_if(&expr->u) != nullptr; case IO::LT: @@ -220,6 +218,22 @@ bool ParseTreeVisitor::compareExprIntrinsicOperator(const Expr* expr, const Defi } } +bool ParseTreeVisitor::isBinaryOperator(const Expr* e) { + if (!e) + return false; + + return holds_any_ofu), Expr::Power, Expr::Multiply, Expr::Divide, Expr::Add, Expr::Subtract, + Expr::Concat, Expr::LT, Expr::LE, Expr::EQ, Expr::NE, Expr::GE, Expr::GT, Expr::AND, Expr::OR, + Expr::EQV, Expr::NEQV, Expr::DefinedBinary>(e->u); +} + +bool ParseTreeVisitor::isUnaryOperator(const Expr* e) { + if (!e) + return false; + + return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::DefinedUnary>(e->u); +} + const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { auto* type = symbol->GetType(); if (!type) @@ -726,33 +740,39 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } for (auto e : exprStmtWithOps) { - // search in interface operators TODO improve this just add everything in the interface instead of comparing - // types - auto it = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& p) { - if (auto* intrinsicOp = std::get_if(p.first)) { + // search in interfaceOperators first before search in derived types + auto interfaceOp = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& op) { + if (const auto* intrinsicOp = std::get_if(op.first)) { return compareExprIntrinsicOperator(e, intrinsicOp); - } - if (auto* definedOpName = std::get_if(p.first)) { + } else if (const auto* definedOpName = std::get_if(op.first)) { if (auto* definedUnary = std::get_if(&e->u)) { auto* exprOpName = &std::get(definedUnary->t); return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); - } - if (auto* definedBinary = std::get_if(&e->u)) { + } else if (auto* definedBinary = std::get_if(&e->u)) { auto* exprOpName = &std::get(definedBinary->t); return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); } - return false; } return false; }); - if (it != interfaceOperators.end()) { - // iterate over all procedures in interface (this vastly overestimates the calls). TODO: look into procdure - // params to identify only the onces that could be called. - for (auto* sym : it->second) { + if (interfaceOp != interfaceOperators.end()) { + bool isUnaryOp = isUnaryOperator(e); + bool isBinaryOp = isBinaryOperator(e); + + for (auto* sym : interfaceOp->second) { // skip self calls if (mangleName(*sym) == mangleName(*functionSymbols.back())) continue; + // if unary only add potential unary operators. Same for binary operators. + auto functionIt = + std::find_if(functions.begin(), functions.end(), [&](const auto& f) { return f.symbol == sym; }); + if (functionIt != functions.end()) { + if ((!isUnaryOp || functionIt->dummyArgs.size() != 1) && (!isBinaryOp || functionIt->dummyArgs.size() != 2)) { + continue; + } + } + edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*sym)); al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), diff --git a/cgfcollector/test/simple/operator/output.json b/cgfcollector/test/simple/operator/output.json index 67b0bae1..aa949d2f 100644 --- a/cgfcollector/test/simple/operator/output.json +++ b/cgfcollector/test/simple/operator/output.json @@ -1,11 +1,10 @@ { "_CG": { "edges": [ - [[7572781303902459746, 2925204865112094556], null], [[7572781303902459746, 11442313694358728277], null], - [[10361574024262302863, 2925204865112094556], null], [[10361574024262302863, 11442313694358728277], null], [[11460580497354340053, 16784127278057253920], null], + [[16014592354400816154, 2925204865112094556], null], [[1923978352858283650, 12300079433521964041], null], [[11278138169644782137, 13832393172155657730], null], [[479400711443056833, 15223348241347075203], null], @@ -195,7 +194,7 @@ "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "d24b04d4dec70317adaeb660996546c13cf4a4d2", + "sha": "fea89c328b25f1f2391c88484d5d3b4f413db4b0", "version": "0.7" }, "version": "3.0" From 88a522e3723fe70e27fdcb50a21aa8d4f0b07a7b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:56 +0100 Subject: [PATCH 059/130] remove typedef we are in c++ --- cgfcollector/include/ParseTreeVisitor.h | 44 ++++++++++++------------- cgfcollector/src/ParseTreeVisitor.cpp | 27 ++++++++------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index ff26d9aa..7c982fe3 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -9,45 +9,45 @@ using namespace Fortran::semantics; using namespace Fortran::common; using Fortran::lower::mangle::mangleName; -typedef std::pair edge; // (caller, callee) +using edge = std::pair; // (caller, callee) -typedef struct type { +struct type { Symbol* type; Symbol* extendsFrom; std::vector> procedures; // name(symbol) => optname(symbol) std::vector> operators; // operator => name(symbol) -} type_t; +}; -typedef struct trackedVar { +struct trackedVar { Symbol* var; Symbol* procedure; // procedure in which var was defined bool hasBeenInitialized = false; bool addFinalizers = false; -} trackedVar_t; +}; -typedef struct function { - typedef struct dummyArg { +struct function { + struct dummyArg { Symbol* symbol; bool hasBeenInitialized = false; - } dummyArg_t; + }; Symbol* symbol; // function - std::vector dummyArgs; -} function_t; + std::vector dummyArgs; +}; -typedef struct potentialFinalizer { +struct potentialFinalizer { std::size_t argPos; std::string procedureCalled; std::vector finalizerEdges; -} potentialFinalizer_t; +}; class ParseTreeVisitor { public: ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; std::vector& getEdges() { return edges; } - std::vector& getPotentialFinalizers() { return potentialFinalizers; } - std::vector& getFunctions() { return functions; } + std::vector& getPotentialFinalizers() { return potentialFinalizers; } + std::vector& getFunctions() { return functions; } template void handleFuncSubStmt(const T& stmt); @@ -56,11 +56,11 @@ class ParseTreeVisitor { void handleTrackedVars(); // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. - std::vector findTypeWithDerivedTypes(const Symbol* typeSymbol); + std::vector findTypeWithDerivedTypes(const Symbol* typeSymbol); // this function searches with typeSymbol for a type in types vector and adds edges for procedures that matches // procedureSymbol. And also adds edges from types that extends from typeSymbol. - void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); + void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); void addEdgesForFinalizers(const Symbol* typeSymbol); void addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol); @@ -91,10 +91,10 @@ class ParseTreeVisitor { const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); - trackedVar_t* getTrackedVarFromSourceName(SourceName sourceName); + trackedVar* getTrackedVarFromSourceName(SourceName sourceName); void handleTrackedVarAssignment(SourceName sourceName); - void addTrackedVar(trackedVar_t var); + void addTrackedVar(trackedVar var); void removeTrackedVars(Symbol* procedureSymbol); template @@ -173,7 +173,7 @@ class ParseTreeVisitor { std::vector functionSymbols; std::vector> functionDummyArgs; - std::vector types; + std::vector types; std::vector*, std::vector>> @@ -182,11 +182,11 @@ class ParseTreeVisitor { std::vector exprStmtWithOps; // mainly used for destructor handling - std::vector trackedVars; + std::vector trackedVars; AL* al = AL::getInstance(); - std::vector functions; + std::vector functions; - std::vector potentialFinalizers; + std::vector potentialFinalizers; }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 0f2ab451..4bdc1fdb 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -8,7 +8,7 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { functionSymbols.emplace_back(sym); functionDummyArgs.emplace_back(std::vector()); cg->insert(std::make_unique(mangleName(*sym), currentFileName, false, false)); - functions.push_back({sym, std::vector()}); + functions.push_back({sym, std::vector()}); al->debug("Add node: {} ({})", mangleName(*sym), fmt::ptr(sym)); } @@ -64,11 +64,11 @@ void ParseTreeVisitor::handleTrackedVars() { removeTrackedVars(functionSymbols.back()); } -std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeSymbol) { - std::vector typeWithDerived; +std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeSymbol) { + std::vector typeWithDerived; auto findTypeIt = - std::find_if(types.begin(), types.end(), [&typeSymbol](const type_t& t) { return t.type == typeSymbol; }); + std::find_if(types.begin(), types.end(), [&typeSymbol](const type& t) { return t.type == typeSymbol; }); if (findTypeIt == types.end()) return typeWithDerived; @@ -87,7 +87,7 @@ std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typ auto* currentExtendsFrom = (*findTypeIt).extendsFrom; while (currentExtendsFrom) { auto currentType = std::find_if(types.begin(), types.end(), - [¤tExtendsFrom](const type_t& t) { return t.type == currentExtendsFrom; }); + [¤tExtendsFrom](const type& t) { return t.type == currentExtendsFrom; }); if (currentType == types.end()) { al->error("Error: Types array (extendsFrom) field entry missing."); return typeWithDerived; @@ -106,9 +106,9 @@ std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typ return typeWithDerived; } -void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, +void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol) { - for (type_t t : typeWithDerived) { + for (type t : typeWithDerived) { auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&procedureSymbol](const auto& p) { return p.first->name() == procedureSymbol->name(); }); @@ -139,7 +139,7 @@ void ParseTreeVisitor::addEdgesForFinalizers(std::vector* edges, const Sym std::vector> ParseTreeVisitor::getEdgesForFinalizers(const Symbol* typeSymbol) { std::vector> edges; - std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); + std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); for (const auto& type : typeSymbols) { const Symbol* typeSymbol = type.type; @@ -247,7 +247,7 @@ const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { return typeSymbol; } -trackedVar_t* ParseTreeVisitor::getTrackedVarFromSourceName(SourceName sourceName) { +trackedVar* ParseTreeVisitor::getTrackedVarFromSourceName(SourceName sourceName) { auto anyTrackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { return t.var->name() == sourceName; }); if (anyTrackedVarIt == trackedVars.end()) @@ -274,9 +274,8 @@ void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { al->debug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); } -void ParseTreeVisitor::addTrackedVar(trackedVar_t var) { - auto it = - std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar_t& t) { return t.var == var.var; }); +void ParseTreeVisitor::addTrackedVar(trackedVar var) { + auto it = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { return t.var == var.var; }); if (it != trackedVars.end()) { // update info it->addFinalizers = var.addFinalizers; @@ -291,7 +290,7 @@ void ParseTreeVisitor::addTrackedVar(trackedVar_t var) { void ParseTreeVisitor::removeTrackedVars(Symbol* procedureSymbol) { trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), - [&](const trackedVar_t& t) { return t.procedure == procedureSymbol; }), + [&](const trackedVar& t) { return t.procedure == procedureSymbol; }), trackedVars.end()); } @@ -796,7 +795,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { auto funcSymbol = opIt->second; bool skipSelfCall = false; - for (type_t t : typeWithDerived) { + for (type t : typeWithDerived) { auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); if (procIt == t.procedures.end()) From c9ba3702586982831a2c6446ad13f6cb0b0e1e88 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:57 +0100 Subject: [PATCH 060/130] add cmake_base.txt to .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 70151b97..241f49c8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ cmake-build*/ spack.yaml spack.lock .spack-env/ + +cgfcollector/test/cmake_base.txt From e3a131760b29ffec89073dbbddd7da664644655b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:57 +0100 Subject: [PATCH 061/130] comments --- cgfcollector/src/ParseTreeVisitor.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 4bdc1fdb..64c63c5f 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -522,7 +522,6 @@ void ParseTreeVisitor::Post(const Call& c) { void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { if (functionSymbols.empty()) { - // type declaration inside a module TODO: return; } @@ -763,7 +762,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (mangleName(*sym) == mangleName(*functionSymbols.back())) continue; - // if unary only add potential unary operators. Same for binary operators. + // if unary, add potential unary operators. Same for binary operators. auto functionIt = std::find_if(functions.begin(), functions.end(), [&](const auto& f) { return f.symbol == sym; }); if (functionIt != functions.end()) { From 703159fadfa82bc42956f4db6dbfd33a889be9af Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:57 +0100 Subject: [PATCH 062/130] improved CMakeLists.txt to make it more readable and now using different vars for install and build dirs --- cgfcollector/CMakeLists.txt | 110 ++++++++++-------- cgfcollector/test/multi/cmake_base.txt | 6 - cgfcollector/test/multi/cmake_base.txt.in | 5 + cgfcollector/tools/cgfcollector_wrapper.sh.in | 2 - cgfcollector/tools/test_runner.sh.in | 5 + 5 files changed, 74 insertions(+), 54 deletions(-) delete mode 100644 cgfcollector/test/multi/cmake_base.txt create mode 100644 cgfcollector/test/multi/cmake_base.txt.in diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 7a4e66b3..69a335ed 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -21,8 +21,6 @@ target_precompile_headers( target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) -# target_compile_options(${PROJECT_NAME} PRIVATE -g) - install( TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} @@ -57,55 +55,75 @@ install( ARCHIVE DESTINATION bin ) -# generate wrapper script with build dir for easy of use during development -set(FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/tools/cgfcollector_wrapper.sh.in") -set(FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") +# generate wrapper scripts -# FCOLLECTOR_WRAPPER_EXPORT_LINE for debuging -option( - FCOLLECTOR_ADD_BUILD_TO_LIB_PATH - "Add build dir to LD_LIBRARY_PATH in wrapper script" - OFF +function( + configure_file_and_install + TEMPLATE + OUTPUT_BASENAME + DESTINATION ) -set(FCOLLECTOR_WRAPPER_EXPORT_LINE "") -if(FCOLLECTOR_ADD_BUILD_TO_LIB_PATH) - set(FCOLLECTOR_WRAPPER_EXPORT_LINE "export LD_LIBRARY_PATH=\"${CMAKE_CURRENT_BINARY_DIR}:\$LD_LIBRARY_PATH\"") -endif() - -set(FCOLLECTOR_FILE_NAME "lib${PROJECT_NAME}.so") - -configure_file( - ${FCOLLECTOR_WRAPPER_SCRIPT_TEMPLATE} - ${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT} - @ONLY + set(BUILD_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_BASENAME}") + set(INSTALL_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_BASENAME}.install") + + # build-time values for development + set(FCOLLECTOR_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.so") + set(FCOLLECTOR_CG_COMPARE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-cgCompare") + set(FCOLLECTOR_WRAPPER "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") + set(FCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") + set(CGMERGE2_EXECUTABLE "${CMAKE_BINARY_DIR}/tools/cgmerge2/cgmerge2") + + configure_file( + "${TEMPLATE}" + "${BUILD_OUTPUT}" + @ONLY + ) + + if(DESTINATION + STREQUAL + "" + ) + return() + endif() + + # install-time values + set(FCOLLECTOR_FILE_NAME "${CMAKE_INSTALL_PREFIX}/lib/lib${PROJECT_NAME}.so") + set(FCOLLECTOR_CG_COMPARE "${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}-cgCompare") + set(FCOLLECTOR_WRAPPER "${CMAKE_INSTALL_PREFIX}/bin/cgfcollector_wrapper.sh") + set(FCOLLECTOR_TEST_CASES_DIR "") + + configure_file( + "${TEMPLATE}" + "${INSTALL_OUTPUT}" + @ONLY + ) + + install( + PROGRAMS "${INSTALL_OUTPUT}" + DESTINATION "${DESTINATION}" + RENAME "${OUTPUT_BASENAME}" + ) +endfunction() + +configure_file_and_install( + "${CMAKE_CURRENT_SOURCE_DIR}/tools/cgfcollector_wrapper.sh.in" + "cgfcollector_wrapper.sh" + "bin" ) -install(PROGRAMS ${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT} DESTINATION bin) - -set(FCOLLECTOR_TEST_SCRIPT_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/tools/test_runner.sh.in") -set(FCOLLECTOR_TEST_SCRIPT_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/test_runner.sh") -set(FCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") -set(FCOLLECTOR_WRAPPER "${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT}") -set(FCOLLECTOR_CG_COMPARE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-cgCompare") - -configure_file( - ${FCOLLECTOR_TEST_SCRIPT_TEMPLATE} - ${FCOLLECTOR_TEST_SCRIPT_OUTPUT} - @ONLY +configure_file_and_install( + "${CMAKE_CURRENT_SOURCE_DIR}/tools/test_runner.sh.in" + "test_runner.sh" + "" ) -install(PROGRAMS ${FCOLLECTOR_TEST_SCRIPT_OUTPUT} DESTINATION bin) - +configure_file_and_install( + "${CMAKE_CURRENT_SOURCE_DIR}/test/multi/cmake_base.txt.in" + "cmake_base.txt" + "" +) file( - GENERATE - OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/test/cmake_base.txt" - CONTENT - "\ -# This file is generated by CMake. -set(CMAKE_Fortran_COMPILER \"${FCOLLECTOR_WRAPPER_SCRIPT_OUTPUT}\") -set(CMAKE_Fortran_FLAGS \"\") -set(CMAKE_Fortran_COMPILE_OBJECT \" -dot -o \") -set(CMAKE_Fortran_LINK_EXECUTABLE \"$ \") -set(CMAKE_EXECUTABLE_SUFFIX .json) -" + RENAME + "${CMAKE_CURRENT_BINARY_DIR}/cmake_base.txt" + "${CMAKE_CURRENT_SOURCE_DIR}/test/multi/cmake_base.txt" ) diff --git a/cgfcollector/test/multi/cmake_base.txt b/cgfcollector/test/multi/cmake_base.txt deleted file mode 100644 index 21108e4a..00000000 --- a/cgfcollector/test/multi/cmake_base.txt +++ /dev/null @@ -1,6 +0,0 @@ -# This file is generated by CMake. -set(CMAKE_Fortran_COMPILER "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/cgfcollector/cgfcollector_wrapper.sh") -set(CMAKE_Fortran_FLAGS "") -set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") -set(CMAKE_Fortran_LINK_EXECUTABLE "/home/marek/doc/studium/sem6/bachelorarbeit/metacg/build/tools/cgmerge2/cgmerge2 ") -set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/test/multi/cmake_base.txt.in b/cgfcollector/test/multi/cmake_base.txt.in new file mode 100644 index 00000000..d0d45e25 --- /dev/null +++ b/cgfcollector/test/multi/cmake_base.txt.in @@ -0,0 +1,5 @@ +set(CMAKE_Fortran_COMPILER "@FCOLLECTOR_WRAPPER@") +set(CMAKE_Fortran_FLAGS "") +set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") +set(CMAKE_Fortran_LINK_EXECUTABLE "@CGMERGE2_EXECUTABLE@ ") +set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index 7632fec8..0762fe89 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -2,8 +2,6 @@ flang_bin="flang-new" -@FCOLLECTOR_WRAPPER_EXPORT_LINE@ - flang_args=("$@") plugin_name="genCG" diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index caedc6cd..c3708dfb 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -2,6 +2,11 @@ test_case_dir="@FCOLLECTOR_TEST_CASES_DIR@" +if ! [ -d "$test_case_dir" ]; then + echo "Error: Test case directory '$test_case_dir' does not exist." + exit 1 +fi + scriptdir="$(cd "$(dirname "$0")" && pwd -P)" out_dir="$scriptdir/out" From 2219eac1ee66b06c017d9f6ecc873d08b4dad940 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 063/130] add test case with fortdepend to generate fortran module dependencies --- cgfcollector/CMakeLists.txt | 11 +++ .../test/multi/fortdepend_deps/Makefile | 35 +++++++++ .../test/multi/fortdepend_deps/main.f90 | 10 +++ .../test/multi/fortdepend_deps/module.f90 | 20 +++++ .../test/multi/fortdepend_deps/module0_5.f90 | 13 ++++ .../test/multi/fortdepend_deps/module2.f90 | 10 +++ .../test/multi/fortdepend_deps/module3.f90 | 13 ++++ .../test/multi/fortdepend_deps/output.json | 74 +++++++++++++++++++ cgfcollector/test/multi/make_base.in | 3 + cgfcollector/tools/test_runner.sh.in | 17 +++++ 10 files changed, 206 insertions(+) create mode 100644 cgfcollector/test/multi/fortdepend_deps/Makefile create mode 100644 cgfcollector/test/multi/fortdepend_deps/main.f90 create mode 100644 cgfcollector/test/multi/fortdepend_deps/module.f90 create mode 100644 cgfcollector/test/multi/fortdepend_deps/module0_5.f90 create mode 100644 cgfcollector/test/multi/fortdepend_deps/module2.f90 create mode 100644 cgfcollector/test/multi/fortdepend_deps/module3.f90 create mode 100644 cgfcollector/test/multi/fortdepend_deps/output.json create mode 100644 cgfcollector/test/multi/make_base.in diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 69a335ed..7e18ebd3 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -127,3 +127,14 @@ file( "${CMAKE_CURRENT_BINARY_DIR}/cmake_base.txt" "${CMAKE_CURRENT_SOURCE_DIR}/test/multi/cmake_base.txt" ) + +configure_file_and_install( + "${CMAKE_CURRENT_SOURCE_DIR}/test/multi/make_base.in" + "make_base" + "" +) +file( + RENAME + "${CMAKE_CURRENT_BINARY_DIR}/make_base" + "${CMAKE_CURRENT_SOURCE_DIR}/test/multi/make_base" +) diff --git a/cgfcollector/test/multi/fortdepend_deps/Makefile b/cgfcollector/test/multi/fortdepend_deps/Makefile new file mode 100644 index 00000000..020b865d --- /dev/null +++ b/cgfcollector/test/multi/fortdepend_deps/Makefile @@ -0,0 +1,35 @@ +include ../make_base + +BUILD_DIR = build +DEP_FILE = $(BUILD_DIR)/Makefile.dep +TARGET = $(BUILD_DIR)/test + +SRCS := $(wildcard *.f90) +OBJS := $(SRCS:.f90=.o) +OBJS := $(addprefix $(BUILD_DIR)/, $(OBJS)) +OBJS_JSON := $(SRCS:.f90=.o.json) +OBJS_JSON := $(addprefix $(BUILD_DIR)/, $(OBJS_JSON)) + +all: $(TARGET) $(DEP_FILE) + +$(TARGET): $(OBJS) + $(LD) $(TARGET).json $(OBJS_JSON) + +$(BUILD_DIR)/%.o: %.f90 | $(BUILD_DIR) + $(FC) $< -o $@.json -module-dir $(BUILD_DIR) + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +.PHONY: depend +depend: $(DEP_FILE) + +$(DEP_FILE): $(SRCS) | $(BUILD_DIR) + @echo "Making dependencies!" + $(MAKEDEPEND) --build $(BUILD_DIR) -w -o $(DEP_FILE) -f $(SRCS) + +.PHONY: clean +clean: + rm -rf $(BUILD_DIR) + +include $(DEP_FILE) diff --git a/cgfcollector/test/multi/fortdepend_deps/main.f90 b/cgfcollector/test/multi/fortdepend_deps/main.f90 new file mode 100644 index 00000000..def16537 --- /dev/null +++ b/cgfcollector/test/multi/fortdepend_deps/main.f90 @@ -0,0 +1,10 @@ +program main + use my_module, only: my_subroutine + use module3 + + implicit none + + call my_subroutine(5) + +end program main + diff --git a/cgfcollector/test/multi/fortdepend_deps/module.f90 b/cgfcollector/test/multi/fortdepend_deps/module.f90 new file mode 100644 index 00000000..2a1b0134 --- /dev/null +++ b/cgfcollector/test/multi/fortdepend_deps/module.f90 @@ -0,0 +1,20 @@ +module my_module + use module0_5 + + implicit none + +contains + subroutine subsub() + integer :: n + end subroutine subsub + + subroutine my_subroutine(n) + integer, intent(in) :: n + + write (*, *) n + + call func() + + end subroutine my_subroutine +end module my_module + diff --git a/cgfcollector/test/multi/fortdepend_deps/module0_5.f90 b/cgfcollector/test/multi/fortdepend_deps/module0_5.f90 new file mode 100644 index 00000000..0ff134db --- /dev/null +++ b/cgfcollector/test/multi/fortdepend_deps/module0_5.f90 @@ -0,0 +1,13 @@ +module module0_5 + use module2, only: func2 => func + + implicit none + +contains + subroutine func() + call func2() + print *, "This is module2" + end subroutine func + +end module module0_5 + diff --git a/cgfcollector/test/multi/fortdepend_deps/module2.f90 b/cgfcollector/test/multi/fortdepend_deps/module2.f90 new file mode 100644 index 00000000..9c7af856 --- /dev/null +++ b/cgfcollector/test/multi/fortdepend_deps/module2.f90 @@ -0,0 +1,10 @@ +module module2 + + implicit none +contains + subroutine func() + print *, "This is module2" + end subroutine func + +end module module2 + diff --git a/cgfcollector/test/multi/fortdepend_deps/module3.f90 b/cgfcollector/test/multi/fortdepend_deps/module3.f90 new file mode 100644 index 00000000..1abff39f --- /dev/null +++ b/cgfcollector/test/multi/fortdepend_deps/module3.f90 @@ -0,0 +1,13 @@ +module module3 + use module2, only: func2 => func + + implicit none + +contains + subroutine func() + call func2() + print *, "This is module3" + end subroutine func + +end module module3 + diff --git a/cgfcollector/test/multi/fortdepend_deps/output.json b/cgfcollector/test/multi/fortdepend_deps/output.json new file mode 100644 index 00000000..681188f1 --- /dev/null +++ b/cgfcollector/test/multi/fortdepend_deps/output.json @@ -0,0 +1,74 @@ +{ + "_CG": { + "edges": [ + [[1083670836535463101, 11444295161927196599], null], + [[11444295161927196599, 9530012462595688619], null], + [[2308780131476637806, 1083670836535463101], null], + [[6753505644903624250, 9530012462595688619], null] + ], + "nodes": [ + [ + 9603846864033343388, + { + "functionName": "_QMmy_modulePsubsub", + "hasBody": true, + "meta": null, + "origin": "module.f90" + } + ], + [ + 11444295161927196599, + { + "functionName": "_QMmodule0_5Pfunc", + "hasBody": true, + "meta": null, + "origin": "module0_5.f90" + } + ], + [ + 1083670836535463101, + { + "functionName": "_QMmy_modulePmy_subroutine", + "hasBody": true, + "meta": null, + "origin": "unknownOrigin" + } + ], + [ + 2308780131476637806, + { + "functionName": "_QQmain", + "hasBody": true, + "meta": null, + "origin": "main.f90" + } + ], + [ + 6753505644903624250, + { + "functionName": "_QMmodule3Pfunc", + "hasBody": true, + "meta": null, + "origin": "module3.f90" + } + ], + [ + 9530012462595688619, + { + "functionName": "_QMmodule2Pfunc", + "hasBody": true, + "meta": null, + "origin": "unknownOrigin" + } + ] + ] + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "d76681a62ca400b6d9ff4a24bde16f373326f998", + "version": "0.7" + }, + "version": "3.0" + } +} diff --git a/cgfcollector/test/multi/make_base.in b/cgfcollector/test/multi/make_base.in new file mode 100644 index 00000000..49a8ac0b --- /dev/null +++ b/cgfcollector/test/multi/make_base.in @@ -0,0 +1,3 @@ +MAKEDEPEND = fortdepend +FC = @FCOLLECTOR_WRAPPER@ +LD = @CGMERGE2_EXECUTABLE@ diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index c3708dfb..830501c2 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -73,6 +73,23 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu continue } + rm -rf "$tmp_dir" + elif find "$dir" -maxdepth 1 -name 'Makefile' | grep -q .; then + # make managed with fortdepend test + tmp_dir="$(mktemp -d)" + + { + cd "$dir" + make BUILD_DIR="$tmp_dir" + find . -maxdepth 1 -name '*.json' | while read -r file; do + cp "$file" "$out_dir/$test_case_name.json" + done + } || { + echo "Error: could not generate CG" + rm -rf "$tmp_dir" + continue + } + rm -rf "$tmp_dir" else # not cmake managed test From 08c08b0aa78e48136e8cec037d9d820ff7fccb34 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 064/130] add fortran test file config for cmake and make to .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 241f49c8..f0f18e4c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ spack.yaml spack.lock .spack-env/ -cgfcollector/test/cmake_base.txt +cgfcollector/test/multi/cmake_base.txt +cgfcollector/test/multi/make_base From 5795fafb92e7eda3c003617b4a9cb96cc57f1823 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 065/130] add script to act as normal compiler that also generates CG --- cgfcollector/CMakeLists.txt | 6 ++++++ cgfcollector/tools/cgfcollector_comp_wrapper.sh.in | 11 +++++++++++ 2 files changed, 17 insertions(+) create mode 100755 cgfcollector/tools/cgfcollector_comp_wrapper.sh.in diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 7e18ebd3..d133b407 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -111,6 +111,12 @@ configure_file_and_install( "bin" ) +configure_file_and_install( + "${CMAKE_CURRENT_SOURCE_DIR}/tools/cgfcollector_comp_wrapper.sh.in" + "cgfcollector_comp_wrapper.sh" + "bin" +) + configure_file_and_install( "${CMAKE_CURRENT_SOURCE_DIR}/tools/test_runner.sh.in" "test_runner.sh" diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in new file mode 100755 index 00000000..848eedd8 --- /dev/null +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -0,0 +1,11 @@ +#!/bin/bash + +flang_bin="flang-new" + +if ! command -v "$flang_bin" &>/dev/null; then + echo "Error: $flang_bin not found in PATH." + exit 1 +fi + +$flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" +$flang_bin "$@" From 4976a16ae440843d7abf9c7b928e01e724630a94 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 066/130] updated README.md --- cgfcollector/README.md | 49 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/cgfcollector/README.md b/cgfcollector/README.md index 86e9e53a..b2eb5cc3 100644 --- a/cgfcollector/README.md +++ b/cgfcollector/README.md @@ -2,9 +2,31 @@ ## Usage -`cgfcollector_wrapper.sh` convenience wrapper to run parse plugin. +The parse plugin is compiled into a dynamic library and can be run with the +Flang compiler like so: -### Generate a callgraph from a CMake project +`flang -fc1 -load "build/cgfcollector/libfcollector.so" -plugin "genCG"` + +There are two kind of plugins: + +- `genCG`: generates a call graphs in the MetaCG json format. +- `genCGwithDot`: like `genCG` but also generate a `.dot` file. Mostly used for debugging. + +Additionally these other tools are included: + +- `cgfcollector_wrapper.sh`: convenience wrapper to run parse plugin. +- `cgfcollector_comp_wrapper.sh`: acts like a normal Flang compiler but also generates a call graph. +- `cgCompare.cpp`: compares two given call graphs. +- `test_runner.sh`: run tests. + +## How to build + +To build the cgfcollector the option `METACG_BUILD_CGFCOLLECTOR` must be set to +`ON`. + +## Generate a call graph + +### from a CMake project Paste this into your CMakeLists.txt. @@ -19,18 +41,31 @@ set(CMAKE_EXECUTABLE_SUFFIX .json) This will hook into the cmake build process and generate a callgraph instead of an executable. +An example can be found in `test/multi/deps`. + +### from other projects + +Use `cgfcollector_comp_wrapper.sh` or `cgfcollector_wrapper.sh` to hook into the +build process of your favorite tool and use the `cgmerge2` utility to merge the +partial generated call graphs. + +An example can be found in `test/multi/fortdepend_deps`. This example uses +fortdepend to generate a module dependency list. If your build system already +generates such a list and executes the compiler on the files in the correct order +you probably don't need this. + ## Running test run `test_runner.sh` -## Debug +NOTE: The test `test/multi/fortdepend_deps` has a dependency on [fortdepend](https://fortdepend.readthedocs.io/en/latest/) + +## Debugging + +Set `CUSTOM_DEBUG` as an environment variable to get more debug output. ### print parse tree ```sh flang-new -fc1 -fdebug-dump-parse-tree file.f90 ``` - -### Grammar - -[Grammar](https://flang.llvm.org/docs/f2018-grammar.html) From 814bbf2224179ac4b1238d8976f21a7e23d723ba Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:59 +0100 Subject: [PATCH 067/130] add partial test examples for function pointer and unlimited polymorphism --- .../test/simple/function_pointer/main.f90 | 36 ++++++++++ .../simple/unlimited_polymorphism/main.f90 | 71 +++++++++++++++++++ 2 files changed, 107 insertions(+) create mode 100644 cgfcollector/test/simple/function_pointer/main.f90 create mode 100644 cgfcollector/test/simple/unlimited_polymorphism/main.f90 diff --git a/cgfcollector/test/simple/function_pointer/main.f90 b/cgfcollector/test/simple/function_pointer/main.f90 new file mode 100644 index 00000000..a08975b0 --- /dev/null +++ b/cgfcollector/test/simple/function_pointer/main.f90 @@ -0,0 +1,36 @@ +program main + implicit none + + abstract interface + function func(x) result(y) + real, intent(in) :: x + end function func + end interface + + procedure(func), pointer :: func_ptr => null() + real :: result + + result = 0.0 + func_ptr => square + result = func_ptr(2.0) + print *, "Square of 2.0 is: ", result + + func_ptr => cube + result = func_ptr(2.0) + print *, "Cube of 2.0 is: ", result + +contains + + function square(x) result(y) + real, intent(in) :: x + real :: y + y = x*x + end function square + + function cube(x) result(y) + real, intent(in) :: x + real :: y + y = x*x*x + end function cube + +end program main diff --git a/cgfcollector/test/simple/unlimited_polymorphism/main.f90 b/cgfcollector/test/simple/unlimited_polymorphism/main.f90 new file mode 100644 index 00000000..58eaea4d --- /dev/null +++ b/cgfcollector/test/simple/unlimited_polymorphism/main.f90 @@ -0,0 +1,71 @@ +module mod + + implicit none + + type :: my_type + integer :: a + real :: b + contains + final :: finalize_my_type + procedure :: print_stuff + end type my_type + + type :: my_type2 + contains + procedure :: print_stuff => print_stuff2 + final :: finalize_my_type2 + end type my_type2 + +contains + + subroutine finalize_my_type(this) + type(my_type), intent(inout) :: this + print *, "Finalizing my_type" + end subroutine finalize_my_type + + subroutine finalize_my_type2(this) + type(my_type2), intent(inout) :: this + print *, "Finalizing my_type2" + end subroutine finalize_my_type2 + + subroutine print_stuff(this) + class(my_type), intent(in) :: this + print *, "stuff" + end subroutine print_stuff + + subroutine print_stuff2(this) + class(my_type2), intent(in) :: this + print *, "stuff" + end subroutine print_stuff2 +end module mod + +program main + use mod + + implicit none + + call func() + +contains + subroutine func() + class(*), allocatable :: obj + + ! allocate (obj, mold=my_type2()) + ! deallocate (obj) + ! allocate (my_type2 :: obj) + ! deallocate (obj) + ! allocate (obj, source=my_type2()) + + ! can be assigned with allocate, move_alloc, = operator and in function arguments + obj = my_type(123, 12) + + select type (obj) + type is (my_type) + call obj%print_stuff() + print *, 'Object is of type my_type2' + class default + print *, 'Object is of an unknown type' + end select + end subroutine func +end program main + From 9cdd2dc077956a3703a51a493720e3fd25928bf8 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:59 +0100 Subject: [PATCH 068/130] update to new metacg version --- cgfcollector/include/AL.h | 2 +- cgfcollector/src/ParseTreeVisitor.cpp | 8 ++++---- cgfcollector/src/main.cpp | 12 ++++-------- cgfcollector/tools/cgCompare.cpp | 10 +++++----- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/cgfcollector/include/AL.h b/cgfcollector/include/AL.h index 74f25d6f..1ee2fad0 100644 --- a/cgfcollector/include/AL.h +++ b/cgfcollector/include/AL.h @@ -9,7 +9,7 @@ struct fmt::formatter { constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); } template - auto format(const Fortran::parser::CharBlock& cb, FormatContext& ctx) { + auto format(const Fortran::parser::CharBlock& cb, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "{}", std::string_view(cb.begin(), cb.size())); } }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 64c63c5f..b90c7489 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -7,7 +7,7 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { functionSymbols.emplace_back(sym); functionDummyArgs.emplace_back(std::vector()); - cg->insert(std::make_unique(mangleName(*sym), currentFileName, false, false)); + cg->insert(mangleName(*sym), currentFileName, false, false); functions.push_back({sym, std::vector()}); al->debug("Add node: {} ({})", mangleName(*sym), fmt::ptr(sym)); @@ -304,7 +304,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { return true; functionSymbols.emplace_back(maybeStmt->statement.v.symbol); - cg->insert(std::make_unique(mangleName(*functionSymbols.back()), currentFileName, false, false)); + cg->insert(mangleName(*functionSymbols.back()), currentFileName, false, false); al->debug("\nIn main program: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); } @@ -341,7 +341,7 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { if (!inFunctionOrSubroutineSubProgram && !inMainProgram) return; - auto* node = cg->getNode(mangleName(*functionSymbols.back())); + auto* node = cg->getFirstNode(mangleName(*functionSymbols.back())); if (!node) { return; } @@ -356,7 +356,7 @@ void ParseTreeVisitor::Post(const EntryStmt& e) { al->debug("Add Entry point: {} ({})", mangleName(*name->symbol), fmt::ptr(name->symbol)); - cg->insert(std::make_unique(mangleName(*name->symbol), currentFileName, false, true)); + cg->insert(mangleName(*name->symbol), currentFileName, false, true); } void ParseTreeVisitor::Post(const FunctionStmt& f) { diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index d25ac18a..e36718fb 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -39,12 +39,8 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { // add edges for (auto edge : visitor.getEdges()) { - auto* callerNode = cg->getOrInsertNode(edge.first); - auto* calleeNode = cg->getOrInsertNode(edge.second); - if (!calleeNode || !callerNode) { - llvm::outs() << "No nodes found for edge: " << edge.first << " -> " << edge.second << "\n"; - continue; - } + const auto& callerNode = cg->getOrInsertNode(edge.first); + const auto& calleeNode = cg->getOrInsertNode(edge.second); cg->addEdge(callerNode, calleeNode); } @@ -53,9 +49,9 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { mcgManager.resetManager(); mcgManager.addToManagedGraphs("test", std::move(cg), true); - mcgManager.mergeIntoActiveGraph(); + mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); - auto mcgWriter = metacg::io::createWriter(3); + auto mcgWriter = metacg::io::createWriter(4); if (!mcgWriter) { llvm::errs() << "Unable to create a writer\n"; return; diff --git a/cgfcollector/tools/cgCompare.cpp b/cgfcollector/tools/cgCompare.cpp index c8ad1113..4c7898f0 100644 --- a/cgfcollector/tools/cgCompare.cpp +++ b/cgfcollector/tools/cgCompare.cpp @@ -16,23 +16,23 @@ bool endsMatch(const std::string& a, const std::string& b) { static bool compareNodesAndEdges(const metacg::Callgraph* cg1, const metacg::Callgraph* cg2) { bool equal = true; - for (const auto& [id, node] : cg1->getNodes()) { + for (const auto& node : cg1->getNodes()) { if (!cg2->hasNode(node->getFunctionName())) { errConsole->error("Node {} is missing in the other call graph.", node->getFunctionName()); equal = false; continue; } - const auto& node2 = cg2->getNode(node->getFunctionName()); + const auto& node2 = cg2->getFirstNode(node->getFunctionName()); if (node2->getHasBody() != node->getHasBody()) { errConsole->error("Node {} has different hasBody flags: expected {} got {}", node->getFunctionName(), node2->getHasBody(), node->getHasBody()); equal = false; } - if (!endsMatch(node->getOrigin(), node2->getOrigin())) { + if (!endsMatch(node->getOrigin().value_or(""), node2->getOrigin().value_or(""))) { errConsole->error("Node {} has different origins: expected '{}' got '{}'", node->getFunctionName(), - node2->getOrigin(), node->getOrigin()); + node2->getOrigin().value_or(""), node->getOrigin().value_or("")); equal = false; } } @@ -41,7 +41,7 @@ static bool compareNodesAndEdges(const metacg::Callgraph* cg1, const metacg::Cal auto name1 = cg1->getNode(id.first)->getFunctionName(); auto name2 = cg1->getNode(id.second)->getFunctionName(); - if (!cg2->existEdgeFromTo(name1, name2)) { + if (!cg2->existsAnyEdge(name1, name2)) { errConsole->error("Edge from {} to {} is missing in the other call graph.", name1, name2); equal = false; } From 9a9e217f5e2fa589edd3206c18680015f63b728f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:59 +0100 Subject: [PATCH 069/130] fix test runner for fortdepend test --- cgfcollector/test/multi/fortdepend_deps/Makefile | 2 +- cgfcollector/tools/test_runner.sh.in | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/cgfcollector/test/multi/fortdepend_deps/Makefile b/cgfcollector/test/multi/fortdepend_deps/Makefile index 020b865d..bf186aef 100644 --- a/cgfcollector/test/multi/fortdepend_deps/Makefile +++ b/cgfcollector/test/multi/fortdepend_deps/Makefile @@ -16,7 +16,7 @@ $(TARGET): $(OBJS) $(LD) $(TARGET).json $(OBJS_JSON) $(BUILD_DIR)/%.o: %.f90 | $(BUILD_DIR) - $(FC) $< -o $@.json -module-dir $(BUILD_DIR) + $(FC) -module-dir $(BUILD_DIR) -o $@.json $< $(BUILD_DIR): mkdir -p $(BUILD_DIR) diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 830501c2..f6636795 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -50,7 +50,7 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu echo "Test case: $test_case_name" - if [ ! -f "$output_file" ] || [ ! -s "$output_file" ] || grep -q '{}' "$output_file"; then + if [ ! -s "$output_file" ]; then echo "Skipping empty or missing output file: $output_file" continue fi @@ -59,7 +59,7 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu # cmake managed test tmp_dir="$(mktemp -d)" - { + ( cd "$dir" cmake -S . -B "$tmp_dir" cd "$tmp_dir" @@ -67,7 +67,7 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu find . -maxdepth 1 -name '*.json' | while read -r file; do cp "$file" "$out_dir/$test_case_name.json" done - } || { + ) || { echo "Error: could not generate CG" rm -rf "$tmp_dir" continue @@ -78,13 +78,14 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu # make managed with fortdepend test tmp_dir="$(mktemp -d)" - { + ( cd "$dir" make BUILD_DIR="$tmp_dir" - find . -maxdepth 1 -name '*.json' | while read -r file; do + cd "$tmp_dir" + find . -maxdepth 1 -name '*.json' ! -name '*.o.json' | while read -r file; do cp "$file" "$out_dir/$test_case_name.json" done - } || { + ) || { echo "Error: could not generate CG" rm -rf "$tmp_dir" continue From d00e1c0c138fb76cf93fae0c014a2ba10e35d6b1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:00 +0100 Subject: [PATCH 070/130] update test metacg format version 3 -> 4 --- cgfcollector/test/multi/deps/output.json | 123 +++---- .../test/multi/fortdepend_deps/output.json | 123 +++---- cgfcollector/test/simple/entry/output.json | 90 ++--- cgfcollector/test/simple/final/output.json | 275 ++++++-------- cgfcollector/test/simple/final2/output.json | 189 +++++----- cgfcollector/test/simple/final3/output.json | 209 +++++------ cgfcollector/test/simple/final4/output.json | 70 ++-- cgfcollector/test/simple/final5/output.json | 86 ++--- .../test/simple/function_test/output.json | 140 +++---- .../test/simple/generic_interface/output.json | 73 ++-- cgfcollector/test/simple/interop/output.json | 54 ++- .../test/simple/interop_external/output.json | 54 ++- .../test/simple/math_demo/output.json | 108 +++--- cgfcollector/test/simple/nesting/output.json | 44 +-- .../test/simple/nesting_calls/output.json | 63 ++-- cgfcollector/test/simple/no_body/output.json | 96 +++-- cgfcollector/test/simple/operator/output.json | 344 ++++++++---------- .../test/simple/operator2/output.json | 90 ++--- .../test/simple/operator3/output.json | 124 +++---- .../test/simple/polymorphism/output.json | 63 ++-- .../test/simple/polymorphism2/output.json | 90 ++--- cgfcollector/test/simple/simple/output.json | 54 ++- cgfcollector/test/simple/use/output.json | 63 ++-- 23 files changed, 1135 insertions(+), 1490 deletions(-) diff --git a/cgfcollector/test/multi/deps/output.json b/cgfcollector/test/multi/deps/output.json index 681188f1..05ed8ec6 100644 --- a/cgfcollector/test/multi/deps/output.json +++ b/cgfcollector/test/multi/deps/output.json @@ -1,74 +1,57 @@ { - "_CG": { - "edges": [ - [[1083670836535463101, 11444295161927196599], null], - [[11444295161927196599, 9530012462595688619], null], - [[2308780131476637806, 1083670836535463101], null], - [[6753505644903624250, 9530012462595688619], null] - ], - "nodes": [ - [ - 9603846864033343388, - { - "functionName": "_QMmy_modulePsubsub", - "hasBody": true, - "meta": null, - "origin": "module.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { "1": {} }, + "functionName": "_QMmodule0_5Pfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "module0_5.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodule2Pfunc", + "hasBody": true, + "meta": {}, + "origin": null + }, + "2": { + "callees": { "1": {} }, + "functionName": "_QMmodule3Pfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "module3.f90" + }, + "3": { + "callees": { "0": {} }, + "functionName": "_QMmy_modulePmy_subroutine", + "hasBody": true, + "meta": {}, + "origin": null + }, + "4": { + "callees": {}, + "functionName": "_QMmy_modulePsubsub", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "module.f90" + }, + "5": { + "callees": { "3": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 11444295161927196599, - { - "functionName": "_QMmodule0_5Pfunc", - "hasBody": true, - "meta": null, - "origin": "module0_5.f90" - } - ], - [ - 1083670836535463101, - { - "functionName": "_QMmy_modulePmy_subroutine", - "hasBody": true, - "meta": null, - "origin": "unknownOrigin" - } - ], - [ - 2308780131476637806, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 6753505644903624250, - { - "functionName": "_QMmodule3Pfunc", - "hasBody": true, - "meta": null, - "origin": "module3.f90" - } - ], - [ - 9530012462595688619, - { - "functionName": "_QMmodule2Pfunc", - "hasBody": true, - "meta": null, - "origin": "unknownOrigin" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "d76681a62ca400b6d9ff4a24bde16f373326f998", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/multi/fortdepend_deps/output.json b/cgfcollector/test/multi/fortdepend_deps/output.json index 681188f1..05ed8ec6 100644 --- a/cgfcollector/test/multi/fortdepend_deps/output.json +++ b/cgfcollector/test/multi/fortdepend_deps/output.json @@ -1,74 +1,57 @@ { - "_CG": { - "edges": [ - [[1083670836535463101, 11444295161927196599], null], - [[11444295161927196599, 9530012462595688619], null], - [[2308780131476637806, 1083670836535463101], null], - [[6753505644903624250, 9530012462595688619], null] - ], - "nodes": [ - [ - 9603846864033343388, - { - "functionName": "_QMmy_modulePsubsub", - "hasBody": true, - "meta": null, - "origin": "module.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { "1": {} }, + "functionName": "_QMmodule0_5Pfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "module0_5.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodule2Pfunc", + "hasBody": true, + "meta": {}, + "origin": null + }, + "2": { + "callees": { "1": {} }, + "functionName": "_QMmodule3Pfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "module3.f90" + }, + "3": { + "callees": { "0": {} }, + "functionName": "_QMmy_modulePmy_subroutine", + "hasBody": true, + "meta": {}, + "origin": null + }, + "4": { + "callees": {}, + "functionName": "_QMmy_modulePsubsub", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "module.f90" + }, + "5": { + "callees": { "3": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 11444295161927196599, - { - "functionName": "_QMmodule0_5Pfunc", - "hasBody": true, - "meta": null, - "origin": "module0_5.f90" - } - ], - [ - 1083670836535463101, - { - "functionName": "_QMmy_modulePmy_subroutine", - "hasBody": true, - "meta": null, - "origin": "unknownOrigin" - } - ], - [ - 2308780131476637806, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 6753505644903624250, - { - "functionName": "_QMmodule3Pfunc", - "hasBody": true, - "meta": null, - "origin": "module3.f90" - } - ], - [ - 9530012462595688619, - { - "functionName": "_QMmodule2Pfunc", - "hasBody": true, - "meta": null, - "origin": "unknownOrigin" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "d76681a62ca400b6d9ff4a24bde16f373326f998", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/entry/output.json b/cgfcollector/test/simple/entry/output.json index be456e51..e81dcd50 100644 --- a/cgfcollector/test/simple/entry/output.json +++ b/cgfcollector/test/simple/entry/output.json @@ -1,55 +1,43 @@ { - "_CG": { - "edges": [ - [[3941336225589151125, 11585286096804977045], null], - [[3941336225589151125, 11158142119152065688], null], - [[3941336225589151125, 2512112026932646668], null] - ], - "nodes": [ - [ - 3941336225589151125, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPentry1", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPentry2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": { "0": {}, "1": {}, "2": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 11585286096804977045, - { - "functionName": "_QMmodPentry2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 11158142119152065688, - { - "functionName": "_QMmodPentry1", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 2512112026932646668, - { - "functionName": "_QMmodPfunc", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "58079ff194dddcb21778f4c423db10fccc27ebae", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/final/output.json b/cgfcollector/test/simple/final/output.json index e6a79d20..5f61b984 100644 --- a/cgfcollector/test/simple/final/output.json +++ b/cgfcollector/test/simple/final/output.json @@ -1,162 +1,121 @@ { - "_CG": { - "edges": [ - [[10431988963324364992, 6149613639027182890], null], - [[10431988963324364992, 5064320190928089604], null], - [[10431988963324364992, 13979031587664314780], null], - [[10431988963324364992, 16645470703084508464], null], - [[911036281617746681, 2052109649073365059], null], - [[10431988963324364992, 2052109649073365059], null], - [[6149613639027182890, 2052109649073365059], null], - [[911036281617746681, 13979031587664314780], null], - [[11433580247649436846, 2052109649073365059], null], - [[6149613639027182890, 5064320190928089604], null], - [[16645470703084508464, 4934351978831295596], null], - [[6149613639027182890, 13979031587664314780], null], - [[2915883528470775764, 2052109649073365059], null], - [[10431988963324364992, 16280562401692851033], null], - [[10552440586870006343, 2052109649073365059], null], - [[10431988963324364992, 11433580247649436846], null], - [[4934351978831295596, 13979031587664314780], null], - [[16645470703084508464, 2052109649073365059], null], - [[10552440586870006343, 13979031587664314780], null], - [[15456597401670502006, 10431988963324364992], null] - ], - "nodes": [ - [ - 10431988963324364992, - { - "functionName": "_QFPfunc", - "hasBody": true, - "meta": null, - "origin": "input.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { + "1": {}, + "10": {}, + "11": {}, + "12": {}, + "2": {}, + "3": {}, + "9": {} + }, + "functionName": "_QFPfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPcreate_polynomial", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "10": { + "callees": { "2": {} }, + "functionName": "_QMmod_usePfunc_calls_final2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "11": { + "callees": { "2": {}, "5": {} }, + "functionName": "_QMmod_usePfunc_calls_final3", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "12": { + "callees": {}, + "functionName": "_QMmod_usePfunc_does_not_call_final", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "13": { + "callees": { "0": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPfinalize_polynomial", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "3": { + "callees": {}, + "functionName": "_QMmodPprint_polynomial", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "4": { + "callees": {}, + "functionName": "_QMmod_useFfunc_calls_final3Pset_nothing", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "5": { + "callees": { "1": {} }, + "functionName": "_QMmod_useFfunc_calls_final3Pset_q", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "6": { + "callees": { "1": {}, "2": {} }, + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "7": { + "callees": { "2": {} }, + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "8": { + "callees": { "1": {}, "2": {} }, + "functionName": "_QMmod_useFfunc_calls_final3Pset_q_move_alloc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "9": { + "callees": { "1": {}, "2": {}, "3": {} }, + "functionName": "_QMmod_usePfunc_calls_final", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + } } - ], - [ - 13979031587664314780, - { - "functionName": "_QMmodPcreate_polynomial", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 5064320190928089604, - { - "functionName": "_QMmodPprint_polynomial", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 2052109649073365059, - { - "functionName": "_QMmodPfinalize_polynomial", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 6149613639027182890, - { - "functionName": "_QMmod_usePfunc_calls_final", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 11433580247649436846, - { - "functionName": "_QMmod_usePfunc_calls_final2", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 4934351978831295596, - { - "functionName": "_QMmod_useFfunc_calls_final3Pset_q", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 2915883528470775764, - { - "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc2", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 16280562401692851033, - { - "functionName": "_QMmod_usePfunc_does_not_call_final", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 911036281617746681, - { - "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 15456597401670502006, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 16645470703084508464, - { - "functionName": "_QMmod_usePfunc_calls_final3", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 1914061080208569539, - { - "functionName": "_QMmod_useFfunc_calls_final3Pset_nothing", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ], - [ - 10552440586870006343, - { - "functionName": "_QMmod_useFfunc_calls_final3Pset_q_move_alloc", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "535f208aefbf510c4c3536f9bf9e6978e2adc2e5", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/final2/output.json b/cgfcollector/test/simple/final2/output.json index 8cc2ee12..6ab80538 100644 --- a/cgfcollector/test/simple/final2/output.json +++ b/cgfcollector/test/simple/final2/output.json @@ -1,111 +1,86 @@ { - "_CG": { - "edges": [ - [[13845067782904566984, 12261923257537545416], null], - [[9132368188186752179, 12261923257537545416], null], - [[8812770886124818036, 9132368188186752179], null], - [[13845067782904566984, 9634951400506836789], null], - [[15997684825707339448, 9634951400506836789], null], - [[11592059056552374804, 8812770886124818036], null], - [[15997684825707339448, 12261923257537545416], null], - [[8812770886124818036, 9634951400506836789], null], - [[8812770886124818036, 15997684825707339448], null], - [[9132368188186752179, 9634951400506836789], null], - [[8812770886124818036, 17137297412558704458], null], - [[8812770886124818036, 12261923257537545416], null], - [[8812770886124818036, 6215837638387017975], null], - [[8812770886124818036, 13845067782904566984], null] - ], - "nodes": [ - [ - 11592059056552374804, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { + "1": {}, + "2": {}, + "3": {}, + "4": {}, + "5": {}, + "6": {}, + "7": {} + }, + "functionName": "_QFPfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPfinalize_base", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPfinalize_derived", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QMmodPfunc_arg", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "4": { + "callees": { "1": {}, "2": {} }, + "functionName": "_QMmodPfunc_arg2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "5": { + "callees": {}, + "functionName": "_QMmodPfunc_arg_inout", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "6": { + "callees": { "1": {}, "2": {} }, + "functionName": "_QMmodPfunc_arg_inout2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "7": { + "callees": { "1": {}, "2": {} }, + "functionName": "_QMmodPfunc_arg_out", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "8": { + "callees": { "0": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 15997684825707339448, - { - "functionName": "_QMmodPfunc_arg_inout2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 17137297412558704458, - { - "functionName": "_QMmodPfunc_arg_inout", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 13845067782904566984, - { - "functionName": "_QMmodPfunc_arg2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 6215837638387017975, - { - "functionName": "_QMmodPfunc_arg", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 8812770886124818036, - { - "functionName": "_QFPfunc", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 9132368188186752179, - { - "functionName": "_QMmodPfunc_arg_out", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 9634951400506836789, - { - "functionName": "_QMmodPfinalize_derived", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 12261923257537545416, - { - "functionName": "_QMmodPfinalize_base", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "535f208aefbf510c4c3536f9bf9e6978e2adc2e5", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/final3/output.json b/cgfcollector/test/simple/final3/output.json index 2377d1af..82a28281 100644 --- a/cgfcollector/test/simple/final3/output.json +++ b/cgfcollector/test/simple/final3/output.json @@ -1,125 +1,92 @@ { - "_CG": { - "edges": [ - [[13057133462220643391, 1454521131396630178], null], - [[14650538270171694253, 4796010356411637051], null], - [[14650538270171694253, 16491118580773965493], null], - [[14650538270171694253, 7396763912426530726], null], - [[13057133462220643391, 4051482389717363313], null], - [[13057133462220643391, 8008070197589690300], null], - [[13057133462220643391, 15912510891746475667], null], - [[13057133462220643391, 3412517584417964141], null], - [[16336895082892013384, 14650538270171694253], null], - [[16336895082892013384, 13057133462220643391], null] - ], - "nodes": [ - [ - 14650538270171694253, - { - "functionName": "_QFPfunc_no_finalize", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { "2": {}, "3": {}, "4": {}, "6": {}, "8": {} }, + "functionName": "_QFPfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": { "5": {}, "7": {}, "9": {} }, + "functionName": "_QFPfunc_no_finalize", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "10": { + "callees": { "0": {}, "1": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPfinalize_base", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QMmodPfinalize_derived", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "4": { + "callees": {}, + "functionName": "_QMmodPfunc_arg", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "5": { + "callees": {}, + "functionName": "_QMmodPfunc_arg2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "6": { + "callees": {}, + "functionName": "_QMmodPfunc_arg_inout", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "7": { + "callees": {}, + "functionName": "_QMmodPfunc_arg_inout2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "8": { + "callees": {}, + "functionName": "_QMmodPfunc_arg_out", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "9": { + "callees": {}, + "functionName": "_QMmodPfunc_arg_out2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 4796010356411637051, - { - "functionName": "_QMmodPfunc_arg_inout2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 16336895082892013384, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 16491118580773965493, - { - "functionName": "_QMmodPfunc_arg_out2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 7396763912426530726, - { - "functionName": "_QMmodPfunc_arg2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 3412517584417964141, - { - "functionName": "_QMmodPfunc_arg", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 8008070197589690300, - { - "functionName": "_QMmodPfinalize_derived", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 13057133462220643391, - { - "functionName": "_QFPfunc", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 15912510891746475667, - { - "functionName": "_QMmodPfunc_arg_inout", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 4051482389717363313, - { - "functionName": "_QMmodPfunc_arg_out", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 1454521131396630178, - { - "functionName": "_QMmodPfinalize_base", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "65b007fe24367861c97fcadcc94cc1c6a165c074", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/final4/output.json b/cgfcollector/test/simple/final4/output.json index 21562fee..a797fb34 100644 --- a/cgfcollector/test/simple/final4/output.json +++ b/cgfcollector/test/simple/final4/output.json @@ -1,42 +1,36 @@ { - "_CG": { - "edges": [[[9628105010719225529, 1576245589781696893], null]], - "nodes": [ - [ - 9628105010719225529, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPcreate_dummy_type", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPfinalize_dummy", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": { "0": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 1576245589781696893, - { - "functionName": "_QMmodPcreate_dummy_type", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 3690541955172721898, - { - "functionName": "_QMmodPfinalize_dummy", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "cff904532489787c5f6ff332d161ed9e1dcb3d9f", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/final5/output.json b/cgfcollector/test/simple/final5/output.json index ca9489ef..615a7df2 100644 --- a/cgfcollector/test/simple/final5/output.json +++ b/cgfcollector/test/simple/final5/output.json @@ -1,51 +1,43 @@ { - "_CG": { - "edges": [], - "nodes": [ - [ - 16500006632140824326, - { - "functionName": "_QFPfunc", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QFPfunc", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPfinalize_base", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPfinalize_derived", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 4938433408998471207, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 16172947494565568999, - { - "functionName": "_QMmodPfinalize_derived", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 1620398472718245726, - { - "functionName": "_QMmodPfinalize_base", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "cff904532489787c5f6ff332d161ed9e1dcb3d9f", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/function_test/output.json b/cgfcollector/test/simple/function_test/output.json index d3ff935b..0ea1f111 100644 --- a/cgfcollector/test/simple/function_test/output.json +++ b/cgfcollector/test/simple/function_test/output.json @@ -1,84 +1,64 @@ { - "_CG": { - "edges": [ - [[14247392955135821084, 10265585301451757595], null], - [[14247392955135821084, 9264498473018160840], null], - [[14247392955135821084, 8180353735812950427], null], - [[3560107481050335624, 8180353735812950427], null], - [[18176357673246278819, 8180353735812950427], null] - ], - "nodes": [ - [ - 18176357673246278819, - { - "functionName": "_QFsizePfunc1", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { "4": {} }, + "functionName": "_QFsizePfunc1", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": { "4": {} }, + "functionName": "_QFsizePfunc2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMvector_operationsPvector_add", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QMvector_operationsPvector_norm", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "4": { + "callees": {}, + "functionName": "_QPsize", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "5": { + "callees": {}, + "functionName": "_QPvector_norm2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "6": { + "callees": { "2": {}, "3": {}, "4": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 8180353735812950427, - { - "functionName": "_QPsize", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 4612314267457897323, - { - "functionName": "_QPvector_norm2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 14247392955135821084, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 3560107481050335624, - { - "functionName": "_QFsizePfunc2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 10265585301451757595, - { - "functionName": "_QMvector_operationsPvector_norm", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 9264498473018160840, - { - "functionName": "_QMvector_operationsPvector_add", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "e484641c4a9cc749eef4bfa01fca86020321e52d", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/generic_interface/output.json b/cgfcollector/test/simple/generic_interface/output.json index 95dba5e2..1c97e32d 100644 --- a/cgfcollector/test/simple/generic_interface/output.json +++ b/cgfcollector/test/simple/generic_interface/output.json @@ -1,45 +1,36 @@ { - "_CG": { - "edges": [ - [[12261419144365086168, 12651824050695698132], null], - [[12261419144365086168, 1355953991221275603], null] - ], - "nodes": [ - [ - 12261419144365086168, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPadd_int", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPadd_real", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": { "0": {}, "1": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 12651824050695698132, - { - "functionName": "_QMmodPadd_real", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 1355953991221275603, - { - "functionName": "_QMmodPadd_int", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "535f208aefbf510c4c3536f9bf9e6978e2adc2e5", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/interop/output.json b/cgfcollector/test/simple/interop/output.json index 2a4e3540..7972eaaf 100644 --- a/cgfcollector/test/simple/interop/output.json +++ b/cgfcollector/test/simple/interop/output.json @@ -1,33 +1,29 @@ { - "_CG": { - "edges": [[[10932468432584512985, 15390318080014301756], null]], - "nodes": [ - [ - 15390318080014301756, - { - "functionName": "add", - "hasBody": false, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { "1": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "add", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 10932468432584512985, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "16c60e49d2f6547d0493529a47951ca207bb5d3d", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/interop_external/output.json b/cgfcollector/test/simple/interop_external/output.json index fc96ac79..18eaf968 100644 --- a/cgfcollector/test/simple/interop_external/output.json +++ b/cgfcollector/test/simple/interop_external/output.json @@ -1,33 +1,29 @@ { - "_CG": { - "edges": [[[13180814381945652224, 9267160619168015051], null]], - "nodes": [ - [ - 9267160619168015051, - { - "functionName": "_QPadd", - "hasBody": false, - "meta": null, - "origin": "unknownOrigin" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QPadd", + "hasBody": false, + "meta": {}, + "origin": null + }, + "1": { + "callees": { "0": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 13180814381945652224, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "16c60e49d2f6547d0493529a47951ca207bb5d3d", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/math_demo/output.json b/cgfcollector/test/simple/math_demo/output.json index c5cd76ae..5baa24be 100644 --- a/cgfcollector/test/simple/math_demo/output.json +++ b/cgfcollector/test/simple/math_demo/output.json @@ -1,66 +1,50 @@ { - "_CG": { - "edges": [ - [[5017978461990509046, 2963930559116137426], null], - [[5017978461990509046, 14284706976400344361], null], - [[5017978461990509046, 17311429370828588933], null], - [[5017978461990509046, 3724766418702407261], null], - [[2963930559116137426, 2963930559116137426], null] - ], - "nodes": [ - [ - 5017978461990509046, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmath_utilsParray_stats", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmath_utilsPdot_product_custom", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": { "2": {} }, + "functionName": "_QMmath_utilsPfactorial", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QMmath_utilsPnormalize_vector", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "4": { + "callees": { "0": {}, "1": {}, "2": {}, "3": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 17311429370828588933, - { - "functionName": "_QMmath_utilsPnormalize_vector", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 14284706976400344361, - { - "functionName": "_QMmath_utilsPdot_product_custom", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 3724766418702407261, - { - "functionName": "_QMmath_utilsParray_stats", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 2963930559116137426, - { - "functionName": "_QMmath_utilsPfactorial", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "e484641c4a9cc749eef4bfa01fca86020321e52d", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/nesting/output.json b/cgfcollector/test/simple/nesting/output.json index a15b0dcd..4b4349d4 100644 --- a/cgfcollector/test/simple/nesting/output.json +++ b/cgfcollector/test/simple/nesting/output.json @@ -1,33 +1,29 @@ { "_CG": { - "edges": [[[16400581539384321784, 12281366146811553951], null]], - "nodes": [ - [ - 16400581539384321784, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 12281366146811553951, - { - "functionName": "_QMmPsay", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmPsay", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": { "0": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } + } }, "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "e7db04da1660956f8319a3d2fa80d30dad332800", - "version": "0.7" + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" }, - "version": "3.0" + "version": "4.0" } } diff --git a/cgfcollector/test/simple/nesting_calls/output.json b/cgfcollector/test/simple/nesting_calls/output.json index ab77bc70..0e58993b 100644 --- a/cgfcollector/test/simple/nesting_calls/output.json +++ b/cgfcollector/test/simple/nesting_calls/output.json @@ -1,45 +1,36 @@ { "_CG": { - "edges": [ - [[3467742329716980950, 17909528412272728826], null], - [[3467742329716980950, 3828209433738255338], null] - ], - "nodes": [ - [ - 3467742329716980950, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 17909528412272728826, - { - "functionName": "_QMmPsay", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 3828209433738255338, - { - "functionName": "_QMmPreturn_ptr", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmPreturn_ptr", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmPsay", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": { "0": {}, "1": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } + } }, "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "e7db04da1660956f8319a3d2fa80d30dad332800", - "version": "0.7" + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" }, - "version": "3.0" + "version": "4.0" } } diff --git a/cgfcollector/test/simple/no_body/output.json b/cgfcollector/test/simple/no_body/output.json index 2106561e..3526c7dd 100644 --- a/cgfcollector/test/simple/no_body/output.json +++ b/cgfcollector/test/simple/no_body/output.json @@ -1,64 +1,50 @@ { "_CG": { - "edges": [ - [[13116615762761984989, 4495794879652727597], null], - [[5993333203045879924, 13116615762761984989], null], - [[5993333203045879924, 4495794879652727597], null] - ], - "nodes": [ - [ - 6918181246387806493, - { - "functionName": "_QFFprint_starsPsubsub", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 4073897767569630127, - { - "functionName": "_QPprint_stuff2", - "hasBody": false, - "meta": null, - "origin": "main.f90" - } - ], - [ - 13116615762761984989, - { - "functionName": "_QFPprint_stars", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 4495794879652727597, - { - "functionName": "_QPprint_stuff", - "hasBody": false, - "meta": null, - "origin": "main.f90" - } - ], - [ - 5993333203045879924, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QFFprint_starsPsubsub", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": { "2": {} }, + "functionName": "_QFPprint_stars", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QPprint_stuff", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QPprint_stuff2", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "4": { + "callees": { "1": {}, "2": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } + } }, "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "e8fbd9caa11ea3da8a149732eeb7b9c88ba5249f", - "version": "0.7" + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" }, - "version": "3.0" + "version": "4.0" } } diff --git a/cgfcollector/test/simple/operator/output.json b/cgfcollector/test/simple/operator/output.json index aa949d2f..01a2e4db 100644 --- a/cgfcollector/test/simple/operator/output.json +++ b/cgfcollector/test/simple/operator/output.json @@ -1,202 +1,150 @@ { - "_CG": { - "edges": [ - [[7572781303902459746, 11442313694358728277], null], - [[10361574024262302863, 11442313694358728277], null], - [[11460580497354340053, 16784127278057253920], null], - [[16014592354400816154, 2925204865112094556], null], - [[1923978352858283650, 12300079433521964041], null], - [[11278138169644782137, 13832393172155657730], null], - [[479400711443056833, 15223348241347075203], null], - [[10361574024262302863, 390067326288520518], null], - [[73841721019936442, 15223348241347075203], null], - [[16784127278057253920, 15223348241347075203], null], - [[7572781303902459746, 390067326288520518], null], - [[16784127278057253920, 13832393172155657730], null], - [[124759220132753432, 479400711443056833], null], - [[11278138169644782137, 15223348241347075203], null], - [[124759220132753432, 10361574024262302863], null], - [[124759220132753432, 11278138169644782137], null], - [[124759220132753432, 11460580497354340053], null], - [[11460580497354340053, 16873906278150271435], null], - [[124759220132753432, 7572781303902459746], null], - [[124759220132753432, 16014592354400816154], null], - [[73841721019936442, 13832393172155657730], null], - [[124759220132753432, 1923978352858283650], null], - [[124759220132753432, 2818987135271915965], null], - [[479400711443056833, 13832393172155657730], null] - ], - "nodes": [ - [ - 2818987135271915965, - { - "functionName": "_QFPtest_expr", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": { "8": {}, "9": {} }, + "functionName": "_QFPtest_add", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": { "8": {}, "9": {} }, + "functionName": "_QFPtest_add2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "10": { + "callees": {}, + "functionName": "_QMmodPless_than_integer", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "11": { + "callees": {}, + "functionName": "_QMmodPnegx", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "12": { + "callees": { "10": {}, "15": {} }, + "functionName": "_QMmodPnot_impl", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "13": { + "callees": { "10": {}, "15": {} }, + "functionName": "_QMmodPnot_impl_integer", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "14": { + "callees": {}, + "functionName": "_QMmodPunary_plus", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "15": { + "callees": {}, + "functionName": "_QPcompare", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "16": { + "callees": {}, + "functionName": "_QPnot", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "17": { + "callees": { + "0": {}, + "1": {}, + "2": {}, + "3": {}, + "4": {}, + "5": {}, + "6": {}, + "7": {} + }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": { "10": {}, "15": {} }, + "functionName": "_QFPtest_compare", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": { "10": {}, "15": {} }, + "functionName": "_QFPtest_compare2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "4": { + "callees": {}, + "functionName": "_QFPtest_expr", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "5": { + "callees": { "11": {} }, + "functionName": "_QFPtest_negx", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "6": { + "callees": { "13": {}, "16": {} }, + "functionName": "_QFPtest_not", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "7": { + "callees": { "14": {} }, + "functionName": "_QFPtest_unary_plus", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "8": { + "callees": {}, + "functionName": "_QMmodPadd_stuff", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "9": { + "callees": {}, + "functionName": "_QMmodPadd_stuff2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 16014592354400816154, - { - "functionName": "_QFPtest_unary_plus", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 7572781303902459746, - { - "functionName": "_QFPtest_add2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 10361574024262302863, - { - "functionName": "_QFPtest_add", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 13832393172155657730, - { - "functionName": "_QPcompare", - "hasBody": false, - "meta": null, - "origin": "main.f90" - } - ], - [ - 16873906278150271435, - { - "functionName": "_QPnot", - "hasBody": false, - "meta": null, - "origin": "main.f90" - } - ], - [ - 15223348241347075203, - { - "functionName": "_QMmodPless_than_integer", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 73841721019936442, - { - "functionName": "_QMmodPnot_impl", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 390067326288520518, - { - "functionName": "_QMmodPadd_stuff2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 479400711443056833, - { - "functionName": "_QFPtest_compare", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 16784127278057253920, - { - "functionName": "_QMmodPnot_impl_integer", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 11460580497354340053, - { - "functionName": "_QFPtest_not", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 11278138169644782137, - { - "functionName": "_QFPtest_compare2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 12300079433521964041, - { - "functionName": "_QMmodPnegx", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 11442313694358728277, - { - "functionName": "_QMmodPadd_stuff", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 1923978352858283650, - { - "functionName": "_QFPtest_negx", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 124759220132753432, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 2925204865112094556, - { - "functionName": "_QMmodPunary_plus", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "fea89c328b25f1f2391c88484d5d3b4f413db4b0", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/operator2/output.json b/cgfcollector/test/simple/operator2/output.json index 9ce32625..5de3b859 100644 --- a/cgfcollector/test/simple/operator2/output.json +++ b/cgfcollector/test/simple/operator2/output.json @@ -1,55 +1,43 @@ { - "_CG": { - "edges": [ - [[6566338197382086444, 18183997041583555397], null], - [[6566338197382086444, 844415908090943580], null], - [[6566338197382086444, 16861331205106935383], null] - ], - "nodes": [ - [ - 6566338197382086444, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPfbase", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPfunction_base2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPnot_base", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": { "0": {}, "1": {}, "2": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 18183997041583555397, - { - "functionName": "_QMmodPfunction_base2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 16861331205106935383, - { - "functionName": "_QMmodPnot_base", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 844415908090943580, - { - "functionName": "_QMmodPfbase", - "hasBody": false, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "1c4c99974efba03080aa26f17f925be8d6f36f2c", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/operator3/output.json b/cgfcollector/test/simple/operator3/output.json index 23f95863..4237db39 100644 --- a/cgfcollector/test/simple/operator3/output.json +++ b/cgfcollector/test/simple/operator3/output.json @@ -1,75 +1,57 @@ { - "_CG": { - "edges": [ - [[3955331171395186654, 959250268827548101], null], - [[3955331171395186654, 12628145085038494357], null], - [[3955331171395186654, 15985040955282811027], null], - [[3955331171395186654, 8300338079480834402], null], - [[3955331171395186654, 5335457571921960532], null] - ], - "nodes": [ - [ - 3955331171395186654, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPadd_base", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPs", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPshow_derived1", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QMmodPshow_derived2", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "4": { + "callees": {}, + "functionName": "_QMmodPsub_derived1", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "5": { + "callees": { "0": {}, "1": {}, "2": {}, "3": {}, "4": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 959250268827548101, - { - "functionName": "_QMmodPshow_derived2", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 12628145085038494357, - { - "functionName": "_QMmodPshow_derived1", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 5335457571921960532, - { - "functionName": "_QMmodPsub_derived1", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 8300338079480834402, - { - "functionName": "_QMmodPadd_base", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 15985040955282811027, - { - "functionName": "_QMmodPs", - "hasBody": false, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "1c4c99974efba03080aa26f17f925be8d6f36f2c", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/polymorphism/output.json b/cgfcollector/test/simple/polymorphism/output.json index 19e90cf5..59598f43 100644 --- a/cgfcollector/test/simple/polymorphism/output.json +++ b/cgfcollector/test/simple/polymorphism/output.json @@ -1,45 +1,36 @@ { "_CG": { - "edges": [ - [[9367282678967586139, 5793715532416084499], null], - [[9367282678967586139, 7473845567962644369], null] - ], - "nodes": [ - [ - 9367282678967586139, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 5793715532416084499, - { - "functionName": "_QMmodPset_mass_charged_body", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 7473845567962644369, - { - "functionName": "_QMmodPset_mass_body", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPset_mass_body", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPset_mass_charged_body", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": { "0": {}, "1": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } + } }, "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "78d1dc4e07062712037ce37336f919faec44b327", - "version": "0.7" + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" }, - "version": "3.0" + "version": "4.0" } } diff --git a/cgfcollector/test/simple/polymorphism2/output.json b/cgfcollector/test/simple/polymorphism2/output.json index 95e81226..53b65da9 100644 --- a/cgfcollector/test/simple/polymorphism2/output.json +++ b/cgfcollector/test/simple/polymorphism2/output.json @@ -1,55 +1,43 @@ { - "_CG": { - "edges": [ - [[10447073260129445604, 5161265265095615880], null], - [[10447073260129445604, 12495205984704274385], null], - [[10447073260129445604, 9011580163634161557], null] - ], - "nodes": [ - [ - 5161265265095615880, - { - "functionName": "_QMshapesPdraw_rectangle", - "hasBody": true, - "meta": null, - "origin": "main.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMshapesPdraw", + "hasBody": false, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMshapesPdraw_circle", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMshapesPdraw_rectangle", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "3": { + "callees": { "0": {}, "1": {}, "2": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } } - ], - [ - 12495205984704274385, - { - "functionName": "_QMshapesPdraw_circle", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 10447073260129445604, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 9011580163634161557, - { - "functionName": "_QMshapesPdraw", - "hasBody": false, - "meta": null, - "origin": "main.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "38e3c2db60ec72712056eabd89642e3a6fb631cc", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/simple/output.json b/cgfcollector/test/simple/simple/output.json index d7c1b198..f4f492d5 100644 --- a/cgfcollector/test/simple/simple/output.json +++ b/cgfcollector/test/simple/simple/output.json @@ -1,33 +1,29 @@ { - "_CG": { - "edges": [[[386379624964062775, 12232342110680008091], null]], - "nodes": [ - [ - 12232342110680008091, - { - "functionName": "_QFPprint_stars", - "hasBody": true, - "meta": null, - "origin": "input.f90" + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QFPprint_stars", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + }, + "1": { + "callees": { "0": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "input.f90" + } } - ], - [ - 386379624964062775, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "input.f90" - } - ] - ] - }, - "_MetaCG": { - "generator": { - "name": "MetaCG", - "sha": "e8fbd9caa11ea3da8a149732eeb7b9c88ba5249f", - "version": "0.7" }, - "version": "3.0" - } + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" + }, + "version": "4.0" + } } diff --git a/cgfcollector/test/simple/use/output.json b/cgfcollector/test/simple/use/output.json index 12a47827..4263c069 100644 --- a/cgfcollector/test/simple/use/output.json +++ b/cgfcollector/test/simple/use/output.json @@ -1,45 +1,36 @@ { "_CG": { - "edges": [ - [[156073635694004542, 15462925951592547380], null], - [[156073635694004542, 8395601259825836172], null] - ], - "nodes": [ - [ - 156073635694004542, - { - "functionName": "_QQmain", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 15462925951592547380, - { - "functionName": "_QMmodPset_var_derived", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ], - [ - 8395601259825836172, - { - "functionName": "_QMmodPset_var_base", - "hasBody": true, - "meta": null, - "origin": "main.f90" - } - ] - ] + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPset_var_base", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPset_var_derived", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + }, + "2": { + "callees": { "0": {}, "1": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": { "fileProperties": { "systemInclude": false } }, + "origin": "main.f90" + } + } }, "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "bcce43c481a129077e674aaa2aa736946bcf925a", - "version": "0.7" + "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "version": "0.9" }, - "version": "3.0" + "version": "4.0" } } From d9ef4a8dcb7112b61326d67d649ff9316edbe386 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:00 +0100 Subject: [PATCH 071/130] swaped insert with getOrInsertNode to only insert nodes with unique names --- cgfcollector/src/ParseTreeVisitor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index b90c7489..75cb160d 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -7,7 +7,7 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { functionSymbols.emplace_back(sym); functionDummyArgs.emplace_back(std::vector()); - cg->insert(mangleName(*sym), currentFileName, false, false); + cg->getOrInsertNode(mangleName(*sym), currentFileName, false, false); functions.push_back({sym, std::vector()}); al->debug("Add node: {} ({})", mangleName(*sym), fmt::ptr(sym)); @@ -304,7 +304,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { return true; functionSymbols.emplace_back(maybeStmt->statement.v.symbol); - cg->insert(mangleName(*functionSymbols.back()), currentFileName, false, false); + cg->getOrInsertNode(mangleName(*functionSymbols.back()), currentFileName, false, false); al->debug("\nIn main program: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); } @@ -356,7 +356,7 @@ void ParseTreeVisitor::Post(const EntryStmt& e) { al->debug("Add Entry point: {} ({})", mangleName(*name->symbol), fmt::ptr(name->symbol)); - cg->insert(mangleName(*name->symbol), currentFileName, false, true); + cg->getOrInsertNode(mangleName(*name->symbol), currentFileName, false, true); } void ParseTreeVisitor::Post(const FunctionStmt& f) { From c4a93c0709f73570d8a7cb23477e87204673b580 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:01 +0100 Subject: [PATCH 072/130] remove unnecessary includes --- cgfcollector/include/AL.h | 1 - cgfcollector/include/headers.h | 1 - 2 files changed, 2 deletions(-) diff --git a/cgfcollector/include/AL.h b/cgfcollector/include/AL.h index 1ee2fad0..d98cbbb4 100644 --- a/cgfcollector/include/AL.h +++ b/cgfcollector/include/AL.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include template <> diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h index 951f69bb..2b6053a2 100644 --- a/cgfcollector/include/headers.h +++ b/cgfcollector/include/headers.h @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include From cf4c17906f09fead3f0e693a950e02ad50dcb35b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:01 +0100 Subject: [PATCH 073/130] add FlangLLVM.cmake for correctly linking flang --- CMakeLists.txt | 2 +- cmake/ClangLLVM.cmake | 5 ----- cmake/FlangLLVM.cmake | 30 ++++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 cmake/FlangLLVM.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index f8364e6d..523567b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -222,6 +222,6 @@ option( ) if(METACG_BUILD_CGFCOLLECTOR) - include(ClangLLVM) + include(FlangLLVM) add_subdirectory(cgfcollector) endif() diff --git a/cmake/ClangLLVM.cmake b/cmake/ClangLLVM.cmake index e7e84ff1..36ef0850 100644 --- a/cmake/ClangLLVM.cmake +++ b/cmake/ClangLLVM.cmake @@ -79,8 +79,3 @@ function(add_clang target) endif() endfunction() - -function(add_flang target) - target_compile_definitions(${target} PRIVATE FLANG_LITTLE_ENDIAN) - target_link_libraries(${target} PUBLIC flangFrontendTool) -endfunction() diff --git a/cmake/FlangLLVM.cmake b/cmake/FlangLLVM.cmake new file mode 100644 index 00000000..f4133085 --- /dev/null +++ b/cmake/FlangLLVM.cmake @@ -0,0 +1,30 @@ +find_package( + MLIR + REQUIRED + CONFIG +) + +find_package( + Flang + REQUIRED + CONFIG +) + +message(STATUS "Found FlangConfig.cmake in: ${Flang_DIR}") +message(STATUS "Using Flang version: ${Flang_VERSION}") +message(STATUS "Found MLIRConfig.cmake in: ${MLIR_DIR}") +message(STATUS "Using MLIR version: ${MLIR_VERSION}") + +function(add_flang target) + target_compile_definitions(${target} PRIVATE FLANG_LITTLE_ENDIAN) + + target_include_directories(${target} SYSTEM PUBLIC ${FLANG_INCLUDE_DIRS}) + + find_library( + FLANG_LIB flang + PATHS ${LLVM_LIBRARY_DIR} + NO_DEFAULT_PATH + ) + + target_link_libraries(${target} PUBLIC flangFrontendTool) +endfunction() From 6eb082a4a965e1784452232a78f8b7ca2d7e2e68 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:01 +0100 Subject: [PATCH 074/130] fix comp_wrapper and add filtering for files in options --- .../tools/cgfcollector_comp_wrapper.sh.in | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 848eedd8..6577bee0 100755 --- a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -1,11 +1,26 @@ #!/bin/bash -flang_bin="flang-new" +flang_bin=${CGFCOLLECTOR_FLANG_BIN:-"flang-new"} if ! command -v "$flang_bin" &>/dev/null; then echo "Error: $flang_bin not found in PATH." exit 1 fi -$flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" +source_files=() + +for arg in "$@"; do + # skip options + [[ "$arg" == -* ]] && continue + + case "$arg" in + *.f90 | *.F90) + source_files+=("$arg") + ;; + esac +done + +if [ ${#source_files[@]} -gt 0 ]; then + $flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" "${source_files[@]}" +fi $flang_bin "$@" From b2db55ae5dcf5c9e28d079c73e5bf27d99db2f2c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:02 +0100 Subject: [PATCH 075/130] flang_bin can now be set throw env var --- cgfcollector/tools/cgfcollector_wrapper.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index 0762fe89..8ad19b09 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -1,6 +1,6 @@ #!/bin/bash -flang_bin="flang-new" +flang_bin=${CGFCOLLECTOR_FLANG_BIN:-"flang-new"} flang_args=("$@") plugin_name="genCG" From d9e4fd4cb495418578e8b2dd0d689ab7a5d6b218 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:02 +0100 Subject: [PATCH 076/130] don't link against flangFrontendTool --- cmake/FlangLLVM.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/FlangLLVM.cmake b/cmake/FlangLLVM.cmake index f4133085..6a9241df 100644 --- a/cmake/FlangLLVM.cmake +++ b/cmake/FlangLLVM.cmake @@ -26,5 +26,5 @@ function(add_flang target) NO_DEFAULT_PATH ) - target_link_libraries(${target} PUBLIC flangFrontendTool) + # target_link_libraries(${target} PUBLIC flangFrontendTool) endfunction() From 8336e498cc4d28d311c493d50ef1e275c524347d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:02 +0100 Subject: [PATCH 077/130] main more consistant output filenames and new norename plugin for cmake test --- cgfcollector/include/headers.h | 1 + cgfcollector/src/main.cpp | 89 +++++++++++++++++++++++++--------- 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h index 2b6053a2..30be4993 100644 --- a/cgfcollector/include/headers.h +++ b/cgfcollector/include/headers.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index e36718fb..3acfd119 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -4,10 +4,23 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { public: - void executeAction() override { + metacg::Callgraph* cg = nullptr; + + ~CollectCG() override { + // if (cg != nullptr) { + // delete cg; + // cg = nullptr; + // } + } + + void generateCG() { + if (cg != nullptr) { + return; + } + AL* al = AL::getInstance(); - auto cg = std::make_unique(); + cg = new metacg::Callgraph(); // TODO: remove if (std::getenv("CUSTOM_DEBUG")) { @@ -15,7 +28,7 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { spdlog::set_level(spdlog::level::debug); } - ParseTreeVisitor visitor(cg.get(), getCurrentFile().str()); + ParseTreeVisitor visitor(cg, getCurrentFile().str()); Fortran::parser::Walk(getParsing().parseTree(), visitor); // handle potential finalizers from function calls @@ -45,25 +58,52 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { cg->addEdge(callerNode, calleeNode); } + al->flush(); + } + + std::string cgToString(metacg::Callgraph* cg) { auto& mcgManager = metacg::graph::MCGManager::get(); mcgManager.resetManager(); - mcgManager.addToManagedGraphs("test", std::move(cg), true); + mcgManager.addToManagedGraphs("test", std::unique_ptr(cg), true); mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); auto mcgWriter = metacg::io::createWriter(4); if (!mcgWriter) { llvm::errs() << "Unable to create a writer\n"; - return; + return ""; }; metacg::io::JsonSink jsonSink; mcgWriter->writeActiveGraph(jsonSink); - auto file = createOutputFile("json"); - file->write(jsonSink.getJson().dump().c_str(), jsonSink.getJson().dump().size()); + return jsonSink.getJson().dump(); + } - al->flush(); + std::unique_ptr createOutputFile(llvm::StringRef extension) { + llvm::SmallString<128> outputPath(getInstance().getFrontendOpts().outputFile); + if (outputPath.empty()) { + outputPath = getCurrentFile(); + } + if (extension != "") + llvm::sys::path::replace_extension(outputPath, extension); + std::unique_ptr os; + std::error_code ec; + os.reset(new llvm::raw_fd_ostream(outputPath.str(), ec, llvm::sys::fs::OF_TextWithCRLF)); + if (ec) { + llvm::errs() << "Error opening output file: " << ec.message() << "\n"; + return nullptr; + } + return os; + } + + void executeAction() override { + generateCG(); + + std::string cgString = cgToString(cg); + + auto file = createOutputFile("json"); + file->write(cgString.c_str(), cgString.size()); } }; @@ -72,25 +112,28 @@ class CollectCGwithDot : public CollectCG { void executeAction() override { CollectCG::executeAction(); - auto& mcgManager = metacg::graph::MCGManager::get(); - - metacg::io::dot::DotGenerator dotGen(mcgManager.getCallgraph("test")); + metacg::io::dot::DotGenerator dotGen(cg); dotGen.generate(); - llvm::SmallString<128> outputPath(getInstance().getFrontendOpts().outputFile); - if (outputPath.empty()) { - outputPath = getCurrentFile(); - } - llvm::sys::path::replace_extension(outputPath, "dot"); - std::error_code ec; - llvm::raw_fd_ostream out(outputPath.str(), ec); - if (ec) { - llvm::errs() << "Error opening output file: " << ec.message() << "\n"; - return; - } - out << dotGen.getDotString(); + auto file = CollectCG::createOutputFile("dot"); + std::string dotString = dotGen.getDotString(); + file->write(dotString.c_str(), dotString.size()); + } +}; + +class CollectCGNoRename : public CollectCG { + public: + void executeAction() override { + generateCG(); + + std::string cgString = cgToString(cg); + + auto file = createOutputFile(""); + file->write(cgString.c_str(), cgString.size()); } }; static Fortran::frontend::FrontendPluginRegistry::Add X("genCG", "Generate Callgraph"); static Fortran::frontend::FrontendPluginRegistry::Add Y("genCGwithDot", "Generate Callgraph"); +static Fortran::frontend::FrontendPluginRegistry::Add Z( + "genCGNoRename", "Generate Callgraph without renaming output file"); From 760c9ddf6c0f32d4919028c8a7ba1e9324a9082f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:03 +0100 Subject: [PATCH 078/130] update tests to reflect main changes --- cgfcollector/test/multi/cmake_base.txt.in | 2 +- cgfcollector/test/multi/fortdepend_deps/Makefile | 6 +++--- cgfcollector/tools/test_runner.sh.in | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cgfcollector/test/multi/cmake_base.txt.in b/cgfcollector/test/multi/cmake_base.txt.in index d0d45e25..c043d5a0 100644 --- a/cgfcollector/test/multi/cmake_base.txt.in +++ b/cgfcollector/test/multi/cmake_base.txt.in @@ -1,5 +1,5 @@ set(CMAKE_Fortran_COMPILER "@FCOLLECTOR_WRAPPER@") set(CMAKE_Fortran_FLAGS "") -set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") +set(CMAKE_Fortran_COMPILE_OBJECT " -norename -o ") set(CMAKE_Fortran_LINK_EXECUTABLE "@CGMERGE2_EXECUTABLE@ ") set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/test/multi/fortdepend_deps/Makefile b/cgfcollector/test/multi/fortdepend_deps/Makefile index bf186aef..47042d46 100644 --- a/cgfcollector/test/multi/fortdepend_deps/Makefile +++ b/cgfcollector/test/multi/fortdepend_deps/Makefile @@ -2,12 +2,12 @@ include ../make_base BUILD_DIR = build DEP_FILE = $(BUILD_DIR)/Makefile.dep -TARGET = $(BUILD_DIR)/test +TARGET = $(BUILD_DIR)/output SRCS := $(wildcard *.f90) OBJS := $(SRCS:.f90=.o) OBJS := $(addprefix $(BUILD_DIR)/, $(OBJS)) -OBJS_JSON := $(SRCS:.f90=.o.json) +OBJS_JSON := $(SRCS:.f90=.json) OBJS_JSON := $(addprefix $(BUILD_DIR)/, $(OBJS_JSON)) all: $(TARGET) $(DEP_FILE) @@ -16,7 +16,7 @@ $(TARGET): $(OBJS) $(LD) $(TARGET).json $(OBJS_JSON) $(BUILD_DIR)/%.o: %.f90 | $(BUILD_DIR) - $(FC) -module-dir $(BUILD_DIR) -o $@.json $< + $(FC) -module-dir $(BUILD_DIR) -o $@ $< $(BUILD_DIR): mkdir -p $(BUILD_DIR) diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index f6636795..debb431d 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -82,7 +82,7 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu cd "$dir" make BUILD_DIR="$tmp_dir" cd "$tmp_dir" - find . -maxdepth 1 -name '*.json' ! -name '*.o.json' | while read -r file; do + find . -maxdepth 1 -name '*output.json' | while read -r file; do cp "$file" "$out_dir/$test_case_name.json" done ) || { From c9a6012e5bb2dec72cdb088a475951ce889c1c79 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:03 +0100 Subject: [PATCH 079/130] add norename option to wrapper script --- cgfcollector/tools/cgfcollector_wrapper.sh.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index 8ad19b09..3a1dd76c 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -11,6 +11,12 @@ if [[ "$1" == "-dot" ]]; then flang_args=("$@") fi +if [[ "$1" == "-norename" ]]; then + plugin_name="genCGNoRename" + shift + flang_args=("$@") +fi + if ! command -v "$flang_bin" &>/dev/null; then echo "Error: $flang_bin not found in PATH." exit 1 From 38345a0f154d4e2890905e6631238b0eaade3813 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:03 +0100 Subject: [PATCH 080/130] compiler wrapper now correctly filters options and also extract options from mpif90 wrapper if used --- .../tools/cgfcollector_comp_wrapper.sh.in | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 6577bee0..1ed60b62 100755 --- a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -1,6 +1,7 @@ #!/bin/bash flang_bin=${CGFCOLLECTOR_FLANG_BIN:-"flang-new"} +flang_fc1_bin=${CGFCOLLECTOR_FLANG_FC1_BIN:-"flang-new"} if ! command -v "$flang_bin" &>/dev/null; then echo "Error: $flang_bin not found in PATH." @@ -8,12 +9,25 @@ if ! command -v "$flang_bin" &>/dev/null; then fi source_files=() +options=() +all_args=("$@") -for arg in "$@"; do - # skip options - [[ "$arg" == -* ]] && continue +# If you're using the `mpif90` wrapper, we are extracting the underlying compiler arguments using `mpif90 --show`. +# +# Important: Ensure that `flang_fc1_bin` is set to the actual Flang binary (e.g. `flang-new`, which it is by default), +# not the `mpif90` wrapper itself. The wrapper adds MPI-specific flags that are incompatible with `-fc1`. +if [[ "$flang_bin" == *mpif90 ]]; then + read -r -a mpi_show_args <<<"$(mpif90 --show)" + all_args=("${mpi_show_args[@]}" "${all_args[@]}") +fi +# Extract all flags that are compatible with `-fc1`. +for arg in "${all_args[@]}"; do case "$arg" in + -I* | -J* | -std=* | -O* | -D* | -f* | -cpp) + options+=("$arg") + continue + ;; *.f90 | *.F90) source_files+=("$arg") ;; @@ -21,6 +35,6 @@ for arg in "$@"; do done if [ ${#source_files[@]} -gt 0 ]; then - $flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" "${source_files[@]}" + $flang_fc1_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" "${options[@]}" "${source_files[@]}" fi $flang_bin "$@" From 975dd0b180f91bdcfbc4e6a5e4701224467468ff Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 081/130] comp wrapper fix -o parsing --- cgfcollector/tools/cgfcollector_comp_wrapper.sh.in | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 1ed60b62..261aa47f 100755 --- a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -22,11 +22,21 @@ if [[ "$flang_bin" == *mpif90 ]]; then fi # Extract all flags that are compatible with `-fc1`. +option_o=false for arg in "${all_args[@]}"; do + if [ "$option_o" = true ]; then + options+=("$arg") + option_o=false + continue + fi + case "$arg" in -I* | -J* | -std=* | -O* | -D* | -f* | -cpp) options+=("$arg") - continue + ;; + -o) + options+=("$arg") + option_o=true ;; *.f90 | *.F90) source_files+=("$arg") From 732245f7ac1ebd1838789e2e53f7ba3b531f2bac Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 082/130] fix for crash in expr's in specification part of module --- cgfcollector/src/ParseTreeVisitor.cpp | 4 +++ cgfcollector/test/simple/operator4/main.f90 | 34 +++++++++++++++++++ .../test/simple/operator4/output.json | 29 ++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 cgfcollector/test/simple/operator4/main.f90 create mode 100644 cgfcollector/test/simple/operator4/output.json diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 75cb160d..e7c2e1d5 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -737,6 +737,10 @@ bool ParseTreeVisitor::Pre(const Expr& e) { return true; } + // not in a function. So no overloaded operator is called. + if (functionSymbols.empty()) + return true; + for (auto e : exprStmtWithOps) { // search in interfaceOperators first before search in derived types auto interfaceOp = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& op) { diff --git a/cgfcollector/test/simple/operator4/main.f90 b/cgfcollector/test/simple/operator4/main.f90 new file mode 100644 index 00000000..4b6e1a40 --- /dev/null +++ b/cgfcollector/test/simple/operator4/main.f90 @@ -0,0 +1,34 @@ +module mod + implicit none + + type :: base + integer :: value + end type base + + interface operator(*) + module procedure multiply_base + end interface + + real, parameter :: num = 2*3.1415926535898 + real, parameter :: num2 = num*0.5, num3 = num2*0.5 + + type(base), parameter :: pi = base(3.1415926535898) + type(base), parameter :: half = base(0.5) + ! type(base) :: half_pi = pi*half not possible becasue no constant + +contains + + function multiply_base(a, b) result(res) + type(base), intent(in) :: a, b + type(base) :: res + res%value = a%value*b%value + print *, "Multiplying base values: ", a%value, " * ", b%value, " = ", res%value + end function multiply_base + +end module mod + +program main + use mod + implicit none + +end program main diff --git a/cgfcollector/test/simple/operator4/output.json b/cgfcollector/test/simple/operator4/output.json new file mode 100644 index 00000000..20660d4d --- /dev/null +++ b/cgfcollector/test/simple/operator4/output.json @@ -0,0 +1,29 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPmultiply_base", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + }, + "1": { + "callees": {}, + "functionName": "_QQmain", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + } + } + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "4cc8210c90cb04678c623259ac8d2665c6474f45", + "version": "0.9" + }, + "version": "4.0" + } +} From a5997d042364b641f6eb1383b2892c1d4ab191f6 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 083/130] more consistent mangling, adjusted tests and fix typo --- cgfcollector/include/ParseTreeVisitor.h | 2 + cgfcollector/src/ParseTreeVisitor.cpp | 87 ++++++++---- .../test/simple/function_test/output.json | 38 ++--- .../test/simple/interop_external/output.json | 2 +- cgfcollector/test/simple/no_body/output.json | 34 ++--- cgfcollector/test/simple/operator/main.f90 | 16 +-- cgfcollector/test/simple/operator/output.json | 134 +++++++++--------- 7 files changed, 170 insertions(+), 143 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 7c982fe3..d57c4553 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -49,6 +49,8 @@ class ParseTreeVisitor { std::vector& getPotentialFinalizers() { return potentialFinalizers; } std::vector& getFunctions() { return functions; } + std::string mangleSymbol(const Symbol* sym); + template void handleFuncSubStmt(const T& stmt); void handleEndFuncSubStmt(); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index e7c2e1d5..0aba5627 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -2,15 +2,40 @@ // util functions +std::string ParseTreeVisitor::mangleSymbol(const Symbol* sym) { + if (!sym) { + al->error("Error: mangleSymbol called with nullptr"); + return ""; + } + + std::string mangledName = mangleName(*sym); + + // Legacy Fortran - C interoperability before BIND(C) existed. + // I have to do this manually because normally it would run as + // a pass (ExternalNameConversionPass). + // + // Disabling underscoring to be compatiable with C. + // WARNING: Fortran normally uses a underscore as postfix with external names. + // NOTE: underscoring can be disabled with `-fno-underscoring` + auto result = fir::NameUniquer::deconstruct(mangledName); + if (fir::NameUniquer::isExternalFacingUniquedName(result)) { + if (result.first == fir::NameUniquer::NameKind::COMMON && result.second.name.empty()) + mangledName = Fortran::common::blankCommonObjectName; + mangledName = Fortran::common::GetExternalAssemblyName(result.second.name, false); + } + + return mangledName; +} + template void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { functionSymbols.emplace_back(sym); functionDummyArgs.emplace_back(std::vector()); - cg->getOrInsertNode(mangleName(*sym), currentFileName, false, false); + cg->getOrInsertNode(mangleSymbol(sym), currentFileName, false, false); functions.push_back({sym, std::vector()}); - al->debug("Add node: {} ({})", mangleName(*sym), fmt::ptr(sym)); + al->debug("Add node: {} ({})", mangleSymbol(sym), fmt::ptr(sym)); } } @@ -29,7 +54,7 @@ void ParseTreeVisitor::handleEndFuncSubStmt() { } void ParseTreeVisitor::handleTrackedVars() { - if (mangleName(*functionSymbols.back()) != "_QQmain") { + if (mangleSymbol(functionSymbols.back()) != "_QQmain") { if (!trackedVars.empty()) al->debug("Handle tracked vars for function"); @@ -115,25 +140,25 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector type if (procIt == t.procedures.end()) continue; - edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*procIt->second)); + edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(procIt->second)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleName(*procIt->second), fmt::ptr(procIt->second)); + al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleSymbol(procIt->second), fmt::ptr(procIt->second)); } } void ParseTreeVisitor::addEdgesForFinalizers(const Symbol* typeSymbol) { for (const auto& edge : getEdgesForFinalizers(typeSymbol)) { - edges.emplace_back(mangleName(*edge.first), mangleName(*edge.second)); + edges.emplace_back(mangleSymbol(edge.first), mangleSymbol(edge.second)); - al->debug("Add edge for finalizer: {} ({}) -> {} ({})", mangleName(*edge.first), fmt::ptr(edge.first), - mangleName(*edge.second), fmt::ptr(edge.second)); + al->debug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.first), fmt::ptr(edge.first), + mangleSymbol(edge.second), fmt::ptr(edge.second)); } } void ParseTreeVisitor::addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol) { for (const auto& edge : getEdgesForFinalizers(typeSymbol)) { - edges->emplace_back(mangleName(*edge.first), mangleName(*edge.second)); + edges->emplace_back(mangleSymbol(edge.first), mangleSymbol(edge.second)); } } @@ -304,9 +329,9 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { return true; functionSymbols.emplace_back(maybeStmt->statement.v.symbol); - cg->getOrInsertNode(mangleName(*functionSymbols.back()), currentFileName, false, false); + cg->getOrInsertNode(mangleSymbol(functionSymbols.back()), currentFileName, false, false); - al->debug("\nIn main program: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); + al->debug("\nIn main program: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); } return true; } @@ -314,7 +339,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { void ParseTreeVisitor::Post(const MainProgram&) { handleTrackedVars(); - al->debug("End main program: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); + al->debug("End main program: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); if (!functionSymbols.empty()) { functionSymbols.pop_back(); @@ -341,7 +366,7 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { if (!inFunctionOrSubroutineSubProgram && !inMainProgram) return; - auto* node = cg->getFirstNode(mangleName(*functionSymbols.back())); + auto* node = cg->getFirstNode(mangleSymbol(functionSymbols.back())); if (!node) { return; } @@ -354,13 +379,13 @@ void ParseTreeVisitor::Post(const EntryStmt& e) { if (!name->symbol) return; - al->debug("Add Entry point: {} ({})", mangleName(*name->symbol), fmt::ptr(name->symbol)); + al->debug("Add Entry point: {} ({})", mangleSymbol(name->symbol), fmt::ptr(name->symbol)); - cg->getOrInsertNode(mangleName(*name->symbol), currentFileName, false, true); + cg->getOrInsertNode(mangleSymbol(name->symbol), currentFileName, false, true); } void ParseTreeVisitor::Post(const FunctionStmt& f) { - al->debug("\nIn function: {} ({})", mangleName(*std::get(f.t).symbol), fmt::ptr(std::get(f.t).symbol)); + al->debug("\nIn function: {} ({})", mangleSymbol(std::get(f.t).symbol), fmt::ptr(std::get(f.t).symbol)); handleFuncSubStmt(f); @@ -380,14 +405,14 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { void ParseTreeVisitor::Post(const EndFunctionStmt&) { if (!functionSymbols.empty()) { - al->debug("End function: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); + al->debug("End function: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); } handleEndFuncSubStmt(); } void ParseTreeVisitor::Post(const SubroutineStmt& s) { - al->debug("\nIn subroutine: {} ({})", mangleName(*std::get(s.t).symbol), fmt::ptr(std::get(s.t).symbol)); + al->debug("\nIn subroutine: {} ({})", mangleSymbol(std::get(s.t).symbol), fmt::ptr(std::get(s.t).symbol)); handleFuncSubStmt(s); @@ -408,7 +433,7 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { void ParseTreeVisitor::Post(const EndSubroutineStmt&) { if (!functionSymbols.empty()) { - al->debug("End subroutine: {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back())); + al->debug("End subroutine: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); } handleEndFuncSubStmt(); @@ -427,10 +452,10 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (name->symbol->attrs().test(Attr::INTRINSIC)) return; - edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*name->symbol)); + edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(name->symbol)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleName(*name->symbol), fmt::ptr(name->symbol)); + al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleSymbol(name->symbol), fmt::ptr(name->symbol)); // if called from a object with %. (base % component) } else if (auto* procCompRef = std::get_if(&p.u)) { @@ -438,10 +463,10 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (!symbolComp) return; - edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*symbolComp)); + edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(symbolComp)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleName(*symbolComp), fmt::ptr(symbolComp)); + al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back()), + mangleSymbol(symbolComp), fmt::ptr(symbolComp)); auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) @@ -511,7 +536,7 @@ void ParseTreeVisitor::Post(const Call& c) { if (!trackedVar) continue; - potentialFinalizer pf = {argPos, mangleName(*procName->symbol), std::vector()}; + potentialFinalizer pf = {argPos, mangleSymbol(procName->symbol), std::vector()}; addEdgesForFinalizers(&pf.finalizerEdges, getTypeSymbolFromSymbol(trackedVar->var)); potentialFinalizers.push_back(pf); @@ -763,7 +788,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { for (auto* sym : interfaceOp->second) { // skip self calls - if (mangleName(*sym) == mangleName(*functionSymbols.back())) + if (mangleSymbol(sym) == mangleSymbol(functionSymbols.back())) continue; // if unary, add potential unary operators. Same for binary operators. @@ -775,10 +800,10 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } } - edges.emplace_back(mangleName(*functionSymbols.back()), mangleName(*sym)); + edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(sym)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleName(*functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleName(*sym), fmt::ptr(sym)); + al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back()), mangleSymbol(sym), fmt::ptr(sym)); } } diff --git a/cgfcollector/test/simple/function_test/output.json b/cgfcollector/test/simple/function_test/output.json index 0ea1f111..9a44af2e 100644 --- a/cgfcollector/test/simple/function_test/output.json +++ b/cgfcollector/test/simple/function_test/output.json @@ -3,52 +3,52 @@ "meta": {}, "nodes": { "0": { - "callees": { "4": {} }, - "functionName": "_QFsizePfunc1", + "callees": {}, + "functionName": "_QMvector_operationsPvector_add", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { - "callees": { "4": {} }, - "functionName": "_QFsizePfunc2", + "callees": {}, + "functionName": "_QMvector_operationsPvector_norm", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, - "functionName": "_QMvector_operationsPvector_add", + "functionName": "vector_norm2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, - "functionName": "_QMvector_operationsPvector_norm", + "functionName": "size", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "4": { - "callees": {}, - "functionName": "_QPsize", + "callees": { "3": {} }, + "functionName": "_QFsizePfunc1", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "5": { - "callees": {}, - "functionName": "_QPvector_norm2", + "callees": { "3": {} }, + "functionName": "_QFsizePfunc2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "6": { - "callees": { "2": {}, "3": {}, "4": {} }, + "callees": { "0": {}, "1": {}, "3": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } @@ -56,7 +56,7 @@ "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "sha": "96b749864d4e9ffe279252205b6ef755a6a86056", "version": "0.9" }, "version": "4.0" diff --git a/cgfcollector/test/simple/interop_external/output.json b/cgfcollector/test/simple/interop_external/output.json index 18eaf968..d66b741a 100644 --- a/cgfcollector/test/simple/interop_external/output.json +++ b/cgfcollector/test/simple/interop_external/output.json @@ -4,7 +4,7 @@ "nodes": { "0": { "callees": {}, - "functionName": "_QPadd", + "functionName": "add", "hasBody": false, "meta": {}, "origin": null diff --git a/cgfcollector/test/simple/no_body/output.json b/cgfcollector/test/simple/no_body/output.json index 3526c7dd..7ed58eee 100644 --- a/cgfcollector/test/simple/no_body/output.json +++ b/cgfcollector/test/simple/no_body/output.json @@ -3,38 +3,38 @@ "meta": {}, "nodes": { "0": { - "callees": {}, - "functionName": "_QFFprint_starsPsubsub", + "callees": { "1": {}, "2": {} }, + "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { - "callees": { "2": {} }, - "functionName": "_QFPprint_stars", - "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "callees": {}, + "functionName": "print_stuff", + "hasBody": false, + "meta": {}, "origin": "main.f90" }, "2": { - "callees": {}, - "functionName": "_QPprint_stuff", - "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "callees": { "1": {} }, + "functionName": "_QFPprint_stars", + "hasBody": true, + "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, - "functionName": "_QPprint_stuff2", + "functionName": "print_stuff2", "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "4": { - "callees": { "1": {}, "2": {} }, - "functionName": "_QQmain", + "callees": {}, + "functionName": "_QFFprint_starsPsubsub", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } @@ -42,7 +42,7 @@ "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "sha": "96b749864d4e9ffe279252205b6ef755a6a86056", "version": "0.9" }, "version": "4.0" diff --git a/cgfcollector/test/simple/operator/main.f90 b/cgfcollector/test/simple/operator/main.f90 index 5983d6a1..d034daf3 100644 --- a/cgfcollector/test/simple/operator/main.f90 +++ b/cgfcollector/test/simple/operator/main.f90 @@ -26,7 +26,7 @@ end function not type, extends(sortable) :: integer_sortable integer :: value contains - procedure :: less_then => less_than_integer + procedure :: less_then => less_then_integer procedure :: not_impl => not_impl_integer end type integer_sortable @@ -44,18 +44,18 @@ end function not end interface contains - logical function less_than_integer(this, other) + logical function less_then_integer(this, other) class(integer_sortable), intent(in) :: this class(sortable), intent(in) :: other select type (other) type is (integer_sortable) - less_than_integer = this%value < other%value + less_then_integer = this%value < other%value print *, "Comparing integer_sortable: ", this%value, " < ", other%value class default error stop "Type mismatch in comparison" end select - end function less_than_integer + end function less_then_integer logical function not_impl(this) class(sortable), intent(in) :: this @@ -125,9 +125,9 @@ subroutine test_compare() allocate (b, source=d) if (a < b) then - print *, "a is less than b" + print *, "a is less then b" else - print *, "a is nat less than b" + print *, "a is nat less then b" end if end subroutine test_compare @@ -142,9 +142,9 @@ subroutine test_compare2() allocate (b, source=d) if (c < d) then - print *, "c is less than d" + print *, "c is less then d" else - print *, "c is not less than d" + print *, "c is not less then d" end if end subroutine test_compare2 diff --git a/cgfcollector/test/simple/operator/output.json b/cgfcollector/test/simple/operator/output.json index 01a2e4db..fc3c3ad0 100644 --- a/cgfcollector/test/simple/operator/output.json +++ b/cgfcollector/test/simple/operator/output.json @@ -3,138 +3,138 @@ "meta": {}, "nodes": { "0": { - "callees": { "8": {}, "9": {} }, - "functionName": "_QFPtest_add", - "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "callees": {}, + "functionName": "compare", + "hasBody": false, + "meta": {}, "origin": "main.f90" }, "1": { - "callees": { "8": {}, "9": {} }, - "functionName": "_QFPtest_add2", - "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "callees": {}, + "functionName": "not", + "hasBody": false, + "meta": {}, "origin": "main.f90" }, "10": { - "callees": {}, - "functionName": "_QMmodPless_than_integer", + "callees": { "0": {}, "2": {} }, + "functionName": "_QFPtest_compare", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "11": { - "callees": {}, - "functionName": "_QMmodPnegx", + "callees": { "0": {}, "2": {} }, + "functionName": "_QFPtest_compare2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "12": { - "callees": { "10": {}, "15": {} }, - "functionName": "_QMmodPnot_impl", + "callees": { "1": {}, "4": {} }, + "functionName": "_QFPtest_not", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "13": { - "callees": { "10": {}, "15": {} }, - "functionName": "_QMmodPnot_impl_integer", + "callees": { "5": {} }, + "functionName": "_QFPtest_negx", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "14": { - "callees": {}, - "functionName": "_QMmodPunary_plus", + "callees": { "6": {}, "7": {} }, + "functionName": "_QFPtest_add", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "15": { - "callees": {}, - "functionName": "_QPcompare", - "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "callees": { "6": {}, "7": {} }, + "functionName": "_QFPtest_add2", + "hasBody": true, + "meta": {}, "origin": "main.f90" }, "16": { - "callees": {}, - "functionName": "_QPnot", - "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "callees": { "8": {} }, + "functionName": "_QFPtest_unary_plus", + "hasBody": true, + "meta": {}, "origin": "main.f90" }, "17": { - "callees": { - "0": {}, - "1": {}, - "2": {}, - "3": {}, - "4": {}, - "5": {}, - "6": {}, - "7": {} - }, - "functionName": "_QQmain", + "callees": {}, + "functionName": "_QFPtest_expr", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { - "callees": { "10": {}, "15": {} }, - "functionName": "_QFPtest_compare", + "callees": {}, + "functionName": "_QMmodPless_then_integer", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { - "callees": { "10": {}, "15": {} }, - "functionName": "_QFPtest_compare2", + "callees": { "0": {}, "2": {} }, + "functionName": "_QMmodPnot_impl", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "4": { - "callees": {}, - "functionName": "_QFPtest_expr", + "callees": { "0": {}, "2": {} }, + "functionName": "_QMmodPnot_impl_integer", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "5": { - "callees": { "11": {} }, - "functionName": "_QFPtest_negx", + "callees": {}, + "functionName": "_QMmodPnegx", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "6": { - "callees": { "13": {}, "16": {} }, - "functionName": "_QFPtest_not", + "callees": {}, + "functionName": "_QMmodPadd_stuff", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "7": { - "callees": { "14": {} }, - "functionName": "_QFPtest_unary_plus", + "callees": {}, + "functionName": "_QMmodPadd_stuff2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "8": { "callees": {}, - "functionName": "_QMmodPadd_stuff", + "functionName": "_QMmodPunary_plus", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "9": { - "callees": {}, - "functionName": "_QMmodPadd_stuff2", + "callees": { + "10": {}, + "11": {}, + "12": {}, + "13": {}, + "14": {}, + "15": {}, + "16": {}, + "17": {} + }, + "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } @@ -142,7 +142,7 @@ "_MetaCG": { "generator": { "name": "MetaCG", - "sha": "68fb73aebcc0af419653b36a6b5e3e9668408d10", + "sha": "96b749864d4e9ffe279252205b6ef755a6a86056", "version": "0.9" }, "version": "4.0" From b908c7e90e8f9d59138703095dfc25f326d5643a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 084/130] FlangLLVM.cmake added required clang package --- cmake/FlangLLVM.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cmake/FlangLLVM.cmake b/cmake/FlangLLVM.cmake index 6a9241df..f636f9f3 100644 --- a/cmake/FlangLLVM.cmake +++ b/cmake/FlangLLVM.cmake @@ -1,3 +1,9 @@ +find_package( + Clang + REQUIRED + CONFIG +) + find_package( MLIR REQUIRED From 7424f198f88b71e7c3ae0d0104a5092eba82a583 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:05 +0100 Subject: [PATCH 085/130] fix cgManager wrong usage --- cgfcollector/src/main.cpp | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 3acfd119..115e400e 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -4,24 +4,17 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { public: - metacg::Callgraph* cg = nullptr; - - ~CollectCG() override { - // if (cg != nullptr) { - // delete cg; - // cg = nullptr; - // } - } + std::string generateCG() { + auto& mcgManager = metacg::graph::MCGManager::get(); + metacg::Callgraph* cg = mcgManager.getCallgraph("cg"); - void generateCG() { - if (cg != nullptr) { - return; + if (cg == nullptr) { + mcgManager.addToManagedGraphs("cg", std::make_unique(), true); + cg = mcgManager.getCallgraph("cg"); } AL* al = AL::getInstance(); - cg = new metacg::Callgraph(); - // TODO: remove if (std::getenv("CUSTOM_DEBUG")) { spdlog::set_pattern("%v"); @@ -59,13 +52,7 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { } al->flush(); - } - - std::string cgToString(metacg::Callgraph* cg) { - auto& mcgManager = metacg::graph::MCGManager::get(); - mcgManager.resetManager(); - mcgManager.addToManagedGraphs("test", std::unique_ptr(cg), true); mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); auto mcgWriter = metacg::io::createWriter(4); @@ -98,9 +85,7 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { } void executeAction() override { - generateCG(); - - std::string cgString = cgToString(cg); + std::string cgString = generateCG(); auto file = createOutputFile("json"); file->write(cgString.c_str(), cgString.size()); @@ -112,6 +97,12 @@ class CollectCGwithDot : public CollectCG { void executeAction() override { CollectCG::executeAction(); + auto& mcgManager = metacg::graph::MCGManager::get(); + metacg::Callgraph* cg = mcgManager.getCallgraph("cg"); + if (cg == nullptr) { + return; + } + metacg::io::dot::DotGenerator dotGen(cg); dotGen.generate(); @@ -124,9 +115,7 @@ class CollectCGwithDot : public CollectCG { class CollectCGNoRename : public CollectCG { public: void executeAction() override { - generateCG(); - - std::string cgString = cgToString(cg); + std::string cgString = generateCG(); auto file = createOutputFile(""); file->write(cgString.c_str(), cgString.size()); From 9d933f439495eb48a86675a4f79f18ce92ad72da Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:05 +0100 Subject: [PATCH 086/130] test_runner.sh now count test cases run and failed --- cgfcollector/tools/test_runner.sh.in | 38 ++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index debb431d..57076515 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -37,7 +37,11 @@ function should_be_run() return 1 } -find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "output.json" \; | while read -r dir; do +test_case_count=0 +skipped_test_case_count=0 +failed_test_case_count=0 + +while read -r dir; do dir="$(dirname "$dir")" output_file="$dir/output.json" @@ -49,9 +53,11 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu fi echo "Test case: $test_case_name" + ((test_case_count++)) - if [ ! -s "$output_file" ]; then - echo "Skipping empty or missing output file: $output_file" + if [ ! -f "$output_file" ]; then + echo "Skipping. Because missing output file: $output_file" + ((skipped_test_case_count++)) continue fi @@ -61,16 +67,21 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu ( cd "$dir" - cmake -S . -B "$tmp_dir" + cmake -S . -B "$tmp_dir" || { + echo "Error: cmake config failed" + exit 1 + } cd "$tmp_dir" - make + make || { + echo "Error: cmake build failed" + exit 1 + } find . -maxdepth 1 -name '*.json' | while read -r file; do cp "$file" "$out_dir/$test_case_name.json" done ) || { echo "Error: could not generate CG" rm -rf "$tmp_dir" - continue } rm -rf "$tmp_dir" @@ -80,7 +91,10 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu ( cd "$dir" - make BUILD_DIR="$tmp_dir" + make BUILD_DIR="$tmp_dir" || { + echo "Error: make build failed" + exit 1 + } cd "$tmp_dir" find . -maxdepth 1 -name '*output.json' | while read -r file; do cp "$file" "$out_dir/$test_case_name.json" @@ -88,7 +102,6 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu ) || { echo "Error: could not generate CG" rm -rf "$tmp_dir" - continue } rm -rf "$tmp_dir" @@ -99,7 +112,6 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu if ! @FCOLLECTOR_WRAPPER@ -dot -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then echo "Error: failed to generate callgraph" - continue fi fi fi @@ -107,7 +119,13 @@ find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "outpu if @FCOLLECTOR_CG_COMPARE@ "$output_file" "$out_dir/$test_case_name.json"; then echo "Test case passed" else + ((failed_test_case_count++)) echo "Error: Output mismatch" fi -done +done < <(find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "output.json" \;) + +echo "" +echo "Test cases run: $test_case_count" +echo "Skipped test cases: $skipped_test_case_count" +echo "Failed test cases: $failed_test_case_count" From b0c3c8ee4cf37bb6f4f54d959d5ea3b52e6ee4ec Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:05 +0100 Subject: [PATCH 087/130] add empty tests --- cgfcollector/test/simple/empty_main/main.f90 | 6 +++++ .../test/simple/empty_main/output.json | 22 +++++++++++++++++++ cgfcollector/test/simple/empty_module/mod.f90 | 5 +++++ .../test/simple/empty_module/output.json | 11 ++++++++++ 4 files changed, 44 insertions(+) create mode 100644 cgfcollector/test/simple/empty_main/main.f90 create mode 100644 cgfcollector/test/simple/empty_main/output.json create mode 100644 cgfcollector/test/simple/empty_module/mod.f90 create mode 100644 cgfcollector/test/simple/empty_module/output.json diff --git a/cgfcollector/test/simple/empty_main/main.f90 b/cgfcollector/test/simple/empty_main/main.f90 new file mode 100644 index 00000000..83b66288 --- /dev/null +++ b/cgfcollector/test/simple/empty_main/main.f90 @@ -0,0 +1,6 @@ +program main + + implicit none + +end program main + diff --git a/cgfcollector/test/simple/empty_main/output.json b/cgfcollector/test/simple/empty_main/output.json new file mode 100644 index 00000000..cf4669c2 --- /dev/null +++ b/cgfcollector/test/simple/empty_main/output.json @@ -0,0 +1,22 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QQmain", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + } + } + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "1cd843384a952a5a8ece2d8f7f3d6726964583f8", + "version": "0.9" + }, + "version": "4.0" + } +} diff --git a/cgfcollector/test/simple/empty_module/mod.f90 b/cgfcollector/test/simple/empty_module/mod.f90 new file mode 100644 index 00000000..2caa6d54 --- /dev/null +++ b/cgfcollector/test/simple/empty_module/mod.f90 @@ -0,0 +1,5 @@ +module mod + + implicit none + +end module mod diff --git a/cgfcollector/test/simple/empty_module/output.json b/cgfcollector/test/simple/empty_module/output.json new file mode 100644 index 00000000..df47c1cb --- /dev/null +++ b/cgfcollector/test/simple/empty_module/output.json @@ -0,0 +1,11 @@ +{ + "_CG": { "meta": {}, "nodes": {} }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "1cd843384a952a5a8ece2d8f7f3d6726964583f8", + "version": "0.9" + }, + "version": "4.0" + } +} From dc031f28eec51ee4a02e5f9a9a89fb8d5fb6420b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:06 +0100 Subject: [PATCH 088/130] reenabled underscoring for mangling --- cgfcollector/src/ParseTreeVisitor.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 0aba5627..f571e23c 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -14,14 +14,12 @@ std::string ParseTreeVisitor::mangleSymbol(const Symbol* sym) { // I have to do this manually because normally it would run as // a pass (ExternalNameConversionPass). // - // Disabling underscoring to be compatiable with C. - // WARNING: Fortran normally uses a underscore as postfix with external names. // NOTE: underscoring can be disabled with `-fno-underscoring` auto result = fir::NameUniquer::deconstruct(mangledName); if (fir::NameUniquer::isExternalFacingUniquedName(result)) { if (result.first == fir::NameUniquer::NameKind::COMMON && result.second.name.empty()) mangledName = Fortran::common::blankCommonObjectName; - mangledName = Fortran::common::GetExternalAssemblyName(result.second.name, false); + mangledName = Fortran::common::GetExternalAssemblyName(result.second.name, true); } return mangledName; From 5fc9a3d1b04569ef8afa12bcc749891f299ea0db Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:06 +0100 Subject: [PATCH 089/130] cgfcollector_comp_wrapper ignore -fPIC for flang -fc1 call --- cgfcollector/tools/cgfcollector_comp_wrapper.sh.in | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 261aa47f..49a80e34 100755 --- a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -31,6 +31,9 @@ for arg in "${all_args[@]}"; do fi case "$arg" in + -fPIC) + # skip + ;; -I* | -J* | -std=* | -O* | -D* | -f* | -cpp) options+=("$arg") ;; From f9e0ce00bb478be151248fa9d721a23d61d69933 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:06 +0100 Subject: [PATCH 090/130] add missing _ to some function names --- cgfcollector/test/simple/function_test/output.json | 4 ++-- cgfcollector/test/simple/interop_external/output.json | 2 +- cgfcollector/test/simple/no_body/output.json | 4 ++-- cgfcollector/test/simple/operator/output.json | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cgfcollector/test/simple/function_test/output.json b/cgfcollector/test/simple/function_test/output.json index 9a44af2e..7b5bcd12 100644 --- a/cgfcollector/test/simple/function_test/output.json +++ b/cgfcollector/test/simple/function_test/output.json @@ -18,14 +18,14 @@ }, "2": { "callees": {}, - "functionName": "vector_norm2", + "functionName": "vector_norm2_", "hasBody": true, "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, - "functionName": "size", + "functionName": "size_", "hasBody": true, "meta": {}, "origin": "main.f90" diff --git a/cgfcollector/test/simple/interop_external/output.json b/cgfcollector/test/simple/interop_external/output.json index d66b741a..399a3cce 100644 --- a/cgfcollector/test/simple/interop_external/output.json +++ b/cgfcollector/test/simple/interop_external/output.json @@ -4,7 +4,7 @@ "nodes": { "0": { "callees": {}, - "functionName": "add", + "functionName": "add_", "hasBody": false, "meta": {}, "origin": null diff --git a/cgfcollector/test/simple/no_body/output.json b/cgfcollector/test/simple/no_body/output.json index 7ed58eee..45d85e95 100644 --- a/cgfcollector/test/simple/no_body/output.json +++ b/cgfcollector/test/simple/no_body/output.json @@ -11,7 +11,7 @@ }, "1": { "callees": {}, - "functionName": "print_stuff", + "functionName": "print_stuff_", "hasBody": false, "meta": {}, "origin": "main.f90" @@ -25,7 +25,7 @@ }, "3": { "callees": {}, - "functionName": "print_stuff2", + "functionName": "print_stuff2_", "hasBody": false, "meta": {}, "origin": "main.f90" diff --git a/cgfcollector/test/simple/operator/output.json b/cgfcollector/test/simple/operator/output.json index fc3c3ad0..39629dbf 100644 --- a/cgfcollector/test/simple/operator/output.json +++ b/cgfcollector/test/simple/operator/output.json @@ -4,14 +4,14 @@ "nodes": { "0": { "callees": {}, - "functionName": "compare", + "functionName": "compare_", "hasBody": false, "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, - "functionName": "not", + "functionName": "not_", "hasBody": false, "meta": {}, "origin": "main.f90" From 75a373b6137280705eb1529358fc912d705ca4e3 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:07 +0100 Subject: [PATCH 091/130] extract info from use definition for derived types and add some tests --- cgfcollector/include/ParseTreeVisitor.h | 23 ++- cgfcollector/src/ParseTreeVisitor.cpp | 142 +++++++++++++++++- cgfcollector/test/multi/operator/1mod.f90 | 39 +++++ cgfcollector/test/multi/operator/2main.f90 | 27 ++++ .../test/multi/operator/CMakeLists.txt | 13 ++ cgfcollector/test/multi/operator/output.json | 43 ++++++ cgfcollector/test/multi/use/1mod.f90 | 34 +++++ cgfcollector/test/multi/use/2main.f90 | 11 ++ cgfcollector/test/multi/use/CMakeLists.txt | 13 ++ cgfcollector/test/multi/use/output.json | 36 +++++ 10 files changed, 368 insertions(+), 13 deletions(-) create mode 100644 cgfcollector/test/multi/operator/1mod.f90 create mode 100644 cgfcollector/test/multi/operator/2main.f90 create mode 100644 cgfcollector/test/multi/operator/CMakeLists.txt create mode 100644 cgfcollector/test/multi/operator/output.json create mode 100644 cgfcollector/test/multi/use/1mod.f90 create mode 100644 cgfcollector/test/multi/use/2main.f90 create mode 100644 cgfcollector/test/multi/use/CMakeLists.txt create mode 100644 cgfcollector/test/multi/use/output.json diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index d57c4553..b3f9bdb8 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -14,8 +14,8 @@ using edge = std::pair; // (caller, callee) struct type { Symbol* type; Symbol* extendsFrom; - std::vector> procedures; // name(symbol) => optname(symbol) - std::vector> operators; // operator => name(symbol) + std::vector> procedures; // name(symbol) => optname(symbol) + std::vector> operators; // operator => name(symbol) }; struct trackedVar { @@ -75,10 +75,14 @@ class ParseTreeVisitor { bool isOperator(const Expr* e); - bool compareExprIntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op); + bool compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op); bool isBinaryOperator(const Expr* e); bool isUnaryOperator(const Expr* e); + DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const RelationalOperator& op); + DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const LogicalOperator& op); + DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const NumericOperator& op); + template const Name* getNameFromClassWithDesignator(const T& t) { if (const auto* designator = std::get_if>(&t.u)) { @@ -160,6 +164,8 @@ class ParseTreeVisitor { bool Pre(const Expr& e); void Post(const Expr& e); + void Post(const UseStmt& u); + private: metacg::Callgraph* cg; std::vector edges; @@ -172,14 +178,15 @@ class ParseTreeVisitor { bool inInterfaceStmtDefinedOperator = false; bool inInterfaceSpecification = false; - std::vector functionSymbols; - std::vector> functionDummyArgs; + std::vector functionSymbols; // intended as a stack. It holds the current procedure symbol when the AST + // walker is in the respective procedure. + std::vector> functionDummyArgs; // some idea, but for dummy args - std::vector types; + std::vector types; // all types std::vector*, std::vector>> - interfaceOperators; // operator name (symbol) => [procedure names (symbols)] + interfaceOperators; // all interface operators. operator name (symbol) => [procedure names (symbols)] std::vector exprStmtWithOps; @@ -188,7 +195,7 @@ class ParseTreeVisitor { AL* al = AL::getInstance(); - std::vector functions; + std::vector functions; // all functions std::vector potentialFinalizers; }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index f571e23c..bbff49b1 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -193,13 +193,13 @@ bool ParseTreeVisitor::isOperator(const Expr* e) { Expr::DefinedBinary>(e->u); } -bool ParseTreeVisitor::compareExprIntrinsicOperator(const Expr* expr, const DefinedOperator::IntrinsicOperator* op) { - if (!expr || !op) +bool ParseTreeVisitor::compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op) { + if (!expr) return false; using IO = DefinedOperator::IntrinsicOperator; - switch (*op) { + switch (op) { case IO::NOT: return std::get_if(&expr->u) != nullptr; case IO::Power: @@ -257,6 +257,71 @@ bool ParseTreeVisitor::isUnaryOperator(const Expr* e) { return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::DefinedUnary>(e->u); } +DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(const RelationalOperator& op) { + using RO = RelationalOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case RO::LT: + return IO::LT; + case RO::LE: + return IO::LE; + case RO::EQ: + return IO::EQ; + case RO::NE: + return IO::NE; + case RO::GE: + return IO::GE; + case RO::GT: + return IO::GT; + default: + al->error("Error: Unknown RelationalOperator in mapToIntrinsicOperator"); + return IO::LT; // avoid warning + } +} + +DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(const LogicalOperator& op) { + using LO = LogicalOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case LO::And: + return IO::AND; + case LO::Or: + return IO::OR; + case LO::Eqv: + return IO::EQV; + case LO::Neqv: + return IO::NEQV; + case LO::Not: + return IO::NOT; + default: + al->error("Error: Unknown LogicalOperator in mapToIntrinsicOperator"); + return IO::AND; // avoid warning + } +} + +DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const NumericOperator& op) { + using NO = NumericOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case NO::Power: + return IO::Power; + case NO::Multiply: + return IO::Multiply; + case NO::Divide: + return IO::Divide; + case NO::Add: + return IO::Add; + case NO::Subtract: + return IO::Subtract; + default: + // should never happen + return IO::Add; // avoid warning + } +} + const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { auto* type = symbol->GetType(); if (!type) @@ -702,7 +767,7 @@ void ParseTreeVisitor::Post(const TypeBoundGenericStmt& s) { if (!name.symbol) continue; - currentType.operators.emplace_back(intrinsicOp, name.symbol); + currentType.operators.emplace_back(*intrinsicOp, name.symbol); al->debug("Add operator: {} -> {} ({})", DefinedOperator::EnumToString(*intrinsicOp), name.symbol->name(), fmt::ptr(name.symbol)); @@ -768,7 +833,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { // search in interfaceOperators first before search in derived types auto interfaceOp = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& op) { if (const auto* intrinsicOp = std::get_if(op.first)) { - return compareExprIntrinsicOperator(e, intrinsicOp); + return compareExprIntrinsicOperator(e, *intrinsicOp); } else if (const auto* definedOpName = std::get_if(op.first)) { if (auto* definedUnary = std::get_if(&e->u)) { auto* exprOpName = &std::get(definedUnary->t); @@ -850,3 +915,70 @@ void ParseTreeVisitor::Post(const Expr& e) { exprStmtWithOps.pop_back(); } } + +// extract additional information from use statements +void ParseTreeVisitor::Post(const UseStmt& u) { + auto* useSymbol = u.moduleName.symbol; + + al->debug("Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); + + if (const Scope* modScope = useSymbol->scope()) { + for (const auto& pair : *modScope) { + Symbol& symbol = *pair.second; + + // extract derived types from module and populate types var + if (const auto* details = symbol.detailsIf()) { + Symbol* extendsFrom = nullptr; + std::vector> procedures; + std::vector> operators; + + for (auto& pair : *symbol.scope()) { + Symbol& component = *pair.second; + + // extends + if (component.test(Symbol::Flag::ParentComp)) { + extendsFrom = const_cast(getTypeSymbolFromSymbol(&component)); // TODO: avoid const cast. ugly. + } + + // type bound procedures + if (component.has()) { + const auto& procDetails = component.get(); + al->debug("Found procedure in module derived type: {} ({})", component.name(), fmt::ptr(&component)); + procedures.emplace_back(&component, &component); + } + + // type generic operators + if (GenericDetails* gen = component.detailsIf()) { + auto op = gen->kind().u; + DefinedOperator::IntrinsicOperator intrinsicOp; + std::visit( + [&](auto&& opVal) { + using T = std::decay_t; + if constexpr (std::is_same_v || std::is_same_v || + std::is_same_v) { + intrinsicOp = mapToIntrinsicOperator(opVal); + } + }, + op); + + if (gen->specificProcs().size() != 1) + al->error("Generic more than one specific proc not handled. Should not happen."); + + Symbol* op_func_sym = nullptr; + op_func_sym = const_cast(&gen->specificProcs().front().get()); + + al->debug("Found operator in module derived type: {} -> {} ({})", + DefinedOperator::EnumToString(intrinsicOp), op_func_sym->name(), fmt::ptr(op_func_sym)); + + operators.push_back({intrinsicOp, op_func_sym}); + } + } + + types.push_back({&symbol, extendsFrom, procedures, operators}); + al->debug("Add derived type from module: {} ({})", symbol.name(), fmt::ptr(&symbol)); + } + } + } + + al->debug("Finished Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); +} diff --git a/cgfcollector/test/multi/operator/1mod.f90 b/cgfcollector/test/multi/operator/1mod.f90 new file mode 100644 index 00000000..2b8a8629 --- /dev/null +++ b/cgfcollector/test/multi/operator/1mod.f90 @@ -0,0 +1,39 @@ +module mod + + implicit none + + type, abstract :: sortable + contains + procedure(compare), deferred :: less_then + generic :: operator(<) => less_then + end type sortable + + interface + logical function compare(this, other) + import :: sortable + implicit none + class(sortable), intent(in) :: this, other + end function compare + end interface + + type, extends(sortable) :: integer_sortable + integer :: value + contains + procedure :: less_then => less_then_integer + end type integer_sortable + +contains + + logical function less_then_integer(this, other) + class(integer_sortable), intent(in) :: this + class(sortable), intent(in) :: other + + select type (other) + type is (integer_sortable) + less_then_integer = this%value < other%value + print *, "Comparing integer_sortable: ", this%value, " < ", other%value + class default + error stop "Type mismatch in comparison" + end select + end function less_then_integer +end module mod diff --git a/cgfcollector/test/multi/operator/2main.f90 b/cgfcollector/test/multi/operator/2main.f90 new file mode 100644 index 00000000..a8766598 --- /dev/null +++ b/cgfcollector/test/multi/operator/2main.f90 @@ -0,0 +1,27 @@ +program main + use mod + + implicit none + + call test_compare() + +contains + subroutine test_compare() + class(sortable), allocatable :: a, b + type(integer_sortable) :: c, d + + c%value = 5 + d%value = 10 + + allocate (a, source=c) + allocate (b, source=d) + + if (a < b) then + print *, "a is less then b" + else + print *, "a is nat less then b" + end if + end subroutine test_compare + +end program main + diff --git a/cgfcollector/test/multi/operator/CMakeLists.txt b/cgfcollector/test/multi/operator/CMakeLists.txt new file mode 100644 index 00000000..bb5d0cbc --- /dev/null +++ b/cgfcollector/test/multi/operator/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project(operator LANGUAGES Fortran) + +include(../cmake_base.txt) + +file( + GLOB + SOURCES + "*.f90" +) + +add_executable(${CMAKE_PROJECT_NAME} ${SOURCES}) diff --git a/cgfcollector/test/multi/operator/output.json b/cgfcollector/test/multi/operator/output.json new file mode 100644 index 00000000..9fcce8c3 --- /dev/null +++ b/cgfcollector/test/multi/operator/output.json @@ -0,0 +1,43 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "compare_", + "hasBody": false, + "meta": {}, + "origin": "1mod.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPless_then_integer", + "hasBody": true, + "meta": {}, + "origin": "1mod.f90" + }, + "2": { + "callees": { "3": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": {}, + "origin": "2main.f90" + }, + "3": { + "callees": { "0": {}, "1": {} }, + "functionName": "_QFPtest_compare", + "hasBody": true, + "meta": {}, + "origin": "2main.f90" + } + } + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "b2cf8d25b16310481bf5ba0fda1c99fbe2e69267", + "version": "0.9" + }, + "version": "4.0" + } +} diff --git a/cgfcollector/test/multi/use/1mod.f90 b/cgfcollector/test/multi/use/1mod.f90 new file mode 100644 index 00000000..e60d4155 --- /dev/null +++ b/cgfcollector/test/multi/use/1mod.f90 @@ -0,0 +1,34 @@ +module mod + + implicit none + + type :: base + private + real ::var + contains + procedure :: set_var => set_var_base + end type base + + type, extends(base) :: derived + contains + procedure :: set_var => set_var_derived + end type derived + +contains + + subroutine set_var_base(this, a) + class(base), intent(inout) :: this + real, intent(in) :: a + print *, 'Setting var from base' + this%var = a + end subroutine set_var_base + + subroutine set_var_derived(this, a) + class(derived), intent(inout) :: this + real, intent(in) :: a + print *, 'Setting var from derived' + this%var = a + end subroutine set_var_derived + +end module mod + diff --git a/cgfcollector/test/multi/use/2main.f90 b/cgfcollector/test/multi/use/2main.f90 new file mode 100644 index 00000000..4faf339b --- /dev/null +++ b/cgfcollector/test/multi/use/2main.f90 @@ -0,0 +1,11 @@ +program main + use mod + implicit none + + class(base), allocatable :: b + allocate (derived :: b) + + call b%set_var(3.14) + +end program main + diff --git a/cgfcollector/test/multi/use/CMakeLists.txt b/cgfcollector/test/multi/use/CMakeLists.txt new file mode 100644 index 00000000..dd95a937 --- /dev/null +++ b/cgfcollector/test/multi/use/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project(use LANGUAGES Fortran) + +include(../cmake_base.txt) + +file( + GLOB + SOURCES + "*.f90" +) + +add_executable(${CMAKE_PROJECT_NAME} ${SOURCES}) diff --git a/cgfcollector/test/multi/use/output.json b/cgfcollector/test/multi/use/output.json new file mode 100644 index 00000000..db9671c8 --- /dev/null +++ b/cgfcollector/test/multi/use/output.json @@ -0,0 +1,36 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPset_var_base", + "hasBody": true, + "meta": {}, + "origin": "1mod.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPset_var_derived", + "hasBody": true, + "meta": {}, + "origin": "1mod.f90" + }, + "2": { + "callees": { "0": {}, "1": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": {}, + "origin": "2main.f90" + } + } + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "b2cf8d25b16310481bf5ba0fda1c99fbe2e69267", + "version": "0.9" + }, + "version": "4.0" + } +} From 59fad1b09a51bb74d7e509727c56443f3db30817 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:07 +0100 Subject: [PATCH 092/130] forget to rename test files --- cgfcollector/test/multi/operator/{2main.f90 => main.f90} | 0 cgfcollector/test/multi/operator/{1mod.f90 => mod.f90} | 0 cgfcollector/test/multi/use/{2main.f90 => main.f90} | 0 cgfcollector/test/multi/use/{1mod.f90 => mod.f90} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename cgfcollector/test/multi/operator/{2main.f90 => main.f90} (100%) rename cgfcollector/test/multi/operator/{1mod.f90 => mod.f90} (100%) rename cgfcollector/test/multi/use/{2main.f90 => main.f90} (100%) rename cgfcollector/test/multi/use/{1mod.f90 => mod.f90} (100%) diff --git a/cgfcollector/test/multi/operator/2main.f90 b/cgfcollector/test/multi/operator/main.f90 similarity index 100% rename from cgfcollector/test/multi/operator/2main.f90 rename to cgfcollector/test/multi/operator/main.f90 diff --git a/cgfcollector/test/multi/operator/1mod.f90 b/cgfcollector/test/multi/operator/mod.f90 similarity index 100% rename from cgfcollector/test/multi/operator/1mod.f90 rename to cgfcollector/test/multi/operator/mod.f90 diff --git a/cgfcollector/test/multi/use/2main.f90 b/cgfcollector/test/multi/use/main.f90 similarity index 100% rename from cgfcollector/test/multi/use/2main.f90 rename to cgfcollector/test/multi/use/main.f90 diff --git a/cgfcollector/test/multi/use/1mod.f90 b/cgfcollector/test/multi/use/mod.f90 similarity index 100% rename from cgfcollector/test/multi/use/1mod.f90 rename to cgfcollector/test/multi/use/mod.f90 From 023fb4d2c6461977196ac40116f176059bb6f09c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:07 +0100 Subject: [PATCH 093/130] fixed origin field in multi operator and use tests --- cgfcollector/test/multi/operator/output.json | 8 ++++---- cgfcollector/test/multi/use/output.json | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cgfcollector/test/multi/operator/output.json b/cgfcollector/test/multi/operator/output.json index 9fcce8c3..58a91784 100644 --- a/cgfcollector/test/multi/operator/output.json +++ b/cgfcollector/test/multi/operator/output.json @@ -7,28 +7,28 @@ "functionName": "compare_", "hasBody": false, "meta": {}, - "origin": "1mod.f90" + "origin": "mod.f90" }, "1": { "callees": {}, "functionName": "_QMmodPless_then_integer", "hasBody": true, "meta": {}, - "origin": "1mod.f90" + "origin": "mod.f90" }, "2": { "callees": { "3": {} }, "functionName": "_QQmain", "hasBody": true, "meta": {}, - "origin": "2main.f90" + "origin": "main.f90" }, "3": { "callees": { "0": {}, "1": {} }, "functionName": "_QFPtest_compare", "hasBody": true, "meta": {}, - "origin": "2main.f90" + "origin": "main.f90" } } }, diff --git a/cgfcollector/test/multi/use/output.json b/cgfcollector/test/multi/use/output.json index db9671c8..2c7c7b15 100644 --- a/cgfcollector/test/multi/use/output.json +++ b/cgfcollector/test/multi/use/output.json @@ -7,21 +7,21 @@ "functionName": "_QMmodPset_var_base", "hasBody": true, "meta": {}, - "origin": "1mod.f90" + "origin": "mod.f90" }, "1": { "callees": {}, "functionName": "_QMmodPset_var_derived", "hasBody": true, "meta": {}, - "origin": "1mod.f90" + "origin": "mod.f90" }, "2": { "callees": { "0": {}, "1": {} }, "functionName": "_QQmain", "hasBody": true, "meta": {}, - "origin": "2main.f90" + "origin": "main.f90" } } }, From ad4c8204734caff703cc721a6ce7284d62e3f941 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:08 +0100 Subject: [PATCH 094/130] add collection of interface operators from use statement. Same idea as collection of derived type info. And add tests --- cgfcollector/include/ParseTreeVisitor.h | 19 +- cgfcollector/src/ParseTreeVisitor.cpp | 220 +++++++++++------- .../multi/operator_interface/CMakeLists.txt | 13 ++ .../test/multi/operator_interface/main.f90 | 20 ++ .../test/multi/operator_interface/mod.f90 | 42 ++++ .../test/multi/operator_interface/output.json | 43 ++++ 6 files changed, 260 insertions(+), 97 deletions(-) create mode 100644 cgfcollector/test/multi/operator_interface/CMakeLists.txt create mode 100644 cgfcollector/test/multi/operator_interface/main.f90 create mode 100644 cgfcollector/test/multi/operator_interface/mod.f90 create mode 100644 cgfcollector/test/multi/operator_interface/output.json diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index b3f9bdb8..ca8528b3 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -79,10 +79,6 @@ class ParseTreeVisitor { bool isBinaryOperator(const Expr* e); bool isUnaryOperator(const Expr* e); - DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const RelationalOperator& op); - DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const LogicalOperator& op); - DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const NumericOperator& op); - template const Name* getNameFromClassWithDesignator(const T& t) { if (const auto* designator = std::get_if>(&t.u)) { @@ -98,11 +94,18 @@ class ParseTreeVisitor { const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); trackedVar* getTrackedVarFromSourceName(SourceName sourceName); + + // search trackedVars for a canditate and set it as initialized. + // Prefers local variables when (shadowed) void handleTrackedVarAssignment(SourceName sourceName); void addTrackedVar(trackedVar var); void removeTrackedVars(Symbol* procedureSymbol); + // map RelationalOperator, LogicalOperator, NumericOperator to DefinedOperator::IntrinsicOperator + template + DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const Variant& op); + template bool Pre(const A&) { return true; @@ -164,6 +167,7 @@ class ParseTreeVisitor { bool Pre(const Expr& e); void Post(const Expr& e); + // extract additional information from use statements void Post(const UseStmt& u); private: @@ -180,13 +184,14 @@ class ParseTreeVisitor { std::vector functionSymbols; // intended as a stack. It holds the current procedure symbol when the AST // walker is in the respective procedure. - std::vector> functionDummyArgs; // some idea, but for dummy args + std::vector> functionDummyArgs; // same idea, but for dummy args std::vector types; // all types - std::vector*, + std::vector, std::vector>> - interfaceOperators; // all interface operators. operator name (symbol) => [procedure names (symbols)] + interfaceOperators; // all interface operators. First is either a symbol of a DefinedOpName or + // IntrinsicOperator. Second is a vector procedure symbols, bound to that operator. std::vector exprStmtWithOps; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index bbff49b1..6213c2f9 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -257,71 +257,6 @@ bool ParseTreeVisitor::isUnaryOperator(const Expr* e) { return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::DefinedUnary>(e->u); } -DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(const RelationalOperator& op) { - using RO = RelationalOperator; - using IO = DefinedOperator::IntrinsicOperator; - - switch (op) { - case RO::LT: - return IO::LT; - case RO::LE: - return IO::LE; - case RO::EQ: - return IO::EQ; - case RO::NE: - return IO::NE; - case RO::GE: - return IO::GE; - case RO::GT: - return IO::GT; - default: - al->error("Error: Unknown RelationalOperator in mapToIntrinsicOperator"); - return IO::LT; // avoid warning - } -} - -DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(const LogicalOperator& op) { - using LO = LogicalOperator; - using IO = DefinedOperator::IntrinsicOperator; - - switch (op) { - case LO::And: - return IO::AND; - case LO::Or: - return IO::OR; - case LO::Eqv: - return IO::EQV; - case LO::Neqv: - return IO::NEQV; - case LO::Not: - return IO::NOT; - default: - al->error("Error: Unknown LogicalOperator in mapToIntrinsicOperator"); - return IO::AND; // avoid warning - } -} - -DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const NumericOperator& op) { - using NO = NumericOperator; - using IO = DefinedOperator::IntrinsicOperator; - - switch (op) { - case NO::Power: - return IO::Power; - case NO::Multiply: - return IO::Multiply; - case NO::Divide: - return IO::Divide; - case NO::Add: - return IO::Add; - case NO::Subtract: - return IO::Subtract; - default: - // should never happen - return IO::Add; // avoid warning - } -} - const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { auto* type = symbol->GetType(); if (!type) @@ -350,8 +285,6 @@ trackedVar* ParseTreeVisitor::getTrackedVarFromSourceName(SourceName sourceName) return (localVarIt != trackedVars.end()) ? &(*localVarIt) : &(*anyTrackedVarIt); } -// search trackedVars for a canditate and set it as initialized. -// Prefers local variables when (shadowed) void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { auto* trackedVar = getTrackedVarFromSourceName(sourceName); if (!trackedVar) @@ -382,6 +315,77 @@ void ParseTreeVisitor::removeTrackedVars(Symbol* procedureSymbol) { trackedVars.end()); } +template +DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(const Variant& op) { + return std::visit(visitors{[this](const RelationalOperator& op) { + using RO = RelationalOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case RO::LT: + return IO::LT; + case RO::LE: + return IO::LE; + case RO::EQ: + return IO::EQ; + case RO::NE: + return IO::NE; + case RO::GE: + return IO::GE; + case RO::GT: + return IO::GT; + default: + al->error("Error: Unknown RelationalOperator in getIntrinsicOperator"); + return IO::LT; // avoid warning + } + }, + [this](const LogicalOperator& op) { + using LO = LogicalOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case LO::And: + return IO::AND; + case LO::Or: + return IO::OR; + case LO::Eqv: + return IO::EQV; + case LO::Neqv: + return IO::NEQV; + case LO::Not: + return IO::NOT; + default: + al->error("Error: Unknown LogicalOperator in getIntrinsicOperator"); + return IO::AND; // avoid warning + } + }, + [this](const NumericOperator& op) { + using NO = NumericOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case NO::Power: + return IO::Power; + case NO::Multiply: + return IO::Multiply; + case NO::Divide: + return IO::Divide; + case NO::Add: + return IO::Add; + case NO::Subtract: + return IO::Subtract; + default: + al->error("Error: Unknown NumericOperator in getIntrinsicOperator"); + return IO::Add; // avoid warning + } + }, + [this](const auto& op) { + al->error("Error: Unknown operator type in getIntrinsicOperator"); + return DefinedOperator::IntrinsicOperator::Add; // avoid warning + }}, + op); +} + // Visitor implementations bool ParseTreeVisitor::Pre(const MainProgram& p) { @@ -793,7 +797,16 @@ void ParseTreeVisitor::Post(const DefinedOperator& op) { inInterfaceStmtDefinedOperator = true; - interfaceOperators.emplace_back(&op.u, std::vector()); + if (std::holds_alternative(op.u)) { + auto intrinsicOp = std::get(op.u); + interfaceOperators.emplace_back(intrinsicOp, std::vector()); + } else if (std::holds_alternative(op.u)) { + const auto& opName = std::get(op.u); + if (!opName.v.symbol) + return; + + interfaceOperators.emplace_back(opName.v.symbol, std::vector()); + } } void ParseTreeVisitor::Post(const ProcedureStmt& p) { @@ -832,15 +845,17 @@ bool ParseTreeVisitor::Pre(const Expr& e) { for (auto e : exprStmtWithOps) { // search in interfaceOperators first before search in derived types auto interfaceOp = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& op) { - if (const auto* intrinsicOp = std::get_if(op.first)) { - return compareExprIntrinsicOperator(e, *intrinsicOp); - } else if (const auto* definedOpName = std::get_if(op.first)) { - if (auto* definedUnary = std::get_if(&e->u)) { - auto* exprOpName = &std::get(definedUnary->t); - return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); + if (std::holds_alternative(op.first)) { + auto intrinsicOp = std::get(op.first); + return compareExprIntrinsicOperator(e, intrinsicOp); + } else if (std::holds_alternative(op.first)) { + Symbol* definedOpNameSym = std::get(op.first); + if (const auto* definedUnary = std::get_if(&e->u)) { + const auto& exprOpName = std::get<0>(definedUnary->t); + return definedOpNameSym->name() == exprOpName.v.symbol->name(); } else if (auto* definedBinary = std::get_if(&e->u)) { - auto* exprOpName = &std::get(definedBinary->t); - return definedOpName->v.symbol->name() == exprOpName->v.symbol->name(); + const auto& exprOpName = std::get<0>(definedBinary->t); + return definedOpNameSym->name() == exprOpName.v.symbol->name(); } } return false; @@ -916,7 +931,6 @@ void ParseTreeVisitor::Post(const Expr& e) { } } -// extract additional information from use statements void ParseTreeVisitor::Post(const UseStmt& u) { auto* useSymbol = u.moduleName.symbol; @@ -949,20 +963,10 @@ void ParseTreeVisitor::Post(const UseStmt& u) { // type generic operators if (GenericDetails* gen = component.detailsIf()) { - auto op = gen->kind().u; - DefinedOperator::IntrinsicOperator intrinsicOp; - std::visit( - [&](auto&& opVal) { - using T = std::decay_t; - if constexpr (std::is_same_v || std::is_same_v || - std::is_same_v) { - intrinsicOp = mapToIntrinsicOperator(opVal); - } - }, - op); + DefinedOperator::IntrinsicOperator intrinsicOp = mapToIntrinsicOperator(gen->kind().u); if (gen->specificProcs().size() != 1) - al->error("Generic more than one specific proc not handled. Should not happen."); + al->error("Type-bound generic more than one specific proc not handled. Should not happen."); Symbol* op_func_sym = nullptr; op_func_sym = const_cast(&gen->specificProcs().front().get()); @@ -975,7 +979,43 @@ void ParseTreeVisitor::Post(const UseStmt& u) { } types.push_back({&symbol, extendsFrom, procedures, operators}); - al->debug("Add derived type from module: {} ({})", symbol.name(), fmt::ptr(&symbol)); + al->debug("Found derived type in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); + } + + // same but with interface operators + if (const auto* gen = symbol.detailsIf()) { + std::variant interfaceOp; + std::vector procs; + + if (gen->kind().IsIntrinsicOperator()) { + interfaceOp = mapToIntrinsicOperator(gen->kind().u); + al->debug("Found interface operator in module: {}", + DefinedOperator::EnumToString(std::get(interfaceOp))); + } else if (gen->kind().IsDefinedOperator()) { + interfaceOp = &symbol; + al->debug("Found interface operator in module: {}", symbol.name()); + } + + for (const auto& p : gen->specificProcs()) { + procs.push_back(const_cast(&p.get())); + al->debug(" with procedure: {} ({})", p.get().name(), fmt::ptr(&p.get())); + } + + interfaceOperators.push_back({interfaceOp, procs}); + } + + // same but with functions + if (const auto* details = symbol.detailsIf()) { + if (!details->isFunction() && !details->isInterface()) + continue; + + std::vector dummyArgs; + for (Symbol* arg : details->dummyArgs()) { + dummyArgs.push_back({arg, false}); + } + + functions.push_back({&symbol, dummyArgs}); + al->debug("Found function in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); } } } diff --git a/cgfcollector/test/multi/operator_interface/CMakeLists.txt b/cgfcollector/test/multi/operator_interface/CMakeLists.txt new file mode 100644 index 00000000..4576061d --- /dev/null +++ b/cgfcollector/test/multi/operator_interface/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project(operator_interface LANGUAGES Fortran) + +include(../cmake_base.txt) + +file( + GLOB + SOURCES + "*.f90" +) + +add_executable(${CMAKE_PROJECT_NAME} ${SOURCES}) diff --git a/cgfcollector/test/multi/operator_interface/main.f90 b/cgfcollector/test/multi/operator_interface/main.f90 new file mode 100644 index 00000000..f3e08d8b --- /dev/null +++ b/cgfcollector/test/multi/operator_interface/main.f90 @@ -0,0 +1,20 @@ +program main + use mod + + implicit none + + integer :: x, y, res + real :: e = 5.0, f + + x = 5 + y = 10 + + res = x + y + + print *, "Result of x + y = ", res + + f = .NEGX.e + + print *, "Negated value: ", f +end program main + diff --git a/cgfcollector/test/multi/operator_interface/mod.f90 b/cgfcollector/test/multi/operator_interface/mod.f90 new file mode 100644 index 00000000..013fc4e8 --- /dev/null +++ b/cgfcollector/test/multi/operator_interface/mod.f90 @@ -0,0 +1,42 @@ +module mod + + implicit none + + type :: integer_wrapper + integer :: value + end type integer_wrapper + + interface operator(+) + procedure add_stuff + module procedure unary_plus + end interface + + interface operator(.NEGX.) + module procedure negx + end interface + +contains + + function add_stuff(x, y) result(res) + type(integer_wrapper), intent(in) :: x, y + type(integer_wrapper) :: res + res%value = x%value + y%value + print *, "Hello from add_stuff" + end function add_stuff + + function unary_plus(x) result(res) + type(integer_wrapper), intent(in) :: x + type(integer_wrapper) :: res + res%value = x%value + print *, "Hello from unary_plus" + end function unary_plus + + function negx(x) result(res) + real, intent(in) :: x + real :: res + res = -x + print *, "Hello from negx" + end function negx + +end module mod + diff --git a/cgfcollector/test/multi/operator_interface/output.json b/cgfcollector/test/multi/operator_interface/output.json new file mode 100644 index 00000000..037ac23c --- /dev/null +++ b/cgfcollector/test/multi/operator_interface/output.json @@ -0,0 +1,43 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPadd_stuff", + "hasBody": true, + "meta": {}, + "origin": "mod.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmodPunary_plus", + "hasBody": true, + "meta": {}, + "origin": "mod.f90" + }, + "2": { + "callees": {}, + "functionName": "_QMmodPnegx", + "hasBody": true, + "meta": {}, + "origin": "mod.f90" + }, + "3": { + "callees": { "0": {}, "2": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + } + } + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "37c33e071f7bc6b1dba2e01ff2e7305aaeb3ccee", + "version": "0.9" + }, + "version": "4.0" + } +} From 6344fba7ff1e0a4ec6cacf0b1e4eb216e5440371 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:08 +0100 Subject: [PATCH 095/130] add comment --- cgfcollector/src/ParseTreeVisitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 6213c2f9..a86eb179 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -1006,7 +1006,7 @@ void ParseTreeVisitor::Post(const UseStmt& u) { // same but with functions if (const auto* details = symbol.detailsIf()) { - if (!details->isFunction() && !details->isInterface()) + if (!details->isFunction() && !details->isInterface()) // function and function dummy definition in interface continue; std::vector dummyArgs; From 150a245a811833034ee7cda57a1798c4db866496 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:08 +0100 Subject: [PATCH 096/130] fix test runner: some failed test got not counted as failed --- cgfcollector/tools/test_runner.sh.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 57076515..0ff0c088 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -82,6 +82,8 @@ while read -r dir; do ) || { echo "Error: could not generate CG" rm -rf "$tmp_dir" + ((failed_test_case_count++)) + continue } rm -rf "$tmp_dir" @@ -102,6 +104,8 @@ while read -r dir; do ) || { echo "Error: could not generate CG" rm -rf "$tmp_dir" + ((failed_test_case_count++)) + continue } rm -rf "$tmp_dir" @@ -112,6 +116,8 @@ while read -r dir; do if ! @FCOLLECTOR_WRAPPER@ -dot -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then echo "Error: failed to generate callgraph" + ((failed_test_case_count++)) + continue fi fi fi From 190c85c50d634b23d63ff5ddd646b4b7b9fadd2d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:09 +0100 Subject: [PATCH 097/130] fix GenericDetails restrict to only IsIntrinsicOperator and fix typo --- cgfcollector/src/ParseTreeVisitor.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index a86eb179..fee1d2ec 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -335,7 +335,7 @@ DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(cons case RO::GT: return IO::GT; default: - al->error("Error: Unknown RelationalOperator in getIntrinsicOperator"); + al->error("Error: Unknown RelationalOperator in mapToIntrinsicOperator"); return IO::LT; // avoid warning } }, @@ -355,7 +355,7 @@ DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(cons case LO::Not: return IO::NOT; default: - al->error("Error: Unknown LogicalOperator in getIntrinsicOperator"); + al->error("Error: Unknown LogicalOperator in mapToIntrinsicOperator"); return IO::AND; // avoid warning } }, @@ -375,12 +375,12 @@ DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(cons case NO::Subtract: return IO::Subtract; default: - al->error("Error: Unknown NumericOperator in getIntrinsicOperator"); + al->error("Error: Unknown NumericOperator in mapToIntrinsicOperator"); return IO::Add; // avoid warning } }, [this](const auto& op) { - al->error("Error: Unknown operator type in getIntrinsicOperator"); + al->error("Error: Unknown operator type in mapToIntrinsicOperator"); return DefinedOperator::IntrinsicOperator::Add; // avoid warning }}, op); @@ -963,6 +963,9 @@ void ParseTreeVisitor::Post(const UseStmt& u) { // type generic operators if (GenericDetails* gen = component.detailsIf()) { + if (!gen->kind().IsIntrinsicOperator()) + continue; + DefinedOperator::IntrinsicOperator intrinsicOp = mapToIntrinsicOperator(gen->kind().u); if (gen->specificProcs().size() != 1) @@ -994,6 +997,8 @@ void ParseTreeVisitor::Post(const UseStmt& u) { } else if (gen->kind().IsDefinedOperator()) { interfaceOp = &symbol; al->debug("Found interface operator in module: {}", symbol.name()); + } else { + continue; } for (const auto& p : gen->specificProcs()) { From 1256b80dd638845e0a85571a7e258c52a47b96b0 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:09 +0100 Subject: [PATCH 098/130] types array missing extendsfrom entry add more descriptive error msg --- cgfcollector/src/ParseTreeVisitor.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index fee1d2ec..8d3317c9 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -112,7 +112,8 @@ std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeS auto currentType = std::find_if(types.begin(), types.end(), [¤tExtendsFrom](const type& t) { return t.type == currentExtendsFrom; }); if (currentType == types.end()) { - al->error("Error: Types array (extendsFrom) field entry missing."); + al->error("Error: Types array (extendsFrom) field entry for \"" + currentExtendsFrom->name().ToString() + + "\" missing."); return typeWithDerived; } From aa03b593195e2648e754784f339c66e857da9b7d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:09 +0100 Subject: [PATCH 099/130] fix: i am now collecting type though the inheritance structure correctly. so mostly findTypeWithDerivedTypes method. and add test. --- cgfcollector/include/ParseTreeVisitor.h | 4 +- cgfcollector/src/ParseTreeVisitor.cpp | 129 ++++++++++++------ cgfcollector/src/main.cpp | 5 + .../test/multi/module_inherit/CMakeLists.txt | 13 ++ .../test/multi/module_inherit/main.f90 | 67 +++++++++ .../test/multi/module_inherit/mod.f90 | 21 +++ .../test/multi/module_inherit/output.json | 64 +++++++++ 7 files changed, 259 insertions(+), 44 deletions(-) create mode 100644 cgfcollector/test/multi/module_inherit/CMakeLists.txt create mode 100644 cgfcollector/test/multi/module_inherit/main.f90 create mode 100644 cgfcollector/test/multi/module_inherit/mod.f90 create mode 100644 cgfcollector/test/multi/module_inherit/output.json diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index ca8528b3..34432221 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -58,11 +58,11 @@ class ParseTreeVisitor { void handleTrackedVars(); // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. - std::vector findTypeWithDerivedTypes(const Symbol* typeSymbol); + std::vector findTypeWithDerivedTypes(const Symbol* typeSymbol); // this function searches with typeSymbol for a type in types vector and adds edges for procedures that matches // procedureSymbol. And also adds edges from types that extends from typeSymbol. - void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); + void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); void addEdgesForFinalizers(const Symbol* typeSymbol); void addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 8d3317c9..53a680a0 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -1,5 +1,41 @@ #include "ParseTreeVisitor.h" +// private functions + +static bool compareSymbols(const Symbol* a, const Symbol* b) { + if (a == b) + return true; + if (!a || !b) + return false; + if (a->name() != b->name()) + return false; + + auto resolveHostAssoc = [](const Symbol* sym) -> const Symbol* { + while (sym) { + if (sym->has()) { + sym = &sym->get().symbol(); + } else if (sym->has()) { + sym = &sym->get().symbol(); + } else { + break; + } + } + return sym; + }; + if (resolveHostAssoc(a) != resolveHostAssoc(b)) + return false; + + if (a->attrs() != b->attrs()) + return false; + + // this only compares only the type and not all details like variables, procedures, generics, etc. But should be + // enough for now. + if (a->GetType() != b->GetType()) + return false; + + return true; +} + // util functions std::string ParseTreeVisitor::mangleSymbol(const Symbol* sym) { @@ -87,56 +123,65 @@ void ParseTreeVisitor::handleTrackedVars() { removeTrackedVars(functionSymbols.back()); } -std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeSymbol) { - std::vector typeWithDerived; +std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeSymbol) { + std::vector typesWithDerived; + std::unordered_set visited; auto findTypeIt = std::find_if(types.begin(), types.end(), [&typeSymbol](const type& t) { return t.type == typeSymbol; }); - if (findTypeIt == types.end()) - return typeWithDerived; - typeWithDerived.push_back(*findTypeIt); + if (findTypeIt == types.end()) { + return typesWithDerived; + } - // base type - if ((*findTypeIt).extendsFrom == nullptr) { - for (auto t : types) { - if (t.extendsFrom != typeSymbol) - continue; + typesWithDerived.push_back(&(*findTypeIt)); // Add the initial type + visited.insert(typeSymbol); - typeWithDerived.push_back(t); - } - // not a base type, go back recursively to find all "base" types - } else { - auto* currentExtendsFrom = (*findTypeIt).extendsFrom; - while (currentExtendsFrom) { - auto currentType = std::find_if(types.begin(), types.end(), - [¤tExtendsFrom](const type& t) { return t.type == currentExtendsFrom; }); - if (currentType == types.end()) { - al->error("Error: Types array (extendsFrom) field entry for \"" + currentExtendsFrom->name().ToString() + - "\" missing."); - return typeWithDerived; + // collect descendants + std::function collectDescendants = [&](const type* parent) { + for (const auto& t : types) { + if (t.extendsFrom == parent->type && !visited.count(t.type)) { + visited.insert(t.type); + typesWithDerived.push_back(&t); + collectDescendants(&t); // recursive call to find further descendants } + } + }; + collectDescendants(&(*findTypeIt)); + + // collect ancestors + const Symbol* currentExtendsFrom = findTypeIt->extendsFrom; + while (currentExtendsFrom) { + // not sure if Fortran even allows this. But better be safe + if (!visited.insert(currentExtendsFrom).second) { + al->error("Error: Detected cyclic inheritance involving type \"" + + (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\""); + break; + } - typeWithDerived.push_back(*currentType); + auto currentTypeIt = std::find_if(types.begin(), types.end(), + [&](const type& t) { return compareSymbols(t.type, currentExtendsFrom); }); - if ((*currentType).extendsFrom != nullptr) { - currentExtendsFrom = (*currentType).extendsFrom; - } else { - currentExtendsFrom = nullptr; - } + if (currentTypeIt == types.end()) { + al->error("Error: Types array (extendsFrom) field entry for \"" + + (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\" missing"); + break; } + + typesWithDerived.push_back(&(*currentTypeIt)); + currentExtendsFrom = currentTypeIt->extendsFrom; } - return typeWithDerived; + return typesWithDerived; } -void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, +void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol) { - for (type t : typeWithDerived) { - auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), [&procedureSymbol](const auto& p) { + for (const type* t : typeWithDerived) { + auto procIt = std::find_if(t->procedures.begin(), t->procedures.end(), [&procedureSymbol](const auto& p) { return p.first->name() == procedureSymbol->name(); }); - if (procIt == t.procedures.end()) + if (procIt == t->procedures.end()) continue; edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(procIt->second)); @@ -163,10 +208,10 @@ void ParseTreeVisitor::addEdgesForFinalizers(std::vector* edges, const Sym std::vector> ParseTreeVisitor::getEdgesForFinalizers(const Symbol* typeSymbol) { std::vector> edges; - std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); + std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); - for (const auto& type : typeSymbols) { - const Symbol* typeSymbol = type.type; + for (const type* type : typeSymbols) { + const Symbol* typeSymbol = type->type; const auto* details = std::get_if(&typeSymbol->details()); if (!details) @@ -893,19 +938,19 @@ bool ParseTreeVisitor::Pre(const Expr& e) { auto typeWithDerived = findTypeWithDerivedTypes(typeSymbol); - for (const auto& t : typeWithDerived) { - auto opIt = std::find_if(t.operators.begin(), t.operators.end(), + for (const type* t : typeWithDerived) { + auto opIt = std::find_if(t->operators.begin(), t->operators.end(), [&](const auto& p) { return compareExprIntrinsicOperator(e, p.first); }); - if (opIt == t.operators.end()) + if (opIt == t->operators.end()) continue; auto funcSymbol = opIt->second; bool skipSelfCall = false; - for (type t : typeWithDerived) { - auto procIt = std::find_if(t.procedures.begin(), t.procedures.end(), + for (const type* t : typeWithDerived) { + auto procIt = std::find_if(t->procedures.begin(), t->procedures.end(), [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); - if (procIt == t.procedures.end()) + if (procIt == t->procedures.end()) continue; if (procIt->second->name() == functionSymbols.back()->name()) { diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 115e400e..879d3bc1 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -43,6 +43,11 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { } } + // sort unique + std::sort(visitor.getEdges().begin(), visitor.getEdges().end()); + auto it = std::unique(visitor.getEdges().begin(), visitor.getEdges().end()); + visitor.getEdges().erase(it, visitor.getEdges().end()); + // add edges for (auto edge : visitor.getEdges()) { const auto& callerNode = cg->getOrInsertNode(edge.first); diff --git a/cgfcollector/test/multi/module_inherit/CMakeLists.txt b/cgfcollector/test/multi/module_inherit/CMakeLists.txt new file mode 100644 index 00000000..4d426e4a --- /dev/null +++ b/cgfcollector/test/multi/module_inherit/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.20) + +project(module_inherit LANGUAGES Fortran) + +include(../cmake_base.txt) + +file( + GLOB + SOURCES + "*.f90" +) + +add_executable(${CMAKE_PROJECT_NAME} ${SOURCES}) diff --git a/cgfcollector/test/multi/module_inherit/main.f90 b/cgfcollector/test/multi/module_inherit/main.f90 new file mode 100644 index 00000000..24caf86e --- /dev/null +++ b/cgfcollector/test/multi/module_inherit/main.f90 @@ -0,0 +1,67 @@ +module mod2 + use mod + implicit none + + type, extends(base) :: derived + contains + procedure :: set_var => set_var_derived + end type derived + + type, extends(derived) :: more_derived + contains + procedure :: set_var => set_var_more_derived + end type more_derived + +contains + + subroutine set_var_derived(this, a) + class(derived), intent(inout) :: this + real, intent(in) :: a + print *, 'Setting var from derived' + this%var = a + end subroutine set_var_derived + + subroutine set_var_more_derived(this, a) + class(more_derived), intent(inout) :: this + real, intent(in) :: a + print *, 'Setting var from more_derived' + this%var = a + end subroutine set_var_more_derived + + subroutine set_from_item(item, a) + class(derived), intent(inout) :: item + real, intent(in) :: a + + print *, 'Setting var from item' + call item%set_var(a) + end subroutine set_from_item + +end module mod2 + +program main + use mod2 + + implicit none + + call case1() + call case2() + +contains + subroutine case1() + type(derived) :: b + + call b%set_var(3.14) + + call set_from_item(b, 2.71) + end subroutine case1 + + subroutine case2() + class(derived), allocatable :: b + + allocate (more_derived :: b) + + call b%set_var(3.14) + end subroutine case2 + +end program main + diff --git a/cgfcollector/test/multi/module_inherit/mod.f90 b/cgfcollector/test/multi/module_inherit/mod.f90 new file mode 100644 index 00000000..417aeaa7 --- /dev/null +++ b/cgfcollector/test/multi/module_inherit/mod.f90 @@ -0,0 +1,21 @@ +module mod + + implicit none + + type :: base + real ::var + contains + procedure :: set_var => set_var_base + end type base + +contains + + subroutine set_var_base(this, a) + class(base), intent(inout) :: this + real, intent(in) :: a + print *, 'Setting var from base' + this%var = a + end subroutine set_var_base + +end module mod + diff --git a/cgfcollector/test/multi/module_inherit/output.json b/cgfcollector/test/multi/module_inherit/output.json new file mode 100644 index 00000000..ee3c8afb --- /dev/null +++ b/cgfcollector/test/multi/module_inherit/output.json @@ -0,0 +1,64 @@ +{ + "_CG": { + "meta": {}, + "nodes": { + "0": { + "callees": {}, + "functionName": "_QMmodPset_var_base", + "hasBody": true, + "meta": {}, + "origin": "mod.f90" + }, + "1": { + "callees": {}, + "functionName": "_QMmod2Pset_var_derived", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + }, + "2": { + "callees": { "0": {}, "1": {}, "3": {} }, + "functionName": "_QMmod2Pset_from_item", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + }, + "3": { + "callees": {}, + "functionName": "_QMmod2Pset_var_more_derived", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + }, + "4": { + "callees": { "5": {}, "6": {} }, + "functionName": "_QQmain", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + }, + "5": { + "callees": { "0": {}, "1": {}, "2": {}, "3": {} }, + "functionName": "_QFPcase1", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + }, + "6": { + "callees": { "0": {}, "1": {}, "3": {} }, + "functionName": "_QFPcase2", + "hasBody": true, + "meta": {}, + "origin": "main.f90" + } + } + }, + "_MetaCG": { + "generator": { + "name": "MetaCG", + "sha": "5b45b5518fe1d09584c1518ceeb337c7389df48a", + "version": "0.9" + }, + "version": "4.0" + } +} From 4a0ba229c44691297a6b6d5820b514b5a91c7b86 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:10 +0100 Subject: [PATCH 100/130] getFileEntry for llvm > 18 --- .../cgcollector2/fileInfoDemoPlugin/FileInfoMetadataPlugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cgcollector2/fileInfoDemoPlugin/FileInfoMetadataPlugin.cpp b/tools/cgcollector2/fileInfoDemoPlugin/FileInfoMetadataPlugin.cpp index 536aa561..61ee9d21 100644 --- a/tools/cgcollector2/fileInfoDemoPlugin/FileInfoMetadataPlugin.cpp +++ b/tools/cgcollector2/fileInfoDemoPlugin/FileInfoMetadataPlugin.cpp @@ -29,7 +29,7 @@ struct FileInfoMetadataPlugin : Plugin { #else const auto fileEntry = fullSrcLoc.getFileEntryRef(); #endif - + if (!fileEntry) { return result; } From d4d5be52e2c2e5e55976ae61ed8df4775a569f94 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:10 +0100 Subject: [PATCH 101/130] moved compareSymbols, mangleSymbol to util file --- cgfcollector/CMakeLists.txt | 6 --- cgfcollector/include/ParseTreeVisitor.h | 24 ++++++++-- cgfcollector/include/headers.h | 21 --------- cgfcollector/include/util.h | 14 ++++++ cgfcollector/src/ParseTreeVisitor.cpp | 61 ------------------------- cgfcollector/src/util.cpp | 57 +++++++++++++++++++++++ 6 files changed, 91 insertions(+), 92 deletions(-) delete mode 100644 cgfcollector/include/headers.h create mode 100644 cgfcollector/include/util.h create mode 100644 cgfcollector/src/util.cpp diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index d133b407..9a24211e 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -13,12 +13,6 @@ add_metacg(${PROJECT_NAME}) add_spdlog_libraries(${PROJECT_NAME}) # add_json(${PROJECT_NAME}) -target_precompile_headers( - ${PROJECT_NAME} - PRIVATE - include/headers.h -) - target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) install( diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 34432221..93a89b44 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -1,13 +1,31 @@ #pragma once -#include "headers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "AL.h" +#include "util.h" using namespace Fortran::parser; using namespace Fortran::semantics; using namespace Fortran::common; -using Fortran::lower::mangle::mangleName; using edge = std::pair; // (caller, callee) @@ -49,8 +67,6 @@ class ParseTreeVisitor { std::vector& getPotentialFinalizers() { return potentialFinalizers; } std::vector& getFunctions() { return functions; } - std::string mangleSymbol(const Symbol* sym); - template void handleFuncSubStmt(const T& stmt); void handleEndFuncSubStmt(); diff --git a/cgfcollector/include/headers.h b/cgfcollector/include/headers.h deleted file mode 100644 index 30be4993..00000000 --- a/cgfcollector/include/headers.h +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h new file mode 100644 index 00000000..b70941ae --- /dev/null +++ b/cgfcollector/include/util.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include + +using namespace Fortran::parser; +using namespace Fortran::semantics; +using namespace Fortran::common; +using Fortran::lower::mangle::mangleName; + +bool compareSymbols(const Symbol* a, const Symbol* b); +std::string mangleSymbol(const Symbol* sym); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 53a680a0..1a2130b6 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -1,66 +1,5 @@ #include "ParseTreeVisitor.h" -// private functions - -static bool compareSymbols(const Symbol* a, const Symbol* b) { - if (a == b) - return true; - if (!a || !b) - return false; - if (a->name() != b->name()) - return false; - - auto resolveHostAssoc = [](const Symbol* sym) -> const Symbol* { - while (sym) { - if (sym->has()) { - sym = &sym->get().symbol(); - } else if (sym->has()) { - sym = &sym->get().symbol(); - } else { - break; - } - } - return sym; - }; - if (resolveHostAssoc(a) != resolveHostAssoc(b)) - return false; - - if (a->attrs() != b->attrs()) - return false; - - // this only compares only the type and not all details like variables, procedures, generics, etc. But should be - // enough for now. - if (a->GetType() != b->GetType()) - return false; - - return true; -} - -// util functions - -std::string ParseTreeVisitor::mangleSymbol(const Symbol* sym) { - if (!sym) { - al->error("Error: mangleSymbol called with nullptr"); - return ""; - } - - std::string mangledName = mangleName(*sym); - - // Legacy Fortran - C interoperability before BIND(C) existed. - // I have to do this manually because normally it would run as - // a pass (ExternalNameConversionPass). - // - // NOTE: underscoring can be disabled with `-fno-underscoring` - auto result = fir::NameUniquer::deconstruct(mangledName); - if (fir::NameUniquer::isExternalFacingUniquedName(result)) { - if (result.first == fir::NameUniquer::NameKind::COMMON && result.second.name.empty()) - mangledName = Fortran::common::blankCommonObjectName; - mangledName = Fortran::common::GetExternalAssemblyName(result.second.name, true); - } - - return mangledName; -} - template void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp new file mode 100644 index 00000000..8d236d69 --- /dev/null +++ b/cgfcollector/src/util.cpp @@ -0,0 +1,57 @@ +#include "util.h" + +// Compares two symbols for equality also resolves the original contruct the symbol comes from. This could have been +// defined in another module/file +bool compareSymbols(const Symbol* a, const Symbol* b) { + if (a == b) + return true; + if (!a || !b) + return false; + if (a->name() != b->name()) + return false; + + auto resolveHostAssoc = [](const Symbol* sym) -> const Symbol* { + while (sym) { + if (sym->has()) { + sym = &sym->get().symbol(); + } else if (sym->has()) { + sym = &sym->get().symbol(); + } else { + break; + } + } + return sym; + }; + if (resolveHostAssoc(a) != resolveHostAssoc(b)) + return false; + + if (a->attrs() != b->attrs()) + return false; + + // this only compares only the type and not all details like variables, procedures, generics, etc. But should be + // enough for now. + if (a->GetType() != b->GetType()) + return false; + + return true; +} + +std::string mangleSymbol(const Symbol* sym) { + assert(sym && "mangleSymbol called with nullptr"); + + std::string mangledName = mangleName(*sym); + + // Legacy Fortran - C interoperability before BIND(C) existed. + // We have to do this manually because normally it would run as + // a pass (ExternalNameConversionPass). + // + // NOTE: underscoring can be disabled with `-fno-underscoring` + auto result = fir::NameUniquer::deconstruct(mangledName); + if (fir::NameUniquer::isExternalFacingUniquedName(result)) { + if (result.first == fir::NameUniquer::NameKind::COMMON && result.second.name.empty()) + mangledName = Fortran::common::blankCommonObjectName; + mangledName = Fortran::common::GetExternalAssemblyName(result.second.name, true); + } + + return mangledName; +} From 1dde3415ac5f4ac42b363563fa8cf86fc82b2c8d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:10 +0100 Subject: [PATCH 102/130] refactor --- cgfcollector/include/ParseTreeVisitor.h | 15 --- cgfcollector/include/util.h | 17 ++- cgfcollector/src/ParseTreeVisitor.cpp | 152 +---------------------- cgfcollector/src/main.cpp | 9 +- cgfcollector/src/util.cpp | 154 +++++++++++++++++++++++- graph/include/LoggerUtil.h | 10 ++ 6 files changed, 185 insertions(+), 172 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 93a89b44..cffc302b 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -84,17 +84,6 @@ class ParseTreeVisitor { void addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol); std::vector> getEdgesForFinalizers(const Symbol* typeSymbol); - template - bool holds_any_of(const Variant& v) { - return (std::holds_alternative(v) || ...); - } - - bool isOperator(const Expr* e); - - bool compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op); - bool isBinaryOperator(const Expr* e); - bool isUnaryOperator(const Expr* e); - template const Name* getNameFromClassWithDesignator(const T& t) { if (const auto* designator = std::get_if>(&t.u)) { @@ -118,10 +107,6 @@ class ParseTreeVisitor { void addTrackedVar(trackedVar var); void removeTrackedVars(Symbol* procedureSymbol); - // map RelationalOperator, LogicalOperator, NumericOperator to DefinedOperator::IntrinsicOperator - template - DefinedOperator::IntrinsicOperator mapToIntrinsicOperator(const Variant& op); - template bool Pre(const A&) { return true; diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h index b70941ae..3acd59a1 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/util.h @@ -1,14 +1,29 @@ #pragma once +#include "LoggerUtil.h" #include #include #include +#include #include using namespace Fortran::parser; using namespace Fortran::semantics; using namespace Fortran::common; -using Fortran::lower::mangle::mangleName; +using namespace metacg; + +template +bool holds_any_of(const Variant& v) { + return (std::holds_alternative(v) || ...); +} bool compareSymbols(const Symbol* a, const Symbol* b); std::string mangleSymbol(const Symbol* sym); +bool isOperator(const Expr* e); +bool compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op); +bool isBinaryOperator(const Expr* e); +bool isUnaryOperator(const Expr* e); + +// map RelationalOperator, LogicalOperator, NumericOperator to DefinedOperator::IntrinsicOperator +template +DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 1a2130b6..9bee145d 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -165,83 +165,6 @@ std::vector> ParseTreeVisitor::getEdgesForFina return edges; } -bool ParseTreeVisitor::isOperator(const Expr* e) { - /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) - Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, - LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, - DefinedUnary, DefinedBinary - */ - - return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::Power, Expr::Multiply, - Expr::Divide, Expr::Add, Expr::Subtract, Expr::Concat, Expr::LT, Expr::LE, Expr::EQ, Expr::NE, - Expr::GE, Expr::GT, Expr::AND, Expr::OR, Expr::EQV, Expr::NEQV, Expr::DefinedUnary, - Expr::DefinedBinary>(e->u); -} - -bool ParseTreeVisitor::compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op) { - if (!expr) - return false; - - using IO = DefinedOperator::IntrinsicOperator; - - switch (op) { - case IO::NOT: - return std::get_if(&expr->u) != nullptr; - case IO::Power: - return std::get_if(&expr->u) != nullptr; - case IO::Multiply: - return std::get_if(&expr->u) != nullptr; - case IO::Divide: - return std::get_if(&expr->u) != nullptr; - case IO::Add: - return std::get_if(&expr->u) != nullptr || - std::get_if(&expr->u) != nullptr; // UnaryPlus also uses + - case IO::Subtract: - return std::get_if(&expr->u) != nullptr || - std::get_if(&expr->u) != nullptr; // Negate also uses - - case IO::Concat: - return std::get_if(&expr->u) != nullptr; - case IO::LT: - return std::get_if(&expr->u) != nullptr; - case IO::LE: - return std::get_if(&expr->u) != nullptr; - case IO::EQ: - return std::get_if(&expr->u) != nullptr; - case IO::NE: - return std::get_if(&expr->u) != nullptr; - case IO::GE: - return std::get_if(&expr->u) != nullptr; - case IO::GT: - return std::get_if(&expr->u) != nullptr; - case IO::AND: - return std::get_if(&expr->u) != nullptr; - case IO::OR: - return std::get_if(&expr->u) != nullptr; - case IO::EQV: - return std::get_if(&expr->u) != nullptr; - case IO::NEQV: - return std::get_if(&expr->u) != nullptr; - default: - return false; - } -} - -bool ParseTreeVisitor::isBinaryOperator(const Expr* e) { - if (!e) - return false; - - return holds_any_ofu), Expr::Power, Expr::Multiply, Expr::Divide, Expr::Add, Expr::Subtract, - Expr::Concat, Expr::LT, Expr::LE, Expr::EQ, Expr::NE, Expr::GE, Expr::GT, Expr::AND, Expr::OR, - Expr::EQV, Expr::NEQV, Expr::DefinedBinary>(e->u); -} - -bool ParseTreeVisitor::isUnaryOperator(const Expr* e) { - if (!e) - return false; - - return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::DefinedUnary>(e->u); -} - const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { auto* type = symbol->GetType(); if (!type) @@ -300,77 +223,6 @@ void ParseTreeVisitor::removeTrackedVars(Symbol* procedureSymbol) { trackedVars.end()); } -template -DefinedOperator::IntrinsicOperator ParseTreeVisitor::mapToIntrinsicOperator(const Variant& op) { - return std::visit(visitors{[this](const RelationalOperator& op) { - using RO = RelationalOperator; - using IO = DefinedOperator::IntrinsicOperator; - - switch (op) { - case RO::LT: - return IO::LT; - case RO::LE: - return IO::LE; - case RO::EQ: - return IO::EQ; - case RO::NE: - return IO::NE; - case RO::GE: - return IO::GE; - case RO::GT: - return IO::GT; - default: - al->error("Error: Unknown RelationalOperator in mapToIntrinsicOperator"); - return IO::LT; // avoid warning - } - }, - [this](const LogicalOperator& op) { - using LO = LogicalOperator; - using IO = DefinedOperator::IntrinsicOperator; - - switch (op) { - case LO::And: - return IO::AND; - case LO::Or: - return IO::OR; - case LO::Eqv: - return IO::EQV; - case LO::Neqv: - return IO::NEQV; - case LO::Not: - return IO::NOT; - default: - al->error("Error: Unknown LogicalOperator in mapToIntrinsicOperator"); - return IO::AND; // avoid warning - } - }, - [this](const NumericOperator& op) { - using NO = NumericOperator; - using IO = DefinedOperator::IntrinsicOperator; - - switch (op) { - case NO::Power: - return IO::Power; - case NO::Multiply: - return IO::Multiply; - case NO::Divide: - return IO::Divide; - case NO::Add: - return IO::Add; - case NO::Subtract: - return IO::Subtract; - default: - al->error("Error: Unknown NumericOperator in mapToIntrinsicOperator"); - return IO::Add; // avoid warning - } - }, - [this](const auto& op) { - al->error("Error: Unknown operator type in mapToIntrinsicOperator"); - return DefinedOperator::IntrinsicOperator::Add; // avoid warning - }}, - op); -} - // Visitor implementations bool ParseTreeVisitor::Pre(const MainProgram& p) { @@ -951,7 +803,7 @@ void ParseTreeVisitor::Post(const UseStmt& u) { if (!gen->kind().IsIntrinsicOperator()) continue; - DefinedOperator::IntrinsicOperator intrinsicOp = mapToIntrinsicOperator(gen->kind().u); + DefinedOperator::IntrinsicOperator intrinsicOp = variantGetIntrinsicOperator(gen->kind().u); if (gen->specificProcs().size() != 1) al->error("Type-bound generic more than one specific proc not handled. Should not happen."); @@ -976,7 +828,7 @@ void ParseTreeVisitor::Post(const UseStmt& u) { std::vector procs; if (gen->kind().IsIntrinsicOperator()) { - interfaceOp = mapToIntrinsicOperator(gen->kind().u); + interfaceOp = variantGetIntrinsicOperator(gen->kind().u); al->debug("Found interface operator in module: {}", DefinedOperator::EnumToString(std::get(interfaceOp))); } else if (gen->kind().IsDefinedOperator()) { diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 879d3bc1..3b48a273 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -15,8 +15,11 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { AL* al = AL::getInstance(); - // TODO: remove - if (std::getenv("CUSTOM_DEBUG")) { + if (std::getenv("DEBUG")) { + metacg::MCGLogger::instance().getConsole()->set_level(spdlog::level::debug); + metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); + + // TODO: remove spdlog::set_pattern("%v"); spdlog::set_level(spdlog::level::debug); } @@ -28,7 +31,7 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { for (const auto pf : visitor.getPotentialFinalizers()) { auto functions = visitor.getFunctions(); auto calledIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& f) { return mangleName(*f.symbol) == pf.procedureCalled; }); + [&](const auto& f) { return mangleSymbol(f.symbol) == pf.procedureCalled; }); if (calledIt == functions.end()) continue; diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index 8d236d69..d2b26ce1 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -39,7 +39,7 @@ bool compareSymbols(const Symbol* a, const Symbol* b) { std::string mangleSymbol(const Symbol* sym) { assert(sym && "mangleSymbol called with nullptr"); - std::string mangledName = mangleName(*sym); + std::string mangledName = Fortran::lower::mangle::mangleName(*sym); // Legacy Fortran - C interoperability before BIND(C) existed. // We have to do this manually because normally it would run as @@ -49,9 +49,157 @@ std::string mangleSymbol(const Symbol* sym) { auto result = fir::NameUniquer::deconstruct(mangledName); if (fir::NameUniquer::isExternalFacingUniquedName(result)) { if (result.first == fir::NameUniquer::NameKind::COMMON && result.second.name.empty()) - mangledName = Fortran::common::blankCommonObjectName; - mangledName = Fortran::common::GetExternalAssemblyName(result.second.name, true); + mangledName = blankCommonObjectName; + mangledName = GetExternalAssemblyName(result.second.name, true); } return mangledName; } + +bool isOperator(const Expr* e) { + /* Operators: see 15.4.3.4.2, 10.1.6.1, 6.2.4 (https://j3-fortran.org/doc/year/23/23-007r1.pdf) + Negate, NOT, Power, Multiply, Divide, Add, Subtract, Concat, + LT, LE, EQ, NE, GE, GT, AND, OR, EQV, NEQV, + DefinedUnary, DefinedBinary + */ + + return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::Power, Expr::Multiply, + Expr::Divide, Expr::Add, Expr::Subtract, Expr::Concat, Expr::LT, Expr::LE, Expr::EQ, Expr::NE, + Expr::GE, Expr::GT, Expr::AND, Expr::OR, Expr::EQV, Expr::NEQV, Expr::DefinedUnary, + Expr::DefinedBinary>(e->u); +} + +bool compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op) { + if (!expr) + return false; + + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case IO::NOT: + return std::get_if(&expr->u) != nullptr; + case IO::Power: + return std::get_if(&expr->u) != nullptr; + case IO::Multiply: + return std::get_if(&expr->u) != nullptr; + case IO::Divide: + return std::get_if(&expr->u) != nullptr; + case IO::Add: + return std::get_if(&expr->u) != nullptr || + std::get_if(&expr->u) != nullptr; // UnaryPlus also uses + + case IO::Subtract: + return std::get_if(&expr->u) != nullptr || + std::get_if(&expr->u) != nullptr; // Negate also uses - + case IO::Concat: + return std::get_if(&expr->u) != nullptr; + case IO::LT: + return std::get_if(&expr->u) != nullptr; + case IO::LE: + return std::get_if(&expr->u) != nullptr; + case IO::EQ: + return std::get_if(&expr->u) != nullptr; + case IO::NE: + return std::get_if(&expr->u) != nullptr; + case IO::GE: + return std::get_if(&expr->u) != nullptr; + case IO::GT: + return std::get_if(&expr->u) != nullptr; + case IO::AND: + return std::get_if(&expr->u) != nullptr; + case IO::OR: + return std::get_if(&expr->u) != nullptr; + case IO::EQV: + return std::get_if(&expr->u) != nullptr; + case IO::NEQV: + return std::get_if(&expr->u) != nullptr; + default: + return false; + } +} + +bool isBinaryOperator(const Expr* e) { + if (!e) + return false; + + return holds_any_ofu), Expr::Power, Expr::Multiply, Expr::Divide, Expr::Add, Expr::Subtract, + Expr::Concat, Expr::LT, Expr::LE, Expr::EQ, Expr::NE, Expr::GE, Expr::GT, Expr::AND, Expr::OR, + Expr::EQV, Expr::NEQV, Expr::DefinedBinary>(e->u); +} + +bool isUnaryOperator(const Expr* e) { + if (!e) + return false; + + return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::DefinedUnary>(e->u); +} + +template +DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op) { + return std::visit(visitors{[](const RelationalOperator& op) { + using RO = RelationalOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case RO::LT: + return IO::LT; + case RO::LE: + return IO::LE; + case RO::EQ: + return IO::EQ; + case RO::NE: + return IO::NE; + case RO::GE: + return IO::GE; + case RO::GT: + return IO::GT; + default: + MCGLogger::logDebug("Error: Unknown RelationalOperator in mapToIntrinsicOperator"); + return IO::LT; // avoid warning + } + }, + [](const LogicalOperator& op) { + using LO = LogicalOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case LO::And: + return IO::AND; + case LO::Or: + return IO::OR; + case LO::Eqv: + return IO::EQV; + case LO::Neqv: + return IO::NEQV; + case LO::Not: + return IO::NOT; + default: + MCGLogger::logDebug("Error: Unknown LogicalOperator in mapToIntrinsicOperator"); + return IO::AND; // avoid warning + } + }, + [](const NumericOperator& op) { + using NO = NumericOperator; + using IO = DefinedOperator::IntrinsicOperator; + + switch (op) { + case NO::Power: + return IO::Power; + case NO::Multiply: + return IO::Multiply; + case NO::Divide: + return IO::Divide; + case NO::Add: + return IO::Add; + case NO::Subtract: + return IO::Subtract; + default: + MCGLogger::logDebug("Error: Unknown NumericOperator in mapToIntrinsicOperator"); + return IO::Add; // avoid warning + } + }, + [](const auto& op) { + MCGLogger::logDebug("Error: Unknown operator type in mapToIntrinsicOperator"); + return DefinedOperator::IntrinsicOperator::Add; // avoid warning + }}, + op); +} diff --git a/graph/include/LoggerUtil.h b/graph/include/LoggerUtil.h index 11d8dbda..2e1e24c0 100644 --- a/graph/include/LoggerUtil.h +++ b/graph/include/LoggerUtil.h @@ -254,6 +254,16 @@ class MCGLogger { metacg::MCGLogger::instance().warn(msg, std::forward(args)...); } + template + static void logDebug(const MSG_t msg, Args&&... args) { + metacg::MCGLogger::instance().debug(msg, std::forward(args)...); + } + + template + static void logDebugUnique(const MSG_t msg, Args&&... args) { + metacg::MCGLogger::instance().debug(msg, std::forward(args)...); + } + /** * Resets the uniqueness-property for all messages. * Any message that has been previously logged as unique can now appear again From 12f988d33310ffb544f361e34a14b18df8e6a96b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:11 +0100 Subject: [PATCH 103/130] refactor --- cgfcollector/include/AL.h | 60 ----------- cgfcollector/include/ParseTreeVisitor.h | 5 - cgfcollector/include/util.h | 12 +++ cgfcollector/src/ParseTreeVisitor.cpp | 126 ++++++++++++------------ cgfcollector/src/main.cpp | 18 +--- cgfcollector/src/util.cpp | 13 +++ 6 files changed, 91 insertions(+), 143 deletions(-) delete mode 100644 cgfcollector/include/AL.h diff --git a/cgfcollector/include/AL.h b/cgfcollector/include/AL.h deleted file mode 100644 index d98cbbb4..00000000 --- a/cgfcollector/include/AL.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include -#include - -template <> -struct fmt::formatter { - constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); } - - template - auto format(const Fortran::parser::CharBlock& cb, FormatContext& ctx) const { - return fmt::format_to(ctx.out(), "{}", std::string_view(cb.begin(), cb.size())); - } -}; - -// aligned logger that formats debug messages with a : . To make it more readable. -class AL { - public: - static AL* getInstance() { - static AL instance; - return &instance; - } - - template - void debug(const std::string& fmt_str, Args&&... args) { - std::string formatted = fmt::format(fmt_str, std::forward(args)...); - size_t colon_pos = formatted.find(':'); - - if (colon_pos != std::string::npos) { - entries.emplace_back(formatted, colon_pos); - max_key_width = std::max(max_key_width, colon_pos); - } else { - entries.emplace_back(formatted, std::string::npos); - } - } - - void error(const std::string& fmt_str) { spdlog::error("{}", fmt_str); } - - void flush() { - for (const auto& [line, colon_pos] : entries) { - if (colon_pos == std::string::npos) { - spdlog::debug("{}", line); // no colon - } else { - std::string key = line.substr(0, colon_pos); - std::string rest = line.substr(colon_pos); // includes : - - std::string padded_key = fmt::format("{:<{}}", key, max_key_width); - spdlog::debug("{}{}", padded_key, rest); - } - } - entries.clear(); - max_key_width = 0; - } - - private: - AL() = default; - - std::vector> entries; - size_t max_key_width = 0; -}; diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index cffc302b..266473f7 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -20,7 +20,6 @@ #include #include -#include "AL.h" #include "util.h" using namespace Fortran::parser; @@ -96,8 +95,6 @@ class ParseTreeVisitor { return nullptr; } - const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); - trackedVar* getTrackedVarFromSourceName(SourceName sourceName); // search trackedVars for a canditate and set it as initialized. @@ -199,8 +196,6 @@ class ParseTreeVisitor { // mainly used for destructor handling std::vector trackedVars; - AL* al = AL::getInstance(); - std::vector functions; // all functions std::vector potentialFinalizers; diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h index 3acd59a1..e83aae24 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/util.h @@ -12,6 +12,16 @@ using namespace Fortran::semantics; using namespace Fortran::common; using namespace metacg; +template <> +struct fmt::formatter { + constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const Fortran::parser::CharBlock& cb, FormatContext& ctx) const { + return fmt::format_to(ctx.out(), "{}", std::string_view(cb.begin(), cb.size())); + } +}; + template bool holds_any_of(const Variant& v) { return (std::holds_alternative(v) || ...); @@ -27,3 +37,5 @@ bool isUnaryOperator(const Expr* e); // map RelationalOperator, LogicalOperator, NumericOperator to DefinedOperator::IntrinsicOperator template DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op); + +const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 9bee145d..e6c61761 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -8,7 +8,7 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { cg->getOrInsertNode(mangleSymbol(sym), currentFileName, false, false); functions.push_back({sym, std::vector()}); - al->debug("Add node: {} ({})", mangleSymbol(sym), fmt::ptr(sym)); + MCGLogger::logDebug("Add node: {} ({})", mangleSymbol(sym), fmt::ptr(sym)); } } @@ -29,7 +29,7 @@ void ParseTreeVisitor::handleEndFuncSubStmt() { void ParseTreeVisitor::handleTrackedVars() { if (mangleSymbol(functionSymbols.back()) != "_QQmain") { if (!trackedVars.empty()) - al->debug("Handle tracked vars for function"); + MCGLogger::logDebug("Handle tracked vars for function"); for (auto& trackedVar : trackedVars) { if (!trackedVar.hasBeenInitialized) @@ -93,8 +93,8 @@ std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol while (currentExtendsFrom) { // not sure if Fortran even allows this. But better be safe if (!visited.insert(currentExtendsFrom).second) { - al->error("Error: Detected cyclic inheritance involving type \"" + - (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\""); + MCGLogger::logError("Error: Detected cyclic inheritance involving type \"" + + (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\""); break; } @@ -102,8 +102,8 @@ std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol [&](const type& t) { return compareSymbols(t.type, currentExtendsFrom); }); if (currentTypeIt == types.end()) { - al->error("Error: Types array (extendsFrom) field entry for \"" + - (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\" missing"); + MCGLogger::logError("Error: Types array (extendsFrom) field entry for \"" + + (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\" missing"); break; } @@ -125,8 +125,8 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vectorsecond)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleSymbol(procIt->second), fmt::ptr(procIt->second)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back()), mangleSymbol(procIt->second), fmt::ptr(procIt->second)); } } @@ -134,8 +134,8 @@ void ParseTreeVisitor::addEdgesForFinalizers(const Symbol* typeSymbol) { for (const auto& edge : getEdgesForFinalizers(typeSymbol)) { edges.emplace_back(mangleSymbol(edge.first), mangleSymbol(edge.second)); - al->debug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.first), fmt::ptr(edge.first), - mangleSymbol(edge.second), fmt::ptr(edge.second)); + MCGLogger::logDebug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.first), fmt::ptr(edge.first), + mangleSymbol(edge.second), fmt::ptr(edge.second)); } } @@ -165,19 +165,6 @@ std::vector> ParseTreeVisitor::getEdgesForFina return edges; } -const Symbol* ParseTreeVisitor::getTypeSymbolFromSymbol(const Symbol* symbol) { - auto* type = symbol->GetType(); - if (!type) - return nullptr; - auto* derived = type->AsDerived(); - if (!derived) - return nullptr; - auto* typeSymbol = &derived->typeSymbol(); - if (!typeSymbol) - return nullptr; - return typeSymbol; -} - trackedVar* ParseTreeVisitor::getTrackedVarFromSourceName(SourceName sourceName) { auto anyTrackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { return t.var->name() == sourceName; }); @@ -200,7 +187,7 @@ void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { trackedVar->hasBeenInitialized = true; - al->debug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); + MCGLogger::logDebug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); } void ParseTreeVisitor::addTrackedVar(trackedVar var) { @@ -209,12 +196,12 @@ void ParseTreeVisitor::addTrackedVar(trackedVar var) { // update info it->addFinalizers = var.addFinalizers; it->hasBeenInitialized = var.hasBeenInitialized; - al->debug("Update tracked variable: {} ({})", var.var->name(), fmt::ptr(var.var)); + MCGLogger::logDebug("Update tracked variable: {} ({})", var.var->name(), fmt::ptr(var.var)); return; } trackedVars.push_back(var); - al->debug("Add tracking for variable: {} ({})", var.var->name(), fmt::ptr(var.var)); + MCGLogger::logDebug("Add tracking for variable: {} ({})", var.var->name(), fmt::ptr(var.var)); } void ParseTreeVisitor::removeTrackedVars(Symbol* procedureSymbol) { @@ -235,7 +222,8 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { functionSymbols.emplace_back(maybeStmt->statement.v.symbol); cg->getOrInsertNode(mangleSymbol(functionSymbols.back()), currentFileName, false, false); - al->debug("\nIn main program: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); + MCGLogger::logDebug("\nIn main program: {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back())); } return true; } @@ -243,7 +231,8 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { void ParseTreeVisitor::Post(const MainProgram&) { handleTrackedVars(); - al->debug("End main program: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); + MCGLogger::logDebug("End main program: {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back())); if (!functionSymbols.empty()) { functionSymbols.pop_back(); @@ -283,13 +272,14 @@ void ParseTreeVisitor::Post(const EntryStmt& e) { if (!name->symbol) return; - al->debug("Add Entry point: {} ({})", mangleSymbol(name->symbol), fmt::ptr(name->symbol)); + MCGLogger::logDebug("Add Entry point: {} ({})", mangleSymbol(name->symbol), fmt::ptr(name->symbol)); cg->getOrInsertNode(mangleSymbol(name->symbol), currentFileName, false, true); } void ParseTreeVisitor::Post(const FunctionStmt& f) { - al->debug("\nIn function: {} ({})", mangleSymbol(std::get(f.t).symbol), fmt::ptr(std::get(f.t).symbol)); + MCGLogger::logDebug("\nIn function: {} ({})", mangleSymbol(std::get(f.t).symbol), + fmt::ptr(std::get(f.t).symbol)); handleFuncSubStmt(f); @@ -309,14 +299,16 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { void ParseTreeVisitor::Post(const EndFunctionStmt&) { if (!functionSymbols.empty()) { - al->debug("End function: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); + MCGLogger::logDebug("End function: {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back())); } handleEndFuncSubStmt(); } void ParseTreeVisitor::Post(const SubroutineStmt& s) { - al->debug("\nIn subroutine: {} ({})", mangleSymbol(std::get(s.t).symbol), fmt::ptr(std::get(s.t).symbol)); + MCGLogger::logDebug("\nIn subroutine: {} ({})", mangleSymbol(std::get(s.t).symbol), + fmt::ptr(std::get(s.t).symbol)); handleFuncSubStmt(s); @@ -337,7 +329,8 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { void ParseTreeVisitor::Post(const EndSubroutineStmt&) { if (!functionSymbols.empty()) { - al->debug("End subroutine: {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back())); + MCGLogger::logDebug("End subroutine: {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back())); } handleEndFuncSubStmt(); @@ -358,8 +351,8 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(name->symbol)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleSymbol(name->symbol), fmt::ptr(name->symbol)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back()), mangleSymbol(name->symbol), fmt::ptr(name->symbol)); // if called from a object with %. (base % component) } else if (auto* procCompRef = std::get_if(&p.u)) { @@ -369,8 +362,8 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(symbolComp)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), fmt::ptr(functionSymbols.back()), - mangleSymbol(symbolComp), fmt::ptr(symbolComp)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back()), mangleSymbol(symbolComp), fmt::ptr(symbolComp)); auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) @@ -444,7 +437,7 @@ void ParseTreeVisitor::Post(const Call& c) { addEdgesForFinalizers(&pf.finalizerEdges, getTypeSymbolFromSymbol(trackedVar->var)); potentialFinalizers.push_back(pf); - al->debug("Add potential finalizer for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); + MCGLogger::logDebug("Add potential finalizer for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); } } } @@ -492,7 +485,8 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { if (!holds_allocatable) { if (!holds_intent) { // no intent attr, if not set does not call finalizer. Why? idk. - al->debug("Add tracking for function argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + MCGLogger::logDebug("Add tracking for function argument: {} ({})", name.symbol->name(), + fmt::ptr(name.symbol)); addTrackedVar({name.symbol, functionSymbols.back(), false, true}); } else { if (holds_intent->v == IntentSpec::Intent::Out) { @@ -500,14 +494,15 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { addEdgesForFinalizers(typeSymbol); } else if (holds_intent->v == IntentSpec::Intent::InOut) { // intent inout, calls finalizer when set. - al->debug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + MCGLogger::logDebug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); addTrackedVar({name.symbol, functionSymbols.back(), false, true}); } } } } else { if (holds_allocatable) { - al->debug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); + MCGLogger::logDebug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), + fmt::ptr(name.symbol)); addTrackedVar({name.symbol, functionSymbols.back(), false, true}); // skip var with allocatable attr. // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. @@ -527,7 +522,7 @@ bool ParseTreeVisitor::Pre(const DerivedTypeDef&) { void ParseTreeVisitor::Post(const DerivedTypeDef&) { inDerivedTypeDef = false; - al->debug("End derived type: {} ({})", types.back().type->name(), fmt::ptr(types.back().type)); + MCGLogger::logDebug("End derived type: {} ({})", types.back().type->name(), fmt::ptr(types.back().type)); } bool ParseTreeVisitor::Pre(const DerivedTypeStmt& t) { @@ -538,7 +533,7 @@ bool ParseTreeVisitor::Pre(const DerivedTypeStmt& t) { const auto& name = std::get(t.t); currentType.type = name.symbol; - al->debug("\nIn derived type: {} ({})", currentType.type->name(), fmt::ptr(currentType.type)); + MCGLogger::logDebug("\nIn derived type: {} ({})", currentType.type->name(), fmt::ptr(currentType.type)); return true; } @@ -552,7 +547,7 @@ void ParseTreeVisitor::Post(const TypeAttrSpec& a) { const auto& extends = std::get(a.u); currentType.extendsFrom = extends.v.symbol; - al->debug("Extends from: {} ({})", currentType.extendsFrom->name(), fmt::ptr(currentType.extendsFrom)); + MCGLogger::logDebug("Extends from: {} ({})", currentType.extendsFrom->name(), fmt::ptr(currentType.extendsFrom)); } } @@ -574,8 +569,8 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { auto& currentType = types.back(); currentType.procedures.emplace_back(name.symbol, optname->symbol); - al->debug("Add procedure: {} ({}) -> {} ({})", name.symbol->name(), fmt::ptr(name.symbol), - optname->symbol->name(), fmt::ptr(optname->symbol)); + MCGLogger::logDebug("Add procedure: {} ({}) -> {} ({})", name.symbol->name(), fmt::ptr(name.symbol), + optname->symbol->name(), fmt::ptr(optname->symbol)); } // only for abstract types, with deferred in binding attr list @@ -587,8 +582,8 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { auto& currentType = types.back(); currentType.procedures.emplace_back(n.symbol, n.symbol); - al->debug("Add procedure: {} ({}) -> {} ({})", n.symbol->name(), fmt::ptr(n.symbol), n.symbol->name(), - fmt::ptr(n.symbol)); + MCGLogger::logDebug("Add procedure: {} ({}) -> {} ({})", n.symbol->name(), fmt::ptr(n.symbol), n.symbol->name(), + fmt::ptr(n.symbol)); } } } @@ -610,8 +605,8 @@ void ParseTreeVisitor::Post(const TypeBoundGenericStmt& s) { currentType.operators.emplace_back(*intrinsicOp, name.symbol); - al->debug("Add operator: {} -> {} ({})", DefinedOperator::EnumToString(*intrinsicOp), name.symbol->name(), - fmt::ptr(name.symbol)); + MCGLogger::logDebug("Add operator: {} -> {} ({})", DefinedOperator::EnumToString(*intrinsicOp), + name.symbol->name(), fmt::ptr(name.symbol)); } } } @@ -656,7 +651,7 @@ void ParseTreeVisitor::Post(const ProcedureStmt& p) { continue; if (interfaceOperators.empty()) { - al->error("This should no happen. Likely there is a bug with parsing DefinedOperator's"); + MCGLogger::logError("This should no happen. Likely there is a bug with parsing DefinedOperator's"); continue; } @@ -717,8 +712,8 @@ bool ParseTreeVisitor::Pre(const Expr& e) { edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(sym)); - al->debug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back()), mangleSymbol(sym), fmt::ptr(sym)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), + fmt::ptr(functionSymbols.back()), mangleSymbol(sym), fmt::ptr(sym)); } } @@ -771,7 +766,7 @@ void ParseTreeVisitor::Post(const Expr& e) { void ParseTreeVisitor::Post(const UseStmt& u) { auto* useSymbol = u.moduleName.symbol; - al->debug("Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); + MCGLogger::logDebug("Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); if (const Scope* modScope = useSymbol->scope()) { for (const auto& pair : *modScope) { @@ -794,7 +789,8 @@ void ParseTreeVisitor::Post(const UseStmt& u) { // type bound procedures if (component.has()) { const auto& procDetails = component.get(); - al->debug("Found procedure in module derived type: {} ({})", component.name(), fmt::ptr(&component)); + MCGLogger::logDebug("Found procedure in module derived type: {} ({})", component.name(), + fmt::ptr(&component)); procedures.emplace_back(&component, &component); } @@ -806,20 +802,20 @@ void ParseTreeVisitor::Post(const UseStmt& u) { DefinedOperator::IntrinsicOperator intrinsicOp = variantGetIntrinsicOperator(gen->kind().u); if (gen->specificProcs().size() != 1) - al->error("Type-bound generic more than one specific proc not handled. Should not happen."); + MCGLogger::logError("Type-bound generic more than one specific proc not handled. Should not happen."); Symbol* op_func_sym = nullptr; op_func_sym = const_cast(&gen->specificProcs().front().get()); - al->debug("Found operator in module derived type: {} -> {} ({})", - DefinedOperator::EnumToString(intrinsicOp), op_func_sym->name(), fmt::ptr(op_func_sym)); + MCGLogger::logDebug("Found operator in module derived type: {} -> {} ({})", + DefinedOperator::EnumToString(intrinsicOp), op_func_sym->name(), fmt::ptr(op_func_sym)); operators.push_back({intrinsicOp, op_func_sym}); } } types.push_back({&symbol, extendsFrom, procedures, operators}); - al->debug("Found derived type in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); + MCGLogger::logDebug("Found derived type in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); } // same but with interface operators @@ -829,18 +825,18 @@ void ParseTreeVisitor::Post(const UseStmt& u) { if (gen->kind().IsIntrinsicOperator()) { interfaceOp = variantGetIntrinsicOperator(gen->kind().u); - al->debug("Found interface operator in module: {}", - DefinedOperator::EnumToString(std::get(interfaceOp))); + MCGLogger::logDebug("Found interface operator in module: {}", + DefinedOperator::EnumToString(std::get(interfaceOp))); } else if (gen->kind().IsDefinedOperator()) { interfaceOp = &symbol; - al->debug("Found interface operator in module: {}", symbol.name()); + MCGLogger::logDebug("Found interface operator in module: {}", symbol.name()); } else { continue; } for (const auto& p : gen->specificProcs()) { procs.push_back(const_cast(&p.get())); - al->debug(" with procedure: {} ({})", p.get().name(), fmt::ptr(&p.get())); + MCGLogger::logDebug(" with procedure: {} ({})", p.get().name(), fmt::ptr(&p.get())); } interfaceOperators.push_back({interfaceOp, procs}); @@ -857,10 +853,10 @@ void ParseTreeVisitor::Post(const UseStmt& u) { } functions.push_back({&symbol, dummyArgs}); - al->debug("Found function in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); + MCGLogger::logDebug("Found function in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); } } } - al->debug("Finished Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); + MCGLogger::logDebug("Finished Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); } diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 3b48a273..43a3a5f1 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -13,16 +13,10 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { cg = mcgManager.getCallgraph("cg"); } - AL* al = AL::getInstance(); - - if (std::getenv("DEBUG")) { - metacg::MCGLogger::instance().getConsole()->set_level(spdlog::level::debug); - metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); - - // TODO: remove - spdlog::set_pattern("%v"); - spdlog::set_level(spdlog::level::debug); - } +#ifndef NDEBUG + metacg::MCGLogger::instance().getConsole()->set_level(spdlog::level::debug); + metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); +#endif ParseTreeVisitor visitor(cg, getCurrentFile().str()); Fortran::parser::Walk(getParsing().parseTree(), visitor); @@ -42,7 +36,7 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { for (const auto& edge : pf.finalizerEdges) { visitor.getEdges().emplace_back(edge.first, edge.second); - al->debug("Add edge for potential finalizer: {} -> {}", edge.first, edge.second); + MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.first, edge.second); } } @@ -59,8 +53,6 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { cg->addEdge(callerNode, calleeNode); } - al->flush(); - mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); auto mcgWriter = metacg::io::createWriter(4); diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index d2b26ce1..16f00461 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -203,3 +203,16 @@ DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op }}, op); } + +const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol) { + auto* type = symbol->GetType(); + if (!type) + return nullptr; + auto* derived = type->AsDerived(); + if (!derived) + return nullptr; + auto* typeSymbol = &derived->typeSymbol(); + if (!typeSymbol) + return nullptr; + return typeSymbol; +} From 13d762c9490df4b91af1acd539982e578a90b573 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:11 +0100 Subject: [PATCH 104/130] refactor --- cgfcollector/include/ParseTreeVisitor.h | 6 +- cgfcollector/src/ParseTreeVisitor.cpp | 33 ++++++ cgfcollector/src/main.cpp | 140 ++++++++++-------------- 3 files changed, 95 insertions(+), 84 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 266473f7..c8730e66 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -62,10 +62,6 @@ class ParseTreeVisitor { public: ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; - std::vector& getEdges() { return edges; } - std::vector& getPotentialFinalizers() { return potentialFinalizers; } - std::vector& getFunctions() { return functions; } - template void handleFuncSubStmt(const T& stmt); void handleEndFuncSubStmt(); @@ -104,6 +100,8 @@ class ParseTreeVisitor { void addTrackedVar(trackedVar var); void removeTrackedVars(Symbol* procedureSymbol); + void postProcess(); + template bool Pre(const A&) { return true; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index e6c61761..feb4bc18 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -210,6 +210,39 @@ void ParseTreeVisitor::removeTrackedVars(Symbol* procedureSymbol) { trackedVars.end()); } +void ParseTreeVisitor::postProcess() { + // handle potential finalizers from function calls + for (const auto pf : potentialFinalizers) { + auto calledIt = std::find_if(functions.begin(), functions.end(), + [&](const auto& f) { return mangleSymbol(f.symbol) == pf.procedureCalled; }); + if (calledIt == functions.end()) + continue; + + auto arg = calledIt->dummyArgs.begin() + pf.argPos; + + if (!arg->hasBeenInitialized) + continue; + + for (const auto& edge : pf.finalizerEdges) { + edges.emplace_back(edge.first, edge.second); + MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.first, edge.second); + } + } + + // sort unique edges + std::sort(edges.begin(), edges.end()); + auto it = std::unique(edges.begin(), edges.end()); + edges.erase(it, edges.end()); + + // add edges + for (auto edge : edges) { + const auto& callerNode = cg->getOrInsertNode(edge.first); + const auto& calleeNode = cg->getOrInsertNode(edge.second); + + cg->addEdge(callerNode, calleeNode); + } +} + // Visitor implementations bool ParseTreeVisitor::Pre(const MainProgram& p) { diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 43a3a5f1..8d9f16c7 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -2,122 +2,102 @@ #include -class CollectCG : public Fortran::frontend::PluginParseTreeAction { - public: - std::string generateCG() { - auto& mcgManager = metacg::graph::MCGManager::get(); - metacg::Callgraph* cg = mcgManager.getCallgraph("cg"); +using namespace metacg; - if (cg == nullptr) { - mcgManager.addToManagedGraphs("cg", std::make_unique(), true); - cg = mcgManager.getCallgraph("cg"); - } +static auto& mcgManager = metacg::graph::MCGManager::get(); -#ifndef NDEBUG - metacg::MCGLogger::instance().getConsole()->set_level(spdlog::level::debug); - metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); -#endif - - ParseTreeVisitor visitor(cg, getCurrentFile().str()); - Fortran::parser::Walk(getParsing().parseTree(), visitor); - - // handle potential finalizers from function calls - for (const auto pf : visitor.getPotentialFinalizers()) { - auto functions = visitor.getFunctions(); - auto calledIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& f) { return mangleSymbol(f.symbol) == pf.procedureCalled; }); - if (calledIt == functions.end()) - continue; - - auto arg = calledIt->dummyArgs.begin() + pf.argPos; - - if (!arg->hasBeenInitialized) - continue; - - for (const auto& edge : pf.finalizerEdges) { - visitor.getEdges().emplace_back(edge.first, edge.second); - MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.first, edge.second); - } - } +std::unique_ptr createOutputFile(Fortran::frontend::CompilerInstance& compInst, + llvm::StringRef currentFile, llvm::StringRef extension) { + llvm::SmallString<128> outputPath(compInst.getFrontendOpts().outputFile); + if (outputPath.empty()) { + outputPath = currentFile; + } + if (extension != "") + llvm::sys::path::replace_extension(outputPath, extension); + std::unique_ptr os; + std::error_code ec; + os.reset(new llvm::raw_fd_ostream(outputPath.str(), ec, llvm::sys::fs::OF_TextWithCRLF)); + if (ec) { + MCGLogger::logError("Error opening output file: {}", ec.message()); + return nullptr; + } + return os; +} - // sort unique - std::sort(visitor.getEdges().begin(), visitor.getEdges().end()); - auto it = std::unique(visitor.getEdges().begin(), visitor.getEdges().end()); - visitor.getEdges().erase(it, visitor.getEdges().end()); +void generateCG(std::optional& parseTree, llvm::StringRef currentFile) { + mcgManager.addToManagedGraphs("cg", std::make_unique(), true); + Callgraph* cg = mcgManager.getCallgraph("cg"); - // add edges - for (auto edge : visitor.getEdges()) { - const auto& callerNode = cg->getOrInsertNode(edge.first); - const auto& calleeNode = cg->getOrInsertNode(edge.second); +#ifndef NDEBUG + metacg::MCGLogger::instance().getConsole()->set_level(spdlog::level::debug); + metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); +#endif - cg->addEdge(callerNode, calleeNode); - } + ParseTreeVisitor visitor(cg, currentFile.str()); + Fortran::parser::Walk(parseTree, visitor); + visitor.postProcess(); - mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); + mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); +} - auto mcgWriter = metacg::io::createWriter(4); - if (!mcgWriter) { - llvm::errs() << "Unable to create a writer\n"; - return ""; - }; +std::string dumpCG() { + Callgraph* cg = mcgManager.getCallgraph("cg"); + if (cg == nullptr) { + MCGLogger::logError("No callgraph generated"); + return ""; + } - metacg::io::JsonSink jsonSink; - mcgWriter->writeActiveGraph(jsonSink); + auto mcgWriter = metacg::io::createWriter(4); + if (!mcgWriter) { + MCGLogger::logError("Unable to create a writer"); + return ""; + }; - return jsonSink.getJson().dump(); - } + metacg::io::JsonSink jsonSink; + mcgWriter->writeActiveGraph(jsonSink); - std::unique_ptr createOutputFile(llvm::StringRef extension) { - llvm::SmallString<128> outputPath(getInstance().getFrontendOpts().outputFile); - if (outputPath.empty()) { - outputPath = getCurrentFile(); - } - if (extension != "") - llvm::sys::path::replace_extension(outputPath, extension); - std::unique_ptr os; - std::error_code ec; - os.reset(new llvm::raw_fd_ostream(outputPath.str(), ec, llvm::sys::fs::OF_TextWithCRLF)); - if (ec) { - llvm::errs() << "Error opening output file: " << ec.message() << "\n"; - return nullptr; - } - return os; - } + return jsonSink.getJson().dump(); +} +class CollectCG : public Fortran::frontend::PluginParseTreeAction { + public: void executeAction() override { - std::string cgString = generateCG(); + generateCG(getParsing().parseTree(), getCurrentFile()); - auto file = createOutputFile("json"); + std::string cgString = dumpCG(); + auto file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); file->write(cgString.c_str(), cgString.size()); } }; -class CollectCGwithDot : public CollectCG { +class CollectCGwithDot : public Fortran::frontend::PluginParseTreeAction { public: void executeAction() override { - CollectCG::executeAction(); + generateCG(getParsing().parseTree(), getCurrentFile()); - auto& mcgManager = metacg::graph::MCGManager::get(); metacg::Callgraph* cg = mcgManager.getCallgraph("cg"); if (cg == nullptr) { + MCGLogger::logError("No callgraph generated"); return; } metacg::io::dot::DotGenerator dotGen(cg); dotGen.generate(); - auto file = CollectCG::createOutputFile("dot"); + auto file = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); std::string dotString = dotGen.getDotString(); file->write(dotString.c_str(), dotString.size()); } }; -class CollectCGNoRename : public CollectCG { +class CollectCGNoRename : public Fortran::frontend::PluginParseTreeAction { public: void executeAction() override { - std::string cgString = generateCG(); + generateCG(getParsing().parseTree(), getCurrentFile()); + + std::string cgString = dumpCG(); - auto file = createOutputFile(""); + auto file = ::createOutputFile(getInstance(), getCurrentFile(), ""); file->write(cgString.c_str(), cgString.size()); } }; From 998274ccc2bfc5b6ac0bb7b1a065f3992bb7fe7e Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:11 +0100 Subject: [PATCH 105/130] add documentation --- cgfcollector/include/ParseTreeVisitor.h | 1 + cgfcollector/include/util.h | 70 ++++++++++++++++++++++++- cgfcollector/src/main.cpp | 34 ++++++++++++ cgfcollector/src/util.cpp | 2 - 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index c8730e66..66e7c606 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -25,6 +25,7 @@ using namespace Fortran::parser; using namespace Fortran::semantics; using namespace Fortran::common; +using namespace metacg; using edge = std::pair; // (caller, callee) diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h index e83aae24..570ce846 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/util.h @@ -12,6 +12,9 @@ using namespace Fortran::semantics; using namespace Fortran::common; using namespace metacg; +/** + * @brief Formatter for CharBlock + */ template <> struct fmt::formatter { constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); } @@ -22,20 +25,85 @@ struct fmt::formatter { } }; +/** + * @brief Variant check if it holds any of the given types + * + * @tparam Variant + * @tparam Ts + * @param v + * @return + */ template bool holds_any_of(const Variant& v) { return (std::holds_alternative(v) || ...); } +/** + * @brief Compares two symbols for equality also resolves the original construct the symbol comes from. + * This could have been defined in another module/file + * + * @param a + * @param b + * @return true if both symbols are equal + */ bool compareSymbols(const Symbol* a, const Symbol* b); + +/** + * @brief Generate mangled name from symbol + * + * @param sym + * @return + */ std::string mangleSymbol(const Symbol* sym); + +/** + * @brief Check if expression is an operator expression + * + * @param e + * @return + */ bool isOperator(const Expr* e); + +/** + * @brief Compare if expression match given intrinsic operator + * + * @param expr + * @param op + * @return + */ bool compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op); + +/** + * @brief Check if binary operator expression + * + * @param e + * @return + */ bool isBinaryOperator(const Expr* e); + +/** + * @brief Check if unary operator expression + * + * @param e + * @return + */ bool isUnaryOperator(const Expr* e); -// map RelationalOperator, LogicalOperator, NumericOperator to DefinedOperator::IntrinsicOperator +/** + * @brief Get intrinsic operator from a variant of several operator types (RelationalOperator, LogicalOperator, + * NumericOperator) + * + * @tparam Variant + * @param op + * @return + */ template DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op); +/** + * @brief Get type symbol from a given symbol + * + * @param symbol + * @return + */ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 8d9f16c7..bf83c4b7 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -6,6 +6,14 @@ using namespace metacg; static auto& mcgManager = metacg::graph::MCGManager::get(); +/** + * @brief Create output file with given extension + * + * @param compInst + * @param currentFile + * @param extension + * @return + */ std::unique_ptr createOutputFile(Fortran::frontend::CompilerInstance& compInst, llvm::StringRef currentFile, llvm::StringRef extension) { llvm::SmallString<128> outputPath(compInst.getFrontendOpts().outputFile); @@ -24,6 +32,12 @@ std::unique_ptr createOutputFile(Fortran::frontend::Com return os; } +/** + * @brief Create callgraph in mcgManager and populate it by traversing the parse tree + * + * @param parseTree + * @param currentFile + */ void generateCG(std::optional& parseTree, llvm::StringRef currentFile) { mcgManager.addToManagedGraphs("cg", std::make_unique(), true); Callgraph* cg = mcgManager.getCallgraph("cg"); @@ -40,6 +54,11 @@ void generateCG(std::optional& parseTree, llvm::StringRef currentFile) mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); } +/** + * @brief Dump callgraph as JSON string + * + * @return + */ std::string dumpCG() { Callgraph* cg = mcgManager.getCallgraph("cg"); if (cg == nullptr) { @@ -59,6 +78,11 @@ std::string dumpCG() { return jsonSink.getJson().dump(); } +/** + * @class CollectCG + * @brief Plugin action to collect callgraph and dump it as JSON file + * + */ class CollectCG : public Fortran::frontend::PluginParseTreeAction { public: void executeAction() override { @@ -70,6 +94,11 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { } }; +/** + * @class CollectCGwithDot + * @brief Like CollectCG but also generates a DOT file of callgraph + * + */ class CollectCGwithDot : public Fortran::frontend::PluginParseTreeAction { public: void executeAction() override { @@ -90,6 +119,11 @@ class CollectCGwithDot : public Fortran::frontend::PluginParseTreeAction { } }; +/** + * @class CollectCGNoRename + * @brief Like CollectCG but does not rename output file + * + */ class CollectCGNoRename : public Fortran::frontend::PluginParseTreeAction { public: void executeAction() override { diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index 16f00461..1c7e9af8 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -1,7 +1,5 @@ #include "util.h" -// Compares two symbols for equality also resolves the original contruct the symbol comes from. This could have been -// defined in another module/file bool compareSymbols(const Symbol* a, const Symbol* b) { if (a == b) return true; From 3a485823e0f4fc340faa9c3d209731520b46b428 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:12 +0100 Subject: [PATCH 106/130] refactor and add documentation --- cgfcollector/include/ParseTreeVisitor.h | 111 +++++++++++++++++------- cgfcollector/include/util.h | 19 ++++ 2 files changed, 98 insertions(+), 32 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 66e7c606..6ed977ea 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -49,7 +49,7 @@ struct function { bool hasBeenInitialized = false; }; - Symbol* symbol; // function + Symbol* symbol; // function symbol std::vector dummyArgs; }; @@ -61,48 +61,52 @@ struct potentialFinalizer { class ParseTreeVisitor { public: - ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; + ParseTreeVisitor(Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; template void handleFuncSubStmt(const T& stmt); void handleEndFuncSubStmt(); - void handleTrackedVars(); - - // searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. + /** + * @brief Searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. + * + * @param typeSymbol symbol to search for + * @return vector with type and all derived types + */ std::vector findTypeWithDerivedTypes(const Symbol* typeSymbol); - // this function searches with typeSymbol for a type in types vector and adds edges for procedures that matches - // procedureSymbol. And also adds edges from types that extends from typeSymbol. + /** + * @brief Searches with typeSymbol for a type in types vector and adds edges for procedures that matches + * procedureSymbol. And also adds edges from types that extends from typeSymbol. + * + * @param typeWithDerived + * @param procedureSymbol + */ void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); void addEdgesForFinalizers(const Symbol* typeSymbol); void addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol); std::vector> getEdgesForFinalizers(const Symbol* typeSymbol); - template - const Name* getNameFromClassWithDesignator(const T& t) { - if (const auto* designator = std::get_if>(&t.u)) { - if (const auto* dataRef = std::get_if(&designator->value().u)) { - if (const auto* name = std::get_if(&dataRef->u)) { - return name; - } - } - } - return nullptr; - } - trackedVar* getTrackedVarFromSourceName(SourceName sourceName); - // search trackedVars for a canditate and set it as initialized. - // Prefers local variables when (shadowed) + /** + * @brief Search trackedVars for a canditate and set it as initialized. + * Prefers local variables when (shadowed). + * + * @param sourceName + */ void handleTrackedVarAssignment(SourceName sourceName); void addTrackedVar(trackedVar var); void removeTrackedVars(Symbol* procedureSymbol); + void handleTrackedVars(); + void postProcess(); + // visitor methods + template bool Pre(const A&) { return true; @@ -133,42 +137,85 @@ class ParseTreeVisitor { void Post(const AllocateStmt& a); void Post(const Call& c); - // handle destructors (finalizers) TODO: test i definitely missed some edges cases + /** + * @brief Handle finalizers (destructors ). TODO: test i definitely missed some edges cases. + * + * @param t + */ void Post(const TypeDeclarationStmt& t); - // following 5 methods are for collecting types and their procedures. see type struct and vector. + // The following methods are for collecting types and their procedures. see type struct and vector. - // type def + /** + * @brief Type definition start + * + * @return + */ bool Pre(const DerivedTypeDef&); + /** + * @brief Type definiiton end + */ void Post(const DerivedTypeDef&); - // type stmt like type [, extends(...)] :: body (not exhaustive and not extends) + /** + * @brief Type stmt like type [, extends(...)] :: body (not exhaustive and not extends) + * + * @param t + * @return + */ bool Pre(const DerivedTypeStmt& t); - // type attrs like extends + /** + * @brief Type attributes like extends + * + * @param a + */ void Post(const TypeAttrSpec& a); - // procedures in type defs + /** + * @brief Collect type bound procedures in derived type definitions + * + * @param s + */ void Post(const TypeBoundProcedureStmt& s); - // collect defined operators in a type def (operator overloading) + /** + * @brief Collect defined operators in type definition (operator overloading) + * + * @param s + */ void Post(const TypeBoundGenericStmt& s); - // the following 4 methods are for collecting defined operators in interface statements + // The following methods are for collecting defined operators in interface statements + bool Pre(const InterfaceStmt&); bool Pre(const EndInterfaceStmt&); void Post(const DefinedOperator& op); void Post(const ProcedureStmt& p); - // parse operators in expressions + /** + * @brief Parse operators in expressions + * + * @param e + * @return + */ bool Pre(const Expr& e); + /** + * @brief Post cleanup parse operators in expressions + * + * @param e + */ void Post(const Expr& e); - // extract additional information from use statements + /** + * @brief Extract additional information from use statements + * + * @param u + */ void Post(const UseStmt& u); private: - metacg::Callgraph* cg; + Callgraph* cg; std::vector edges; std::string currentFileName; diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h index 570ce846..4efbf234 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/util.h @@ -38,6 +38,25 @@ bool holds_any_of(const Variant& v) { return (std::holds_alternative(v) || ...); } +/** + * @brief Get Name from type that has a designator + * + * @tparam T + * @param t + * @return + */ +template +const Name* getNameFromClassWithDesignator(const T& t) { + if (const auto* designator = std::get_if>(&t.u)) { + if (const auto* dataRef = std::get_if(&designator->value().u)) { + if (const auto* name = std::get_if(&dataRef->u)) { + return name; + } + } + } + return nullptr; +} + /** * @brief Compares two symbols for equality also resolves the original construct the symbol comes from. * This could have been defined in another module/file From 9a762628cdf505b6a82959bbb723bd1c74e69db9 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:12 +0100 Subject: [PATCH 107/130] use function struct for currentFunctions vector --- cgfcollector/include/ParseTreeVisitor.h | 20 ++-- cgfcollector/include/util.h | 3 +- cgfcollector/src/ParseTreeVisitor.cpp | 147 +++++++++++++----------- cgfcollector/src/main.cpp | 9 +- cgfcollector/src/util.cpp | 5 +- 5 files changed, 105 insertions(+), 79 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 6ed977ea..eafa6752 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -47,10 +47,17 @@ struct function { struct dummyArg { Symbol* symbol; bool hasBeenInitialized = false; + + explicit dummyArg(Symbol* sym) : symbol(sym) {} + explicit dummyArg(Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} }; Symbol* symbol; // function symbol std::vector dummyArgs; + + explicit function(Symbol* sym) : symbol(sym) {} + explicit function(Symbol* sym, std::vector args) : symbol(sym), dummyArgs(std::move(args)) {} + void addDummyArg(Symbol* sym) { dummyArgs.emplace_back(sym); } }; struct potentialFinalizer { @@ -138,7 +145,7 @@ class ParseTreeVisitor { void Post(const Call& c); /** - * @brief Handle finalizers (destructors ). TODO: test i definitely missed some edges cases. + * @brief Handle finalizers (destructors ). * * @param t */ @@ -216,7 +223,6 @@ class ParseTreeVisitor { private: Callgraph* cg; - std::vector edges; std::string currentFileName; bool inFunctionOrSubroutineSubProgram = false; @@ -226,9 +232,11 @@ class ParseTreeVisitor { bool inInterfaceStmtDefinedOperator = false; bool inInterfaceSpecification = false; - std::vector functionSymbols; // intended as a stack. It holds the current procedure symbol when the AST - // walker is in the respective procedure. - std::vector> functionDummyArgs; // same idea, but for dummy args + std::vector edges; // added to cg in postProcess step + + std::vector functions; // all functions + std::vector currentFunctions; // intended as a stack. It holds the current function symbol and its dummy + // args when the AST walker is in the respective function. std::vector types; // all types @@ -242,7 +250,5 @@ class ParseTreeVisitor { // mainly used for destructor handling std::vector trackedVars; - std::vector functions; // all functions - std::vector potentialFinalizers; }; diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h index 4efbf234..fb50a180 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/util.h @@ -116,8 +116,7 @@ bool isUnaryOperator(const Expr* e); * @param op * @return */ -template -DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op); +DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const GenericKind& gk); /** * @brief Get type symbol from a given symbol diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index feb4bc18..b8d240e8 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -3,10 +3,9 @@ template void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { - functionSymbols.emplace_back(sym); - functionDummyArgs.emplace_back(std::vector()); + currentFunctions.emplace_back(sym, std::vector()); + functions.emplace_back(sym, std::vector()); cg->getOrInsertNode(mangleSymbol(sym), currentFileName, false, false); - functions.push_back({sym, std::vector()}); MCGLogger::logDebug("Add node: {} ({})", mangleSymbol(sym), fmt::ptr(sym)); } @@ -18,23 +17,22 @@ template void ParseTreeVisitor::handleFuncSubStmt(const Subrouti void ParseTreeVisitor::handleEndFuncSubStmt() { handleTrackedVars(); - if (!functionSymbols.empty()) { - functionSymbols.pop_back(); - } - if (!functionDummyArgs.empty()) { - functionDummyArgs.pop_back(); + if (!currentFunctions.empty()) { + currentFunctions.pop_back(); } } void ParseTreeVisitor::handleTrackedVars() { - if (mangleSymbol(functionSymbols.back()) != "_QQmain") { + auto* currentFunctionSymbol = currentFunctions.back().symbol; + + if (mangleSymbol(currentFunctionSymbol) != "_QQmain") { if (!trackedVars.empty()) MCGLogger::logDebug("Handle tracked vars for function"); for (auto& trackedVar : trackedVars) { if (!trackedVar.hasBeenInitialized) continue; - if (trackedVar.procedure != functionSymbols.back()) + if (trackedVar.procedure != currentFunctionSymbol) continue; // add edge for deconstruction (finalizer) @@ -47,7 +45,7 @@ void ParseTreeVisitor::handleTrackedVars() { // set init on dummy function args auto functionIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& f) { return f.symbol == functionSymbols.back(); }); + [&](const auto& f) { return f.symbol == currentFunctionSymbol; }); if (functionIt != functions.end()) { auto dummyArgIt = std::find_if(functionIt->dummyArgs.begin(), functionIt->dummyArgs.end(), [&](const auto& d) { return d.symbol == trackedVar.var; }); @@ -59,7 +57,7 @@ void ParseTreeVisitor::handleTrackedVars() { } // cleanup trackedVars - removeTrackedVars(functionSymbols.back()); + removeTrackedVars(currentFunctionSymbol); } std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeSymbol) { @@ -123,10 +121,12 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vectorprocedures.end()) continue; - edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(procIt->second)); + auto* currentFunctionSymbol = currentFunctions.back().symbol; + + edges.emplace_back(mangleSymbol(currentFunctionSymbol), mangleSymbol(procIt->second)); - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back()), mangleSymbol(procIt->second), fmt::ptr(procIt->second)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), + fmt::ptr(currentFunctionSymbol), mangleSymbol(procIt->second), fmt::ptr(procIt->second)); } } @@ -158,7 +158,7 @@ std::vector> ParseTreeVisitor::getEdgesForFina // add edges for finalizers for (const auto& final : details->finals()) { - edges.emplace_back(functionSymbols.back(), &final.second.get()); + edges.emplace_back(currentFunctions.back().symbol, &final.second.get()); } } @@ -173,7 +173,7 @@ trackedVar* ParseTreeVisitor::getTrackedVarFromSourceName(SourceName sourceName) // find local variable with the same name in the current function scope (shadowed) auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { - return t.var->name() == sourceName && t.procedure == functionSymbols.back(); + return t.var->name() == sourceName && t.procedure == currentFunctions.back().symbol; }); // prefer local var if found @@ -252,11 +252,12 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { if (!maybeStmt->statement.v.symbol) return true; - functionSymbols.emplace_back(maybeStmt->statement.v.symbol); - cg->getOrInsertNode(mangleSymbol(functionSymbols.back()), currentFileName, false, false); + auto* currentFunctionSymbol = + currentFunctions.emplace_back(maybeStmt->statement.v.symbol, std::vector()).symbol; + cg->getOrInsertNode(mangleSymbol(currentFunctionSymbol), currentFileName, false, false); - MCGLogger::logDebug("\nIn main program: {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back())); + MCGLogger::logDebug("\nIn main program: {} ({})", mangleSymbol(currentFunctionSymbol), + fmt::ptr(currentFunctionSymbol)); } return true; } @@ -264,11 +265,13 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { void ParseTreeVisitor::Post(const MainProgram&) { handleTrackedVars(); - MCGLogger::logDebug("End main program: {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back())); + auto* currentFunctionSymbol = currentFunctions.back().symbol; - if (!functionSymbols.empty()) { - functionSymbols.pop_back(); + MCGLogger::logDebug("End main program: {} ({})", mangleSymbol(currentFunctionSymbol), + fmt::ptr(currentFunctionSymbol)); + + if (!currentFunctions.empty()) { + currentFunctions.pop_back(); } inMainProgram = false; @@ -292,7 +295,7 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { if (!inFunctionOrSubroutineSubProgram && !inMainProgram) return; - auto* node = cg->getFirstNode(mangleSymbol(functionSymbols.back())); + auto* node = cg->getFirstNode(mangleSymbol(currentFunctions.back().symbol)); if (!node) { return; } @@ -316,24 +319,27 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { handleFuncSubStmt(f); + auto* currentFunctionSymbol = currentFunctions.back().symbol; + auto functionsIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& func) { return func.symbol == functionSymbols.back(); }); + [&](const auto& func) { return func.symbol == currentFunctionSymbol; }); // collect function arguments const auto& name_list = std::get>(f.t); for (auto name : name_list) { - functionDummyArgs.back().push_back(&name); + currentFunctions.back().addDummyArg(name.symbol); if (functionsIt != functions.end()) { - functionsIt->dummyArgs.push_back({name.symbol, false}); - addTrackedVar({name.symbol, functionSymbols.back(), false, false}); + functionsIt->addDummyArg(name.symbol); + addTrackedVar({name.symbol, currentFunctionSymbol, false, false}); } } } void ParseTreeVisitor::Post(const EndFunctionStmt&) { - if (!functionSymbols.empty()) { - MCGLogger::logDebug("End function: {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back())); + if (!currentFunctions.empty()) { + auto* currentFunctionSymbol = currentFunctions.back().symbol; + + MCGLogger::logDebug("End function: {} ({})", mangleSymbol(currentFunctionSymbol), fmt::ptr(currentFunctionSymbol)); } handleEndFuncSubStmt(); @@ -345,34 +351,40 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { handleFuncSubStmt(s); + auto* currentFunctionSymbol = currentFunctions.back().symbol; + auto functionsIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& func) { return func.symbol == functionSymbols.back(); }); + [&](const auto& func) { return func.symbol == currentFunctionSymbol; }); // collect subroutine arguments (dummy args) const auto* dummyArg_list = &std::get>(s.t); for (const auto& dummyArg : *dummyArg_list) { const auto* name = std::get_if(&dummyArg.u); - functionDummyArgs.back().push_back(name); + currentFunctions.back().addDummyArg(name->symbol); if (functionsIt != functions.end()) { - functionsIt->dummyArgs.push_back({name->symbol, false}); - addTrackedVar({name->symbol, functionSymbols.back(), false, false}); + functionsIt->addDummyArg(name->symbol); + addTrackedVar({name->symbol, currentFunctionSymbol, false, false}); } } } void ParseTreeVisitor::Post(const EndSubroutineStmt&) { - if (!functionSymbols.empty()) { - MCGLogger::logDebug("End subroutine: {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back())); + if (!currentFunctions.empty()) { + auto* currentFunctionSymbol = currentFunctions.back().symbol; + + MCGLogger::logDebug("End subroutine: {} ({})", mangleSymbol(currentFunctionSymbol), + fmt::ptr(currentFunctionSymbol)); } handleEndFuncSubStmt(); } void ParseTreeVisitor::Post(const ProcedureDesignator& p) { - if (functionSymbols.empty()) + if (currentFunctions.empty()) return; + auto* currentFunctionSymbol = currentFunctions.back().symbol; + // if just the name is called. (as subroutine with call and as function without call) if (auto* name = std::get_if(&p.u)) { if (!name->symbol) @@ -382,10 +394,10 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (name->symbol->attrs().test(Attr::INTRINSIC)) return; - edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(name->symbol)); + edges.emplace_back(mangleSymbol(currentFunctionSymbol), mangleSymbol(name->symbol)); - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back()), mangleSymbol(name->symbol), fmt::ptr(name->symbol)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), + fmt::ptr(currentFunctionSymbol), mangleSymbol(name->symbol), fmt::ptr(name->symbol)); // if called from a object with %. (base % component) } else if (auto* procCompRef = std::get_if(&p.u)) { @@ -393,10 +405,10 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (!symbolComp) return; - edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(symbolComp)); + edges.emplace_back(mangleSymbol(currentFunctionSymbol), mangleSymbol(symbolComp)); - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back()), mangleSymbol(symbolComp), fmt::ptr(symbolComp)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), + fmt::ptr(currentFunctionSymbol), mangleSymbol(symbolComp), fmt::ptr(symbolComp)); auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) @@ -476,7 +488,7 @@ void ParseTreeVisitor::Post(const Call& c) { } void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { - if (functionSymbols.empty()) { + if (currentFunctions.empty()) { return; } @@ -487,11 +499,12 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { // skip if name is an argument to a function or subroutine bool isFunctionArg = false; - if (!functionDummyArgs.empty()) { - auto it = std::find_if(functionDummyArgs.back().begin(), functionDummyArgs.back().end(), - [&name](const Name* dummyArg) { return dummyArg->symbol == name.symbol; }); + auto& currentDummyArgs = currentFunctions.back().dummyArgs; + if (!currentDummyArgs.empty()) { + auto it = std::find_if(currentDummyArgs.begin(), currentDummyArgs.end(), + [&name](const function::dummyArg& dummyArg) { return dummyArg.symbol == name.symbol; }); - if (it != functionDummyArgs.back().end()) + if (it != currentDummyArgs.end()) isFunctionArg = true; } @@ -514,13 +527,15 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { if (holds_save) continue; // vars with save attr are not destructed + auto* currentFunctionSymbol = currentFunctions.back().symbol; + if (isFunctionArg) { if (!holds_allocatable) { if (!holds_intent) { // no intent attr, if not set does not call finalizer. Why? idk. MCGLogger::logDebug("Add tracking for function argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - addTrackedVar({name.symbol, functionSymbols.back(), false, true}); + addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); } else { if (holds_intent->v == IntentSpec::Intent::Out) { // intent out, calls finalizer because (7.5.6.3 line 21 and onwards) @@ -528,7 +543,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { } else if (holds_intent->v == IntentSpec::Intent::InOut) { // intent inout, calls finalizer when set. MCGLogger::logDebug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - addTrackedVar({name.symbol, functionSymbols.back(), false, true}); + addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); } } } @@ -536,7 +551,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { if (holds_allocatable) { MCGLogger::logDebug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - addTrackedVar({name.symbol, functionSymbols.back(), false, true}); + addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); // skip var with allocatable attr. // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. } else { @@ -704,7 +719,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } // not in a function. So no overloaded operator is called. - if (functionSymbols.empty()) + if (currentFunctions.empty()) return true; for (auto e : exprStmtWithOps) { @@ -730,8 +745,10 @@ bool ParseTreeVisitor::Pre(const Expr& e) { bool isBinaryOp = isBinaryOperator(e); for (auto* sym : interfaceOp->second) { + auto* currentFunctionSymbol = currentFunctions.back().symbol; + // skip self calls - if (mangleSymbol(sym) == mangleSymbol(functionSymbols.back())) + if (mangleSymbol(sym) == mangleSymbol(currentFunctionSymbol)) continue; // if unary, add potential unary operators. Same for binary operators. @@ -743,10 +760,10 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } } - edges.emplace_back(mangleSymbol(functionSymbols.back()), mangleSymbol(sym)); + edges.emplace_back(mangleSymbol(currentFunctionSymbol), mangleSymbol(sym)); - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(functionSymbols.back()), - fmt::ptr(functionSymbols.back()), mangleSymbol(sym), fmt::ptr(sym)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), + fmt::ptr(currentFunctionSymbol), mangleSymbol(sym), fmt::ptr(sym)); } } @@ -772,7 +789,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (procIt == t->procedures.end()) continue; - if (procIt->second->name() == functionSymbols.back()->name()) { + if (procIt->second->name() == currentFunctions.back().symbol->name()) { skipSelfCall = true; break; } @@ -832,7 +849,7 @@ void ParseTreeVisitor::Post(const UseStmt& u) { if (!gen->kind().IsIntrinsicOperator()) continue; - DefinedOperator::IntrinsicOperator intrinsicOp = variantGetIntrinsicOperator(gen->kind().u); + DefinedOperator::IntrinsicOperator intrinsicOp = variantGetIntrinsicOperator(gen->kind()); if (gen->specificProcs().size() != 1) MCGLogger::logError("Type-bound generic more than one specific proc not handled. Should not happen."); @@ -857,7 +874,7 @@ void ParseTreeVisitor::Post(const UseStmt& u) { std::vector procs; if (gen->kind().IsIntrinsicOperator()) { - interfaceOp = variantGetIntrinsicOperator(gen->kind().u); + interfaceOp = variantGetIntrinsicOperator(gen->kind()); MCGLogger::logDebug("Found interface operator in module: {}", DefinedOperator::EnumToString(std::get(interfaceOp))); } else if (gen->kind().IsDefinedOperator()) { @@ -882,10 +899,10 @@ void ParseTreeVisitor::Post(const UseStmt& u) { std::vector dummyArgs; for (Symbol* arg : details->dummyArgs()) { - dummyArgs.push_back({arg, false}); + dummyArgs.emplace_back(arg, false); } - functions.push_back({&symbol, dummyArgs}); + functions.emplace_back(&symbol, dummyArgs); MCGLogger::logDebug("Found function in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); } } diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index bf83c4b7..65c4b6ee 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -104,6 +104,11 @@ class CollectCGwithDot : public Fortran::frontend::PluginParseTreeAction { void executeAction() override { generateCG(getParsing().parseTree(), getCurrentFile()); + std::string cgString = dumpCG(); + auto file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); + file->write(cgString.c_str(), cgString.size()); + + // dot file metacg::Callgraph* cg = mcgManager.getCallgraph("cg"); if (cg == nullptr) { MCGLogger::logError("No callgraph generated"); @@ -113,9 +118,9 @@ class CollectCGwithDot : public Fortran::frontend::PluginParseTreeAction { metacg::io::dot::DotGenerator dotGen(cg); dotGen.generate(); - auto file = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); + auto dotfile = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); std::string dotString = dotGen.getDotString(); - file->write(dotString.c_str(), dotString.size()); + dotfile->write(dotString.c_str(), dotString.size()); } }; diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index 1c7e9af8..e65adae6 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -131,8 +131,7 @@ bool isUnaryOperator(const Expr* e) { return holds_any_ofu), Expr::UnaryPlus, Expr::Negate, Expr::NOT, Expr::DefinedUnary>(e->u); } -template -DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op) { +DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const GenericKind& gk) { return std::visit(visitors{[](const RelationalOperator& op) { using RO = RelationalOperator; using IO = DefinedOperator::IntrinsicOperator; @@ -199,7 +198,7 @@ DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const Variant& op MCGLogger::logDebug("Error: Unknown operator type in mapToIntrinsicOperator"); return DefinedOperator::IntrinsicOperator::Add; // avoid warning }}, - op); + gk.u); } const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol) { From 56458a24e2b180743b20672eab07c56912dee42b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:12 +0100 Subject: [PATCH 108/130] doc and refector --- cgfcollector/include/ParseTreeVisitor.h | 67 +++++++++-------------- cgfcollector/include/function.h | 22 ++++++++ cgfcollector/include/potentialFinalizer.h | 15 +++++ cgfcollector/include/trackedVar.h | 10 ++++ cgfcollector/include/type.h | 13 +++++ cgfcollector/src/ParseTreeVisitor.cpp | 14 ++--- 6 files changed, 91 insertions(+), 50 deletions(-) create mode 100644 cgfcollector/include/function.h create mode 100644 cgfcollector/include/potentialFinalizer.h create mode 100644 cgfcollector/include/trackedVar.h create mode 100644 cgfcollector/include/type.h diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index eafa6752..b752b18d 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -20,6 +20,10 @@ #include #include +#include "function.h" +#include "potentialFinalizer.h" +#include "trackedVar.h" +#include "type.h" #include "util.h" using namespace Fortran::parser; @@ -29,49 +33,26 @@ using namespace metacg; using edge = std::pair; // (caller, callee) -struct type { - Symbol* type; - Symbol* extendsFrom; - std::vector> procedures; // name(symbol) => optname(symbol) - std::vector> operators; // operator => name(symbol) -}; - -struct trackedVar { - Symbol* var; - Symbol* procedure; // procedure in which var was defined - bool hasBeenInitialized = false; - bool addFinalizers = false; -}; - -struct function { - struct dummyArg { - Symbol* symbol; - bool hasBeenInitialized = false; - - explicit dummyArg(Symbol* sym) : symbol(sym) {} - explicit dummyArg(Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} - }; - - Symbol* symbol; // function symbol - std::vector dummyArgs; - - explicit function(Symbol* sym) : symbol(sym) {} - explicit function(Symbol* sym, std::vector args) : symbol(sym), dummyArgs(std::move(args)) {} - void addDummyArg(Symbol* sym) { dummyArgs.emplace_back(sym); } -}; - -struct potentialFinalizer { - std::size_t argPos; - std::string procedureCalled; - std::vector finalizerEdges; -}; - +/** + * @class ParseTreeVisitor + * @brief Implements visitor methods to traverse parse tree and generate callgraph + * + */ class ParseTreeVisitor { public: ParseTreeVisitor(Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; + /** + * @brief Collects function/subroutine statements (begin) and their dummy args. + * + * @tparam T + * @param stmt + */ template void handleFuncSubStmt(const T& stmt); + /** + * @brief Handles function/subroutine end statements. + */ void handleEndFuncSubStmt(); /** @@ -92,7 +73,11 @@ class ParseTreeVisitor { void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); void addEdgesForFinalizers(const Symbol* typeSymbol); - void addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol); + /** + * @brief For a given type symbol, returns a list of edges from the current function to all finalizers of that type. + * + * @param typeSymbol + */ std::vector> getEdgesForFinalizers(const Symbol* typeSymbol); trackedVar* getTrackedVarFromSourceName(SourceName sourceName); @@ -108,6 +93,9 @@ class ParseTreeVisitor { void addTrackedVar(trackedVar var); void removeTrackedVars(Symbol* procedureSymbol); + /** + * @brief Go through trackedVars vector and + */ void handleTrackedVars(); void postProcess(); @@ -247,8 +235,7 @@ class ParseTreeVisitor { std::vector exprStmtWithOps; - // mainly used for destructor handling - std::vector trackedVars; + std::vector trackedVars; // mainly used for destructor handling std::vector potentialFinalizers; }; diff --git a/cgfcollector/include/function.h b/cgfcollector/include/function.h new file mode 100644 index 00000000..69c557c1 --- /dev/null +++ b/cgfcollector/include/function.h @@ -0,0 +1,22 @@ +#include +#include + +using namespace Fortran::semantics; + +struct function { + struct dummyArg { + Symbol* symbol; + bool hasBeenInitialized = false; + + explicit dummyArg(Symbol* sym) : symbol(sym) {} + explicit dummyArg(Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} + }; + + Symbol* symbol; // function symbol + std::vector dummyArgs; + + explicit function(Symbol* sym) : symbol(sym) {} + explicit function(Symbol* sym, std::vector args) : symbol(sym), dummyArgs(std::move(args)) {} + + void addDummyArg(Symbol* sym) { dummyArgs.emplace_back(sym); } +}; diff --git a/cgfcollector/include/potentialFinalizer.h b/cgfcollector/include/potentialFinalizer.h new file mode 100644 index 00000000..55bb3bbc --- /dev/null +++ b/cgfcollector/include/potentialFinalizer.h @@ -0,0 +1,15 @@ +#include +#include + +using edge = std::pair; // (caller, callee) + +struct potentialFinalizer { + std::size_t argPos; + std::string procedureCalled; + std::vector finalizerEdges; + + explicit potentialFinalizer(std::size_t pos, std::string procCalled) + : argPos(pos), procedureCalled(std::move(procCalled)) {} + + void addFinalizerEdge(const edge& e) { finalizerEdges.emplace_back(e); } +}; diff --git a/cgfcollector/include/trackedVar.h b/cgfcollector/include/trackedVar.h new file mode 100644 index 00000000..7c697434 --- /dev/null +++ b/cgfcollector/include/trackedVar.h @@ -0,0 +1,10 @@ +#include + +using namespace Fortran::semantics; + +struct trackedVar { + Symbol* var; + Symbol* procedure; // procedure in which var was defined + bool hasBeenInitialized = false; + bool addFinalizers = false; +}; diff --git a/cgfcollector/include/type.h b/cgfcollector/include/type.h new file mode 100644 index 00000000..2e1d5e94 --- /dev/null +++ b/cgfcollector/include/type.h @@ -0,0 +1,13 @@ +#include +#include +#include + +using namespace Fortran::semantics; +using namespace Fortran::parser; + +struct type { + Symbol* type; + Symbol* extendsFrom; + std::vector> procedures; // name(symbol) => optname(symbol) + std::vector> operators; // operator => name(symbol) +}; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index b8d240e8..a515f3ac 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -139,12 +139,6 @@ void ParseTreeVisitor::addEdgesForFinalizers(const Symbol* typeSymbol) { } } -void ParseTreeVisitor::addEdgesForFinalizers(std::vector* edges, const Symbol* typeSymbol) { - for (const auto& edge : getEdgesForFinalizers(typeSymbol)) { - edges->emplace_back(mangleSymbol(edge.first), mangleSymbol(edge.second)); - } -} - std::vector> ParseTreeVisitor::getEdgesForFinalizers(const Symbol* typeSymbol) { std::vector> edges; std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); @@ -478,10 +472,10 @@ void ParseTreeVisitor::Post(const Call& c) { if (!trackedVar) continue; - potentialFinalizer pf = {argPos, mangleSymbol(procName->symbol), std::vector()}; - addEdgesForFinalizers(&pf.finalizerEdges, getTypeSymbolFromSymbol(trackedVar->var)); - - potentialFinalizers.push_back(pf); + potentialFinalizer& pf = potentialFinalizers.emplace_back(argPos, mangleSymbol(procName->symbol)); + for (const auto& edge : getEdgesForFinalizers(getTypeSymbolFromSymbol(trackedVar->var))) { + pf.addFinalizerEdge({mangleSymbol(edge.first), mangleSymbol(edge.second)}); + } MCGLogger::logDebug("Add potential finalizer for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); } } From 0c9782c20b4f27d0ea71a6c871fd7bb082d28cf7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:13 +0100 Subject: [PATCH 109/130] refactor in individual files --- cgfcollector/include/ParseTreeVisitor.h | 47 +---- cgfcollector/include/edge.h | 70 +++++++ cgfcollector/include/function.h | 2 + cgfcollector/include/potentialFinalizer.h | 4 +- cgfcollector/include/trackedVar.h | 10 - cgfcollector/include/type.h | 5 + cgfcollector/include/util.h | 11 + cgfcollector/include/variableTracking.h | 50 +++++ cgfcollector/src/ParseTreeVisitor.cpp | 232 +++------------------- cgfcollector/src/edge.cpp | 39 ++++ cgfcollector/src/util.cpp | 64 ++++++ cgfcollector/src/variableTracking.cpp | 81 ++++++++ 12 files changed, 365 insertions(+), 250 deletions(-) create mode 100644 cgfcollector/include/edge.h delete mode 100644 cgfcollector/include/trackedVar.h create mode 100644 cgfcollector/include/variableTracking.h create mode 100644 cgfcollector/src/edge.cpp create mode 100644 cgfcollector/src/variableTracking.cpp diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index b752b18d..78cea08f 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -20,19 +20,18 @@ #include #include +#include "edge.h" #include "function.h" #include "potentialFinalizer.h" -#include "trackedVar.h" #include "type.h" #include "util.h" +#include "variableTracking.h" using namespace Fortran::parser; using namespace Fortran::semantics; using namespace Fortran::common; using namespace metacg; -using edge = std::pair; // (caller, callee) - /** * @class ParseTreeVisitor * @brief Implements visitor methods to traverse parse tree and generate callgraph @@ -40,7 +39,11 @@ using edge = std::pair; // (caller, callee) */ class ParseTreeVisitor { public: - ParseTreeVisitor(Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName) {}; + ParseTreeVisitor(Callgraph* cg, std::string currentFileName) + : cg(cg), + currentFileName(currentFileName), + edgeM(std::make_unique(edges)), + varTracking(std::make_unique(trackedVars)) {}; /** * @brief Collects function/subroutine statements (begin) and their dummy args. @@ -55,14 +58,6 @@ class ParseTreeVisitor { */ void handleEndFuncSubStmt(); - /** - * @brief Searches the types vector for given typeSymbol and returns pointers to vectors of type with derived types. - * - * @param typeSymbol symbol to search for - * @return vector with type and all derived types - */ - std::vector findTypeWithDerivedTypes(const Symbol* typeSymbol); - /** * @brief Searches with typeSymbol for a type in types vector and adds edges for procedures that matches * procedureSymbol. And also adds edges from types that extends from typeSymbol. @@ -72,32 +67,6 @@ class ParseTreeVisitor { */ void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); - void addEdgesForFinalizers(const Symbol* typeSymbol); - /** - * @brief For a given type symbol, returns a list of edges from the current function to all finalizers of that type. - * - * @param typeSymbol - */ - std::vector> getEdgesForFinalizers(const Symbol* typeSymbol); - - trackedVar* getTrackedVarFromSourceName(SourceName sourceName); - - /** - * @brief Search trackedVars for a canditate and set it as initialized. - * Prefers local variables when (shadowed). - * - * @param sourceName - */ - void handleTrackedVarAssignment(SourceName sourceName); - - void addTrackedVar(trackedVar var); - void removeTrackedVars(Symbol* procedureSymbol); - - /** - * @brief Go through trackedVars vector and - */ - void handleTrackedVars(); - void postProcess(); // visitor methods @@ -212,6 +181,8 @@ class ParseTreeVisitor { private: Callgraph* cg; std::string currentFileName; + std::unique_ptr edgeM; + std::unique_ptr varTracking; bool inFunctionOrSubroutineSubProgram = false; bool inMainProgram = false; diff --git a/cgfcollector/include/edge.h b/cgfcollector/include/edge.h new file mode 100644 index 00000000..eabb7667 --- /dev/null +++ b/cgfcollector/include/edge.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "type.h" +#include "util.h" + +using namespace Fortran::semantics; +using namespace Fortran::parser; +using namespace metacg; + +struct edge { + std::string caller; + std::string callee; + + edge(std::string caller, std::string callee) : caller(std::move(caller)), callee(std::move(callee)) {} + + bool operator==(const edge& other) const { return caller == other.caller && callee == other.callee; } + bool operator<(const edge& other) const { + return caller < other.caller || (caller == other.caller && callee < other.callee); + } +}; + +struct edgeSymbol { + Symbol* caller; + Symbol* callee; + + edgeSymbol(Symbol* caller, Symbol* callee) : caller(caller), callee(callee) {} + + bool operator==(const edgeSymbol& other) const { return caller == other.caller && callee == other.callee; } + bool operator<(const edgeSymbol& other) const { + return caller < other.caller || (caller == other.caller && callee < other.callee); + } +}; + +struct edgeManager { + std::vector& edges; + + edgeManager(std::vector& edges) : edges(edges) {} + + void addEdge(const edgeSymbol& e) { edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); } + void addEdge(const edge& e) { edges.emplace_back(e.caller, e.callee); } + void addEdges(const std::vector& newEdges, bool debug = false) { + for (const auto& e : newEdges) { + edges.emplace_back(e.caller, e.callee); + + if (debug) { + MCGLogger::logDebug("Add edge: {} -> {}", e.caller, e.callee); + } + } + } + void addEdges(const std::vector& newEdges, bool debug = true) { + for (const auto& e : newEdges) { + edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); + + if (debug) { + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), + mangleSymbol(e.callee), fmt::ptr(e.callee)); + } + } + } + + static std::vector getEdgesForFinalizers(std::vector types, + const Symbol* currentFunctionSymbol); + // void addEdgesForFinalizers(std::vector types, const Symbol* currentFunctionSymbol); +}; diff --git a/cgfcollector/include/function.h b/cgfcollector/include/function.h index 69c557c1..df341e48 100644 --- a/cgfcollector/include/function.h +++ b/cgfcollector/include/function.h @@ -1,3 +1,5 @@ +#pragma once + #include #include diff --git a/cgfcollector/include/potentialFinalizer.h b/cgfcollector/include/potentialFinalizer.h index 55bb3bbc..48f954d6 100644 --- a/cgfcollector/include/potentialFinalizer.h +++ b/cgfcollector/include/potentialFinalizer.h @@ -1,7 +1,9 @@ +#pragma once + #include #include -using edge = std::pair; // (caller, callee) +#include "edge.h" struct potentialFinalizer { std::size_t argPos; diff --git a/cgfcollector/include/trackedVar.h b/cgfcollector/include/trackedVar.h deleted file mode 100644 index 7c697434..00000000 --- a/cgfcollector/include/trackedVar.h +++ /dev/null @@ -1,10 +0,0 @@ -#include - -using namespace Fortran::semantics; - -struct trackedVar { - Symbol* var; - Symbol* procedure; // procedure in which var was defined - bool hasBeenInitialized = false; - bool addFinalizers = false; -}; diff --git a/cgfcollector/include/type.h b/cgfcollector/include/type.h index 2e1d5e94..2fdf9ec6 100644 --- a/cgfcollector/include/type.h +++ b/cgfcollector/include/type.h @@ -1,9 +1,14 @@ +#pragma once + +#include #include #include +#include #include using namespace Fortran::semantics; using namespace Fortran::parser; +using namespace metacg; struct type { Symbol* type; diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h index fb50a180..82b9f24b 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/util.h @@ -7,6 +7,8 @@ #include #include +#include "type.h" + using namespace Fortran::parser; using namespace Fortran::semantics; using namespace Fortran::common; @@ -125,3 +127,12 @@ DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const GenericKind * @return */ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); + +/** + * @brief Searches the types vector for given symbol and returns pointers to vectors of type with derived types. The + * type symbol is derived from the given symbol. + * + * @param typeSymbol symbol to search for + * @return vector with type and all derived types + */ +std::vector findTypeWithDerivedTypes(std::vector& types, const Symbol* symbol); diff --git a/cgfcollector/include/variableTracking.h b/cgfcollector/include/variableTracking.h new file mode 100644 index 00000000..32a3c5c1 --- /dev/null +++ b/cgfcollector/include/variableTracking.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +#include "edge.h" +#include "function.h" +#include "type.h" +#include "util.h" + +using namespace Fortran::semantics; + +struct trackedVar { + Symbol* var; + Symbol* procedure; // procedure in which var was defined + bool hasBeenInitialized = false; + bool addFinalizers = false; + + trackedVar(Symbol* var, Symbol* procedure) + : var(var), procedure(procedure), hasBeenInitialized(false), addFinalizers(false) {} + trackedVar(Symbol* var, Symbol* procedure, bool initialized, bool addFinalizers) + : var(var), procedure(procedure), hasBeenInitialized(initialized), addFinalizers(addFinalizers) {} +}; + +struct variableTracking { + std::vector& trackedVars; + + variableTracking(std::vector& trackedVars) : trackedVars(trackedVars) {} + + trackedVar* getTrackedVarFromSourceName(Symbol* currentFunctionSymbol, SourceName sourceName); + + /** + * @brief Search trackedVars for a canditate and set it as initialized. + * Prefers local variables when (shadowed). + * + * @param sourceName + */ + void handleTrackedVarAssignment(Symbol* currentFunctionSymbol, SourceName sourceName); + + /** + * @brief Go through trackedVars vector and + * + * @param functions + * @param currentFunctionSymbol + */ + void handleTrackedVars(std::unique_ptr& edgeM, std::vector& types, + std::vector& functions, Symbol* currentFunctionSymbol); + + void addTrackedVar(trackedVar var); + void removeTrackedVars(Symbol* procedureSymbol); +}; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index a515f3ac..2426c1ca 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -1,5 +1,8 @@ #include "ParseTreeVisitor.h" +template void ParseTreeVisitor::handleFuncSubStmt(const FunctionStmt&); +template void ParseTreeVisitor::handleFuncSubStmt(const SubroutineStmt&); + template void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (auto* sym = std::get(stmt.t).symbol) { @@ -11,107 +14,14 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { } } -template void ParseTreeVisitor::handleFuncSubStmt(const FunctionStmt&); -template void ParseTreeVisitor::handleFuncSubStmt(const SubroutineStmt&); - void ParseTreeVisitor::handleEndFuncSubStmt() { - handleTrackedVars(); + varTracking->handleTrackedVars(edgeM, types, functions, currentFunctions.back().symbol); if (!currentFunctions.empty()) { currentFunctions.pop_back(); } } -void ParseTreeVisitor::handleTrackedVars() { - auto* currentFunctionSymbol = currentFunctions.back().symbol; - - if (mangleSymbol(currentFunctionSymbol) != "_QQmain") { - if (!trackedVars.empty()) - MCGLogger::logDebug("Handle tracked vars for function"); - - for (auto& trackedVar : trackedVars) { - if (!trackedVar.hasBeenInitialized) - continue; - if (trackedVar.procedure != currentFunctionSymbol) - continue; - - // add edge for deconstruction (finalizer) - if (trackedVar.addFinalizers) { - auto* typeSymbol = getTypeSymbolFromSymbol(trackedVar.var); - if (!typeSymbol) - continue; - addEdgesForFinalizers(typeSymbol); - } - - // set init on dummy function args - auto functionIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& f) { return f.symbol == currentFunctionSymbol; }); - if (functionIt != functions.end()) { - auto dummyArgIt = std::find_if(functionIt->dummyArgs.begin(), functionIt->dummyArgs.end(), - [&](const auto& d) { return d.symbol == trackedVar.var; }); - if (dummyArgIt != functionIt->dummyArgs.end()) { - dummyArgIt->hasBeenInitialized = true; - } - } - } - } - - // cleanup trackedVars - removeTrackedVars(currentFunctionSymbol); -} - -std::vector ParseTreeVisitor::findTypeWithDerivedTypes(const Symbol* typeSymbol) { - std::vector typesWithDerived; - std::unordered_set visited; - - auto findTypeIt = - std::find_if(types.begin(), types.end(), [&typeSymbol](const type& t) { return t.type == typeSymbol; }); - - if (findTypeIt == types.end()) { - return typesWithDerived; - } - - typesWithDerived.push_back(&(*findTypeIt)); // Add the initial type - visited.insert(typeSymbol); - - // collect descendants - std::function collectDescendants = [&](const type* parent) { - for (const auto& t : types) { - if (t.extendsFrom == parent->type && !visited.count(t.type)) { - visited.insert(t.type); - typesWithDerived.push_back(&t); - collectDescendants(&t); // recursive call to find further descendants - } - } - }; - collectDescendants(&(*findTypeIt)); - - // collect ancestors - const Symbol* currentExtendsFrom = findTypeIt->extendsFrom; - while (currentExtendsFrom) { - // not sure if Fortran even allows this. But better be safe - if (!visited.insert(currentExtendsFrom).second) { - MCGLogger::logError("Error: Detected cyclic inheritance involving type \"" + - (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\""); - break; - } - - auto currentTypeIt = std::find_if(types.begin(), types.end(), - [&](const type& t) { return compareSymbols(t.type, currentExtendsFrom); }); - - if (currentTypeIt == types.end()) { - MCGLogger::logError("Error: Types array (extendsFrom) field entry for \"" + - (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\" missing"); - break; - } - - typesWithDerived.push_back(&(*currentTypeIt)); - currentExtendsFrom = currentTypeIt->extendsFrom; - } - - return typesWithDerived; -} - void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol) { for (const type* t : typeWithDerived) { @@ -130,80 +40,6 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector {} ({})", mangleSymbol(edge.first), fmt::ptr(edge.first), - mangleSymbol(edge.second), fmt::ptr(edge.second)); - } -} - -std::vector> ParseTreeVisitor::getEdgesForFinalizers(const Symbol* typeSymbol) { - std::vector> edges; - std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); - - for (const type* type : typeSymbols) { - const Symbol* typeSymbol = type->type; - - const auto* details = std::get_if(&typeSymbol->details()); - if (!details) - continue; - - // add edges for finalizers - for (const auto& final : details->finals()) { - edges.emplace_back(currentFunctions.back().symbol, &final.second.get()); - } - } - - return edges; -} - -trackedVar* ParseTreeVisitor::getTrackedVarFromSourceName(SourceName sourceName) { - auto anyTrackedVarIt = - std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { return t.var->name() == sourceName; }); - if (anyTrackedVarIt == trackedVars.end()) - return nullptr; - - // find local variable with the same name in the current function scope (shadowed) - auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { - return t.var->name() == sourceName && t.procedure == currentFunctions.back().symbol; - }); - - // prefer local var if found - return (localVarIt != trackedVars.end()) ? &(*localVarIt) : &(*anyTrackedVarIt); -} - -void ParseTreeVisitor::handleTrackedVarAssignment(SourceName sourceName) { - auto* trackedVar = getTrackedVarFromSourceName(sourceName); - if (!trackedVar) - return; - - trackedVar->hasBeenInitialized = true; - - MCGLogger::logDebug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); -} - -void ParseTreeVisitor::addTrackedVar(trackedVar var) { - auto it = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { return t.var == var.var; }); - if (it != trackedVars.end()) { - // update info - it->addFinalizers = var.addFinalizers; - it->hasBeenInitialized = var.hasBeenInitialized; - MCGLogger::logDebug("Update tracked variable: {} ({})", var.var->name(), fmt::ptr(var.var)); - return; - } - - trackedVars.push_back(var); - MCGLogger::logDebug("Add tracking for variable: {} ({})", var.var->name(), fmt::ptr(var.var)); -} - -void ParseTreeVisitor::removeTrackedVars(Symbol* procedureSymbol) { - trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), - [&](const trackedVar& t) { return t.procedure == procedureSymbol; }), - trackedVars.end()); -} - void ParseTreeVisitor::postProcess() { // handle potential finalizers from function calls for (const auto pf : potentialFinalizers) { @@ -218,8 +54,8 @@ void ParseTreeVisitor::postProcess() { continue; for (const auto& edge : pf.finalizerEdges) { - edges.emplace_back(edge.first, edge.second); - MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.first, edge.second); + edgeM->addEdge(edge); + MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.caller, edge.callee); } } @@ -230,8 +66,8 @@ void ParseTreeVisitor::postProcess() { // add edges for (auto edge : edges) { - const auto& callerNode = cg->getOrInsertNode(edge.first); - const auto& calleeNode = cg->getOrInsertNode(edge.second); + const auto& callerNode = cg->getOrInsertNode(edge.caller); + const auto& calleeNode = cg->getOrInsertNode(edge.callee); cg->addEdge(callerNode, calleeNode); } @@ -257,7 +93,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { } void ParseTreeVisitor::Post(const MainProgram&) { - handleTrackedVars(); + varTracking->handleTrackedVars(edgeM, types, functions, currentFunctions.back().symbol); auto* currentFunctionSymbol = currentFunctions.back().symbol; @@ -324,7 +160,7 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { currentFunctions.back().addDummyArg(name.symbol); if (functionsIt != functions.end()) { functionsIt->addDummyArg(name.symbol); - addTrackedVar({name.symbol, currentFunctionSymbol, false, false}); + varTracking->addTrackedVar({name.symbol, currentFunctionSymbol}); } } } @@ -357,7 +193,7 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { currentFunctions.back().addDummyArg(name->symbol); if (functionsIt != functions.end()) { functionsIt->addDummyArg(name->symbol); - addTrackedVar({name->symbol, currentFunctionSymbol, false, false}); + varTracking->addTrackedVar({name->symbol, currentFunctionSymbol}); } } } @@ -411,11 +247,7 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { // handle derived types edges - auto* typeSymbol = getTypeSymbolFromSymbol(symbolBase); - if (!typeSymbol) - return; - - addEdgesForProducesAndDerivedTypes(findTypeWithDerivedTypes(typeSymbol), symbolComp); + addEdgesForProducesAndDerivedTypes(findTypeWithDerivedTypes(types, symbolBase), symbolComp); } } @@ -426,7 +258,7 @@ void ParseTreeVisitor::Post(const AssignmentStmt& a) { if (!name || !name->symbol) return; - handleTrackedVarAssignment(name->symbol->name()); + varTracking->handleTrackedVarAssignment(currentFunctions.back().symbol, name->symbol->name()); } void ParseTreeVisitor::Post(const AllocateStmt& a) { @@ -439,7 +271,7 @@ void ParseTreeVisitor::Post(const AllocateStmt& a) { continue; } - handleTrackedVarAssignment(name->symbol->name()); + varTracking->handleTrackedVarAssignment(currentFunctions.back().symbol, name->symbol->name()); } } @@ -463,20 +295,23 @@ void ParseTreeVisitor::Post(const Call& c) { // handle move_alloc intrinsic for allocatable vars if (procName->symbol->attrs().test(Attr::INTRINSIC) && procName->symbol->name() == "move_alloc") { - handleTrackedVarAssignment(name->symbol->name()); + varTracking->handleTrackedVarAssignment(currentFunctions.back().symbol, name->symbol->name()); } else { // handle finalizers for allocatable vars. // This collects info from variables that are parse as arguments to functions. Function are defined below the // execution part, so this need to be handled at the end of the parse tree traversal. - auto* trackedVar = getTrackedVarFromSourceName(name->symbol->name()); + auto* trackedVar = varTracking->getTrackedVarFromSourceName(currentFunctions.back().symbol, name->symbol->name()); if (!trackedVar) continue; + MCGLogger::logDebug("Add potential finalizers for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); potentialFinalizer& pf = potentialFinalizers.emplace_back(argPos, mangleSymbol(procName->symbol)); - for (const auto& edge : getEdgesForFinalizers(getTypeSymbolFromSymbol(trackedVar->var))) { - pf.addFinalizerEdge({mangleSymbol(edge.first), mangleSymbol(edge.second)}); + for (const auto& edge : edgeM->getEdgesForFinalizers(findTypeWithDerivedTypes(types, trackedVar->var), + currentFunctions.back().symbol)) { + pf.addFinalizerEdge({mangleSymbol(edge.caller), mangleSymbol(edge.callee)}); + MCGLogger::logDebug(" Potential finalizer edge: {} -> {}", mangleSymbol(edge.caller), + mangleSymbol(edge.callee)); } - MCGLogger::logDebug("Add potential finalizer for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); } } } @@ -502,10 +337,6 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { isFunctionArg = true; } - auto* typeSymbol = getTypeSymbolFromSymbol(name.symbol); - if (!typeSymbol) - continue; - bool holds_allocatable = false; const IntentSpec* holds_intent = nullptr; bool holds_save = false; @@ -529,15 +360,16 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { // no intent attr, if not set does not call finalizer. Why? idk. MCGLogger::logDebug("Add tracking for function argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); + varTracking->addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); } else { if (holds_intent->v == IntentSpec::Intent::Out) { // intent out, calls finalizer because (7.5.6.3 line 21 and onwards) - addEdgesForFinalizers(typeSymbol); + edgeM->addEdges(edgeManager::getEdgesForFinalizers(findTypeWithDerivedTypes(types, name.symbol), + currentFunctionSymbol)); } else if (holds_intent->v == IntentSpec::Intent::InOut) { // intent inout, calls finalizer when set. MCGLogger::logDebug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); + varTracking->addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); } } } @@ -545,11 +377,12 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { if (holds_allocatable) { MCGLogger::logDebug("Add tracking for allocatable variable: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); - addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); + varTracking->addTrackedVar({name.symbol, currentFunctionSymbol, false, true}); // skip var with allocatable attr. // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. } else { - addEdgesForFinalizers(typeSymbol); + edgeM->addEdges( + edgeManager::getEdgesForFinalizers(findTypeWithDerivedTypes(types, name.symbol), currentFunctionSymbol)); } } } @@ -762,11 +595,8 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } // search in derived types - auto* typeSymbol = getTypeSymbolFromSymbol(name->symbol); - if (!typeSymbol) - continue; - auto typeWithDerived = findTypeWithDerivedTypes(typeSymbol); + auto typeWithDerived = findTypeWithDerivedTypes(types, name->symbol); for (const type* t : typeWithDerived) { auto opIt = std::find_if(t->operators.begin(), t->operators.end(), @@ -774,7 +604,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (opIt == t->operators.end()) continue; - auto funcSymbol = opIt->second; + Symbol* funcSymbol = opIt->second; bool skipSelfCall = false; for (const type* t : typeWithDerived) { diff --git a/cgfcollector/src/edge.cpp b/cgfcollector/src/edge.cpp new file mode 100644 index 00000000..d7382080 --- /dev/null +++ b/cgfcollector/src/edge.cpp @@ -0,0 +1,39 @@ +#include "edge.h" + +/** + * @brief For a given type symbol, returns a list of edges from the current function to all finalizers of that type. + * + * @param typeSymbol + */ +std::vector edgeManager::getEdgesForFinalizers(std::vector types, + const Symbol* currentFunctionSymbol) { + std::vector edges; + // std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); + + for (const type* type : types) { + const Symbol* typeSymbol = type->type; + + const auto* details = std::get_if(&typeSymbol->details()); + if (!details) + continue; + + // add edges for finalizers + for (const auto& final : details->finals()) { + auto second = const_cast(&final.second.get()); // TODO: remove const cast + auto currentFunctionSymbolNoConst = const_cast(currentFunctionSymbol); + edges.emplace_back(currentFunctionSymbolNoConst, second); + } + } + + return edges; +} + +// void edgeManager::addEdgesForFinalizers(std::vector types, const Symbol* currentFunctionSymbol) { +// for (const auto& edge : getEdgesForFinalizers(types, currentFunctionSymbol)) { +// edges.emplace_back(mangleSymbol(edge.caller), mangleSymbol(edge.callee)); + +// MCGLogger::logDebug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.caller), +// fmt::ptr(edge.caller), +// mangleSymbol(edge.callee), fmt::ptr(edge.callee)); +// } +// } diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index e65adae6..a4698562 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -213,3 +213,67 @@ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol) { return nullptr; return typeSymbol; } + +/** + * @brief Searches the types vector for given symbol and returns pointers to vectors of type with derived types. The + * type symbol is derived from the given symbol. + * + * @param typeSymbol symbol to search for + * @return vector with type and all derived types + */ +std::vector findTypeWithDerivedTypes(std::vector& types, const Symbol* symbol) { + std::vector typesWithDerived; + std::unordered_set visited; + + const Symbol* typeSymbol = getTypeSymbolFromSymbol(symbol); + if (!typeSymbol) { + return typesWithDerived; + } + + auto findTypeIt = + std::find_if(types.begin(), types.end(), [&typeSymbol](const type& t) { return t.type == typeSymbol; }); + + if (findTypeIt == types.end()) { + return typesWithDerived; + } + + typesWithDerived.push_back(&(*findTypeIt)); // Add the initial type + visited.insert(typeSymbol); + + // collect descendants + std::function collectDescendants = [&](const type* parent) { + for (const auto& t : types) { + if (t.extendsFrom == parent->type && !visited.count(t.type)) { + visited.insert(t.type); + typesWithDerived.push_back(&t); + collectDescendants(&t); // recursive call to find further descendants + } + } + }; + collectDescendants(&(*findTypeIt)); + + // collect ancestors + const Symbol* currentExtendsFrom = findTypeIt->extendsFrom; + while (currentExtendsFrom) { + // not sure if Fortran even allows this. But better be safe + if (!visited.insert(currentExtendsFrom).second) { + MCGLogger::logError("Error: Detected cyclic inheritance involving type \"" + + (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\""); + break; + } + + auto currentTypeIt = std::find_if(types.begin(), types.end(), + [&](const type& t) { return compareSymbols(t.type, currentExtendsFrom); }); + + if (currentTypeIt == types.end()) { + MCGLogger::logError("Error: Types array (extendsFrom) field entry for \"" + + (currentExtendsFrom ? currentExtendsFrom->name().ToString() : "null") + "\" missing"); + break; + } + + typesWithDerived.push_back(&(*currentTypeIt)); + currentExtendsFrom = currentTypeIt->extendsFrom; + } + + return typesWithDerived; +} diff --git a/cgfcollector/src/variableTracking.cpp b/cgfcollector/src/variableTracking.cpp new file mode 100644 index 00000000..a1b378f6 --- /dev/null +++ b/cgfcollector/src/variableTracking.cpp @@ -0,0 +1,81 @@ +#include "variableTracking.h" + +trackedVar* variableTracking::getTrackedVarFromSourceName(Symbol* currentFunctionSymbol, SourceName sourceName) { + auto anyTrackedVarIt = + std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { return t.var->name() == sourceName; }); + if (anyTrackedVarIt == trackedVars.end()) + return nullptr; + + // find local variable with the same name in the current function scope (shadowed) + auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { + return t.var->name() == sourceName && t.procedure == currentFunctionSymbol; + }); + + // prefer local var if found + return (localVarIt != trackedVars.end()) ? &(*localVarIt) : &(*anyTrackedVarIt); +} + +void variableTracking::handleTrackedVarAssignment(Symbol* currentFunctionSymbol, SourceName sourceName) { + auto* trackedVar = getTrackedVarFromSourceName(currentFunctionSymbol, sourceName); + if (!trackedVar) + return; + + trackedVar->hasBeenInitialized = true; + + MCGLogger::logDebug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); +} + +void variableTracking::handleTrackedVars(std::unique_ptr& edgeM, std::vector& types, + std::vector& functions, Symbol* currentFunctionSymbol) { + if (mangleSymbol(currentFunctionSymbol) != "_QQmain") { + if (!trackedVars.empty()) + MCGLogger::logDebug("Handle tracked vars for function"); + + for (auto& trackedVar : trackedVars) { + if (!trackedVar.hasBeenInitialized) + continue; + if (trackedVar.procedure != currentFunctionSymbol) + continue; + + // add edge for deconstruction (finalizer) + if (trackedVar.addFinalizers) { + edgeM->addEdges( + edgeManager::getEdgesForFinalizers(findTypeWithDerivedTypes(types, trackedVar.var), currentFunctionSymbol)); + } + + // set init on dummy function args + auto functionIt = std::find_if(functions.begin(), functions.end(), + [&](const auto& f) { return f.symbol == currentFunctionSymbol; }); + if (functionIt != functions.end()) { + auto dummyArgIt = std::find_if(functionIt->dummyArgs.begin(), functionIt->dummyArgs.end(), + [&](const auto& d) { return d.symbol == trackedVar.var; }); + if (dummyArgIt != functionIt->dummyArgs.end()) { + dummyArgIt->hasBeenInitialized = true; + } + } + } + } + + // cleanup trackedVars + removeTrackedVars(currentFunctionSymbol); +} + +void variableTracking::addTrackedVar(trackedVar var) { + auto it = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { return t.var == var.var; }); + if (it != trackedVars.end()) { + // update info + it->addFinalizers = var.addFinalizers; + it->hasBeenInitialized = var.hasBeenInitialized; + MCGLogger::logDebug("Update tracked variable: {} ({})", var.var->name(), fmt::ptr(var.var)); + return; + } + + trackedVars.push_back(var); + MCGLogger::logDebug("Add tracking for variable: {} ({})", var.var->name(), fmt::ptr(var.var)); +} + +void variableTracking::removeTrackedVars(Symbol* procedureSymbol) { + trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), + [&](const trackedVar& t) { return t.procedure == procedureSymbol; }), + trackedVars.end()); +} From f2693439fa847bea1ead66b22f8015b389fbc4d5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:13 +0100 Subject: [PATCH 110/130] variableTracking doc --- cgfcollector/include/ParseTreeVisitor.h | 2 +- cgfcollector/include/variableTracking.h | 40 ++++++++++++++++++++----- cgfcollector/src/ParseTreeVisitor.cpp | 4 +-- cgfcollector/src/variableTracking.cpp | 3 +- 4 files changed, 36 insertions(+), 13 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 78cea08f..8db6cb9d 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -43,7 +43,7 @@ class ParseTreeVisitor { : cg(cg), currentFileName(currentFileName), edgeM(std::make_unique(edges)), - varTracking(std::make_unique(trackedVars)) {}; + varTracking(std::make_unique(trackedVars, types, functions)) {}; /** * @brief Collects function/subroutine statements (begin) and their dummy args. diff --git a/cgfcollector/include/variableTracking.h b/cgfcollector/include/variableTracking.h index 32a3c5c1..d537e14f 100644 --- a/cgfcollector/include/variableTracking.h +++ b/cgfcollector/include/variableTracking.h @@ -22,29 +22,53 @@ struct trackedVar { }; struct variableTracking { - std::vector& trackedVars; - - variableTracking(std::vector& trackedVars) : trackedVars(trackedVars) {} + public: + variableTracking(std::vector& trackedVars, std::vector& types, std::vector& functions) + : trackedVars(trackedVars), types(types), functions(functions) {} + /** + * @brief Search trackedVars for a canditate by sourceName. Also taking the current function scope into account. + * + * @param currentFunctionSymbol + * @param sourceName + * @return trackedVar* or nullptr if not found + */ trackedVar* getTrackedVarFromSourceName(Symbol* currentFunctionSymbol, SourceName sourceName); /** * @brief Search trackedVars for a canditate and set it as initialized. - * Prefers local variables when (shadowed). * + * @param currentFunctionSymbol * @param sourceName */ void handleTrackedVarAssignment(Symbol* currentFunctionSymbol, SourceName sourceName); /** - * @brief Go through trackedVars vector and + * @brief Is called at the end of a function/subroutine end statement. It checks trackedVars for any initialized + * variables and adds edges. Currently only adds finalizer edges. * - * @param functions * @param currentFunctionSymbol + * @param edgeM TODO: change dep */ - void handleTrackedVars(std::unique_ptr& edgeM, std::vector& types, - std::vector& functions, Symbol* currentFunctionSymbol); + void handleTrackedVars(Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); + /** + * @brief Register a variable for tracking. + * + * @param var + */ void addTrackedVar(trackedVar var); + + /** + * @brief Remove all tracked variables that are not needed anymore after the function/subroutine end statement. Should + * be called at the end of function/subroutine processing. + * + * @param procedureSymbol + */ void removeTrackedVars(Symbol* procedureSymbol); + + private: + std::vector& trackedVars; + std::vector& types; + std::vector& functions; }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 2426c1ca..f369eb55 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -15,7 +15,7 @@ void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { } void ParseTreeVisitor::handleEndFuncSubStmt() { - varTracking->handleTrackedVars(edgeM, types, functions, currentFunctions.back().symbol); + varTracking->handleTrackedVars(currentFunctions.back().symbol, edgeM); if (!currentFunctions.empty()) { currentFunctions.pop_back(); @@ -93,7 +93,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { } void ParseTreeVisitor::Post(const MainProgram&) { - varTracking->handleTrackedVars(edgeM, types, functions, currentFunctions.back().symbol); + varTracking->handleTrackedVars(currentFunctions.back().symbol, edgeM); auto* currentFunctionSymbol = currentFunctions.back().symbol; diff --git a/cgfcollector/src/variableTracking.cpp b/cgfcollector/src/variableTracking.cpp index a1b378f6..ac934929 100644 --- a/cgfcollector/src/variableTracking.cpp +++ b/cgfcollector/src/variableTracking.cpp @@ -25,8 +25,7 @@ void variableTracking::handleTrackedVarAssignment(Symbol* currentFunctionSymbol, MCGLogger::logDebug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); } -void variableTracking::handleTrackedVars(std::unique_ptr& edgeM, std::vector& types, - std::vector& functions, Symbol* currentFunctionSymbol) { +void variableTracking::handleTrackedVars(Symbol* currentFunctionSymbol, std::unique_ptr& edgeM) { if (mangleSymbol(currentFunctionSymbol) != "_QQmain") { if (!trackedVars.empty()) MCGLogger::logDebug("Handle tracked vars for function"); From f9a31bc894d9c7edf5b7cf24ac4c7a1a7f24ed2c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:13 +0100 Subject: [PATCH 111/130] ParseTreeVisitor more doc --- cgfcollector/include/ParseTreeVisitor.h | 36 +++++++++++++++++++++++-- cgfcollector/src/ParseTreeVisitor.cpp | 1 + 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 8db6cb9d..ee7f833a 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -53,6 +53,7 @@ class ParseTreeVisitor { */ template void handleFuncSubStmt(const T& stmt); + /** * @brief Handles function/subroutine end statements. */ @@ -67,6 +68,9 @@ class ParseTreeVisitor { */ void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); + /** + * @brief Adds edges and potential finalizers edges to cg. + */ void postProcess(); // visitor methods @@ -86,6 +90,11 @@ class ParseTreeVisitor { bool Pre(const SubroutineSubprogram&); void Post(const SubroutineSubprogram&); + /** + * @brief Set hasBody field. + * + * @param e + */ void Post(const ExecutionPart& e); void Post(const EntryStmt& e); @@ -95,20 +104,43 @@ class ParseTreeVisitor { void Post(const SubroutineStmt& s); void Post(const EndSubroutineStmt&); + /** + * @brief ProcedureDesignator: A procedure being called. Handles both cases a call with call statement and without. + * + * @param p + */ void Post(const ProcedureDesignator& p); + /** + * @brief Handle trackedVar assignment + * + * @param a + */ void Post(const AssignmentStmt& a); + + /** + * @brief Handle trackedVar assignment through allocate statement. + * + * @param a + */ void Post(const AllocateStmt& a); + + /** + * @brief Mostly add potential finalizers for variables that get initialized through procedure arguments. + * + * @param c + */ void Post(const Call& c); /** - * @brief Handle finalizers (destructors ). + * @brief Mostly handles finalizers. Handles the different ways a variable can be parsed to a procedure and gets + * initialized. * * @param t */ void Post(const TypeDeclarationStmt& t); - // The following methods are for collecting types and their procedures. see type struct and vector. + // The following methods are for collecting types and their procedures. See type struct and vector. /** * @brief Type definition start diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index f369eb55..b1438c54 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -140,6 +140,7 @@ void ParseTreeVisitor::Post(const EntryStmt& e) { MCGLogger::logDebug("Add Entry point: {} ({})", mangleSymbol(name->symbol), fmt::ptr(name->symbol)); + // handle entry statement as normal function. cg->getOrInsertNode(mangleSymbol(name->symbol), currentFileName, false, true); } From 257093994759bc483f9300f45d7396c1e26c03df Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:14 +0100 Subject: [PATCH 112/130] edgeManager refactor --- cgfcollector/include/edge.h | 34 ++++++---------- cgfcollector/src/ParseTreeVisitor.cpp | 22 +++-------- cgfcollector/src/edge.cpp | 57 ++++++++++++++++++++++++--- 3 files changed, 68 insertions(+), 45 deletions(-) diff --git a/cgfcollector/include/edge.h b/cgfcollector/include/edge.h index eabb7667..0bc240df 100644 --- a/cgfcollector/include/edge.h +++ b/cgfcollector/include/edge.h @@ -42,28 +42,18 @@ struct edgeManager { edgeManager(std::vector& edges) : edges(edges) {} - void addEdge(const edgeSymbol& e) { edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); } - void addEdge(const edge& e) { edges.emplace_back(e.caller, e.callee); } - void addEdges(const std::vector& newEdges, bool debug = false) { - for (const auto& e : newEdges) { - edges.emplace_back(e.caller, e.callee); - - if (debug) { - MCGLogger::logDebug("Add edge: {} -> {}", e.caller, e.callee); - } - } - } - void addEdges(const std::vector& newEdges, bool debug = true) { - for (const auto& e : newEdges) { - edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); - - if (debug) { - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), - mangleSymbol(e.callee), fmt::ptr(e.callee)); - } - } - } - + void addEdge(const edgeSymbol& e, bool debug = true); + void addEdge(Symbol* caller, Symbol* callee, bool debug = true); + void addEdge(const edge& e, bool debug = true); + void addEdge(const std::string& caller, const std::string& callee, bool debug = true); + void addEdges(const std::vector& newEdges, bool debug = true); + void addEdges(const std::vector& newEdges, bool debug = true); + + /** + * @brief For a given type symbol, returns a list of edges from the current function to all finalizers of that type. + * + * @param typeSymbol + */ static std::vector getEdgesForFinalizers(std::vector types, const Symbol* currentFunctionSymbol); // void addEdgesForFinalizers(std::vector types, const Symbol* currentFunctionSymbol); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index b1438c54..914f8b6d 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -33,10 +33,7 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vectorsecond)); - - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), - fmt::ptr(currentFunctionSymbol), mangleSymbol(procIt->second), fmt::ptr(procIt->second)); + edgeM->addEdge(currentFunctionSymbol, procIt->second); } } @@ -54,7 +51,7 @@ void ParseTreeVisitor::postProcess() { continue; for (const auto& edge : pf.finalizerEdges) { - edgeM->addEdge(edge); + edgeM->addEdge(edge, false); MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.caller, edge.callee); } } @@ -225,10 +222,7 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (name->symbol->attrs().test(Attr::INTRINSIC)) return; - edges.emplace_back(mangleSymbol(currentFunctionSymbol), mangleSymbol(name->symbol)); - - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), - fmt::ptr(currentFunctionSymbol), mangleSymbol(name->symbol), fmt::ptr(name->symbol)); + edgeM->addEdge(currentFunctionSymbol, name->symbol); // if called from a object with %. (base % component) } else if (auto* procCompRef = std::get_if(&p.u)) { @@ -236,10 +230,7 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (!symbolComp) return; - edges.emplace_back(mangleSymbol(currentFunctionSymbol), mangleSymbol(symbolComp)); - - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), - fmt::ptr(currentFunctionSymbol), mangleSymbol(symbolComp), fmt::ptr(symbolComp)); + edgeM->addEdge(currentFunctionSymbol, symbolComp); auto* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) @@ -588,10 +579,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { } } - edges.emplace_back(mangleSymbol(currentFunctionSymbol), mangleSymbol(sym)); - - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(currentFunctionSymbol), - fmt::ptr(currentFunctionSymbol), mangleSymbol(sym), fmt::ptr(sym)); + edgeM->addEdge(currentFunctionSymbol, sym); } } diff --git a/cgfcollector/src/edge.cpp b/cgfcollector/src/edge.cpp index d7382080..f2bb9e95 100644 --- a/cgfcollector/src/edge.cpp +++ b/cgfcollector/src/edge.cpp @@ -1,14 +1,8 @@ #include "edge.h" -/** - * @brief For a given type symbol, returns a list of edges from the current function to all finalizers of that type. - * - * @param typeSymbol - */ std::vector edgeManager::getEdgesForFinalizers(std::vector types, const Symbol* currentFunctionSymbol) { std::vector edges; - // std::vector typeSymbols = findTypeWithDerivedTypes(typeSymbol); for (const type* type : types) { const Symbol* typeSymbol = type->type; @@ -37,3 +31,54 @@ std::vector edgeManager::getEdgesForFinalizers(std::vector {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), + mangleSymbol(e.callee), fmt::ptr(e.callee)); + } +} + +void edgeManager::addEdge(Symbol* caller, Symbol* callee, bool debug) { + edges.emplace_back(mangleSymbol(caller), mangleSymbol(callee)); + if (debug) { + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(caller), fmt::ptr(caller), mangleSymbol(callee), + fmt::ptr(callee)); + } +} + +void edgeManager::addEdge(const edge& e, bool debug) { + edges.emplace_back(e.caller, e.callee); + if (debug) { + MCGLogger::logDebug("Add edge: {} -> {}", e.caller, e.callee); + } +} + +void edgeManager::addEdge(const std::string& caller, const std::string& callee, bool debug) { + edges.emplace_back(caller, callee); + if (debug) { + MCGLogger::logDebug("Add edge: {} -> {}", caller, callee); + } +} + +void edgeManager::addEdges(const std::vector& newEdges, bool debug) { + for (const auto& e : newEdges) { + edges.emplace_back(e.caller, e.callee); + + if (debug) { + MCGLogger::logDebug("Add edge: {} -> {}", e.caller, e.callee); + } + } +} + +void edgeManager::addEdges(const std::vector& newEdges, bool debug) { + for (const auto& e : newEdges) { + edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); + + if (debug) { + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), + mangleSymbol(e.callee), fmt::ptr(e.callee)); + } + } +} From e5ef515048779cbcfdb0b506b38087cc84911f97 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:14 +0100 Subject: [PATCH 113/130] edgeManager refactor --- cgfcollector/include/edge.h | 19 ++++++++++---- cgfcollector/src/ParseTreeVisitor.cpp | 14 +++++----- cgfcollector/src/edge.cpp | 38 +++++++++++---------------- cgfcollector/src/variableTracking.cpp | 3 +-- 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/cgfcollector/include/edge.h b/cgfcollector/include/edge.h index 0bc240df..4e1d7f37 100644 --- a/cgfcollector/include/edge.h +++ b/cgfcollector/include/edge.h @@ -50,11 +50,20 @@ struct edgeManager { void addEdges(const std::vector& newEdges, bool debug = true); /** - * @brief For a given type symbol, returns a list of edges from the current function to all finalizers of that type. + * @brief For a given symbol, returns a list of edges from the current function to all finalizers of that type. * - * @param typeSymbol + * @param types + * @param currentFunctionSymbol + * @param symbol */ - static std::vector getEdgesForFinalizers(std::vector types, - const Symbol* currentFunctionSymbol); - // void addEdgesForFinalizers(std::vector types, const Symbol* currentFunctionSymbol); + std::vector getEdgesForFinalizers(std::vector& types, const Symbol* currentFunctionSymbol, + const Symbol* symbol); + /** + * @brief Calls getEdgesForFinalizers and adds them to the edges vector. + * + * @param types + * @param currentFunctionSymbol + * @param symbol + */ + void addEdgesForFinalizers(std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol); }; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 914f8b6d..5719bbf3 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -270,6 +270,7 @@ void ParseTreeVisitor::Post(const AllocateStmt& a) { void ParseTreeVisitor::Post(const Call& c) { const auto* designator = &std::get(c.t); const auto* args = &std::get>(c.t); + auto* currentFunctionSymbol = currentFunctions.back().symbol; const auto* procName = std::get_if(&designator->u); if (!procName || !procName->symbol) @@ -287,19 +288,18 @@ void ParseTreeVisitor::Post(const Call& c) { // handle move_alloc intrinsic for allocatable vars if (procName->symbol->attrs().test(Attr::INTRINSIC) && procName->symbol->name() == "move_alloc") { - varTracking->handleTrackedVarAssignment(currentFunctions.back().symbol, name->symbol->name()); + varTracking->handleTrackedVarAssignment(currentFunctionSymbol, name->symbol->name()); } else { // handle finalizers for allocatable vars. // This collects info from variables that are parse as arguments to functions. Function are defined below the // execution part, so this need to be handled at the end of the parse tree traversal. - auto* trackedVar = varTracking->getTrackedVarFromSourceName(currentFunctions.back().symbol, name->symbol->name()); + auto* trackedVar = varTracking->getTrackedVarFromSourceName(currentFunctionSymbol, name->symbol->name()); if (!trackedVar) continue; MCGLogger::logDebug("Add potential finalizers for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); potentialFinalizer& pf = potentialFinalizers.emplace_back(argPos, mangleSymbol(procName->symbol)); - for (const auto& edge : edgeM->getEdgesForFinalizers(findTypeWithDerivedTypes(types, trackedVar->var), - currentFunctions.back().symbol)) { + for (const auto& edge : edgeM->getEdgesForFinalizers(types, currentFunctionSymbol, trackedVar->var)) { pf.addFinalizerEdge({mangleSymbol(edge.caller), mangleSymbol(edge.callee)}); MCGLogger::logDebug(" Potential finalizer edge: {} -> {}", mangleSymbol(edge.caller), mangleSymbol(edge.callee)); @@ -356,8 +356,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { } else { if (holds_intent->v == IntentSpec::Intent::Out) { // intent out, calls finalizer because (7.5.6.3 line 21 and onwards) - edgeM->addEdges(edgeManager::getEdgesForFinalizers(findTypeWithDerivedTypes(types, name.symbol), - currentFunctionSymbol)); + edgeM->addEdgesForFinalizers(types, currentFunctionSymbol, name.symbol); } else if (holds_intent->v == IntentSpec::Intent::InOut) { // intent inout, calls finalizer when set. MCGLogger::logDebug("Add tracking for inout argument: {} ({})", name.symbol->name(), fmt::ptr(name.symbol)); @@ -373,8 +372,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { // skip var with allocatable attr. // Add to trackedVars because it needs to be assigned at least once before calling a finalizers make sense. } else { - edgeM->addEdges( - edgeManager::getEdgesForFinalizers(findTypeWithDerivedTypes(types, name.symbol), currentFunctionSymbol)); + edgeM->addEdgesForFinalizers(types, currentFunctionSymbol, name.symbol); } } } diff --git a/cgfcollector/src/edge.cpp b/cgfcollector/src/edge.cpp index f2bb9e95..544ee574 100644 --- a/cgfcollector/src/edge.cpp +++ b/cgfcollector/src/edge.cpp @@ -1,10 +1,12 @@ #include "edge.h" -std::vector edgeManager::getEdgesForFinalizers(std::vector types, - const Symbol* currentFunctionSymbol) { +std::vector edgeManager::getEdgesForFinalizers(std::vector& types, + const Symbol* currentFunctionSymbol, const Symbol* symbol) { std::vector edges; - for (const type* type : types) { + std::vector typePtrs = findTypeWithDerivedTypes(types, symbol); + + for (const type* type : typePtrs) { const Symbol* typeSymbol = type->type; const auto* details = std::get_if(&typeSymbol->details()); @@ -22,15 +24,14 @@ std::vector edgeManager::getEdgesForFinalizers(std::vector types, const Symbol* currentFunctionSymbol) { -// for (const auto& edge : getEdgesForFinalizers(types, currentFunctionSymbol)) { -// edges.emplace_back(mangleSymbol(edge.caller), mangleSymbol(edge.callee)); - -// MCGLogger::logDebug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.caller), -// fmt::ptr(edge.caller), -// mangleSymbol(edge.callee), fmt::ptr(edge.callee)); -// } -// } +void edgeManager::addEdgesForFinalizers(std::vector& types, const Symbol* currentFunctionSymbol, + const Symbol* symbol) { + for (const auto& edge : getEdgesForFinalizers(types, currentFunctionSymbol, symbol)) { + addEdge(edge, false); + MCGLogger::logDebug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.caller), fmt::ptr(edge.caller), + mangleSymbol(edge.callee), fmt::ptr(edge.callee)); + } +} void edgeManager::addEdge(const edgeSymbol& e, bool debug) { edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); @@ -64,21 +65,12 @@ void edgeManager::addEdge(const std::string& caller, const std::string& callee, void edgeManager::addEdges(const std::vector& newEdges, bool debug) { for (const auto& e : newEdges) { - edges.emplace_back(e.caller, e.callee); - - if (debug) { - MCGLogger::logDebug("Add edge: {} -> {}", e.caller, e.callee); - } + addEdge(e, debug); } } void edgeManager::addEdges(const std::vector& newEdges, bool debug) { for (const auto& e : newEdges) { - edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); - - if (debug) { - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), - mangleSymbol(e.callee), fmt::ptr(e.callee)); - } + addEdge(e, debug); } } diff --git a/cgfcollector/src/variableTracking.cpp b/cgfcollector/src/variableTracking.cpp index ac934929..98fef458 100644 --- a/cgfcollector/src/variableTracking.cpp +++ b/cgfcollector/src/variableTracking.cpp @@ -38,8 +38,7 @@ void variableTracking::handleTrackedVars(Symbol* currentFunctionSymbol, std::uni // add edge for deconstruction (finalizer) if (trackedVar.addFinalizers) { - edgeM->addEdges( - edgeManager::getEdgesForFinalizers(findTypeWithDerivedTypes(types, trackedVar.var), currentFunctionSymbol)); + edgeM->addEdgesForFinalizers(types, currentFunctionSymbol, trackedVar.var); } // set init on dummy function args From 4e7ace687cfe1a9513ef6e3c13c1f9813ffe755a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:14 +0100 Subject: [PATCH 114/130] rename type typeSymbol --- cgfcollector/include/type.h | 2 +- cgfcollector/src/ParseTreeVisitor.cpp | 6 +++--- cgfcollector/src/edge.cpp | 2 +- cgfcollector/src/util.cpp | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cgfcollector/include/type.h b/cgfcollector/include/type.h index 2fdf9ec6..10f0ef0f 100644 --- a/cgfcollector/include/type.h +++ b/cgfcollector/include/type.h @@ -11,7 +11,7 @@ using namespace Fortran::parser; using namespace metacg; struct type { - Symbol* type; + Symbol* typeSymbol; Symbol* extendsFrom; std::vector> procedures; // name(symbol) => optname(symbol) std::vector> operators; // operator => name(symbol) diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 5719bbf3..191bfb7e 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -387,7 +387,7 @@ bool ParseTreeVisitor::Pre(const DerivedTypeDef&) { void ParseTreeVisitor::Post(const DerivedTypeDef&) { inDerivedTypeDef = false; - MCGLogger::logDebug("End derived type: {} ({})", types.back().type->name(), fmt::ptr(types.back().type)); + MCGLogger::logDebug("End derived type: {} ({})", types.back().typeSymbol->name(), fmt::ptr(types.back().typeSymbol)); } bool ParseTreeVisitor::Pre(const DerivedTypeStmt& t) { @@ -396,9 +396,9 @@ bool ParseTreeVisitor::Pre(const DerivedTypeStmt& t) { auto& currentType = types.back(); const auto& name = std::get(t.t); - currentType.type = name.symbol; + currentType.typeSymbol = name.symbol; - MCGLogger::logDebug("\nIn derived type: {} ({})", currentType.type->name(), fmt::ptr(currentType.type)); + MCGLogger::logDebug("\nIn derived type: {} ({})", currentType.typeSymbol->name(), fmt::ptr(currentType.typeSymbol)); return true; } diff --git a/cgfcollector/src/edge.cpp b/cgfcollector/src/edge.cpp index 544ee574..b93f50ad 100644 --- a/cgfcollector/src/edge.cpp +++ b/cgfcollector/src/edge.cpp @@ -7,7 +7,7 @@ std::vector edgeManager::getEdgesForFinalizers(std::vector& ty std::vector typePtrs = findTypeWithDerivedTypes(types, symbol); for (const type* type : typePtrs) { - const Symbol* typeSymbol = type->type; + const Symbol* typeSymbol = type->typeSymbol; const auto* details = std::get_if(&typeSymbol->details()); if (!details) diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index a4698562..e965437c 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -231,7 +231,7 @@ std::vector findTypeWithDerivedTypes(std::vector& types, cons } auto findTypeIt = - std::find_if(types.begin(), types.end(), [&typeSymbol](const type& t) { return t.type == typeSymbol; }); + std::find_if(types.begin(), types.end(), [&typeSymbol](const type& t) { return t.typeSymbol == typeSymbol; }); if (findTypeIt == types.end()) { return typesWithDerived; @@ -243,8 +243,8 @@ std::vector findTypeWithDerivedTypes(std::vector& types, cons // collect descendants std::function collectDescendants = [&](const type* parent) { for (const auto& t : types) { - if (t.extendsFrom == parent->type && !visited.count(t.type)) { - visited.insert(t.type); + if (t.extendsFrom == parent->typeSymbol && !visited.count(t.typeSymbol)) { + visited.insert(t.typeSymbol); typesWithDerived.push_back(&t); collectDescendants(&t); // recursive call to find further descendants } @@ -263,7 +263,7 @@ std::vector findTypeWithDerivedTypes(std::vector& types, cons } auto currentTypeIt = std::find_if(types.begin(), types.end(), - [&](const type& t) { return compareSymbols(t.type, currentExtendsFrom); }); + [&](const type& t) { return compareSymbols(t.typeSymbol, currentExtendsFrom); }); if (currentTypeIt == types.end()) { MCGLogger::logError("Error: Types array (extendsFrom) field entry for \"" + From 1130db00b6d674a47517405792037650bc7a9f00 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:15 +0100 Subject: [PATCH 115/130] change to remove in handleTrackedVars --- cgfcollector/include/variableTracking.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgfcollector/include/variableTracking.h b/cgfcollector/include/variableTracking.h index d537e14f..a895c5f1 100644 --- a/cgfcollector/include/variableTracking.h +++ b/cgfcollector/include/variableTracking.h @@ -48,7 +48,7 @@ struct variableTracking { * variables and adds edges. Currently only adds finalizer edges. * * @param currentFunctionSymbol - * @param edgeM TODO: change dep + * @param edgeM TODO: remove dep */ void handleTrackedVars(Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); From 35f7387d2e3f6959ec2b4655f0d71565c5e3227f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:15 +0100 Subject: [PATCH 116/130] removed const_cast and resolved auto for clarity --- cgfcollector/include/ParseTreeVisitor.h | 4 +- cgfcollector/include/edge.h | 12 +- cgfcollector/include/function.h | 14 +- cgfcollector/include/type.h | 8 +- cgfcollector/include/util.h | 8 +- cgfcollector/include/variableTracking.h | 16 +- cgfcollector/src/ParseTreeVisitor.cpp | 233 ++++++++++++------------ cgfcollector/src/edge.cpp | 18 +- cgfcollector/src/main.cpp | 20 +- cgfcollector/src/util.cpp | 10 +- cgfcollector/src/variableTracking.cpp | 22 +-- 11 files changed, 184 insertions(+), 181 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index ee7f833a..cab6f71a 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -231,8 +231,8 @@ class ParseTreeVisitor { std::vector types; // all types - std::vector, - std::vector>> + std::vector, + std::vector>> interfaceOperators; // all interface operators. First is either a symbol of a DefinedOpName or // IntrinsicOperator. Second is a vector procedure symbols, bound to that operator. diff --git a/cgfcollector/include/edge.h b/cgfcollector/include/edge.h index 4e1d7f37..39f01812 100644 --- a/cgfcollector/include/edge.h +++ b/cgfcollector/include/edge.h @@ -26,10 +26,10 @@ struct edge { }; struct edgeSymbol { - Symbol* caller; - Symbol* callee; + const Symbol* caller; + const Symbol* callee; - edgeSymbol(Symbol* caller, Symbol* callee) : caller(caller), callee(callee) {} + edgeSymbol(const Symbol* caller, const Symbol* callee) : caller(caller), callee(callee) {} bool operator==(const edgeSymbol& other) const { return caller == other.caller && callee == other.callee; } bool operator<(const edgeSymbol& other) const { @@ -43,7 +43,7 @@ struct edgeManager { edgeManager(std::vector& edges) : edges(edges) {} void addEdge(const edgeSymbol& e, bool debug = true); - void addEdge(Symbol* caller, Symbol* callee, bool debug = true); + void addEdge(const Symbol* caller, const Symbol* callee, bool debug = true); void addEdge(const edge& e, bool debug = true); void addEdge(const std::string& caller, const std::string& callee, bool debug = true); void addEdges(const std::vector& newEdges, bool debug = true); @@ -56,7 +56,7 @@ struct edgeManager { * @param currentFunctionSymbol * @param symbol */ - std::vector getEdgesForFinalizers(std::vector& types, const Symbol* currentFunctionSymbol, + std::vector getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol); /** * @brief Calls getEdgesForFinalizers and adds them to the edges vector. @@ -65,5 +65,5 @@ struct edgeManager { * @param currentFunctionSymbol * @param symbol */ - void addEdgesForFinalizers(std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol); + void addEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol); }; diff --git a/cgfcollector/include/function.h b/cgfcollector/include/function.h index df341e48..701e3148 100644 --- a/cgfcollector/include/function.h +++ b/cgfcollector/include/function.h @@ -7,18 +7,18 @@ using namespace Fortran::semantics; struct function { struct dummyArg { - Symbol* symbol; + const Symbol* symbol; bool hasBeenInitialized = false; - explicit dummyArg(Symbol* sym) : symbol(sym) {} - explicit dummyArg(Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} + explicit dummyArg(const Symbol* sym) : symbol(sym) {} + explicit dummyArg(const Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} }; - Symbol* symbol; // function symbol + const Symbol* symbol; // function symbol std::vector dummyArgs; - explicit function(Symbol* sym) : symbol(sym) {} - explicit function(Symbol* sym, std::vector args) : symbol(sym), dummyArgs(std::move(args)) {} + explicit function(const Symbol* sym) : symbol(sym) {} + explicit function(const Symbol* sym, std::vector args) : symbol(sym), dummyArgs(std::move(args)) {} - void addDummyArg(Symbol* sym) { dummyArgs.emplace_back(sym); } + void addDummyArg(const Symbol* sym) { dummyArgs.emplace_back(sym); } }; diff --git a/cgfcollector/include/type.h b/cgfcollector/include/type.h index 10f0ef0f..77fbe398 100644 --- a/cgfcollector/include/type.h +++ b/cgfcollector/include/type.h @@ -11,8 +11,8 @@ using namespace Fortran::parser; using namespace metacg; struct type { - Symbol* typeSymbol; - Symbol* extendsFrom; - std::vector> procedures; // name(symbol) => optname(symbol) - std::vector> operators; // operator => name(symbol) + const Symbol* typeSymbol; + const Symbol* extendsFrom; + std::vector> procedures; // name(symbol) => optname(symbol) + std::vector> operators; // operator => name(symbol) }; diff --git a/cgfcollector/include/util.h b/cgfcollector/include/util.h index 82b9f24b..6d15e7dc 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/util.h @@ -49,9 +49,9 @@ bool holds_any_of(const Variant& v) { */ template const Name* getNameFromClassWithDesignator(const T& t) { - if (const auto* designator = std::get_if>(&t.u)) { - if (const auto* dataRef = std::get_if(&designator->value().u)) { - if (const auto* name = std::get_if(&dataRef->u)) { + if (const Indirection* designator = std::get_if>(&t.u)) { + if (const DataRef* dataRef = std::get_if(&designator->value().u)) { + if (const Name* name = std::get_if(&dataRef->u)) { return name; } } @@ -135,4 +135,4 @@ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); * @param typeSymbol symbol to search for * @return vector with type and all derived types */ -std::vector findTypeWithDerivedTypes(std::vector& types, const Symbol* symbol); +std::vector findTypeWithDerivedTypes(const std::vector& types, const Symbol* symbol); diff --git a/cgfcollector/include/variableTracking.h b/cgfcollector/include/variableTracking.h index a895c5f1..c9b04a41 100644 --- a/cgfcollector/include/variableTracking.h +++ b/cgfcollector/include/variableTracking.h @@ -10,14 +10,14 @@ using namespace Fortran::semantics; struct trackedVar { - Symbol* var; - Symbol* procedure; // procedure in which var was defined + const Symbol* var; + const Symbol* procedure; // procedure in which var was defined bool hasBeenInitialized = false; bool addFinalizers = false; - trackedVar(Symbol* var, Symbol* procedure) + trackedVar(const Symbol* var, const Symbol* procedure) : var(var), procedure(procedure), hasBeenInitialized(false), addFinalizers(false) {} - trackedVar(Symbol* var, Symbol* procedure, bool initialized, bool addFinalizers) + trackedVar(const Symbol* var, const Symbol* procedure, bool initialized, bool addFinalizers) : var(var), procedure(procedure), hasBeenInitialized(initialized), addFinalizers(addFinalizers) {} }; @@ -33,7 +33,7 @@ struct variableTracking { * @param sourceName * @return trackedVar* or nullptr if not found */ - trackedVar* getTrackedVarFromSourceName(Symbol* currentFunctionSymbol, SourceName sourceName); + trackedVar* getTrackedVarFromSourceName(const Symbol* currentFunctionSymbol, SourceName sourceName); /** * @brief Search trackedVars for a canditate and set it as initialized. @@ -41,7 +41,7 @@ struct variableTracking { * @param currentFunctionSymbol * @param sourceName */ - void handleTrackedVarAssignment(Symbol* currentFunctionSymbol, SourceName sourceName); + void handleTrackedVarAssignment(const Symbol* currentFunctionSymbol, SourceName sourceName); /** * @brief Is called at the end of a function/subroutine end statement. It checks trackedVars for any initialized @@ -50,7 +50,7 @@ struct variableTracking { * @param currentFunctionSymbol * @param edgeM TODO: remove dep */ - void handleTrackedVars(Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); + void handleTrackedVars(const Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); /** * @brief Register a variable for tracking. @@ -65,7 +65,7 @@ struct variableTracking { * * @param procedureSymbol */ - void removeTrackedVars(Symbol* procedureSymbol); + void removeTrackedVars(const Symbol* procedureSymbol); private: std::vector& trackedVars; diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 191bfb7e..2d37121e 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -5,7 +5,7 @@ template void ParseTreeVisitor::handleFuncSubStmt(const Subrouti template void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { - if (auto* sym = std::get(stmt.t).symbol) { + if (const Symbol* sym = std::get(stmt.t).symbol) { currentFunctions.emplace_back(sym, std::vector()); functions.emplace_back(sym, std::vector()); cg->getOrInsertNode(mangleSymbol(sym), currentFileName, false, false); @@ -31,7 +31,7 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vectorprocedures.end()) continue; - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; edgeM->addEdge(currentFunctionSymbol, procIt->second); } @@ -39,9 +39,9 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vectorhasBeenInitialized) continue; - for (const auto& edge : pf.finalizerEdges) { + for (const edge& edge : pf.finalizerEdges) { edgeM->addEdge(edge, false); MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.caller, edge.callee); } @@ -62,9 +62,9 @@ void ParseTreeVisitor::postProcess() { edges.erase(it, edges.end()); // add edges - for (auto edge : edges) { - const auto& callerNode = cg->getOrInsertNode(edge.caller); - const auto& calleeNode = cg->getOrInsertNode(edge.callee); + for (const edge& edge : edges) { + const CgNode& callerNode = cg->getOrInsertNode(edge.caller); + const CgNode& calleeNode = cg->getOrInsertNode(edge.callee); cg->addEdge(callerNode, calleeNode); } @@ -79,7 +79,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { if (!maybeStmt->statement.v.symbol) return true; - auto* currentFunctionSymbol = + const Symbol* currentFunctionSymbol = currentFunctions.emplace_back(maybeStmt->statement.v.symbol, std::vector()).symbol; cg->getOrInsertNode(mangleSymbol(currentFunctionSymbol), currentFileName, false, false); @@ -92,7 +92,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { void ParseTreeVisitor::Post(const MainProgram&) { varTracking->handleTrackedVars(currentFunctions.back().symbol, edgeM); - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; MCGLogger::logDebug("End main program: {} ({})", mangleSymbol(currentFunctionSymbol), fmt::ptr(currentFunctionSymbol)); @@ -122,7 +122,7 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { if (!inFunctionOrSubroutineSubProgram && !inMainProgram) return; - auto* node = cg->getFirstNode(mangleSymbol(currentFunctions.back().symbol)); + CgNode* node = cg->getFirstNode(mangleSymbol(currentFunctions.back().symbol)); if (!node) { return; } @@ -131,7 +131,7 @@ void ParseTreeVisitor::Post(const ExecutionPart& e) { } void ParseTreeVisitor::Post(const EntryStmt& e) { - auto* name = &std::get(e.t); + const Name* name = &std::get(e.t); if (!name->symbol) return; @@ -147,14 +147,14 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { handleFuncSubStmt(f); - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; auto functionsIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& func) { return func.symbol == currentFunctionSymbol; }); + [&](const function& func) { return func.symbol == currentFunctionSymbol; }); // collect function arguments - const auto& name_list = std::get>(f.t); - for (auto name : name_list) { + const std::list& name_list = std::get>(f.t); + for (const Name& name : name_list) { currentFunctions.back().addDummyArg(name.symbol); if (functionsIt != functions.end()) { functionsIt->addDummyArg(name.symbol); @@ -165,7 +165,7 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { void ParseTreeVisitor::Post(const EndFunctionStmt&) { if (!currentFunctions.empty()) { - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; MCGLogger::logDebug("End function: {} ({})", mangleSymbol(currentFunctionSymbol), fmt::ptr(currentFunctionSymbol)); } @@ -179,15 +179,15 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { handleFuncSubStmt(s); - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; auto functionsIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& func) { return func.symbol == currentFunctionSymbol; }); + [&](const function& func) { return func.symbol == currentFunctionSymbol; }); // collect subroutine arguments (dummy args) - const auto* dummyArg_list = &std::get>(s.t); - for (const auto& dummyArg : *dummyArg_list) { - const auto* name = std::get_if(&dummyArg.u); + const std::list* dummyArg_list = &std::get>(s.t); + for (const DummyArg& dummyArg : *dummyArg_list) { + const Name* name = std::get_if(&dummyArg.u); currentFunctions.back().addDummyArg(name->symbol); if (functionsIt != functions.end()) { functionsIt->addDummyArg(name->symbol); @@ -198,7 +198,7 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { void ParseTreeVisitor::Post(const EndSubroutineStmt&) { if (!currentFunctions.empty()) { - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; MCGLogger::logDebug("End subroutine: {} ({})", mangleSymbol(currentFunctionSymbol), fmt::ptr(currentFunctionSymbol)); @@ -211,10 +211,10 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { if (currentFunctions.empty()) return; - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; // if just the name is called. (as subroutine with call and as function without call) - if (auto* name = std::get_if(&p.u)) { + if (const Name* name = std::get_if(&p.u)) { if (!name->symbol) return; @@ -225,17 +225,17 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { edgeM->addEdge(currentFunctionSymbol, name->symbol); // if called from a object with %. (base % component) - } else if (auto* procCompRef = std::get_if(&p.u)) { - auto* symbolComp = procCompRef->v.thing.component.symbol; + } else if (const ProcComponentRef* procCompRef = std::get_if(&p.u)) { + const Symbol* symbolComp = procCompRef->v.thing.component.symbol; if (!symbolComp) return; edgeM->addEdge(currentFunctionSymbol, symbolComp); - auto* baseName = std::get_if(&procCompRef->v.thing.base.u); + const Name* baseName = std::get_if(&procCompRef->v.thing.base.u); if (!baseName || !baseName->symbol) return; - auto* symbolBase = baseName->symbol; + const Symbol* symbolBase = baseName->symbol; // handle derived types edges @@ -244,9 +244,9 @@ void ParseTreeVisitor::Post(const ProcedureDesignator& p) { } void ParseTreeVisitor::Post(const AssignmentStmt& a) { - const auto* var = &std::get(a.t); + const Variable* var = &std::get(a.t); - auto* name = getNameFromClassWithDesignator(*var); + const Name* name = getNameFromClassWithDesignator(*var); if (!name || !name->symbol) return; @@ -254,11 +254,11 @@ void ParseTreeVisitor::Post(const AssignmentStmt& a) { } void ParseTreeVisitor::Post(const AllocateStmt& a) { - const auto* allocs = &std::get>(a.t); + const std::list* allocs = &std::get>(a.t); - for (const auto& alloc : *allocs) { - const auto* allocObj = &std::get(alloc.t); - const auto* name = std::get_if(&allocObj->u); + for (const Allocation& alloc : *allocs) { + const AllocateObject* allocObj = &std::get(alloc.t); + const Name* name = std::get_if(&allocObj->u); if (!name || !name->symbol) { continue; } @@ -268,21 +268,21 @@ void ParseTreeVisitor::Post(const AllocateStmt& a) { } void ParseTreeVisitor::Post(const Call& c) { - const auto* designator = &std::get(c.t); - const auto* args = &std::get>(c.t); - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const ProcedureDesignator* designator = &std::get(c.t); + const std::list* args = &std::get>(c.t); + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; - const auto* procName = std::get_if(&designator->u); + const Name* procName = std::get_if(&designator->u); if (!procName || !procName->symbol) return; std::size_t argPos = 0; - for (const auto& arg : *args) { - const auto* actualArg = &std::get(arg.t); - const auto* expr = std::get_if>(&actualArg->u); + for (const ActualArgSpec& arg : *args) { + const ActualArg* actualArg = &std::get(arg.t); + const Indirection* expr = std::get_if>(&actualArg->u); if (!expr) return; - auto* name = getNameFromClassWithDesignator(expr->value()); + const Name* name = getNameFromClassWithDesignator(expr->value()); if (!name || !name->symbol) return; @@ -293,13 +293,13 @@ void ParseTreeVisitor::Post(const Call& c) { // handle finalizers for allocatable vars. // This collects info from variables that are parse as arguments to functions. Function are defined below the // execution part, so this need to be handled at the end of the parse tree traversal. - auto* trackedVar = varTracking->getTrackedVarFromSourceName(currentFunctionSymbol, name->symbol->name()); + trackedVar* trackedVar = varTracking->getTrackedVarFromSourceName(currentFunctionSymbol, name->symbol->name()); if (!trackedVar) continue; MCGLogger::logDebug("Add potential finalizers for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); potentialFinalizer& pf = potentialFinalizers.emplace_back(argPos, mangleSymbol(procName->symbol)); - for (const auto& edge : edgeM->getEdgesForFinalizers(types, currentFunctionSymbol, trackedVar->var)) { + for (const edgeSymbol& edge : edgeM->getEdgesForFinalizers(types, currentFunctionSymbol, trackedVar->var)) { pf.addFinalizerEdge({mangleSymbol(edge.caller), mangleSymbol(edge.callee)}); MCGLogger::logDebug(" Potential finalizer edge: {} -> {}", mangleSymbol(edge.caller), mangleSymbol(edge.callee)); @@ -313,14 +313,14 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { return; } - for (const auto& entity : std::get>(t.t)) { - const auto& name = std::get(entity.t); + for (const EntityDecl& entity : std::get>(t.t)) { + const Name& name = std::get(entity.t); if (!name.symbol) continue; // skip if name is an argument to a function or subroutine bool isFunctionArg = false; - auto& currentDummyArgs = currentFunctions.back().dummyArgs; + std::vector& currentDummyArgs = currentFunctions.back().dummyArgs; if (!currentDummyArgs.empty()) { auto it = std::find_if(currentDummyArgs.begin(), currentDummyArgs.end(), [&name](const function::dummyArg& dummyArg) { return dummyArg.symbol == name.symbol; }); @@ -332,7 +332,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { bool holds_allocatable = false; const IntentSpec* holds_intent = nullptr; bool holds_save = false; - for (const auto& attr : std::get>(t.t)) { + for (const AttrSpec& attr : std::get>(t.t)) { if (std::holds_alternative(attr.u)) holds_allocatable = true; else if (std::holds_alternative(attr.u)) @@ -344,7 +344,7 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { if (holds_save) continue; // vars with save attr are not destructed - auto* currentFunctionSymbol = currentFunctions.back().symbol; + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; if (isFunctionArg) { if (!holds_allocatable) { @@ -394,8 +394,8 @@ bool ParseTreeVisitor::Pre(const DerivedTypeStmt& t) { if (!inDerivedTypeDef) return true; - auto& currentType = types.back(); - const auto& name = std::get(t.t); + type& currentType = types.back(); + const Name& name = std::get(t.t); currentType.typeSymbol = name.symbol; MCGLogger::logDebug("\nIn derived type: {} ({})", currentType.typeSymbol->name(), fmt::ptr(currentType.typeSymbol)); @@ -407,9 +407,9 @@ void ParseTreeVisitor::Post(const TypeAttrSpec& a) { if (!inDerivedTypeDef) return; - auto& currentType = types.back(); + type& currentType = types.back(); if (std::holds_alternative(a.u)) { - const auto& extends = std::get(a.u); + const TypeAttrSpec::Extends& extends = std::get(a.u); currentType.extendsFrom = extends.v.symbol; MCGLogger::logDebug("Extends from: {} ({})", currentType.extendsFrom->name(), fmt::ptr(currentType.extendsFrom)); @@ -420,18 +420,19 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { if (!inDerivedTypeDef) return; - if (auto* withoutInterface = std::get_if(&s.u)) { - for (const auto& d : withoutInterface->declarations) { - auto& name = std::get(d.t); + if (const TypeBoundProcedureStmt::WithoutInterface* withoutInterface = + std::get_if(&s.u)) { + for (const TypeBoundProcDecl& d : withoutInterface->declarations) { + const Name& name = std::get(d.t); if (!name.symbol) return; - auto& optname = std::get>(d.t); + const std::optional& optname = std::get>(d.t); if (!optname || !optname->symbol) { return; } - auto& currentType = types.back(); + type& currentType = types.back(); currentType.procedures.emplace_back(name.symbol, optname->symbol); MCGLogger::logDebug("Add procedure: {} ({}) -> {} ({})", name.symbol->name(), fmt::ptr(name.symbol), @@ -439,12 +440,13 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { } // only for abstract types, with deferred in binding attr list - } else if (auto* withInterface = std::get_if(&s.u)) { - for (const auto& n : withInterface->bindingNames) { + } else if (const TypeBoundProcedureStmt::WithInterface* withInterface = + std::get_if(&s.u)) { + for (const Name& n : withInterface->bindingNames) { if (!n.symbol) return; - auto& currentType = types.back(); + type& currentType = types.back(); currentType.procedures.emplace_back(n.symbol, n.symbol); MCGLogger::logDebug("Add procedure: {} ({}) -> {} ({})", n.symbol->name(), fmt::ptr(n.symbol), n.symbol->name(), @@ -457,14 +459,15 @@ void ParseTreeVisitor::Post(const TypeBoundGenericStmt& s) { if (!inDerivedTypeDef) return; - const auto& genericSpec = std::get>(s.t); - if (auto* definedOperator = std::get_if(&genericSpec.value().u)) { - if (auto* intrinsicOp = std::get_if(&definedOperator->u)) { - const auto& names = std::get>(s.t); + const Indirection& genericSpec = std::get>(s.t); + if (const DefinedOperator* definedOperator = std::get_if(&genericSpec.value().u)) { + if (const DefinedOperator::IntrinsicOperator* intrinsicOp = + std::get_if(&definedOperator->u)) { + const std::list& names = std::get>(s.t); - auto& currentType = types.back(); + type& currentType = types.back(); - for (auto name : names) { + for (const Name& name : names) { if (!name.symbol) continue; @@ -495,14 +498,14 @@ void ParseTreeVisitor::Post(const DefinedOperator& op) { inInterfaceStmtDefinedOperator = true; if (std::holds_alternative(op.u)) { - auto intrinsicOp = std::get(op.u); - interfaceOperators.emplace_back(intrinsicOp, std::vector()); + DefinedOperator::IntrinsicOperator intrinsicOp = std::get(op.u); + interfaceOperators.emplace_back(intrinsicOp, std::vector()); } else if (std::holds_alternative(op.u)) { - const auto& opName = std::get(op.u); + const DefinedOpName& opName = std::get(op.u); if (!opName.v.symbol) return; - interfaceOperators.emplace_back(opName.v.symbol, std::vector()); + interfaceOperators.emplace_back(opName.v.symbol, std::vector()); } } @@ -510,8 +513,8 @@ void ParseTreeVisitor::Post(const ProcedureStmt& p) { if (!inInterfaceStmtDefinedOperator) return; - auto name = std::get>(p.t); - for (const auto& n : name) { + const std::list& name = std::get>(p.t); + for (const Name& n : name) { if (!n.symbol) continue; @@ -525,7 +528,7 @@ void ParseTreeVisitor::Post(const ProcedureStmt& p) { } bool ParseTreeVisitor::Pre(const Expr& e) { - const auto* name = getNameFromClassWithDesignator(e); + const Name* name = getNameFromClassWithDesignator(e); // true if not in a designator expr if (!name || !name->symbol || exprStmtWithOps.empty()) { if (isOperator(&e)) { @@ -539,18 +542,18 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (currentFunctions.empty()) return true; - for (auto e : exprStmtWithOps) { + for (const Expr* e : exprStmtWithOps) { // search in interfaceOperators first before search in derived types auto interfaceOp = std::find_if(interfaceOperators.begin(), interfaceOperators.end(), [&](const auto& op) { if (std::holds_alternative(op.first)) { - auto intrinsicOp = std::get(op.first); + DefinedOperator::IntrinsicOperator intrinsicOp = std::get(op.first); return compareExprIntrinsicOperator(e, intrinsicOp); - } else if (std::holds_alternative(op.first)) { - Symbol* definedOpNameSym = std::get(op.first); - if (const auto* definedUnary = std::get_if(&e->u)) { + } else if (std::holds_alternative(op.first)) { + const Symbol* definedOpNameSym = std::get(op.first); + if (const Expr::DefinedUnary* definedUnary = std::get_if(&e->u)) { const auto& exprOpName = std::get<0>(definedUnary->t); return definedOpNameSym->name() == exprOpName.v.symbol->name(); - } else if (auto* definedBinary = std::get_if(&e->u)) { + } else if (const Expr::DefinedBinary* definedBinary = std::get_if(&e->u)) { const auto& exprOpName = std::get<0>(definedBinary->t); return definedOpNameSym->name() == exprOpName.v.symbol->name(); } @@ -561,8 +564,8 @@ bool ParseTreeVisitor::Pre(const Expr& e) { bool isUnaryOp = isUnaryOperator(e); bool isBinaryOp = isBinaryOperator(e); - for (auto* sym : interfaceOp->second) { - auto* currentFunctionSymbol = currentFunctions.back().symbol; + for (const Symbol* sym : interfaceOp->second) { + const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; // skip self calls if (mangleSymbol(sym) == mangleSymbol(currentFunctionSymbol)) @@ -570,7 +573,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { // if unary, add potential unary operators. Same for binary operators. auto functionIt = - std::find_if(functions.begin(), functions.end(), [&](const auto& f) { return f.symbol == sym; }); + std::find_if(functions.begin(), functions.end(), [&](const function& f) { return f.symbol == sym; }); if (functionIt != functions.end()) { if ((!isUnaryOp || functionIt->dummyArgs.size() != 1) && (!isBinaryOp || functionIt->dummyArgs.size() != 2)) { continue; @@ -583,7 +586,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { // search in derived types - auto typeWithDerived = findTypeWithDerivedTypes(types, name->symbol); + std::vector typeWithDerived = findTypeWithDerivedTypes(types, name->symbol); for (const type* t : typeWithDerived) { auto opIt = std::find_if(t->operators.begin(), t->operators.end(), @@ -591,7 +594,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { if (opIt == t->operators.end()) continue; - Symbol* funcSymbol = opIt->second; + const Symbol* funcSymbol = opIt->second; bool skipSelfCall = false; for (const type* t : typeWithDerived) { @@ -625,38 +628,38 @@ void ParseTreeVisitor::Post(const Expr& e) { } void ParseTreeVisitor::Post(const UseStmt& u) { - auto* useSymbol = u.moduleName.symbol; + const Symbol* useSymbol = u.moduleName.symbol; MCGLogger::logDebug("Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); if (const Scope* modScope = useSymbol->scope()) { for (const auto& pair : *modScope) { - Symbol& symbol = *pair.second; + const Symbol* symbol = &*pair.second; // extract derived types from module and populate types var - if (const auto* details = symbol.detailsIf()) { - Symbol* extendsFrom = nullptr; - std::vector> procedures; - std::vector> operators; + if (const DerivedTypeDetails* details = symbol->detailsIf()) { + const Symbol* extendsFrom = nullptr; + std::vector> procedures; + std::vector> operators; - for (auto& pair : *symbol.scope()) { - Symbol& component = *pair.second; + for (auto pair : *symbol->scope()) { + const Symbol* component = &*pair.second; // extends - if (component.test(Symbol::Flag::ParentComp)) { - extendsFrom = const_cast(getTypeSymbolFromSymbol(&component)); // TODO: avoid const cast. ugly. + if (component->test(Symbol::Flag::ParentComp)) { + extendsFrom = getTypeSymbolFromSymbol(component); } // type bound procedures - if (component.has()) { - const auto& procDetails = component.get(); - MCGLogger::logDebug("Found procedure in module derived type: {} ({})", component.name(), + if (component->has()) { + const ProcBindingDetails& procDetails = component->get(); + MCGLogger::logDebug("Found procedure in module derived type: {} ({})", component->name(), fmt::ptr(&component)); - procedures.emplace_back(&component, &component); + procedures.emplace_back(component, component); } // type generic operators - if (GenericDetails* gen = component.detailsIf()) { + if (const GenericDetails* gen = component->detailsIf()) { if (!gen->kind().IsIntrinsicOperator()) continue; @@ -665,8 +668,8 @@ void ParseTreeVisitor::Post(const UseStmt& u) { if (gen->specificProcs().size() != 1) MCGLogger::logError("Type-bound generic more than one specific proc not handled. Should not happen."); - Symbol* op_func_sym = nullptr; - op_func_sym = const_cast(&gen->specificProcs().front().get()); + const Symbol* op_func_sym = nullptr; + op_func_sym = &gen->specificProcs().front().get(); MCGLogger::logDebug("Found operator in module derived type: {} -> {} ({})", DefinedOperator::EnumToString(intrinsicOp), op_func_sym->name(), fmt::ptr(op_func_sym)); @@ -675,28 +678,28 @@ void ParseTreeVisitor::Post(const UseStmt& u) { } } - types.push_back({&symbol, extendsFrom, procedures, operators}); - MCGLogger::logDebug("Found derived type in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); + types.push_back({symbol, extendsFrom, procedures, operators}); + MCGLogger::logDebug("Found derived type in module: {} ({})", symbol->name(), fmt::ptr(symbol)); } // same but with interface operators - if (const auto* gen = symbol.detailsIf()) { - std::variant interfaceOp; - std::vector procs; + if (const GenericDetails* gen = symbol->detailsIf()) { + std::variant interfaceOp; + std::vector procs; if (gen->kind().IsIntrinsicOperator()) { interfaceOp = variantGetIntrinsicOperator(gen->kind()); MCGLogger::logDebug("Found interface operator in module: {}", DefinedOperator::EnumToString(std::get(interfaceOp))); } else if (gen->kind().IsDefinedOperator()) { - interfaceOp = &symbol; - MCGLogger::logDebug("Found interface operator in module: {}", symbol.name()); + interfaceOp = symbol; + MCGLogger::logDebug("Found interface operator in module: {}", symbol->name()); } else { continue; } for (const auto& p : gen->specificProcs()) { - procs.push_back(const_cast(&p.get())); + procs.push_back(&p.get()); MCGLogger::logDebug(" with procedure: {} ({})", p.get().name(), fmt::ptr(&p.get())); } @@ -704,17 +707,17 @@ void ParseTreeVisitor::Post(const UseStmt& u) { } // same but with functions - if (const auto* details = symbol.detailsIf()) { + if (const SubprogramDetails* details = symbol->detailsIf()) { if (!details->isFunction() && !details->isInterface()) // function and function dummy definition in interface continue; std::vector dummyArgs; - for (Symbol* arg : details->dummyArgs()) { + for (const Symbol* arg : details->dummyArgs()) { dummyArgs.emplace_back(arg, false); } - functions.emplace_back(&symbol, dummyArgs); - MCGLogger::logDebug("Found function in module: {} ({})", symbol.name(), fmt::ptr(&symbol)); + functions.emplace_back(symbol, dummyArgs); + MCGLogger::logDebug("Found function in module: {} ({})", symbol->name(), fmt::ptr(symbol)); } } } diff --git a/cgfcollector/src/edge.cpp b/cgfcollector/src/edge.cpp index b93f50ad..eaf4ee0e 100644 --- a/cgfcollector/src/edge.cpp +++ b/cgfcollector/src/edge.cpp @@ -1,6 +1,6 @@ #include "edge.h" -std::vector edgeManager::getEdgesForFinalizers(std::vector& types, +std::vector edgeManager::getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { std::vector edges; @@ -9,24 +9,22 @@ std::vector edgeManager::getEdgesForFinalizers(std::vector& ty for (const type* type : typePtrs) { const Symbol* typeSymbol = type->typeSymbol; - const auto* details = std::get_if(&typeSymbol->details()); + const DerivedTypeDetails* details = std::get_if(&typeSymbol->details()); if (!details) continue; // add edges for finalizers for (const auto& final : details->finals()) { - auto second = const_cast(&final.second.get()); // TODO: remove const cast - auto currentFunctionSymbolNoConst = const_cast(currentFunctionSymbol); - edges.emplace_back(currentFunctionSymbolNoConst, second); + edges.emplace_back(currentFunctionSymbol, &final.second.get()); } } return edges; } -void edgeManager::addEdgesForFinalizers(std::vector& types, const Symbol* currentFunctionSymbol, +void edgeManager::addEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { - for (const auto& edge : getEdgesForFinalizers(types, currentFunctionSymbol, symbol)) { + for (const edgeSymbol& edge : getEdgesForFinalizers(types, currentFunctionSymbol, symbol)) { addEdge(edge, false); MCGLogger::logDebug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.caller), fmt::ptr(edge.caller), mangleSymbol(edge.callee), fmt::ptr(edge.callee)); @@ -41,7 +39,7 @@ void edgeManager::addEdge(const edgeSymbol& e, bool debug) { } } -void edgeManager::addEdge(Symbol* caller, Symbol* callee, bool debug) { +void edgeManager::addEdge(const Symbol* caller, const Symbol* callee, bool debug) { edges.emplace_back(mangleSymbol(caller), mangleSymbol(callee)); if (debug) { MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(caller), fmt::ptr(caller), mangleSymbol(callee), @@ -64,13 +62,13 @@ void edgeManager::addEdge(const std::string& caller, const std::string& callee, } void edgeManager::addEdges(const std::vector& newEdges, bool debug) { - for (const auto& e : newEdges) { + for (const edge& e : newEdges) { addEdge(e, debug); } } void edgeManager::addEdges(const std::vector& newEdges, bool debug) { - for (const auto& e : newEdges) { + for (const edgeSymbol& e : newEdges) { addEdge(e, debug); } } diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/main.cpp index 65c4b6ee..d67461ca 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/main.cpp @@ -3,8 +3,10 @@ #include using namespace metacg; +using namespace metacg::graph; +using namespace metacg::io; -static auto& mcgManager = metacg::graph::MCGManager::get(); +static MCGManager& mcgManager = MCGManager::get(); /** * @brief Create output file with given extension @@ -66,13 +68,13 @@ std::string dumpCG() { return ""; } - auto mcgWriter = metacg::io::createWriter(4); + std::unique_ptr mcgWriter = createWriter(4); if (!mcgWriter) { MCGLogger::logError("Unable to create a writer"); return ""; }; - metacg::io::JsonSink jsonSink; + JsonSink jsonSink; mcgWriter->writeActiveGraph(jsonSink); return jsonSink.getJson().dump(); @@ -89,7 +91,7 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { generateCG(getParsing().parseTree(), getCurrentFile()); std::string cgString = dumpCG(); - auto file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); + std::unique_ptr file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); file->write(cgString.c_str(), cgString.size()); } }; @@ -105,20 +107,20 @@ class CollectCGwithDot : public Fortran::frontend::PluginParseTreeAction { generateCG(getParsing().parseTree(), getCurrentFile()); std::string cgString = dumpCG(); - auto file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); + std::unique_ptr file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); file->write(cgString.c_str(), cgString.size()); // dot file - metacg::Callgraph* cg = mcgManager.getCallgraph("cg"); + Callgraph* cg = mcgManager.getCallgraph("cg"); if (cg == nullptr) { MCGLogger::logError("No callgraph generated"); return; } - metacg::io::dot::DotGenerator dotGen(cg); + dot::DotGenerator dotGen(cg); dotGen.generate(); - auto dotfile = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); + std::unique_ptr dotfile = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); std::string dotString = dotGen.getDotString(); dotfile->write(dotString.c_str(), dotString.size()); } @@ -136,7 +138,7 @@ class CollectCGNoRename : public Fortran::frontend::PluginParseTreeAction { std::string cgString = dumpCG(); - auto file = ::createOutputFile(getInstance(), getCurrentFile(), ""); + std::unique_ptr file = ::createOutputFile(getInstance(), getCurrentFile(), ""); file->write(cgString.c_str(), cgString.size()); } }; diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index e965437c..c134dec7 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -202,13 +202,13 @@ DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const GenericKind } const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol) { - auto* type = symbol->GetType(); + const DeclTypeSpec* type = symbol->GetType(); if (!type) return nullptr; - auto* derived = type->AsDerived(); + const Fortran::semantics::DerivedTypeSpec* derived = type->AsDerived(); if (!derived) return nullptr; - auto* typeSymbol = &derived->typeSymbol(); + const Symbol* typeSymbol = &derived->typeSymbol(); if (!typeSymbol) return nullptr; return typeSymbol; @@ -221,7 +221,7 @@ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol) { * @param typeSymbol symbol to search for * @return vector with type and all derived types */ -std::vector findTypeWithDerivedTypes(std::vector& types, const Symbol* symbol) { +std::vector findTypeWithDerivedTypes(const std::vector& types, const Symbol* symbol) { std::vector typesWithDerived; std::unordered_set visited; @@ -242,7 +242,7 @@ std::vector findTypeWithDerivedTypes(std::vector& types, cons // collect descendants std::function collectDescendants = [&](const type* parent) { - for (const auto& t : types) { + for (const type& t : types) { if (t.extendsFrom == parent->typeSymbol && !visited.count(t.typeSymbol)) { visited.insert(t.typeSymbol); typesWithDerived.push_back(&t); diff --git a/cgfcollector/src/variableTracking.cpp b/cgfcollector/src/variableTracking.cpp index 98fef458..3f05d883 100644 --- a/cgfcollector/src/variableTracking.cpp +++ b/cgfcollector/src/variableTracking.cpp @@ -1,13 +1,13 @@ #include "variableTracking.h" -trackedVar* variableTracking::getTrackedVarFromSourceName(Symbol* currentFunctionSymbol, SourceName sourceName) { - auto anyTrackedVarIt = - std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { return t.var->name() == sourceName; }); +trackedVar* variableTracking::getTrackedVarFromSourceName(const Symbol* currentFunctionSymbol, SourceName sourceName) { + auto anyTrackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), + [&](const trackedVar& t) { return t.var->name() == sourceName; }); if (anyTrackedVarIt == trackedVars.end()) return nullptr; // find local variable with the same name in the current function scope (shadowed) - auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const auto& t) { + auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { return t.var->name() == sourceName && t.procedure == currentFunctionSymbol; }); @@ -15,8 +15,8 @@ trackedVar* variableTracking::getTrackedVarFromSourceName(Symbol* currentFunctio return (localVarIt != trackedVars.end()) ? &(*localVarIt) : &(*anyTrackedVarIt); } -void variableTracking::handleTrackedVarAssignment(Symbol* currentFunctionSymbol, SourceName sourceName) { - auto* trackedVar = getTrackedVarFromSourceName(currentFunctionSymbol, sourceName); +void variableTracking::handleTrackedVarAssignment(const Symbol* currentFunctionSymbol, SourceName sourceName) { + trackedVar* trackedVar = getTrackedVarFromSourceName(currentFunctionSymbol, sourceName); if (!trackedVar) return; @@ -25,12 +25,12 @@ void variableTracking::handleTrackedVarAssignment(Symbol* currentFunctionSymbol, MCGLogger::logDebug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); } -void variableTracking::handleTrackedVars(Symbol* currentFunctionSymbol, std::unique_ptr& edgeM) { +void variableTracking::handleTrackedVars(const Symbol* currentFunctionSymbol, std::unique_ptr& edgeM) { if (mangleSymbol(currentFunctionSymbol) != "_QQmain") { if (!trackedVars.empty()) MCGLogger::logDebug("Handle tracked vars for function"); - for (auto& trackedVar : trackedVars) { + for (trackedVar& trackedVar : trackedVars) { if (!trackedVar.hasBeenInitialized) continue; if (trackedVar.procedure != currentFunctionSymbol) @@ -43,10 +43,10 @@ void variableTracking::handleTrackedVars(Symbol* currentFunctionSymbol, std::uni // set init on dummy function args auto functionIt = std::find_if(functions.begin(), functions.end(), - [&](const auto& f) { return f.symbol == currentFunctionSymbol; }); + [&](const function& f) { return f.symbol == currentFunctionSymbol; }); if (functionIt != functions.end()) { auto dummyArgIt = std::find_if(functionIt->dummyArgs.begin(), functionIt->dummyArgs.end(), - [&](const auto& d) { return d.symbol == trackedVar.var; }); + [&](const function::dummyArg& d) { return d.symbol == trackedVar.var; }); if (dummyArgIt != functionIt->dummyArgs.end()) { dummyArgIt->hasBeenInitialized = true; } @@ -72,7 +72,7 @@ void variableTracking::addTrackedVar(trackedVar var) { MCGLogger::logDebug("Add tracking for variable: {} ({})", var.var->name(), fmt::ptr(var.var)); } -void variableTracking::removeTrackedVars(Symbol* procedureSymbol) { +void variableTracking::removeTrackedVars(const Symbol* procedureSymbol) { trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { return t.procedure == procedureSymbol; }), trackedVars.end()); From 9efa7ace2a865a2b623821f3f035ad74d0be9444 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:15 +0100 Subject: [PATCH 117/130] add doc in tool scripts --- cgfcollector/tools/cgfcollector_comp_wrapper.sh.in | 14 ++++++++++++++ cgfcollector/tools/cgfcollector_wrapper.sh.in | 9 +++++++++ cgfcollector/tools/test_runner.sh.in | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 49a80e34..06e234a2 100755 --- a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -1,5 +1,19 @@ #!/bin/bash +# Wrapper script for running cgfcollector plugin with flang. +# This script acts as a drop-in replacement for the flang compiler +# while addtionally generating call graphs for each source file. +# +# Note: The plugin requires the `-fc1` flag to be passed to flang. +# However, flang invokations with and without `-fc1` accept different +# sets of options. This script filters the original compiler options, +# keeping only those compatible with `-fc1`, and invokes flang with +# the plugin to generate call graphs. Finally, it invokes flang again +# with the original arguments to perform the actual compilation. +# +# Usage: +# cgfcollector_comp_wrapper.sh [flang options and source files] + flang_bin=${CGFCOLLECTOR_FLANG_BIN:-"flang-new"} flang_fc1_bin=${CGFCOLLECTOR_FLANG_FC1_BIN:-"flang-new"} diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index 3a1dd76c..a2111404 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -1,5 +1,14 @@ #!/bin/bash +# Wrapper script for running cgfcollector plugin with flang. +# +# Usage: +# cgfcollector_wrapper.sh [-dot|-norename] [flang options and source files] +# +# Options: +# -dot # Generate call graph with dot format +# -norename # Do not rename output files + flang_bin=${CGFCOLLECTOR_FLANG_BIN:-"flang-new"} flang_args=("$@") diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 0ff0c088..0e4955d4 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -1,5 +1,11 @@ #!/bin/bash +# Test runner for fcollector +# +# Usage: +# test_runner.sh # run all tests +# test_runner.sh [test_case_name] # run specific test cases + test_case_dir="@FCOLLECTOR_TEST_CASES_DIR@" if ! [ -d "$test_case_dir" ]; then From e743f97a6bcf5bd7491f6f5c7c574381315f9321 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:16 +0100 Subject: [PATCH 118/130] updated cgfcollector README --- cgfcollector/README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/cgfcollector/README.md b/cgfcollector/README.md index b2eb5cc3..3a19cafe 100644 --- a/cgfcollector/README.md +++ b/cgfcollector/README.md @@ -1,16 +1,17 @@ -# CG fortran collector +# CG Fortran collector ## Usage -The parse plugin is compiled into a dynamic library and can be run with the +The plugin is compiled into a dynamic library and can be run with the Flang compiler like so: `flang -fc1 -load "build/cgfcollector/libfcollector.so" -plugin "genCG"` -There are two kind of plugins: +There are three kinds of plugins: -- `genCG`: generates a call graphs in the MetaCG json format. +- `genCG`: generates a call graph in the MetaCG json format. - `genCGwithDot`: like `genCG` but also generate a `.dot` file. Mostly used for debugging. +- `genCGNoRename`: like `genCG` but does not rename the output file. Additionally these other tools are included: @@ -62,8 +63,6 @@ NOTE: The test `test/multi/fortdepend_deps` has a dependency on [fortdepend](htt ## Debugging -Set `CUSTOM_DEBUG` as an environment variable to get more debug output. - ### print parse tree ```sh From c092fcf0a9a023d19b6156f352ca11ec23e921cb Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:16 +0100 Subject: [PATCH 119/130] removed duplicate comment --- cgfcollector/src/util.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/util.cpp index c134dec7..df38793f 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/util.cpp @@ -214,13 +214,6 @@ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol) { return typeSymbol; } -/** - * @brief Searches the types vector for given symbol and returns pointers to vectors of type with derived types. The - * type symbol is derived from the given symbol. - * - * @param typeSymbol symbol to search for - * @return vector with type and all derived types - */ std::vector findTypeWithDerivedTypes(const std::vector& types, const Symbol* symbol) { std::vector typesWithDerived; std::unordered_set visited; From b5503fc6cd1451501aa26b142b36936d2fa86029 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:02:25 +0100 Subject: [PATCH 120/130] cmake uncomment/removed lines --- cgfcollector/CMakeLists.txt | 1 - cmake/FlangLLVM.cmake | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 9a24211e..7001c984 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -11,7 +11,6 @@ add_library(${PROJECT_NAME} SHARED ${FCOLLECTOR_SOURCES}) add_flang(${PROJECT_NAME}) add_metacg(${PROJECT_NAME}) add_spdlog_libraries(${PROJECT_NAME}) -# add_json(${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/cmake/FlangLLVM.cmake b/cmake/FlangLLVM.cmake index f636f9f3..cf1215d6 100644 --- a/cmake/FlangLLVM.cmake +++ b/cmake/FlangLLVM.cmake @@ -32,5 +32,5 @@ function(add_flang target) NO_DEFAULT_PATH ) - # target_link_libraries(${target} PUBLIC flangFrontendTool) + target_link_libraries(${target} PUBLIC flangFrontendTool) endfunction() From 3e8de05ad8ee3264791bac32544911289310f27f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:02:36 +0100 Subject: [PATCH 121/130] README: add context and fix typo --- cgfcollector/README.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/cgfcollector/README.md b/cgfcollector/README.md index 3a19cafe..0e771daf 100644 --- a/cgfcollector/README.md +++ b/cgfcollector/README.md @@ -1,11 +1,26 @@ # CG Fortran collector +Fortran call graph generation tool for MetaCG. This tool is implemented as a +Flang plugin and generates a call graph from source-level. + ## Usage -The plugin is compiled into a dynamic library and can be run with the -Flang compiler like so: +For single file projects or projects not using modules use: +```sh +cgfcollector_wrapper.sh +``` + +For any other projects you need a build system. For this we provide another +script that acts as the normal Flang compiler but also produces a call graph. +[More info below](#generate-a-call-graph). +```sh +cgfcollector_comp_wrapper.sh +``` -`flang -fc1 -load "build/cgfcollector/libfcollector.so" -plugin "genCG"` +You can also run the plugin directly with Flang: +```sh +flang -fc1 -load "libfcollector.so" -plugin "genCG" +``` There are three kinds of plugins: @@ -39,7 +54,7 @@ set(CMAKE_Fortran_LINK_EXECUTABLE " ") set(CMAKE_EXECUTABLE_SUFFIX .json) ``` -This will hook into the cmake build process and generate a callgraph instead of +This will hook into the CMake build process and generate a call graph instead of an executable. An example can be found in `test/multi/deps`. From 9c912e521428646df74650c85efb49543c50530a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:47:36 +0100 Subject: [PATCH 122/130] migration: CGCompare to CGDiff --- cgfcollector/CMakeLists.txt | 15 +--------- cgfcollector/test/multi/deps/output.json | 8 +++--- .../test/multi/fortdepend_deps/output.json | 8 +++--- cgfcollector/test/simple/entry/output.json | 8 +++--- cgfcollector/test/simple/final/output.json | 28 +++++++++---------- cgfcollector/test/simple/final2/output.json | 18 ++++++------ cgfcollector/test/simple/final3/output.json | 22 +++++++-------- cgfcollector/test/simple/final4/output.json | 6 ++-- cgfcollector/test/simple/final5/output.json | 8 +++--- .../test/simple/generic_interface/output.json | 6 ++-- cgfcollector/test/simple/interop/output.json | 4 +-- .../test/simple/interop_external/output.json | 2 +- .../test/simple/math_demo/output.json | 10 +++---- cgfcollector/test/simple/nesting/output.json | 4 +-- .../test/simple/nesting_calls/output.json | 6 ++-- .../test/simple/operator2/output.json | 8 +++--- .../test/simple/operator3/output.json | 12 ++++---- .../test/simple/polymorphism/output.json | 6 ++-- .../test/simple/polymorphism2/output.json | 8 +++--- cgfcollector/test/simple/simple/output.json | 4 +-- cgfcollector/test/simple/use/output.json | 6 ++-- cgfcollector/tools/test_runner.sh.in | 2 +- 22 files changed, 93 insertions(+), 106 deletions(-) diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 7001c984..8e92433e 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -28,16 +28,6 @@ configure_package_config_file( install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION lib/cmake) -# cgCompare program -add_executable(${PROJECT_NAME}-cgCompare ${CMAKE_CURRENT_SOURCE_DIR}/tools/cgCompare.cpp) -add_metacg(${PROJECT_NAME}-cgCompare) -add_spdlog_libraries(${PROJECT_NAME}-cgCompare) -install( - TARGETS ${PROJECT_NAME}-cgCompare - LIBRARY DESTINATION bin - ARCHIVE DESTINATION bin -) - # visuel program to generate dot file from graph add_executable(${PROJECT_NAME}-visuel ${CMAKE_CURRENT_SOURCE_DIR}/tools/visuel.cpp) add_metacg(${PROJECT_NAME}-visuel) @@ -61,7 +51,7 @@ function( # build-time values for development set(FCOLLECTOR_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.so") - set(FCOLLECTOR_CG_COMPARE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-cgCompare") + set(FCOLLECTOR_CGDIFF "${CMAKE_BINARY_DIR}/tools/cgdiff/cgdiff") set(FCOLLECTOR_WRAPPER "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") set(FCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") set(CGMERGE2_EXECUTABLE "${CMAKE_BINARY_DIR}/tools/cgmerge2/cgmerge2") @@ -81,9 +71,6 @@ function( # install-time values set(FCOLLECTOR_FILE_NAME "${CMAKE_INSTALL_PREFIX}/lib/lib${PROJECT_NAME}.so") - set(FCOLLECTOR_CG_COMPARE "${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}-cgCompare") - set(FCOLLECTOR_WRAPPER "${CMAKE_INSTALL_PREFIX}/bin/cgfcollector_wrapper.sh") - set(FCOLLECTOR_TEST_CASES_DIR "") configure_file( "${TEMPLATE}" diff --git a/cgfcollector/test/multi/deps/output.json b/cgfcollector/test/multi/deps/output.json index 05ed8ec6..1e0e91a5 100644 --- a/cgfcollector/test/multi/deps/output.json +++ b/cgfcollector/test/multi/deps/output.json @@ -6,7 +6,7 @@ "callees": { "1": {} }, "functionName": "_QMmodule0_5Pfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "module0_5.f90" }, "1": { @@ -20,7 +20,7 @@ "callees": { "1": {} }, "functionName": "_QMmodule3Pfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "module3.f90" }, "3": { @@ -34,14 +34,14 @@ "callees": {}, "functionName": "_QMmy_modulePsubsub", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "module.f90" }, "5": { "callees": { "3": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/multi/fortdepend_deps/output.json b/cgfcollector/test/multi/fortdepend_deps/output.json index 05ed8ec6..1e0e91a5 100644 --- a/cgfcollector/test/multi/fortdepend_deps/output.json +++ b/cgfcollector/test/multi/fortdepend_deps/output.json @@ -6,7 +6,7 @@ "callees": { "1": {} }, "functionName": "_QMmodule0_5Pfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "module0_5.f90" }, "1": { @@ -20,7 +20,7 @@ "callees": { "1": {} }, "functionName": "_QMmodule3Pfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "module3.f90" }, "3": { @@ -34,14 +34,14 @@ "callees": {}, "functionName": "_QMmy_modulePsubsub", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "module.f90" }, "5": { "callees": { "3": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/entry/output.json b/cgfcollector/test/simple/entry/output.json index e81dcd50..0fae7506 100644 --- a/cgfcollector/test/simple/entry/output.json +++ b/cgfcollector/test/simple/entry/output.json @@ -6,28 +6,28 @@ "callees": {}, "functionName": "_QMmodPentry1", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPentry2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, "functionName": "_QMmodPfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": { "0": {}, "1": {}, "2": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/final/output.json b/cgfcollector/test/simple/final/output.json index 5f61b984..66fe22a7 100644 --- a/cgfcollector/test/simple/final/output.json +++ b/cgfcollector/test/simple/final/output.json @@ -14,98 +14,98 @@ }, "functionName": "_QFPfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "1": { "callees": {}, "functionName": "_QMmodPcreate_polynomial", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "10": { "callees": { "2": {} }, "functionName": "_QMmod_usePfunc_calls_final2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "11": { "callees": { "2": {}, "5": {} }, "functionName": "_QMmod_usePfunc_calls_final3", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "12": { "callees": {}, "functionName": "_QMmod_usePfunc_does_not_call_final", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "13": { "callees": { "0": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "2": { "callees": {}, "functionName": "_QMmodPfinalize_polynomial", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "3": { "callees": {}, "functionName": "_QMmodPprint_polynomial", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "4": { "callees": {}, "functionName": "_QMmod_useFfunc_calls_final3Pset_nothing", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "5": { "callees": { "1": {} }, "functionName": "_QMmod_useFfunc_calls_final3Pset_q", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "6": { "callees": { "1": {}, "2": {} }, "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "7": { "callees": { "2": {} }, "functionName": "_QMmod_useFfunc_calls_final3Pset_q_alloc2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "8": { "callees": { "1": {}, "2": {} }, "functionName": "_QMmod_useFfunc_calls_final3Pset_q_move_alloc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "9": { "callees": { "1": {}, "2": {}, "3": {} }, "functionName": "_QMmod_usePfunc_calls_final", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" } } diff --git a/cgfcollector/test/simple/final2/output.json b/cgfcollector/test/simple/final2/output.json index 6ab80538..139e208a 100644 --- a/cgfcollector/test/simple/final2/output.json +++ b/cgfcollector/test/simple/final2/output.json @@ -14,63 +14,63 @@ }, "functionName": "_QFPfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPfinalize_base", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, "functionName": "_QMmodPfinalize_derived", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, "functionName": "_QMmodPfunc_arg", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "4": { "callees": { "1": {}, "2": {} }, "functionName": "_QMmodPfunc_arg2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "5": { "callees": {}, "functionName": "_QMmodPfunc_arg_inout", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "6": { "callees": { "1": {}, "2": {} }, "functionName": "_QMmodPfunc_arg_inout2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "7": { "callees": { "1": {}, "2": {} }, "functionName": "_QMmodPfunc_arg_out", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "8": { "callees": { "0": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/final3/output.json b/cgfcollector/test/simple/final3/output.json index 82a28281..548c165d 100644 --- a/cgfcollector/test/simple/final3/output.json +++ b/cgfcollector/test/simple/final3/output.json @@ -6,77 +6,77 @@ "callees": { "2": {}, "3": {}, "4": {}, "6": {}, "8": {} }, "functionName": "_QFPfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": { "5": {}, "7": {}, "9": {} }, "functionName": "_QFPfunc_no_finalize", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "10": { "callees": { "0": {}, "1": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, "functionName": "_QMmodPfinalize_base", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, "functionName": "_QMmodPfinalize_derived", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "4": { "callees": {}, "functionName": "_QMmodPfunc_arg", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "5": { "callees": {}, "functionName": "_QMmodPfunc_arg2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "6": { "callees": {}, "functionName": "_QMmodPfunc_arg_inout", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "7": { "callees": {}, "functionName": "_QMmodPfunc_arg_inout2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "8": { "callees": {}, "functionName": "_QMmodPfunc_arg_out", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "9": { "callees": {}, "functionName": "_QMmodPfunc_arg_out2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/final4/output.json b/cgfcollector/test/simple/final4/output.json index a797fb34..f633ac20 100644 --- a/cgfcollector/test/simple/final4/output.json +++ b/cgfcollector/test/simple/final4/output.json @@ -6,21 +6,21 @@ "callees": {}, "functionName": "_QMmodPcreate_dummy_type", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPfinalize_dummy", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": { "0": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/final5/output.json b/cgfcollector/test/simple/final5/output.json index 615a7df2..c54c2e59 100644 --- a/cgfcollector/test/simple/final5/output.json +++ b/cgfcollector/test/simple/final5/output.json @@ -6,28 +6,28 @@ "callees": {}, "functionName": "_QFPfunc", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPfinalize_base", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, "functionName": "_QMmodPfinalize_derived", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/generic_interface/output.json b/cgfcollector/test/simple/generic_interface/output.json index 1c97e32d..48e756a3 100644 --- a/cgfcollector/test/simple/generic_interface/output.json +++ b/cgfcollector/test/simple/generic_interface/output.json @@ -6,21 +6,21 @@ "callees": {}, "functionName": "_QMmodPadd_int", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPadd_real", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": { "0": {}, "1": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/interop/output.json b/cgfcollector/test/simple/interop/output.json index 7972eaaf..5fda0b43 100644 --- a/cgfcollector/test/simple/interop/output.json +++ b/cgfcollector/test/simple/interop/output.json @@ -6,14 +6,14 @@ "callees": { "1": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "add", "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/interop_external/output.json b/cgfcollector/test/simple/interop_external/output.json index 399a3cce..9e49af94 100644 --- a/cgfcollector/test/simple/interop_external/output.json +++ b/cgfcollector/test/simple/interop_external/output.json @@ -13,7 +13,7 @@ "callees": { "0": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/math_demo/output.json b/cgfcollector/test/simple/math_demo/output.json index 5baa24be..01155e90 100644 --- a/cgfcollector/test/simple/math_demo/output.json +++ b/cgfcollector/test/simple/math_demo/output.json @@ -6,35 +6,35 @@ "callees": {}, "functionName": "_QMmath_utilsParray_stats", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmath_utilsPdot_product_custom", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": { "2": {} }, "functionName": "_QMmath_utilsPfactorial", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, "functionName": "_QMmath_utilsPnormalize_vector", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "4": { "callees": { "0": {}, "1": {}, "2": {}, "3": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/nesting/output.json b/cgfcollector/test/simple/nesting/output.json index 4b4349d4..e827c325 100644 --- a/cgfcollector/test/simple/nesting/output.json +++ b/cgfcollector/test/simple/nesting/output.json @@ -6,14 +6,14 @@ "callees": {}, "functionName": "_QMmPsay", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": { "0": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/nesting_calls/output.json b/cgfcollector/test/simple/nesting_calls/output.json index 0e58993b..2f3a3626 100644 --- a/cgfcollector/test/simple/nesting_calls/output.json +++ b/cgfcollector/test/simple/nesting_calls/output.json @@ -6,21 +6,21 @@ "callees": {}, "functionName": "_QMmPreturn_ptr", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmPsay", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": { "0": {}, "1": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/operator2/output.json b/cgfcollector/test/simple/operator2/output.json index 5de3b859..a5663baa 100644 --- a/cgfcollector/test/simple/operator2/output.json +++ b/cgfcollector/test/simple/operator2/output.json @@ -6,28 +6,28 @@ "callees": {}, "functionName": "_QMmodPfbase", "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPfunction_base2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, "functionName": "_QMmodPnot_base", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": { "0": {}, "1": {}, "2": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/operator3/output.json b/cgfcollector/test/simple/operator3/output.json index 4237db39..f1c5b0f2 100644 --- a/cgfcollector/test/simple/operator3/output.json +++ b/cgfcollector/test/simple/operator3/output.json @@ -6,42 +6,42 @@ "callees": {}, "functionName": "_QMmodPadd_base", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPs", "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, "functionName": "_QMmodPshow_derived1", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": {}, "functionName": "_QMmodPshow_derived2", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "4": { "callees": {}, "functionName": "_QMmodPsub_derived1", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "5": { "callees": { "0": {}, "1": {}, "2": {}, "3": {}, "4": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/polymorphism/output.json b/cgfcollector/test/simple/polymorphism/output.json index 59598f43..92d5227e 100644 --- a/cgfcollector/test/simple/polymorphism/output.json +++ b/cgfcollector/test/simple/polymorphism/output.json @@ -6,21 +6,21 @@ "callees": {}, "functionName": "_QMmodPset_mass_body", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPset_mass_charged_body", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": { "0": {}, "1": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/polymorphism2/output.json b/cgfcollector/test/simple/polymorphism2/output.json index 53b65da9..8b6fcd7a 100644 --- a/cgfcollector/test/simple/polymorphism2/output.json +++ b/cgfcollector/test/simple/polymorphism2/output.json @@ -6,28 +6,28 @@ "callees": {}, "functionName": "_QMshapesPdraw", "hasBody": false, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMshapesPdraw_circle", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": {}, "functionName": "_QMshapesPdraw_rectangle", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "3": { "callees": { "0": {}, "1": {}, "2": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/test/simple/simple/output.json b/cgfcollector/test/simple/simple/output.json index f4f492d5..2821fc4a 100644 --- a/cgfcollector/test/simple/simple/output.json +++ b/cgfcollector/test/simple/simple/output.json @@ -6,14 +6,14 @@ "callees": {}, "functionName": "_QFPprint_stars", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" }, "1": { "callees": { "0": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "input.f90" } } diff --git a/cgfcollector/test/simple/use/output.json b/cgfcollector/test/simple/use/output.json index 4263c069..90fa13f9 100644 --- a/cgfcollector/test/simple/use/output.json +++ b/cgfcollector/test/simple/use/output.json @@ -6,21 +6,21 @@ "callees": {}, "functionName": "_QMmodPset_var_base", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "1": { "callees": {}, "functionName": "_QMmodPset_var_derived", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" }, "2": { "callees": { "0": {}, "1": {} }, "functionName": "_QQmain", "hasBody": true, - "meta": { "fileProperties": { "systemInclude": false } }, + "meta": {}, "origin": "main.f90" } } diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 0e4955d4..83b83693 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -128,7 +128,7 @@ while read -r dir; do fi fi - if @FCOLLECTOR_CG_COMPARE@ "$output_file" "$out_dir/$test_case_name.json"; then + if @FCOLLECTOR_CGDIFF@ "$output_file" "$out_dir/$test_case_name.json"; then echo "Test case passed" else ((failed_test_case_count++)) From d84aef754e0b47f67c5a3640a54980e790b89077 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:51:37 +0100 Subject: [PATCH 123/130] removed CGCompare --- cgfcollector/README.md | 1 - cgfcollector/tools/cgCompare.cpp | 80 -------------------------------- 2 files changed, 81 deletions(-) delete mode 100644 cgfcollector/tools/cgCompare.cpp diff --git a/cgfcollector/README.md b/cgfcollector/README.md index 0e771daf..9b6cc7e9 100644 --- a/cgfcollector/README.md +++ b/cgfcollector/README.md @@ -32,7 +32,6 @@ Additionally these other tools are included: - `cgfcollector_wrapper.sh`: convenience wrapper to run parse plugin. - `cgfcollector_comp_wrapper.sh`: acts like a normal Flang compiler but also generates a call graph. -- `cgCompare.cpp`: compares two given call graphs. - `test_runner.sh`: run tests. ## How to build diff --git a/cgfcollector/tools/cgCompare.cpp b/cgfcollector/tools/cgCompare.cpp deleted file mode 100644 index 4c7898f0..00000000 --- a/cgfcollector/tools/cgCompare.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -static auto console = metacg::MCGLogger::instance().getConsole(); -static auto errConsole = metacg::MCGLogger::instance().getErrConsole(); - -bool endsMatch(const std::string& a, const std::string& b) { - if (a.size() >= b.size()) { - return a.compare(a.size() - b.size(), b.size(), b) == 0; - } else { - return b.compare(b.size() - a.size(), a.size(), a) == 0; - } -} - -static bool compareNodesAndEdges(const metacg::Callgraph* cg1, const metacg::Callgraph* cg2) { - bool equal = true; - - for (const auto& node : cg1->getNodes()) { - if (!cg2->hasNode(node->getFunctionName())) { - errConsole->error("Node {} is missing in the other call graph.", node->getFunctionName()); - equal = false; - continue; - } - - const auto& node2 = cg2->getFirstNode(node->getFunctionName()); - if (node2->getHasBody() != node->getHasBody()) { - errConsole->error("Node {} has different hasBody flags: expected {} got {}", node->getFunctionName(), - node2->getHasBody(), node->getHasBody()); - equal = false; - } - - if (!endsMatch(node->getOrigin().value_or(""), node2->getOrigin().value_or(""))) { - errConsole->error("Node {} has different origins: expected '{}' got '{}'", node->getFunctionName(), - node2->getOrigin().value_or(""), node->getOrigin().value_or("")); - equal = false; - } - } - - for (const auto& [id, edge] : cg1->getEdges()) { - auto name1 = cg1->getNode(id.first)->getFunctionName(); - auto name2 = cg1->getNode(id.second)->getFunctionName(); - - if (!cg2->existsAnyEdge(name1, name2)) { - errConsole->error("Edge from {} to {} is missing in the other call graph.", name1, name2); - equal = false; - } - } - - return equal; -} - -int main(int argc, char* argv[]) { - if (argc != 3) { - errConsole->error("Usage: cgCompare "); - return EXIT_FAILURE; - } - - metacg::io::FileSource fs1(argv[1]); - metacg::io::FileSource fs2(argv[2]); - - auto mcgReader1 = metacg::io::createReader(fs1); - auto mcgReader2 = metacg::io::createReader(fs2); - if (!mcgReader1 || !mcgReader2) { - return EXIT_FAILURE; - } - - auto cg1 = mcgReader1->read(); - auto cg2 = mcgReader2->read(); - if (!cg1 || !cg2) { - errConsole->error("Error reading call graphs."); - return EXIT_FAILURE; - } - - if (!compareNodesAndEdges(cg1.get(), cg2.get()) || !compareNodesAndEdges(cg2.get(), cg1.get())) { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} From 2d0dfdd6a2146974de2875480305da1d00cac4ad Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 17:43:42 +0100 Subject: [PATCH 124/130] simplify test script and integrate in cmake --- cgfcollector/CMakeLists.txt | 3 + cgfcollector/tools/test_runner.sh.in | 220 ++++++++++++++------------- 2 files changed, 115 insertions(+), 108 deletions(-) diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 8e92433e..76c98e5b 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -124,3 +124,6 @@ file( "${CMAKE_CURRENT_BINARY_DIR}/make_base" "${CMAKE_CURRENT_SOURCE_DIR}/test/multi/make_base" ) + +# tests +add_test(NAME fcollector_tests COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_runner.sh) diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 83b83693..14d99f4c 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -4,140 +4,144 @@ # # Usage: # test_runner.sh # run all tests -# test_runner.sh [test_case_name] # run specific test cases +# test_runner.sh [test_name] # run specific test cases test_case_dir="@FCOLLECTOR_TEST_CASES_DIR@" +scriptdir="$(cd "$(dirname "$0")" && pwd -P)" +out_dir="$scriptdir/out" + +mkdir -p "$out_dir" if ! [ -d "$test_case_dir" ]; then echo "Error: Test case directory '$test_case_dir' does not exist." exit 1 fi -scriptdir="$(cd "$(dirname "$0")" && pwd -P)" -out_dir="$scriptdir/out" - -if [[ ! -d "$out_dir" ]]; then - mkdir "$out_dir" -fi - -run_tests_with_name=() -while [[ $# -gt 0 ]]; do - run_tests_with_name+=("$1") - shift -done - -function should_be_run() +function find_test_dir() { - local test_case_name="$1" + local test_name="${1#*_}" + local test_category="${1%%_*}" + local test_dir="$test_case_dir/$test_category/$test_name" - if [[ ${#run_tests_with_name[@]} -eq 0 ]]; then + if [ -d "$test_dir" ] && [ -f "$test_dir/output.json" ]; then + echo "$test_dir" return 0 fi - - for name in "${run_tests_with_name[@]}"; do - if [[ "$test_case_name" == "$name" ]]; then - return 0 - fi - done - return 1 } -test_case_count=0 -skipped_test_case_count=0 -failed_test_case_count=0 - -while read -r dir; do - dir="$(dirname "$dir")" - output_file="$dir/output.json" - - # generate test case name from dir path. test/simple/01 becomes simple_01 - test_case_name="$(basename "$(dirname "$dir")")_$(basename "$dir")" - - if ! should_be_run "$test_case_name"; then - continue - fi +function run_single_test() +{ + local test_dir="$1" + local test_name="$2" + local expected_output="$test_dir/output.json" + local actual_output="$out_dir/$test_name.json" - echo "Test case: $test_case_name" - ((test_case_count++)) + if [ -f "$test_dir/CMakeLists.txt" ]; then + # cmake managed test + local tmp_dir="$(mktemp -d)" + trap 'rm -rf '"$tmp_dir"'' RETURN - if [ ! -f "$output_file" ]; then - echo "Skipping. Because missing output file: $output_file" - ((skipped_test_case_count++)) - continue - fi + cmake -S "$test_dir" -B "$tmp_dir" || { + echo "Failed: cmake config failed" + return 1 + } + cmake --build "$tmp_dir" || { + echo "Failed: cmake build failed" + return 1 + } + find "$tmp_dir" -maxdepth 1 -name '*.json' -exec cp {} "$actual_output" \; || { + echo "Failed: could not generate CG" + return 1 + } + elif [ -f "$test_dir/Makefile" ]; then + # make managed with fortdepend test + local tmp_dir="$(mktemp -d)" + trap 'rm -rf '"$tmp_dir"'' RETURN - if find "$dir" -maxdepth 1 -name 'CMakeLists.txt' | grep -q .; then - # cmake managed test - tmp_dir="$(mktemp -d)" - - ( - cd "$dir" - cmake -S . -B "$tmp_dir" || { - echo "Error: cmake config failed" - exit 1 - } - cd "$tmp_dir" - make || { - echo "Error: cmake build failed" - exit 1 - } - find . -maxdepth 1 -name '*.json' | while read -r file; do - cp "$file" "$out_dir/$test_case_name.json" - done - ) || { - echo "Error: could not generate CG" - rm -rf "$tmp_dir" - ((failed_test_case_count++)) - continue + make -C "$test_dir" BUILD_DIR="$tmp_dir" || { + echo "Failed: make build failed" + return 1 } + find "$tmp_dir" -maxdepth 1 -name 'output.json' -exec cp {} "$actual_output" \; || { + echo "Failed: could not generate CG" + return 1 + } + else + # not make/cmake managed test + local input_files=("$test_dir"/*.f90) + if ! [ -f "${input_files[0]}" ]; then + echo "Failed: no .f90 files found" + return 1 + fi - rm -rf "$tmp_dir" - elif find "$dir" -maxdepth 1 -name 'Makefile' | grep -q .; then - # make managed with fortdepend test - tmp_dir="$(mktemp -d)" - - ( - cd "$dir" - make BUILD_DIR="$tmp_dir" || { - echo "Error: make build failed" - exit 1 - } - cd "$tmp_dir" - find . -maxdepth 1 -name '*output.json' | while read -r file; do - cp "$file" "$out_dir/$test_case_name.json" - done - ) || { - echo "Error: could not generate CG" - rm -rf "$tmp_dir" - ((failed_test_case_count++)) - continue + @FCOLLECTOR_WRAPPER@ -dot -o "$actual_output" "${input_files[@]}" || { + echo "Failed: could not generate CG" + return 1 } + fi - rm -rf "$tmp_dir" + if @FCOLLECTOR_CGDIFF@ "$expected_output" "$actual_output"; then + echo "Passed" + return 0 else - # not cmake managed test - mapfile -t input_files < <(find "$dir" -maxdepth 1 -name '*.f90') - if [[ ${#input_files[@]} -gt 0 ]]; then - - if ! @FCOLLECTOR_WRAPPER@ -dot -o "$out_dir/$test_case_name.json" "${input_files[@]}"; then - echo "Error: failed to generate callgraph" - ((failed_test_case_count++)) - continue - fi - fi + echo "Failed: Output mismatch" + return 1 fi +} - if @FCOLLECTOR_CGDIFF@ "$output_file" "$out_dir/$test_case_name.json"; then - echo "Test case passed" +if [ $# -gt 0 ]; then + # specific test cases + test_cases=() + for test_name in "$@"; do + test_dir=$(find_test_dir "$test_name") + if [ -n "$test_dir" ]; then + test_cases+=("$test_dir:$test_name") + else + echo "Warning: Test '$test_name' not found" + fi + done +else + # all test cases + test_cases=() + while read -r output_file; do + test_dir="$(dirname "$output_file")" + test_name="$(basename "$(dirname "$test_dir")")_$(basename "$test_dir")" + test_cases+=("$test_dir:$test_name") + done < <(find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "output.json" \;) +fi + +total_tests=${#test_cases[@]} +passed=0 +failed=0 +failed_tests=() + +# run tests +for i in "${!test_cases[@]}"; do + IFS=':' read -r test_dir test_name <<<"${test_cases[i]}" + + printf "Test %d/%d %s\n" "$((i + 1))" "$total_tests" "$test_name" + + if run_single_test "$test_dir" "$test_name"; then + ((passed++)) else - ((failed_test_case_count++)) - echo "Error: Output mismatch" + ((failed++)) + failed_tests+=("$test_name") fi +done -done < <(find "$test_case_dir" -mindepth 1 -type d -exec find {} -maxdepth 1 -name "output.json" \;) +# summary +if [ "$total_tests" -gt 0 ]; then + printf "%d%% tests passed, %d tests failed out of %d\n" \ + "$((passed * 100 / total_tests))" "$failed" "$total_tests" +else + echo "No tests found" +fi -echo "" -echo "Test cases run: $test_case_count" -echo "Skipped test cases: $skipped_test_case_count" -echo "Failed test cases: $failed_test_case_count" +if [ ${#failed_tests[@]} -gt 0 ]; then + echo "Failed tests:" + for test in "${failed_tests[@]}"; do + echo " $test" + done + exit 1 +fi From 9602d67d5124833c75e8cf1033749c4ca7d63e00 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Wed, 11 Feb 2026 14:02:42 +0100 Subject: [PATCH 125/130] cmake add flangFrontendTool find --- cmake/FlangLLVM.cmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmake/FlangLLVM.cmake b/cmake/FlangLLVM.cmake index cf1215d6..59557789 100644 --- a/cmake/FlangLLVM.cmake +++ b/cmake/FlangLLVM.cmake @@ -27,10 +27,9 @@ function(add_flang target) target_include_directories(${target} SYSTEM PUBLIC ${FLANG_INCLUDE_DIRS}) find_library( - FLANG_LIB flang + FLANG_FRONTEND_TOOL flangFrontendTool PATHS ${LLVM_LIBRARY_DIR} NO_DEFAULT_PATH ) - - target_link_libraries(${target} PUBLIC flangFrontendTool) + target_link_libraries(${target} PUBLIC ${FLANG_FRONTEND_TOOL}) endfunction() From 2570553f7b5d68cf995fdb137d4bf6d82f32be5c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 14:18:12 +0100 Subject: [PATCH 126/130] Filename capitalization and license headers --- cgfcollector/include/{edge.h => Edge.h} | 10 ++++++++-- .../include/{function.h => Function.h} | 6 ++++++ cgfcollector/include/ParseTreeVisitor.h | 18 ++++++++++++------ ...tentialFinalizer.h => PotentialFinalizer.h} | 8 +++++++- cgfcollector/include/{type.h => Type.h} | 6 ++++++ cgfcollector/include/{util.h => Util.h} | 8 +++++++- .../{variableTracking.h => VariableTracking.h} | 14 ++++++++++---- cgfcollector/src/{edge.cpp => Edge.cpp} | 8 +++++++- cgfcollector/src/{main.cpp => Main.cpp} | 6 ++++++ cgfcollector/src/ParseTreeVisitor.cpp | 6 ++++++ cgfcollector/src/{util.cpp => Util.cpp} | 8 +++++++- ...riableTracking.cpp => VariableTracking.cpp} | 8 +++++++- .../tools/cgfcollector_comp_wrapper.sh.in | 2 +- cgfcollector/tools/cgfcollector_wrapper.sh.in | 2 +- cgfcollector/tools/test_runner.sh.in | 2 +- 15 files changed, 92 insertions(+), 20 deletions(-) rename cgfcollector/include/{edge.h => Edge.h} (91%) rename cgfcollector/include/{function.h => Function.h} (79%) rename cgfcollector/include/{potentialFinalizer.h => PotentialFinalizer.h} (65%) rename cgfcollector/include/{type.h => Type.h} (75%) rename cgfcollector/include/{util.h => Util.h} (94%) rename cgfcollector/include/{variableTracking.h => VariableTracking.h} (89%) rename cgfcollector/src/{edge.cpp => Edge.cpp} (92%) rename cgfcollector/src/{main.cpp => Main.cpp} (96%) rename cgfcollector/src/{util.cpp => Util.cpp} (98%) rename cgfcollector/src/{variableTracking.cpp => VariableTracking.cpp} (93%) diff --git a/cgfcollector/include/edge.h b/cgfcollector/include/Edge.h similarity index 91% rename from cgfcollector/include/edge.h rename to cgfcollector/include/Edge.h index 39f01812..9f64d780 100644 --- a/cgfcollector/include/edge.h +++ b/cgfcollector/include/Edge.h @@ -1,3 +1,9 @@ +/** + * File: Edge.h + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #pragma once #include @@ -6,8 +12,8 @@ #include #include -#include "type.h" -#include "util.h" +#include "Type.h" +#include "Util.h" using namespace Fortran::semantics; using namespace Fortran::parser; diff --git a/cgfcollector/include/function.h b/cgfcollector/include/Function.h similarity index 79% rename from cgfcollector/include/function.h rename to cgfcollector/include/Function.h index 701e3148..0a61a18a 100644 --- a/cgfcollector/include/function.h +++ b/cgfcollector/include/Function.h @@ -1,3 +1,9 @@ +/** + * File: Function.h + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #pragma once #include diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index cab6f71a..957c706c 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -1,3 +1,9 @@ +/** + * File: ParseTreeVisitor.h + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #pragma once #include @@ -20,12 +26,12 @@ #include #include -#include "edge.h" -#include "function.h" -#include "potentialFinalizer.h" -#include "type.h" -#include "util.h" -#include "variableTracking.h" +#include "Edge.h" +#include "Function.h" +#include "PotentialFinalizer.h" +#include "Type.h" +#include "Util.h" +#include "VariableTracking.h" using namespace Fortran::parser; using namespace Fortran::semantics; diff --git a/cgfcollector/include/potentialFinalizer.h b/cgfcollector/include/PotentialFinalizer.h similarity index 65% rename from cgfcollector/include/potentialFinalizer.h rename to cgfcollector/include/PotentialFinalizer.h index 48f954d6..906412af 100644 --- a/cgfcollector/include/potentialFinalizer.h +++ b/cgfcollector/include/PotentialFinalizer.h @@ -1,9 +1,15 @@ +/** + * File: PotentialFinalizer.h + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #pragma once #include #include -#include "edge.h" +#include "Edge.h" struct potentialFinalizer { std::size_t argPos; diff --git a/cgfcollector/include/type.h b/cgfcollector/include/Type.h similarity index 75% rename from cgfcollector/include/type.h rename to cgfcollector/include/Type.h index 77fbe398..f13e235b 100644 --- a/cgfcollector/include/type.h +++ b/cgfcollector/include/Type.h @@ -1,3 +1,9 @@ +/** + * File: Type.h + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #pragma once #include diff --git a/cgfcollector/include/util.h b/cgfcollector/include/Util.h similarity index 94% rename from cgfcollector/include/util.h rename to cgfcollector/include/Util.h index 6d15e7dc..85184d8a 100644 --- a/cgfcollector/include/util.h +++ b/cgfcollector/include/Util.h @@ -1,3 +1,9 @@ +/** + * File: Util.h + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #pragma once #include "LoggerUtil.h" @@ -7,7 +13,7 @@ #include #include -#include "type.h" +#include "Type.h" using namespace Fortran::parser; using namespace Fortran::semantics; diff --git a/cgfcollector/include/variableTracking.h b/cgfcollector/include/VariableTracking.h similarity index 89% rename from cgfcollector/include/variableTracking.h rename to cgfcollector/include/VariableTracking.h index c9b04a41..ff4e7024 100644 --- a/cgfcollector/include/variableTracking.h +++ b/cgfcollector/include/VariableTracking.h @@ -1,11 +1,17 @@ +/** + * File: VariableTracking.h + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #pragma once #include -#include "edge.h" -#include "function.h" -#include "type.h" -#include "util.h" +#include "Edge.h" +#include "Function.h" +#include "Type.h" +#include "Util.h" using namespace Fortran::semantics; diff --git a/cgfcollector/src/edge.cpp b/cgfcollector/src/Edge.cpp similarity index 92% rename from cgfcollector/src/edge.cpp rename to cgfcollector/src/Edge.cpp index eaf4ee0e..10a9b6f7 100644 --- a/cgfcollector/src/edge.cpp +++ b/cgfcollector/src/Edge.cpp @@ -1,4 +1,10 @@ -#include "edge.h" +/** + * File: Edge.cpp + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + +#include "Edge.h" std::vector edgeManager::getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { diff --git a/cgfcollector/src/main.cpp b/cgfcollector/src/Main.cpp similarity index 96% rename from cgfcollector/src/main.cpp rename to cgfcollector/src/Main.cpp index d67461ca..0472965b 100644 --- a/cgfcollector/src/main.cpp +++ b/cgfcollector/src/Main.cpp @@ -1,3 +1,9 @@ +/** + * File: Main.cpp + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #include "ParseTreeVisitor.h" #include diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 2d37121e..2df6cd83 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -1,3 +1,9 @@ +/** + * File: ParseTreeVisitor.cpp + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + #include "ParseTreeVisitor.h" template void ParseTreeVisitor::handleFuncSubStmt(const FunctionStmt&); diff --git a/cgfcollector/src/util.cpp b/cgfcollector/src/Util.cpp similarity index 98% rename from cgfcollector/src/util.cpp rename to cgfcollector/src/Util.cpp index df38793f..2c7d56cc 100644 --- a/cgfcollector/src/util.cpp +++ b/cgfcollector/src/Util.cpp @@ -1,4 +1,10 @@ -#include "util.h" +/** + * File: Util.cpp + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + +#include "Util.h" bool compareSymbols(const Symbol* a, const Symbol* b) { if (a == b) diff --git a/cgfcollector/src/variableTracking.cpp b/cgfcollector/src/VariableTracking.cpp similarity index 93% rename from cgfcollector/src/variableTracking.cpp rename to cgfcollector/src/VariableTracking.cpp index 3f05d883..85548f25 100644 --- a/cgfcollector/src/variableTracking.cpp +++ b/cgfcollector/src/VariableTracking.cpp @@ -1,4 +1,10 @@ -#include "variableTracking.h" +/** + * File: VariableTracking.cpp + * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at + * https://github.com/tudasc/metacg/LICENSE.txt + */ + +#include "VariableTracking.h" trackedVar* variableTracking::getTrackedVarFromSourceName(const Symbol* currentFunctionSymbol, SourceName sourceName) { auto anyTrackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 06e234a2..666c8af4 100755 --- a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Wrapper script for running cgfcollector plugin with flang. # This script acts as a drop-in replacement for the flang compiler diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index a2111404..6c3dd276 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Wrapper script for running cgfcollector plugin with flang. # diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 14d99f4c..ade3b003 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Test runner for fcollector # From 59e8b74336e6f095c4ca2472aa1f255a56a82d66 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 15:28:45 +0100 Subject: [PATCH 127/130] removed using namespace from header files and project-local includes first --- cgfcollector/include/Edge.h | 27 +++--- .../include/{Util.h => FortranUtil.h} | 43 ++++----- cgfcollector/include/Function.h | 17 ++-- cgfcollector/include/ParseTreeVisitor.h | 95 +++++++++---------- cgfcollector/include/PotentialFinalizer.h | 4 +- cgfcollector/include/Type.h | 15 ++- cgfcollector/include/VariableTracking.h | 24 ++--- cgfcollector/src/Edge.cpp | 4 + .../src/{Util.cpp => FortranUtil.cpp} | 9 +- cgfcollector/src/Main.cpp | 1 + cgfcollector/src/ParseTreeVisitor.cpp | 5 + cgfcollector/src/VariableTracking.cpp | 5 + 12 files changed, 129 insertions(+), 120 deletions(-) rename cgfcollector/include/{Util.h => FortranUtil.h} (65%) rename cgfcollector/src/{Util.cpp => FortranUtil.cpp} (98%) diff --git a/cgfcollector/include/Edge.h b/cgfcollector/include/Edge.h index 9f64d780..8554d00d 100644 --- a/cgfcollector/include/Edge.h +++ b/cgfcollector/include/Edge.h @@ -6,19 +6,15 @@ #pragma once +#include "FortranUtil.h" +#include "Type.h" + #include #include #include #include #include -#include "Type.h" -#include "Util.h" - -using namespace Fortran::semantics; -using namespace Fortran::parser; -using namespace metacg; - struct edge { std::string caller; std::string callee; @@ -32,10 +28,11 @@ struct edge { }; struct edgeSymbol { - const Symbol* caller; - const Symbol* callee; + const Fortran::semantics::Symbol* caller; + const Fortran::semantics::Symbol* callee; - edgeSymbol(const Symbol* caller, const Symbol* callee) : caller(caller), callee(callee) {} + edgeSymbol(const Fortran::semantics::Symbol* caller, const Fortran::semantics::Symbol* callee) + : caller(caller), callee(callee) {} bool operator==(const edgeSymbol& other) const { return caller == other.caller && callee == other.callee; } bool operator<(const edgeSymbol& other) const { @@ -49,7 +46,7 @@ struct edgeManager { edgeManager(std::vector& edges) : edges(edges) {} void addEdge(const edgeSymbol& e, bool debug = true); - void addEdge(const Symbol* caller, const Symbol* callee, bool debug = true); + void addEdge(const Fortran::semantics::Symbol* caller, const Fortran::semantics::Symbol* callee, bool debug = true); void addEdge(const edge& e, bool debug = true); void addEdge(const std::string& caller, const std::string& callee, bool debug = true); void addEdges(const std::vector& newEdges, bool debug = true); @@ -62,8 +59,9 @@ struct edgeManager { * @param currentFunctionSymbol * @param symbol */ - std::vector getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, - const Symbol* symbol); + std::vector getEdgesForFinalizers(const std::vector& types, + const Fortran::semantics::Symbol* currentFunctionSymbol, + const Fortran::semantics::Symbol* symbol); /** * @brief Calls getEdgesForFinalizers and adds them to the edges vector. * @@ -71,5 +69,6 @@ struct edgeManager { * @param currentFunctionSymbol * @param symbol */ - void addEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol); + void addEdgesForFinalizers(const std::vector& types, const Fortran::semantics::Symbol* currentFunctionSymbol, + const Fortran::semantics::Symbol* symbol); }; diff --git a/cgfcollector/include/Util.h b/cgfcollector/include/FortranUtil.h similarity index 65% rename from cgfcollector/include/Util.h rename to cgfcollector/include/FortranUtil.h index 85184d8a..d5e19388 100644 --- a/cgfcollector/include/Util.h +++ b/cgfcollector/include/FortranUtil.h @@ -1,25 +1,20 @@ /** - * File: Util.h + * File: FortranUtil.h * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at * https://github.com/tudasc/metacg/LICENSE.txt */ #pragma once -#include "LoggerUtil.h" +#include "Type.h" + +#include #include #include #include #include #include -#include "Type.h" - -using namespace Fortran::parser; -using namespace Fortran::semantics; -using namespace Fortran::common; -using namespace metacg; - /** * @brief Formatter for CharBlock */ @@ -54,10 +49,11 @@ bool holds_any_of(const Variant& v) { * @return */ template -const Name* getNameFromClassWithDesignator(const T& t) { - if (const Indirection* designator = std::get_if>(&t.u)) { - if (const DataRef* dataRef = std::get_if(&designator->value().u)) { - if (const Name* name = std::get_if(&dataRef->u)) { +const Fortran::parser::Name* getNameFromClassWithDesignator(const T& t) { + if (const Fortran::common::Indirection* designator = + std::get_if>(&t.u)) { + if (const Fortran::parser::DataRef* dataRef = std::get_if(&designator->value().u)) { + if (const Fortran::parser::Name* name = std::get_if(&dataRef->u)) { return name; } } @@ -73,7 +69,7 @@ const Name* getNameFromClassWithDesignator(const T& t) { * @param b * @return true if both symbols are equal */ -bool compareSymbols(const Symbol* a, const Symbol* b); +bool compareSymbols(const Fortran::semantics::Symbol* a, const Fortran::semantics::Symbol* b); /** * @brief Generate mangled name from symbol @@ -81,7 +77,7 @@ bool compareSymbols(const Symbol* a, const Symbol* b); * @param sym * @return */ -std::string mangleSymbol(const Symbol* sym); +std::string mangleSymbol(const Fortran::semantics::Symbol* sym); /** * @brief Check if expression is an operator expression @@ -89,7 +85,7 @@ std::string mangleSymbol(const Symbol* sym); * @param e * @return */ -bool isOperator(const Expr* e); +bool isOperator(const Fortran::parser::Expr* e); /** * @brief Compare if expression match given intrinsic operator @@ -98,7 +94,8 @@ bool isOperator(const Expr* e); * @param op * @return */ -bool compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOperator op); +bool compareExprIntrinsicOperator(const Fortran::parser::Expr* expr, + Fortran::parser::DefinedOperator::IntrinsicOperator op); /** * @brief Check if binary operator expression @@ -106,7 +103,7 @@ bool compareExprIntrinsicOperator(const Expr* expr, DefinedOperator::IntrinsicOp * @param e * @return */ -bool isBinaryOperator(const Expr* e); +bool isBinaryOperator(const Fortran::parser::Expr* e); /** * @brief Check if unary operator expression @@ -114,7 +111,7 @@ bool isBinaryOperator(const Expr* e); * @param e * @return */ -bool isUnaryOperator(const Expr* e); +bool isUnaryOperator(const Fortran::parser::Expr* e); /** * @brief Get intrinsic operator from a variant of several operator types (RelationalOperator, LogicalOperator, @@ -124,7 +121,8 @@ bool isUnaryOperator(const Expr* e); * @param op * @return */ -DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const GenericKind& gk); +Fortran::parser::DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator( + const Fortran::semantics::GenericKind& gk); /** * @brief Get type symbol from a given symbol @@ -132,7 +130,7 @@ DefinedOperator::IntrinsicOperator variantGetIntrinsicOperator(const GenericKind * @param symbol * @return */ -const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); +const Fortran::semantics::Symbol* getTypeSymbolFromSymbol(const Fortran::semantics::Symbol* symbol); /** * @brief Searches the types vector for given symbol and returns pointers to vectors of type with derived types. The @@ -141,4 +139,5 @@ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol); * @param typeSymbol symbol to search for * @return vector with type and all derived types */ -std::vector findTypeWithDerivedTypes(const std::vector& types, const Symbol* symbol); +std::vector findTypeWithDerivedTypes(const std::vector& types, + const Fortran::semantics::Symbol* symbol); diff --git a/cgfcollector/include/Function.h b/cgfcollector/include/Function.h index 0a61a18a..6bb7f088 100644 --- a/cgfcollector/include/Function.h +++ b/cgfcollector/include/Function.h @@ -9,22 +9,21 @@ #include #include -using namespace Fortran::semantics; - struct function { struct dummyArg { - const Symbol* symbol; + const Fortran::semantics::Symbol* symbol; bool hasBeenInitialized = false; - explicit dummyArg(const Symbol* sym) : symbol(sym) {} - explicit dummyArg(const Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} + explicit dummyArg(const Fortran::semantics::Symbol* sym) : symbol(sym) {} + explicit dummyArg(const Fortran::semantics::Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} }; - const Symbol* symbol; // function symbol + const Fortran::semantics::Symbol* symbol; // function symbol std::vector dummyArgs; - explicit function(const Symbol* sym) : symbol(sym) {} - explicit function(const Symbol* sym, std::vector args) : symbol(sym), dummyArgs(std::move(args)) {} + explicit function(const Fortran::semantics::Symbol* sym) : symbol(sym) {} + explicit function(const Fortran::semantics::Symbol* sym, std::vector args) + : symbol(sym), dummyArgs(std::move(args)) {} - void addDummyArg(const Symbol* sym) { dummyArgs.emplace_back(sym); } + void addDummyArg(const Fortran::semantics::Symbol* sym) { dummyArgs.emplace_back(sym); } }; diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 957c706c..274b62b5 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -6,6 +6,13 @@ #pragma once +#include "Edge.h" +#include "FortranUtil.h" +#include "Function.h" +#include "PotentialFinalizer.h" +#include "Type.h" +#include "VariableTracking.h" + #include #include #include @@ -21,23 +28,9 @@ #include #include #include -#include -#include #include #include -#include "Edge.h" -#include "Function.h" -#include "PotentialFinalizer.h" -#include "Type.h" -#include "Util.h" -#include "VariableTracking.h" - -using namespace Fortran::parser; -using namespace Fortran::semantics; -using namespace Fortran::common; -using namespace metacg; - /** * @class ParseTreeVisitor * @brief Implements visitor methods to traverse parse tree and generate callgraph @@ -45,7 +38,7 @@ using namespace metacg; */ class ParseTreeVisitor { public: - ParseTreeVisitor(Callgraph* cg, std::string currentFileName) + ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName), edgeM(std::make_unique(edges)), @@ -72,7 +65,8 @@ class ParseTreeVisitor { * @param typeWithDerived * @param procedureSymbol */ - void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol); + void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, + const Fortran::semantics::Symbol* procedureSymbol); /** * @brief Adds edges and potential finalizers edges to cg. @@ -88,55 +82,55 @@ class ParseTreeVisitor { template void Post(const A&) {} - bool Pre(const MainProgram& p); - void Post(const MainProgram&); + bool Pre(const Fortran::parser::MainProgram& p); + void Post(const Fortran::parser::MainProgram&); - bool Pre(const FunctionSubprogram&); - void Post(const FunctionSubprogram&); - bool Pre(const SubroutineSubprogram&); - void Post(const SubroutineSubprogram&); + bool Pre(const Fortran::parser::FunctionSubprogram&); + void Post(const Fortran::parser::FunctionSubprogram&); + bool Pre(const Fortran::parser::SubroutineSubprogram&); + void Post(const Fortran::parser::SubroutineSubprogram&); /** * @brief Set hasBody field. * * @param e */ - void Post(const ExecutionPart& e); + void Post(const Fortran::parser::ExecutionPart& e); - void Post(const EntryStmt& e); + void Post(const Fortran::parser::EntryStmt& e); - void Post(const FunctionStmt& f); - void Post(const EndFunctionStmt&); - void Post(const SubroutineStmt& s); - void Post(const EndSubroutineStmt&); + void Post(const Fortran::parser::FunctionStmt& f); + void Post(const Fortran::parser::EndFunctionStmt&); + void Post(const Fortran::parser::SubroutineStmt& s); + void Post(const Fortran::parser::EndSubroutineStmt&); /** * @brief ProcedureDesignator: A procedure being called. Handles both cases a call with call statement and without. * * @param p */ - void Post(const ProcedureDesignator& p); + void Post(const Fortran::parser::ProcedureDesignator& p); /** * @brief Handle trackedVar assignment * * @param a */ - void Post(const AssignmentStmt& a); + void Post(const Fortran::parser::AssignmentStmt& a); /** * @brief Handle trackedVar assignment through allocate statement. * * @param a */ - void Post(const AllocateStmt& a); + void Post(const Fortran::parser::AllocateStmt& a); /** * @brief Mostly add potential finalizers for variables that get initialized through procedure arguments. * * @param c */ - void Post(const Call& c); + void Post(const Fortran::parser::Call& c); /** * @brief Mostly handles finalizers. Handles the different ways a variable can be parsed to a procedure and gets @@ -144,7 +138,7 @@ class ParseTreeVisitor { * * @param t */ - void Post(const TypeDeclarationStmt& t); + void Post(const Fortran::parser::TypeDeclarationStmt& t); // The following methods are for collecting types and their procedures. See type struct and vector. @@ -153,11 +147,11 @@ class ParseTreeVisitor { * * @return */ - bool Pre(const DerivedTypeDef&); + bool Pre(const Fortran::parser::DerivedTypeDef&); /** * @brief Type definiiton end */ - void Post(const DerivedTypeDef&); + void Post(const Fortran::parser::DerivedTypeDef&); /** * @brief Type stmt like type [, extends(...)] :: body (not exhaustive and not extends) @@ -165,35 +159,35 @@ class ParseTreeVisitor { * @param t * @return */ - bool Pre(const DerivedTypeStmt& t); + bool Pre(const Fortran::parser::DerivedTypeStmt& t); /** * @brief Type attributes like extends * * @param a */ - void Post(const TypeAttrSpec& a); + void Post(const Fortran::parser::TypeAttrSpec& a); /** * @brief Collect type bound procedures in derived type definitions * * @param s */ - void Post(const TypeBoundProcedureStmt& s); + void Post(const Fortran::parser::TypeBoundProcedureStmt& s); /** * @brief Collect defined operators in type definition (operator overloading) * * @param s */ - void Post(const TypeBoundGenericStmt& s); + void Post(const Fortran::parser::TypeBoundGenericStmt& s); // The following methods are for collecting defined operators in interface statements - bool Pre(const InterfaceStmt&); - bool Pre(const EndInterfaceStmt&); - void Post(const DefinedOperator& op); - void Post(const ProcedureStmt& p); + bool Pre(const Fortran::parser::InterfaceStmt&); + bool Pre(const Fortran::parser::EndInterfaceStmt&); + void Post(const Fortran::parser::DefinedOperator& op); + void Post(const Fortran::parser::ProcedureStmt& p); /** * @brief Parse operators in expressions @@ -201,23 +195,23 @@ class ParseTreeVisitor { * @param e * @return */ - bool Pre(const Expr& e); + bool Pre(const Fortran::parser::Expr& e); /** * @brief Post cleanup parse operators in expressions * * @param e */ - void Post(const Expr& e); + void Post(const Fortran::parser::Expr& e); /** * @brief Extract additional information from use statements * * @param u */ - void Post(const UseStmt& u); + void Post(const Fortran::parser::UseStmt& u); private: - Callgraph* cg; + metacg::Callgraph* cg; std::string currentFileName; std::unique_ptr edgeM; std::unique_ptr varTracking; @@ -237,12 +231,13 @@ class ParseTreeVisitor { std::vector types; // all types - std::vector, - std::vector>> + std::vector< + std::pair, + std::vector>> interfaceOperators; // all interface operators. First is either a symbol of a DefinedOpName or // IntrinsicOperator. Second is a vector procedure symbols, bound to that operator. - std::vector exprStmtWithOps; + std::vector exprStmtWithOps; std::vector trackedVars; // mainly used for destructor handling diff --git a/cgfcollector/include/PotentialFinalizer.h b/cgfcollector/include/PotentialFinalizer.h index 906412af..aeedde90 100644 --- a/cgfcollector/include/PotentialFinalizer.h +++ b/cgfcollector/include/PotentialFinalizer.h @@ -6,11 +6,11 @@ #pragma once +#include "Edge.h" + #include #include -#include "Edge.h" - struct potentialFinalizer { std::size_t argPos; std::string procedureCalled; diff --git a/cgfcollector/include/Type.h b/cgfcollector/include/Type.h index f13e235b..94a92709 100644 --- a/cgfcollector/include/Type.h +++ b/cgfcollector/include/Type.h @@ -9,16 +9,13 @@ #include #include #include -#include #include -using namespace Fortran::semantics; -using namespace Fortran::parser; -using namespace metacg; - struct type { - const Symbol* typeSymbol; - const Symbol* extendsFrom; - std::vector> procedures; // name(symbol) => optname(symbol) - std::vector> operators; // operator => name(symbol) + const Fortran::semantics::Symbol* typeSymbol; + const Fortran::semantics::Symbol* extendsFrom; + std::vector> + procedures; // name(symbol) => optname(symbol) + std::vector> + operators; // operator => name(symbol) }; diff --git a/cgfcollector/include/VariableTracking.h b/cgfcollector/include/VariableTracking.h index ff4e7024..d29f82be 100644 --- a/cgfcollector/include/VariableTracking.h +++ b/cgfcollector/include/VariableTracking.h @@ -6,24 +6,22 @@ #pragma once -#include - #include "Edge.h" #include "Function.h" #include "Type.h" -#include "Util.h" -using namespace Fortran::semantics; +#include struct trackedVar { - const Symbol* var; - const Symbol* procedure; // procedure in which var was defined + const Fortran::semantics::Symbol* var; + const Fortran::semantics::Symbol* procedure; // procedure in which var was defined bool hasBeenInitialized = false; bool addFinalizers = false; - trackedVar(const Symbol* var, const Symbol* procedure) + trackedVar(const Fortran::semantics::Symbol* var, const Fortran::semantics::Symbol* procedure) : var(var), procedure(procedure), hasBeenInitialized(false), addFinalizers(false) {} - trackedVar(const Symbol* var, const Symbol* procedure, bool initialized, bool addFinalizers) + trackedVar(const Fortran::semantics::Symbol* var, const Fortran::semantics::Symbol* procedure, bool initialized, + bool addFinalizers) : var(var), procedure(procedure), hasBeenInitialized(initialized), addFinalizers(addFinalizers) {} }; @@ -39,7 +37,8 @@ struct variableTracking { * @param sourceName * @return trackedVar* or nullptr if not found */ - trackedVar* getTrackedVarFromSourceName(const Symbol* currentFunctionSymbol, SourceName sourceName); + trackedVar* getTrackedVarFromSourceName(const Fortran::semantics::Symbol* currentFunctionSymbol, + Fortran::semantics::SourceName sourceName); /** * @brief Search trackedVars for a canditate and set it as initialized. @@ -47,7 +46,8 @@ struct variableTracking { * @param currentFunctionSymbol * @param sourceName */ - void handleTrackedVarAssignment(const Symbol* currentFunctionSymbol, SourceName sourceName); + void handleTrackedVarAssignment(const Fortran::semantics::Symbol* currentFunctionSymbol, + Fortran::semantics::SourceName sourceName); /** * @brief Is called at the end of a function/subroutine end statement. It checks trackedVars for any initialized @@ -56,7 +56,7 @@ struct variableTracking { * @param currentFunctionSymbol * @param edgeM TODO: remove dep */ - void handleTrackedVars(const Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); + void handleTrackedVars(const Fortran::semantics::Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); /** * @brief Register a variable for tracking. @@ -71,7 +71,7 @@ struct variableTracking { * * @param procedureSymbol */ - void removeTrackedVars(const Symbol* procedureSymbol); + void removeTrackedVars(const Fortran::semantics::Symbol* procedureSymbol); private: std::vector& trackedVars; diff --git a/cgfcollector/src/Edge.cpp b/cgfcollector/src/Edge.cpp index 10a9b6f7..225a08e9 100644 --- a/cgfcollector/src/Edge.cpp +++ b/cgfcollector/src/Edge.cpp @@ -6,6 +6,10 @@ #include "Edge.h" +using namespace Fortran::semantics; +using namespace Fortran::parser; +using namespace metacg; + std::vector edgeManager::getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { std::vector edges; diff --git a/cgfcollector/src/Util.cpp b/cgfcollector/src/FortranUtil.cpp similarity index 98% rename from cgfcollector/src/Util.cpp rename to cgfcollector/src/FortranUtil.cpp index 2c7d56cc..dcd7429a 100644 --- a/cgfcollector/src/Util.cpp +++ b/cgfcollector/src/FortranUtil.cpp @@ -1,10 +1,15 @@ /** - * File: Util.cpp + * File: FortranUtil.cpp * License: Part of the MetaCG project. Licensed under BSD 3 clause license. See LICENSE.txt file at * https://github.com/tudasc/metacg/LICENSE.txt */ -#include "Util.h" +#include "FortranUtil.h" + +using namespace Fortran::semantics; +using namespace Fortran::parser; +using namespace Fortran::common; +using namespace metacg; bool compareSymbols(const Symbol* a, const Symbol* b) { if (a == b) diff --git a/cgfcollector/src/Main.cpp b/cgfcollector/src/Main.cpp index 0472965b..014efb26 100644 --- a/cgfcollector/src/Main.cpp +++ b/cgfcollector/src/Main.cpp @@ -11,6 +11,7 @@ using namespace metacg; using namespace metacg::graph; using namespace metacg::io; +using namespace Fortran::parser; static MCGManager& mcgManager = MCGManager::get(); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 2df6cd83..24c4099a 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -6,6 +6,11 @@ #include "ParseTreeVisitor.h" +using namespace Fortran::parser; +using namespace Fortran::semantics; +using namespace Fortran::common; +using namespace metacg; + template void ParseTreeVisitor::handleFuncSubStmt(const FunctionStmt&); template void ParseTreeVisitor::handleFuncSubStmt(const SubroutineStmt&); diff --git a/cgfcollector/src/VariableTracking.cpp b/cgfcollector/src/VariableTracking.cpp index 85548f25..4e6201cc 100644 --- a/cgfcollector/src/VariableTracking.cpp +++ b/cgfcollector/src/VariableTracking.cpp @@ -6,6 +6,11 @@ #include "VariableTracking.h" +#include "FortranUtil.h" + +using namespace Fortran::semantics; +using namespace metacg; + trackedVar* variableTracking::getTrackedVarFromSourceName(const Symbol* currentFunctionSymbol, SourceName sourceName) { auto anyTrackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { return t.var->name() == sourceName; }); From 056dfe6055e11c64aaea28adeab41b5ed72b52f8 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 15:59:51 +0100 Subject: [PATCH 128/130] use upper case type names --- cgfcollector/include/Edge.h | 34 +++++++-------- cgfcollector/include/FortranUtil.h | 2 +- cgfcollector/include/Function.h | 14 +++--- cgfcollector/include/ParseTreeVisitor.h | 22 +++++----- cgfcollector/include/PotentialFinalizer.h | 8 ++-- cgfcollector/include/Type.h | 2 +- cgfcollector/include/VariableTracking.h | 22 +++++----- cgfcollector/src/Edge.cpp | 28 ++++++------ cgfcollector/src/FortranUtil.cpp | 12 +++--- cgfcollector/src/ParseTreeVisitor.cpp | 52 +++++++++++------------ cgfcollector/src/VariableTracking.cpp | 26 ++++++------ 11 files changed, 111 insertions(+), 111 deletions(-) diff --git a/cgfcollector/include/Edge.h b/cgfcollector/include/Edge.h index 8554d00d..1ef03242 100644 --- a/cgfcollector/include/Edge.h +++ b/cgfcollector/include/Edge.h @@ -15,42 +15,42 @@ #include #include -struct edge { +struct Edge { std::string caller; std::string callee; - edge(std::string caller, std::string callee) : caller(std::move(caller)), callee(std::move(callee)) {} + Edge(std::string caller, std::string callee) : caller(std::move(caller)), callee(std::move(callee)) {} - bool operator==(const edge& other) const { return caller == other.caller && callee == other.callee; } - bool operator<(const edge& other) const { + bool operator==(const Edge& other) const { return caller == other.caller && callee == other.callee; } + bool operator<(const Edge& other) const { return caller < other.caller || (caller == other.caller && callee < other.callee); } }; -struct edgeSymbol { +struct EdgeSymbol { const Fortran::semantics::Symbol* caller; const Fortran::semantics::Symbol* callee; - edgeSymbol(const Fortran::semantics::Symbol* caller, const Fortran::semantics::Symbol* callee) + EdgeSymbol(const Fortran::semantics::Symbol* caller, const Fortran::semantics::Symbol* callee) : caller(caller), callee(callee) {} - bool operator==(const edgeSymbol& other) const { return caller == other.caller && callee == other.callee; } - bool operator<(const edgeSymbol& other) const { + bool operator==(const EdgeSymbol& other) const { return caller == other.caller && callee == other.callee; } + bool operator<(const EdgeSymbol& other) const { return caller < other.caller || (caller == other.caller && callee < other.callee); } }; -struct edgeManager { - std::vector& edges; +struct EdgeManager { + std::vector& edges; - edgeManager(std::vector& edges) : edges(edges) {} + EdgeManager(std::vector& edges) : edges(edges) {} - void addEdge(const edgeSymbol& e, bool debug = true); + void addEdge(const EdgeSymbol& e, bool debug = true); void addEdge(const Fortran::semantics::Symbol* caller, const Fortran::semantics::Symbol* callee, bool debug = true); - void addEdge(const edge& e, bool debug = true); + void addEdge(const Edge& e, bool debug = true); void addEdge(const std::string& caller, const std::string& callee, bool debug = true); - void addEdges(const std::vector& newEdges, bool debug = true); - void addEdges(const std::vector& newEdges, bool debug = true); + void addEdges(const std::vector& newEdges, bool debug = true); + void addEdges(const std::vector& newEdges, bool debug = true); /** * @brief For a given symbol, returns a list of edges from the current function to all finalizers of that type. @@ -59,7 +59,7 @@ struct edgeManager { * @param currentFunctionSymbol * @param symbol */ - std::vector getEdgesForFinalizers(const std::vector& types, + std::vector getEdgesForFinalizers(const std::vector& types, const Fortran::semantics::Symbol* currentFunctionSymbol, const Fortran::semantics::Symbol* symbol); /** @@ -69,6 +69,6 @@ struct edgeManager { * @param currentFunctionSymbol * @param symbol */ - void addEdgesForFinalizers(const std::vector& types, const Fortran::semantics::Symbol* currentFunctionSymbol, + void addEdgesForFinalizers(const std::vector& types, const Fortran::semantics::Symbol* currentFunctionSymbol, const Fortran::semantics::Symbol* symbol); }; diff --git a/cgfcollector/include/FortranUtil.h b/cgfcollector/include/FortranUtil.h index d5e19388..035d516c 100644 --- a/cgfcollector/include/FortranUtil.h +++ b/cgfcollector/include/FortranUtil.h @@ -139,5 +139,5 @@ const Fortran::semantics::Symbol* getTypeSymbolFromSymbol(const Fortran::semanti * @param typeSymbol symbol to search for * @return vector with type and all derived types */ -std::vector findTypeWithDerivedTypes(const std::vector& types, +std::vector findTypeWithDerivedTypes(const std::vector& types, const Fortran::semantics::Symbol* symbol); diff --git a/cgfcollector/include/Function.h b/cgfcollector/include/Function.h index 6bb7f088..5e9d8cd7 100644 --- a/cgfcollector/include/Function.h +++ b/cgfcollector/include/Function.h @@ -9,20 +9,20 @@ #include #include -struct function { - struct dummyArg { +struct Function { + struct DummyArg { const Fortran::semantics::Symbol* symbol; bool hasBeenInitialized = false; - explicit dummyArg(const Fortran::semantics::Symbol* sym) : symbol(sym) {} - explicit dummyArg(const Fortran::semantics::Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} + explicit DummyArg(const Fortran::semantics::Symbol* sym) : symbol(sym) {} + explicit DummyArg(const Fortran::semantics::Symbol* sym, bool init) : symbol(sym), hasBeenInitialized(init) {} }; const Fortran::semantics::Symbol* symbol; // function symbol - std::vector dummyArgs; + std::vector dummyArgs; - explicit function(const Fortran::semantics::Symbol* sym) : symbol(sym) {} - explicit function(const Fortran::semantics::Symbol* sym, std::vector args) + explicit Function(const Fortran::semantics::Symbol* sym) : symbol(sym) {} + explicit Function(const Fortran::semantics::Symbol* sym, std::vector args) : symbol(sym), dummyArgs(std::move(args)) {} void addDummyArg(const Fortran::semantics::Symbol* sym) { dummyArgs.emplace_back(sym); } diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 274b62b5..3da6f3cf 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -41,8 +41,8 @@ class ParseTreeVisitor { ParseTreeVisitor(metacg::Callgraph* cg, std::string currentFileName) : cg(cg), currentFileName(currentFileName), - edgeM(std::make_unique(edges)), - varTracking(std::make_unique(trackedVars, types, functions)) {}; + edgeM(std::make_unique(edges)), + varTracking(std::make_unique(trackedVars, types, functions)) {}; /** * @brief Collects function/subroutine statements (begin) and their dummy args. @@ -65,7 +65,7 @@ class ParseTreeVisitor { * @param typeWithDerived * @param procedureSymbol */ - void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, + void addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Fortran::semantics::Symbol* procedureSymbol); /** @@ -213,8 +213,8 @@ class ParseTreeVisitor { private: metacg::Callgraph* cg; std::string currentFileName; - std::unique_ptr edgeM; - std::unique_ptr varTracking; + std::unique_ptr edgeM; + std::unique_ptr varTracking; bool inFunctionOrSubroutineSubProgram = false; bool inMainProgram = false; @@ -223,13 +223,13 @@ class ParseTreeVisitor { bool inInterfaceStmtDefinedOperator = false; bool inInterfaceSpecification = false; - std::vector edges; // added to cg in postProcess step + std::vector edges; // added to cg in postProcess step - std::vector functions; // all functions - std::vector currentFunctions; // intended as a stack. It holds the current function symbol and its dummy + std::vector functions; // all functions + std::vector currentFunctions; // intended as a stack. It holds the current function symbol and its dummy // args when the AST walker is in the respective function. - std::vector types; // all types + std::vector types; // all types std::vector< std::pair, @@ -239,7 +239,7 @@ class ParseTreeVisitor { std::vector exprStmtWithOps; - std::vector trackedVars; // mainly used for destructor handling + std::vector trackedVars; // mainly used for destructor handling - std::vector potentialFinalizers; + std::vector potentialFinalizers; }; diff --git a/cgfcollector/include/PotentialFinalizer.h b/cgfcollector/include/PotentialFinalizer.h index aeedde90..e3d6cf8b 100644 --- a/cgfcollector/include/PotentialFinalizer.h +++ b/cgfcollector/include/PotentialFinalizer.h @@ -11,13 +11,13 @@ #include #include -struct potentialFinalizer { +struct PotentialFinalizer { std::size_t argPos; std::string procedureCalled; - std::vector finalizerEdges; + std::vector finalizerEdges; - explicit potentialFinalizer(std::size_t pos, std::string procCalled) + explicit PotentialFinalizer(std::size_t pos, std::string procCalled) : argPos(pos), procedureCalled(std::move(procCalled)) {} - void addFinalizerEdge(const edge& e) { finalizerEdges.emplace_back(e); } + void addFinalizerEdge(const Edge& e) { finalizerEdges.emplace_back(e); } }; diff --git a/cgfcollector/include/Type.h b/cgfcollector/include/Type.h index 94a92709..d1c528c6 100644 --- a/cgfcollector/include/Type.h +++ b/cgfcollector/include/Type.h @@ -11,7 +11,7 @@ #include #include -struct type { +struct Type { const Fortran::semantics::Symbol* typeSymbol; const Fortran::semantics::Symbol* extendsFrom; std::vector> diff --git a/cgfcollector/include/VariableTracking.h b/cgfcollector/include/VariableTracking.h index d29f82be..11342402 100644 --- a/cgfcollector/include/VariableTracking.h +++ b/cgfcollector/include/VariableTracking.h @@ -12,22 +12,22 @@ #include -struct trackedVar { +struct TrackedVar { const Fortran::semantics::Symbol* var; const Fortran::semantics::Symbol* procedure; // procedure in which var was defined bool hasBeenInitialized = false; bool addFinalizers = false; - trackedVar(const Fortran::semantics::Symbol* var, const Fortran::semantics::Symbol* procedure) + TrackedVar(const Fortran::semantics::Symbol* var, const Fortran::semantics::Symbol* procedure) : var(var), procedure(procedure), hasBeenInitialized(false), addFinalizers(false) {} - trackedVar(const Fortran::semantics::Symbol* var, const Fortran::semantics::Symbol* procedure, bool initialized, + TrackedVar(const Fortran::semantics::Symbol* var, const Fortran::semantics::Symbol* procedure, bool initialized, bool addFinalizers) : var(var), procedure(procedure), hasBeenInitialized(initialized), addFinalizers(addFinalizers) {} }; -struct variableTracking { +struct VariableTracking { public: - variableTracking(std::vector& trackedVars, std::vector& types, std::vector& functions) + VariableTracking(std::vector& trackedVars, std::vector& types, std::vector& functions) : trackedVars(trackedVars), types(types), functions(functions) {} /** @@ -37,7 +37,7 @@ struct variableTracking { * @param sourceName * @return trackedVar* or nullptr if not found */ - trackedVar* getTrackedVarFromSourceName(const Fortran::semantics::Symbol* currentFunctionSymbol, + TrackedVar* getTrackedVarFromSourceName(const Fortran::semantics::Symbol* currentFunctionSymbol, Fortran::semantics::SourceName sourceName); /** @@ -56,14 +56,14 @@ struct variableTracking { * @param currentFunctionSymbol * @param edgeM TODO: remove dep */ - void handleTrackedVars(const Fortran::semantics::Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); + void handleTrackedVars(const Fortran::semantics::Symbol* currentFunctionSymbol, std::unique_ptr& edgeM); /** * @brief Register a variable for tracking. * * @param var */ - void addTrackedVar(trackedVar var); + void addTrackedVar(TrackedVar var); /** * @brief Remove all tracked variables that are not needed anymore after the function/subroutine end statement. Should @@ -74,7 +74,7 @@ struct variableTracking { void removeTrackedVars(const Fortran::semantics::Symbol* procedureSymbol); private: - std::vector& trackedVars; - std::vector& types; - std::vector& functions; + std::vector& trackedVars; + std::vector& types; + std::vector& functions; }; diff --git a/cgfcollector/src/Edge.cpp b/cgfcollector/src/Edge.cpp index 225a08e9..8ed29895 100644 --- a/cgfcollector/src/Edge.cpp +++ b/cgfcollector/src/Edge.cpp @@ -10,13 +10,13 @@ using namespace Fortran::semantics; using namespace Fortran::parser; using namespace metacg; -std::vector edgeManager::getEdgesForFinalizers(const std::vector& types, +std::vector EdgeManager::getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { - std::vector edges; + std::vector edges; - std::vector typePtrs = findTypeWithDerivedTypes(types, symbol); + std::vector typePtrs = findTypeWithDerivedTypes(types, symbol); - for (const type* type : typePtrs) { + for (const Type* type : typePtrs) { const Symbol* typeSymbol = type->typeSymbol; const DerivedTypeDetails* details = std::get_if(&typeSymbol->details()); @@ -32,16 +32,16 @@ std::vector edgeManager::getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, +void EdgeManager::addEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { - for (const edgeSymbol& edge : getEdgesForFinalizers(types, currentFunctionSymbol, symbol)) { + for (const EdgeSymbol& edge : getEdgesForFinalizers(types, currentFunctionSymbol, symbol)) { addEdge(edge, false); MCGLogger::logDebug("Add edge for finalizer: {} ({}) -> {} ({})", mangleSymbol(edge.caller), fmt::ptr(edge.caller), mangleSymbol(edge.callee), fmt::ptr(edge.callee)); } } -void edgeManager::addEdge(const edgeSymbol& e, bool debug) { +void EdgeManager::addEdge(const EdgeSymbol& e, bool debug) { edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); if (debug) { MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), @@ -49,7 +49,7 @@ void edgeManager::addEdge(const edgeSymbol& e, bool debug) { } } -void edgeManager::addEdge(const Symbol* caller, const Symbol* callee, bool debug) { +void EdgeManager::addEdge(const Symbol* caller, const Symbol* callee, bool debug) { edges.emplace_back(mangleSymbol(caller), mangleSymbol(callee)); if (debug) { MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(caller), fmt::ptr(caller), mangleSymbol(callee), @@ -57,28 +57,28 @@ void edgeManager::addEdge(const Symbol* caller, const Symbol* callee, bool debug } } -void edgeManager::addEdge(const edge& e, bool debug) { +void EdgeManager::addEdge(const Edge& e, bool debug) { edges.emplace_back(e.caller, e.callee); if (debug) { MCGLogger::logDebug("Add edge: {} -> {}", e.caller, e.callee); } } -void edgeManager::addEdge(const std::string& caller, const std::string& callee, bool debug) { +void EdgeManager::addEdge(const std::string& caller, const std::string& callee, bool debug) { edges.emplace_back(caller, callee); if (debug) { MCGLogger::logDebug("Add edge: {} -> {}", caller, callee); } } -void edgeManager::addEdges(const std::vector& newEdges, bool debug) { - for (const edge& e : newEdges) { +void EdgeManager::addEdges(const std::vector& newEdges, bool debug) { + for (const Edge& e : newEdges) { addEdge(e, debug); } } -void edgeManager::addEdges(const std::vector& newEdges, bool debug) { - for (const edgeSymbol& e : newEdges) { +void EdgeManager::addEdges(const std::vector& newEdges, bool debug) { + for (const EdgeSymbol& e : newEdges) { addEdge(e, debug); } } diff --git a/cgfcollector/src/FortranUtil.cpp b/cgfcollector/src/FortranUtil.cpp index dcd7429a..1581ec07 100644 --- a/cgfcollector/src/FortranUtil.cpp +++ b/cgfcollector/src/FortranUtil.cpp @@ -225,8 +225,8 @@ const Symbol* getTypeSymbolFromSymbol(const Symbol* symbol) { return typeSymbol; } -std::vector findTypeWithDerivedTypes(const std::vector& types, const Symbol* symbol) { - std::vector typesWithDerived; +std::vector findTypeWithDerivedTypes(const std::vector& types, const Symbol* symbol) { + std::vector typesWithDerived; std::unordered_set visited; const Symbol* typeSymbol = getTypeSymbolFromSymbol(symbol); @@ -235,7 +235,7 @@ std::vector findTypeWithDerivedTypes(const std::vector& types } auto findTypeIt = - std::find_if(types.begin(), types.end(), [&typeSymbol](const type& t) { return t.typeSymbol == typeSymbol; }); + std::find_if(types.begin(), types.end(), [&typeSymbol](const Type& t) { return t.typeSymbol == typeSymbol; }); if (findTypeIt == types.end()) { return typesWithDerived; @@ -245,8 +245,8 @@ std::vector findTypeWithDerivedTypes(const std::vector& types visited.insert(typeSymbol); // collect descendants - std::function collectDescendants = [&](const type* parent) { - for (const type& t : types) { + std::function collectDescendants = [&](const Type* parent) { + for (const Type& t : types) { if (t.extendsFrom == parent->typeSymbol && !visited.count(t.typeSymbol)) { visited.insert(t.typeSymbol); typesWithDerived.push_back(&t); @@ -267,7 +267,7 @@ std::vector findTypeWithDerivedTypes(const std::vector& types } auto currentTypeIt = std::find_if(types.begin(), types.end(), - [&](const type& t) { return compareSymbols(t.typeSymbol, currentExtendsFrom); }); + [&](const Type& t) { return compareSymbols(t.typeSymbol, currentExtendsFrom); }); if (currentTypeIt == types.end()) { MCGLogger::logError("Error: Types array (extendsFrom) field entry for \"" + diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 24c4099a..f4df18c7 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -17,8 +17,8 @@ template void ParseTreeVisitor::handleFuncSubStmt(const Subrouti template void ParseTreeVisitor::handleFuncSubStmt(const T& stmt) { if (const Symbol* sym = std::get(stmt.t).symbol) { - currentFunctions.emplace_back(sym, std::vector()); - functions.emplace_back(sym, std::vector()); + currentFunctions.emplace_back(sym, std::vector()); + functions.emplace_back(sym, std::vector()); cg->getOrInsertNode(mangleSymbol(sym), currentFileName, false, false); MCGLogger::logDebug("Add node: {} ({})", mangleSymbol(sym), fmt::ptr(sym)); @@ -33,9 +33,9 @@ void ParseTreeVisitor::handleEndFuncSubStmt() { } } -void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, +void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vector typeWithDerived, const Symbol* procedureSymbol) { - for (const type* t : typeWithDerived) { + for (const Type* t : typeWithDerived) { auto procIt = std::find_if(t->procedures.begin(), t->procedures.end(), [&procedureSymbol](const auto& p) { return p.first->name() == procedureSymbol->name(); }); @@ -50,9 +50,9 @@ void ParseTreeVisitor::addEdgesForProducesAndDerivedTypes(std::vectorhasBeenInitialized) continue; - for (const edge& edge : pf.finalizerEdges) { + for (const Edge& edge : pf.finalizerEdges) { edgeM->addEdge(edge, false); MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.caller, edge.callee); } @@ -73,7 +73,7 @@ void ParseTreeVisitor::postProcess() { edges.erase(it, edges.end()); // add edges - for (const edge& edge : edges) { + for (const Edge& edge : edges) { const CgNode& callerNode = cg->getOrInsertNode(edge.caller); const CgNode& calleeNode = cg->getOrInsertNode(edge.callee); @@ -91,7 +91,7 @@ bool ParseTreeVisitor::Pre(const MainProgram& p) { return true; const Symbol* currentFunctionSymbol = - currentFunctions.emplace_back(maybeStmt->statement.v.symbol, std::vector()).symbol; + currentFunctions.emplace_back(maybeStmt->statement.v.symbol, std::vector()).symbol; cg->getOrInsertNode(mangleSymbol(currentFunctionSymbol), currentFileName, false, false); MCGLogger::logDebug("\nIn main program: {} ({})", mangleSymbol(currentFunctionSymbol), @@ -161,7 +161,7 @@ void ParseTreeVisitor::Post(const FunctionStmt& f) { const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; auto functionsIt = std::find_if(functions.begin(), functions.end(), - [&](const function& func) { return func.symbol == currentFunctionSymbol; }); + [&](const Function& func) { return func.symbol == currentFunctionSymbol; }); // collect function arguments const std::list& name_list = std::get>(f.t); @@ -193,7 +193,7 @@ void ParseTreeVisitor::Post(const SubroutineStmt& s) { const Symbol* currentFunctionSymbol = currentFunctions.back().symbol; auto functionsIt = std::find_if(functions.begin(), functions.end(), - [&](const function& func) { return func.symbol == currentFunctionSymbol; }); + [&](const Function& func) { return func.symbol == currentFunctionSymbol; }); // collect subroutine arguments (dummy args) const std::list* dummyArg_list = &std::get>(s.t); @@ -304,13 +304,13 @@ void ParseTreeVisitor::Post(const Call& c) { // handle finalizers for allocatable vars. // This collects info from variables that are parse as arguments to functions. Function are defined below the // execution part, so this need to be handled at the end of the parse tree traversal. - trackedVar* trackedVar = varTracking->getTrackedVarFromSourceName(currentFunctionSymbol, name->symbol->name()); + TrackedVar* trackedVar = varTracking->getTrackedVarFromSourceName(currentFunctionSymbol, name->symbol->name()); if (!trackedVar) continue; MCGLogger::logDebug("Add potential finalizers for var: {} ({})", name->symbol->name(), fmt::ptr(name->symbol)); - potentialFinalizer& pf = potentialFinalizers.emplace_back(argPos, mangleSymbol(procName->symbol)); - for (const edgeSymbol& edge : edgeM->getEdgesForFinalizers(types, currentFunctionSymbol, trackedVar->var)) { + PotentialFinalizer& pf = potentialFinalizers.emplace_back(argPos, mangleSymbol(procName->symbol)); + for (const EdgeSymbol& edge : edgeM->getEdgesForFinalizers(types, currentFunctionSymbol, trackedVar->var)) { pf.addFinalizerEdge({mangleSymbol(edge.caller), mangleSymbol(edge.callee)}); MCGLogger::logDebug(" Potential finalizer edge: {} -> {}", mangleSymbol(edge.caller), mangleSymbol(edge.callee)); @@ -331,10 +331,10 @@ void ParseTreeVisitor::Post(const TypeDeclarationStmt& t) { // skip if name is an argument to a function or subroutine bool isFunctionArg = false; - std::vector& currentDummyArgs = currentFunctions.back().dummyArgs; + std::vector& currentDummyArgs = currentFunctions.back().dummyArgs; if (!currentDummyArgs.empty()) { auto it = std::find_if(currentDummyArgs.begin(), currentDummyArgs.end(), - [&name](const function::dummyArg& dummyArg) { return dummyArg.symbol == name.symbol; }); + [&name](const Function::DummyArg& dummyArg) { return dummyArg.symbol == name.symbol; }); if (it != currentDummyArgs.end()) isFunctionArg = true; @@ -405,7 +405,7 @@ bool ParseTreeVisitor::Pre(const DerivedTypeStmt& t) { if (!inDerivedTypeDef) return true; - type& currentType = types.back(); + Type& currentType = types.back(); const Name& name = std::get(t.t); currentType.typeSymbol = name.symbol; @@ -418,7 +418,7 @@ void ParseTreeVisitor::Post(const TypeAttrSpec& a) { if (!inDerivedTypeDef) return; - type& currentType = types.back(); + Type& currentType = types.back(); if (std::holds_alternative(a.u)) { const TypeAttrSpec::Extends& extends = std::get(a.u); currentType.extendsFrom = extends.v.symbol; @@ -443,7 +443,7 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { return; } - type& currentType = types.back(); + Type& currentType = types.back(); currentType.procedures.emplace_back(name.symbol, optname->symbol); MCGLogger::logDebug("Add procedure: {} ({}) -> {} ({})", name.symbol->name(), fmt::ptr(name.symbol), @@ -457,7 +457,7 @@ void ParseTreeVisitor::Post(const TypeBoundProcedureStmt& s) { if (!n.symbol) return; - type& currentType = types.back(); + Type& currentType = types.back(); currentType.procedures.emplace_back(n.symbol, n.symbol); MCGLogger::logDebug("Add procedure: {} ({}) -> {} ({})", n.symbol->name(), fmt::ptr(n.symbol), n.symbol->name(), @@ -476,7 +476,7 @@ void ParseTreeVisitor::Post(const TypeBoundGenericStmt& s) { std::get_if(&definedOperator->u)) { const std::list& names = std::get>(s.t); - type& currentType = types.back(); + Type& currentType = types.back(); for (const Name& name : names) { if (!name.symbol) @@ -584,7 +584,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { // if unary, add potential unary operators. Same for binary operators. auto functionIt = - std::find_if(functions.begin(), functions.end(), [&](const function& f) { return f.symbol == sym; }); + std::find_if(functions.begin(), functions.end(), [&](const Function& f) { return f.symbol == sym; }); if (functionIt != functions.end()) { if ((!isUnaryOp || functionIt->dummyArgs.size() != 1) && (!isBinaryOp || functionIt->dummyArgs.size() != 2)) { continue; @@ -597,9 +597,9 @@ bool ParseTreeVisitor::Pre(const Expr& e) { // search in derived types - std::vector typeWithDerived = findTypeWithDerivedTypes(types, name->symbol); + std::vector typeWithDerived = findTypeWithDerivedTypes(types, name->symbol); - for (const type* t : typeWithDerived) { + for (const Type* t : typeWithDerived) { auto opIt = std::find_if(t->operators.begin(), t->operators.end(), [&](const auto& p) { return compareExprIntrinsicOperator(e, p.first); }); if (opIt == t->operators.end()) @@ -608,7 +608,7 @@ bool ParseTreeVisitor::Pre(const Expr& e) { const Symbol* funcSymbol = opIt->second; bool skipSelfCall = false; - for (const type* t : typeWithDerived) { + for (const Type* t : typeWithDerived) { auto procIt = std::find_if(t->procedures.begin(), t->procedures.end(), [&funcSymbol](const auto& p) { return p.first->name() == funcSymbol->name(); }); if (procIt == t->procedures.end()) @@ -722,7 +722,7 @@ void ParseTreeVisitor::Post(const UseStmt& u) { if (!details->isFunction() && !details->isInterface()) // function and function dummy definition in interface continue; - std::vector dummyArgs; + std::vector dummyArgs; for (const Symbol* arg : details->dummyArgs()) { dummyArgs.emplace_back(arg, false); } diff --git a/cgfcollector/src/VariableTracking.cpp b/cgfcollector/src/VariableTracking.cpp index 4e6201cc..17b23afd 100644 --- a/cgfcollector/src/VariableTracking.cpp +++ b/cgfcollector/src/VariableTracking.cpp @@ -11,14 +11,14 @@ using namespace Fortran::semantics; using namespace metacg; -trackedVar* variableTracking::getTrackedVarFromSourceName(const Symbol* currentFunctionSymbol, SourceName sourceName) { +TrackedVar* VariableTracking::getTrackedVarFromSourceName(const Symbol* currentFunctionSymbol, SourceName sourceName) { auto anyTrackedVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), - [&](const trackedVar& t) { return t.var->name() == sourceName; }); + [&](const TrackedVar& t) { return t.var->name() == sourceName; }); if (anyTrackedVarIt == trackedVars.end()) return nullptr; // find local variable with the same name in the current function scope (shadowed) - auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { + auto localVarIt = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const TrackedVar& t) { return t.var->name() == sourceName && t.procedure == currentFunctionSymbol; }); @@ -26,8 +26,8 @@ trackedVar* variableTracking::getTrackedVarFromSourceName(const Symbol* currentF return (localVarIt != trackedVars.end()) ? &(*localVarIt) : &(*anyTrackedVarIt); } -void variableTracking::handleTrackedVarAssignment(const Symbol* currentFunctionSymbol, SourceName sourceName) { - trackedVar* trackedVar = getTrackedVarFromSourceName(currentFunctionSymbol, sourceName); +void VariableTracking::handleTrackedVarAssignment(const Symbol* currentFunctionSymbol, SourceName sourceName) { + TrackedVar* trackedVar = getTrackedVarFromSourceName(currentFunctionSymbol, sourceName); if (!trackedVar) return; @@ -36,12 +36,12 @@ void variableTracking::handleTrackedVarAssignment(const Symbol* currentFunctionS MCGLogger::logDebug("Tracked var assigned: {} ({})", trackedVar->var->name(), fmt::ptr(trackedVar->var)); } -void variableTracking::handleTrackedVars(const Symbol* currentFunctionSymbol, std::unique_ptr& edgeM) { +void VariableTracking::handleTrackedVars(const Symbol* currentFunctionSymbol, std::unique_ptr& edgeM) { if (mangleSymbol(currentFunctionSymbol) != "_QQmain") { if (!trackedVars.empty()) MCGLogger::logDebug("Handle tracked vars for function"); - for (trackedVar& trackedVar : trackedVars) { + for (TrackedVar& trackedVar : trackedVars) { if (!trackedVar.hasBeenInitialized) continue; if (trackedVar.procedure != currentFunctionSymbol) @@ -54,10 +54,10 @@ void variableTracking::handleTrackedVars(const Symbol* currentFunctionSymbol, st // set init on dummy function args auto functionIt = std::find_if(functions.begin(), functions.end(), - [&](const function& f) { return f.symbol == currentFunctionSymbol; }); + [&](const Function& f) { return f.symbol == currentFunctionSymbol; }); if (functionIt != functions.end()) { auto dummyArgIt = std::find_if(functionIt->dummyArgs.begin(), functionIt->dummyArgs.end(), - [&](const function::dummyArg& d) { return d.symbol == trackedVar.var; }); + [&](const Function::DummyArg& d) { return d.symbol == trackedVar.var; }); if (dummyArgIt != functionIt->dummyArgs.end()) { dummyArgIt->hasBeenInitialized = true; } @@ -69,8 +69,8 @@ void variableTracking::handleTrackedVars(const Symbol* currentFunctionSymbol, st removeTrackedVars(currentFunctionSymbol); } -void variableTracking::addTrackedVar(trackedVar var) { - auto it = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const trackedVar& t) { return t.var == var.var; }); +void VariableTracking::addTrackedVar(TrackedVar var) { + auto it = std::find_if(trackedVars.begin(), trackedVars.end(), [&](const TrackedVar& t) { return t.var == var.var; }); if (it != trackedVars.end()) { // update info it->addFinalizers = var.addFinalizers; @@ -83,8 +83,8 @@ void variableTracking::addTrackedVar(trackedVar var) { MCGLogger::logDebug("Add tracking for variable: {} ({})", var.var->name(), fmt::ptr(var.var)); } -void variableTracking::removeTrackedVars(const Symbol* procedureSymbol) { +void VariableTracking::removeTrackedVars(const Symbol* procedureSymbol) { trackedVars.erase(std::remove_if(trackedVars.begin(), trackedVars.end(), - [&](const trackedVar& t) { return t.procedure == procedureSymbol; }), + [&](const TrackedVar& t) { return t.procedure == procedureSymbol; }), trackedVars.end()); } From ed5d7fff309b15379a346a51eb542a70a2bd2090 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 16:14:45 +0100 Subject: [PATCH 129/130] comment style --- cgfcollector/include/ParseTreeVisitor.h | 23 +++++++++++++++-------- cgfcollector/include/Type.h | 9 ++++++--- cgfcollector/include/VariableTracking.h | 5 ++++- cgfcollector/src/FortranUtil.cpp | 8 ++++++-- 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 3da6f3cf..1403f83a 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -223,23 +223,30 @@ class ParseTreeVisitor { bool inInterfaceStmtDefinedOperator = false; bool inInterfaceSpecification = false; - std::vector edges; // added to cg in postProcess step + // added to cg in postProcess step + std::vector edges; - std::vector functions; // all functions - std::vector currentFunctions; // intended as a stack. It holds the current function symbol and its dummy - // args when the AST walker is in the respective function. + // all functions + std::vector functions; - std::vector types; // all types + // intended as a stack. It holds the current function symbol and its dummy + // args when the AST walker is in the respective function. + std::vector currentFunctions; + // all types + std::vector types; + + // all interface operators. First is either a symbol of a DefinedOpName or + // IntrinsicOperator. Second is a vector procedure symbols, bound to that operator. std::vector< std::pair, std::vector>> - interfaceOperators; // all interface operators. First is either a symbol of a DefinedOpName or - // IntrinsicOperator. Second is a vector procedure symbols, bound to that operator. + interfaceOperators; std::vector exprStmtWithOps; - std::vector trackedVars; // mainly used for destructor handling + // mainly used for destructor handling + std::vector trackedVars; std::vector potentialFinalizers; }; diff --git a/cgfcollector/include/Type.h b/cgfcollector/include/Type.h index d1c528c6..ba90b8ef 100644 --- a/cgfcollector/include/Type.h +++ b/cgfcollector/include/Type.h @@ -14,8 +14,11 @@ struct Type { const Fortran::semantics::Symbol* typeSymbol; const Fortran::semantics::Symbol* extendsFrom; - std::vector> - procedures; // name(symbol) => optname(symbol) + + // name(symbol) => optname(symbol) + std::vector> procedures; + + // operator => name(symbol) std::vector> - operators; // operator => name(symbol) + operators; }; diff --git a/cgfcollector/include/VariableTracking.h b/cgfcollector/include/VariableTracking.h index 11342402..3441bbea 100644 --- a/cgfcollector/include/VariableTracking.h +++ b/cgfcollector/include/VariableTracking.h @@ -14,7 +14,10 @@ struct TrackedVar { const Fortran::semantics::Symbol* var; - const Fortran::semantics::Symbol* procedure; // procedure in which var was defined + + // procedure in which var was defined + const Fortran::semantics::Symbol* procedure; + bool hasBeenInitialized = false; bool addFinalizers = false; diff --git a/cgfcollector/src/FortranUtil.cpp b/cgfcollector/src/FortranUtil.cpp index 1581ec07..7adbff2b 100644 --- a/cgfcollector/src/FortranUtil.cpp +++ b/cgfcollector/src/FortranUtil.cpp @@ -241,7 +241,9 @@ std::vector findTypeWithDerivedTypes(const std::vector& types return typesWithDerived; } - typesWithDerived.push_back(&(*findTypeIt)); // Add the initial type + // Add the initial type + typesWithDerived.push_back(&(*findTypeIt)); + visited.insert(typeSymbol); // collect descendants @@ -250,7 +252,9 @@ std::vector findTypeWithDerivedTypes(const std::vector& types if (t.extendsFrom == parent->typeSymbol && !visited.count(t.typeSymbol)) { visited.insert(t.typeSymbol); typesWithDerived.push_back(&t); - collectDescendants(&t); // recursive call to find further descendants + + // recursive call to find further descendants + collectDescendants(&t); } } }; From a5f9ffe20887cc847bf2c4924180e56c71782406 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 16:23:00 +0100 Subject: [PATCH 130/130] Consistent naming for cgfcollector --- cgfcollector/CMakeLists.txt | 18 +++++++++--------- cgfcollector/README.md | 5 ++++- cgfcollector/test/multi/cmake_base.txt.in | 2 +- cgfcollector/test/multi/make_base.in | 2 +- .../tools/cgfcollector_comp_wrapper.sh.in | 2 +- cgfcollector/tools/cgfcollector_wrapper.sh.in | 2 +- cgfcollector/tools/test_runner.sh.in | 8 ++++---- 7 files changed, 21 insertions(+), 18 deletions(-) diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 76c98e5b..5e54cd99 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -1,13 +1,13 @@ -set(PROJECT_NAME fcollector) +set(PROJECT_NAME cgfcollector) set(TARGETS_EXPORT_NAME ${PROJECT_NAME}-target) file( GLOB - FCOLLECTOR_SOURCES + CGFCOLLECTOR_SOURCES src/*.cpp ) -add_library(${PROJECT_NAME} SHARED ${FCOLLECTOR_SOURCES}) +add_library(${PROJECT_NAME} SHARED ${CGFCOLLECTOR_SOURCES}) add_flang(${PROJECT_NAME}) add_metacg(${PROJECT_NAME}) add_spdlog_libraries(${PROJECT_NAME}) @@ -50,10 +50,10 @@ function( set(INSTALL_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_BASENAME}.install") # build-time values for development - set(FCOLLECTOR_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.so") - set(FCOLLECTOR_CGDIFF "${CMAKE_BINARY_DIR}/tools/cgdiff/cgdiff") - set(FCOLLECTOR_WRAPPER "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") - set(FCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") + set(CGFCOLLECTOR_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.so") + set(CGFCOLLECTOR_CGDIFF "${CMAKE_BINARY_DIR}/tools/cgdiff/cgdiff") + set(CGFCOLLECTOR_WRAPPER "${CMAKE_CURRENT_BINARY_DIR}/cgfcollector_wrapper.sh") + set(CGFCOLLECTOR_TEST_CASES_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test") set(CGMERGE2_EXECUTABLE "${CMAKE_BINARY_DIR}/tools/cgmerge2/cgmerge2") configure_file( @@ -70,7 +70,7 @@ function( endif() # install-time values - set(FCOLLECTOR_FILE_NAME "${CMAKE_INSTALL_PREFIX}/lib/lib${PROJECT_NAME}.so") + set(CGFCOLLECTOR_FILE_NAME "${CMAKE_INSTALL_PREFIX}/lib/lib${PROJECT_NAME}.so") configure_file( "${TEMPLATE}" @@ -126,4 +126,4 @@ file( ) # tests -add_test(NAME fcollector_tests COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_runner.sh) +add_test(NAME cgfcollector_tests COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test_runner.sh) diff --git a/cgfcollector/README.md b/cgfcollector/README.md index 9b6cc7e9..0506eff3 100644 --- a/cgfcollector/README.md +++ b/cgfcollector/README.md @@ -6,6 +6,7 @@ Flang plugin and generates a call graph from source-level. ## Usage For single file projects or projects not using modules use: + ```sh cgfcollector_wrapper.sh ``` @@ -13,13 +14,15 @@ cgfcollector_wrapper.sh For any other projects you need a build system. For this we provide another script that acts as the normal Flang compiler but also produces a call graph. [More info below](#generate-a-call-graph). + ```sh cgfcollector_comp_wrapper.sh ``` You can also run the plugin directly with Flang: + ```sh -flang -fc1 -load "libfcollector.so" -plugin "genCG" +flang -fc1 -load "libcgfcollector.so" -plugin "genCG" ``` There are three kinds of plugins: diff --git a/cgfcollector/test/multi/cmake_base.txt.in b/cgfcollector/test/multi/cmake_base.txt.in index c043d5a0..33500d99 100644 --- a/cgfcollector/test/multi/cmake_base.txt.in +++ b/cgfcollector/test/multi/cmake_base.txt.in @@ -1,4 +1,4 @@ -set(CMAKE_Fortran_COMPILER "@FCOLLECTOR_WRAPPER@") +set(CMAKE_Fortran_COMPILER "@CGFCOLLECTOR_WRAPPER@") set(CMAKE_Fortran_FLAGS "") set(CMAKE_Fortran_COMPILE_OBJECT " -norename -o ") set(CMAKE_Fortran_LINK_EXECUTABLE "@CGMERGE2_EXECUTABLE@ ") diff --git a/cgfcollector/test/multi/make_base.in b/cgfcollector/test/multi/make_base.in index 49a8ac0b..230b993d 100644 --- a/cgfcollector/test/multi/make_base.in +++ b/cgfcollector/test/multi/make_base.in @@ -1,3 +1,3 @@ MAKEDEPEND = fortdepend -FC = @FCOLLECTOR_WRAPPER@ +FC = @CGFCOLLECTOR_WRAPPER@ LD = @CGMERGE2_EXECUTABLE@ diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 666c8af4..37384e13 100755 --- a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in @@ -62,6 +62,6 @@ for arg in "${all_args[@]}"; do done if [ ${#source_files[@]} -gt 0 ]; then - $flang_fc1_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "genCG" "${options[@]}" "${source_files[@]}" + $flang_fc1_bin -fc1 -load "@CGFCOLLECTOR_FILE_NAME@" -plugin "genCG" "${options[@]}" "${source_files[@]}" fi $flang_bin "$@" diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index 6c3dd276..34fe5858 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -31,4 +31,4 @@ if ! command -v "$flang_bin" &>/dev/null; then exit 1 fi -$flang_bin -fc1 -load "@FCOLLECTOR_FILE_NAME@" -plugin "$plugin_name" "${flang_args[@]}" +$flang_bin -fc1 -load "@CGFCOLLECTOR_FILE_NAME@" -plugin "$plugin_name" "${flang_args[@]}" diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index ade3b003..217c2bc3 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -1,12 +1,12 @@ #!/usr/bin/env bash -# Test runner for fcollector +# Test runner for cgfcollector # # Usage: # test_runner.sh # run all tests # test_runner.sh [test_name] # run specific test cases -test_case_dir="@FCOLLECTOR_TEST_CASES_DIR@" +test_case_dir="@CGFCOLLECTOR_TEST_CASES_DIR@" scriptdir="$(cd "$(dirname "$0")" && pwd -P)" out_dir="$scriptdir/out" @@ -75,13 +75,13 @@ function run_single_test() return 1 fi - @FCOLLECTOR_WRAPPER@ -dot -o "$actual_output" "${input_files[@]}" || { + @CGFCOLLECTOR_WRAPPER@ -dot -o "$actual_output" "${input_files[@]}" || { echo "Failed: could not generate CG" return 1 } fi - if @FCOLLECTOR_CGDIFF@ "$expected_output" "$actual_output"; then + if @CGFCOLLECTOR_CGDIFF@ "$expected_output" "$actual_output"; then echo "Passed" return 0 else