From b0e04d2ad67e785bd370464728a62cb23a118f92 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:37 +0100 Subject: [PATCH 001/143] 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 814f9f6e1880036134663b0f7b116e3b19daa7b2 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:38 +0100 Subject: [PATCH 002/143] 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 6975bcc9d2511071c8de438c0f861f9d9feb4d69 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:38 +0100 Subject: [PATCH 003/143] 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 87b0684d3ae763b36459f640df00e8ef445c5814 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:38 +0100 Subject: [PATCH 004/143] 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 8d643f3f76a44a0f7466291731f1cf51aacbf4e5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 005/143] 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 037ab40da109f4ab7a8510e38ff66f64a365b01d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 006/143] 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 a080385690b09cfd9a619b9f0ff055c8884a5a49 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 007/143] 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 404c22d0866f9b0b43c32deefe3e604dd373aaa2 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:39 +0100 Subject: [PATCH 008/143] 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 bdbcebddcbb93b36b06131a2898baf2010f1b6fa Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:40 +0100 Subject: [PATCH 009/143] 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 8f1be87a12e9911ab31cd7a537b885ed36c62a68 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:40 +0100 Subject: [PATCH 010/143] 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 64bc58928c5875e3bf0dc353c16df03f1c8d57f5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:40 +0100 Subject: [PATCH 011/143] 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 7119e684cbb396b90ee8cdd431e535319b26245f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:41 +0100 Subject: [PATCH 012/143] 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 2e822c513dba8646168d1c8354feb4fd494e438f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:41 +0100 Subject: [PATCH 013/143] 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 ce27a50dcdeef9f6b1c901ddae0afd00e80ec7f7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:41 +0100 Subject: [PATCH 014/143] 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 450775da00dc0fca4f03a58ba4251e077d03075a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:42 +0100 Subject: [PATCH 015/143] 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 93be4124494dc5c164e98856a17a747ecec41046 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:42 +0100 Subject: [PATCH 016/143] 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 678fc67c412e4b16a9d4a435bc3081ec02aaaaf9 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:42 +0100 Subject: [PATCH 017/143] 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 a4f8a57b55973063788437bdd4c444a72dafafef Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:43 +0100 Subject: [PATCH 018/143] 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 6515153ac81969adabc6837a430c72698ccdb37d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:43 +0100 Subject: [PATCH 019/143] 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 01d37abd61425358335f49ce1ed120e24cce0de8 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:43 +0100 Subject: [PATCH 020/143] 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 8d1f3e21f405c5e0bc952e9a1e1510b6e4bf92e6 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:44 +0100 Subject: [PATCH 021/143] 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 8e3a2988f34449af5432a9dd1d4c3686b4e234ed Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:44 +0100 Subject: [PATCH 022/143] 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 39d72473ff95ae4eecbd0ff459db246862b12c42 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:44 +0100 Subject: [PATCH 023/143] 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 b8af6c822e46eeddf6007a12ada6a10fa356738b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:45 +0100 Subject: [PATCH 024/143] 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 fe2c06f3bec8596234f35be4e2b8fe44f315f976 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:45 +0100 Subject: [PATCH 025/143] 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 94310d5d1d918634adc0144524682a8a5380171c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:45 +0100 Subject: [PATCH 026/143] 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 6713d16e8f21af652cdf2a9bcff695c1ad841caa Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:46 +0100 Subject: [PATCH 027/143] 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 ffded8ed18bba990b9cdd0aa737a18d65a80eeca Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:46 +0100 Subject: [PATCH 028/143] 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 f9479d1d5e04b639e61e2b0215a8b12fc86db72a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:46 +0100 Subject: [PATCH 029/143] 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 a96f4439da54951a95d2d9ade17649bb89ae2160 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:47 +0100 Subject: [PATCH 030/143] 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 8f7eb79e56d4fa402977ea8c532154b047846061 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:47 +0100 Subject: [PATCH 031/143] 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 3ad3851892ed2189185dfe49e3206098b8c1e8ce Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:47 +0100 Subject: [PATCH 032/143] 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 361ad160ecd6a0c777af75e6b87713adefc03924 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:48 +0100 Subject: [PATCH 033/143] 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 b7cfd40cad09181e469e126811a648261fe1e7dd Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:48 +0100 Subject: [PATCH 034/143] 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 4b3f6f69dac38967afc6c228ad8cc409da3e3a72 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:48 +0100 Subject: [PATCH 035/143] 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 b3ab70960ee2558e7438c60642a133ff14b13448 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:49 +0100 Subject: [PATCH 036/143] 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 2fc0b0325c7ba17e5d4e18098a0f8272b9507d35 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:49 +0100 Subject: [PATCH 037/143] 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 5bac021038b5008f0b0d2c48320115720f227786 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:49 +0100 Subject: [PATCH 038/143] 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 811ef39b0aa5010f3979ae25da4542de381cb9bb Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:50 +0100 Subject: [PATCH 039/143] 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 73840e27d6674fd330f793f4a2cf1526c9cdc9ab Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:50 +0100 Subject: [PATCH 040/143] 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 80fce0fc9fe1259ac120a5ca4d5c56202b4c9963 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:50 +0100 Subject: [PATCH 041/143] 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 81acb858e76b239b40bb4516e2a8baa6c3228013 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:51 +0100 Subject: [PATCH 042/143] 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 7e4a7c25b13ec99c2f91dd085921a33c8e4e6b53 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:51 +0100 Subject: [PATCH 043/143] 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 12b98ff6744c517a43dd513dd180020a58044ba2 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:51 +0100 Subject: [PATCH 044/143] 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 78bdd0c4cd4c0b0c4e74ddd206315f106da86bbf Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:52 +0100 Subject: [PATCH 045/143] 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 8f73fbb8d804b848e7ccd0b4e1a92953223b64f1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:52 +0100 Subject: [PATCH 046/143] 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 8e1189ca4acab2343151b1d12c9e6896b196190d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:52 +0100 Subject: [PATCH 047/143] 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 f78f839cf7cd5f24da81d0e30f1e8ab7b2ed1c13 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:53 +0100 Subject: [PATCH 048/143] 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 9de5e89588c8d486eeea06cee444c939a2fecc56 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:53 +0100 Subject: [PATCH 049/143] 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 1b1e7f04c8923b19e7dff2ea34bb735ece6f6f82 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:53 +0100 Subject: [PATCH 050/143] 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 2af28d07fcd555f07eaab53cd241af9fe62db5b5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:54 +0100 Subject: [PATCH 051/143] 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 e0b1139e7a2bc3930e58886bd70729c39000020b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:54 +0100 Subject: [PATCH 052/143] 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 db74d60b4b5bae09d014d65a226a618233dd37a1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:54 +0100 Subject: [PATCH 053/143] 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 8817e5b12f53bfce553af13766e6ff763e9d448c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:55 +0100 Subject: [PATCH 054/143] 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 c309b9a845e8a1e1e4b0588b4fdeba7b8c1f6c7e Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:55 +0100 Subject: [PATCH 055/143] 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 c9b40580967d8a56aa3f605b5d9e41dea0d5508d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:55 +0100 Subject: [PATCH 056/143] 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 3a12a5b5d6402691f43e9704890c2c6ba99e0735 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:56 +0100 Subject: [PATCH 057/143] 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 8b086ff6eec9642c05b4fdf95b61338d0b14f3a4 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:56 +0100 Subject: [PATCH 058/143] 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 32f0a272a460c96c8e7372cd96bbe305ce0e0e9b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:56 +0100 Subject: [PATCH 059/143] 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 2d439a20e9b2b5f092a705108a5bc963c204a109 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:57 +0100 Subject: [PATCH 060/143] 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 d98c443b00373b744f01f1bd85537c5d9d0aa791 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:57 +0100 Subject: [PATCH 061/143] 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 e024be2a906845bd586aeebdba75c5bea441e748 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:57 +0100 Subject: [PATCH 062/143] 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 e08743b2bd33b8bace582520f9c7eb6892828e23 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 063/143] 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 9e5ea738e1a0cdf41cf0860de8f88360e04ac726 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 064/143] 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 101128f3f0bf06fa0b5f03400e22fb02514d02b7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 065/143] 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 45eb6198fccac205ebfd72256c0e41dd3aa378f9 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:58 +0100 Subject: [PATCH 066/143] 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 f544221d74825fbb0a2c0dd9db345a0724d5ff78 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:59 +0100 Subject: [PATCH 067/143] 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 db5b06d77e00db8d5bd02008884508fdbc5c31b9 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:59 +0100 Subject: [PATCH 068/143] 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 e74398b72b5ebb5d4288f0e2af1d04c35011058a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:32:59 +0100 Subject: [PATCH 069/143] 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 ed57966a44859870f88a21e0f0df57b008d37bd2 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:00 +0100 Subject: [PATCH 070/143] 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 ca89d9a2ad6e6c543515eebba400708c57d66b40 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:00 +0100 Subject: [PATCH 071/143] 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 c9424a410e28dab8d22ce0d626aec2b68d0eb424 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:01 +0100 Subject: [PATCH 072/143] 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 ac6eaa3c98eed22783cf58f0271a7be41155635c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:01 +0100 Subject: [PATCH 073/143] 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 a6b609a0503debfb71bbfb23d40d7e3156630e8f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:01 +0100 Subject: [PATCH 074/143] 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 9cf0abd24c71b644878ea346980b6031809508d7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:02 +0100 Subject: [PATCH 075/143] 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 85be0aab0c0924dad97f2070472b66750dc83eee Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:02 +0100 Subject: [PATCH 076/143] 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 2f88d1cb31b242a1522729974b3ac0b7966caf22 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:02 +0100 Subject: [PATCH 077/143] 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 87f5b14772b0d8560c9cf19f43bee0b69899eb55 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:03 +0100 Subject: [PATCH 078/143] 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 b4aed455ab11570835cba96952be08aec09292e0 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:03 +0100 Subject: [PATCH 079/143] 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 54f7399cd37f7b57fbf21ba66777ee72a7427aaf Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:03 +0100 Subject: [PATCH 080/143] 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 b51b756ba03bb6024111c6252375976dac048776 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 081/143] 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 e7db8b425f6f8a7cbe89c0f4c53518fc732f10d1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 082/143] 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 005ff71b4d8bf401ec56655bc6e9f47ad68e96b4 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 083/143] 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 7cc1851af20e03206908ae45996c3970e738d6b5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:04 +0100 Subject: [PATCH 084/143] 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 462637f36cd17879291845752e231b4f3c3b1c5a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:05 +0100 Subject: [PATCH 085/143] 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 c25a95dd9d8c2d89decd5f6362e74e6fe11e437f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:05 +0100 Subject: [PATCH 086/143] 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 23fcdef12c1351bd06ff7fe6e2f9177d23135d51 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:05 +0100 Subject: [PATCH 087/143] 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 0e3dd5fc81b676daf9de3fa1fd5851eee3e08639 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:06 +0100 Subject: [PATCH 088/143] 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 4c08e21db8eab616ae3e531fdb643500423141a1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:06 +0100 Subject: [PATCH 089/143] 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 4d7b92cb81251b22eb3817d46083f128328e795a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:06 +0100 Subject: [PATCH 090/143] 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 9505fe17e63d68426f8fbad0033382a4aadfaef5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:07 +0100 Subject: [PATCH 091/143] 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 f8515024f8e8047518ecd7cd52cd612450bd14b8 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:07 +0100 Subject: [PATCH 092/143] 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 086cbebefed4fc095d9440a5c5619120a0922a70 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:07 +0100 Subject: [PATCH 093/143] 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 80c6f63323b61bcfeaead6efc67748339f7d32a7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:08 +0100 Subject: [PATCH 094/143] 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 c3f9a3ddf20a49fa00aacff1c08077c144dad5fc Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:08 +0100 Subject: [PATCH 095/143] 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 2290fe2c795755d16cdd1c6ff75dc512023c9b30 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:08 +0100 Subject: [PATCH 096/143] 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 d889e86970a37b938c571006168a259d8eba8a11 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:09 +0100 Subject: [PATCH 097/143] 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 6a477d8442858e08f7e5a500efec6f662eb14e21 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:09 +0100 Subject: [PATCH 098/143] 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 9b5e41eaf9ec9d09d87ccb58e9d21f38ece5b3b7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:09 +0100 Subject: [PATCH 099/143] 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 ed5bd29fd9d01f799ab49ac47a75a64f26fd450d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:10 +0100 Subject: [PATCH 100/143] 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 95a747bbfc721d9eeae87a42524e7bd9f0695359 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:10 +0100 Subject: [PATCH 101/143] 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 5adc85f319eb33e2e65937cef56950f97217b406 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:10 +0100 Subject: [PATCH 102/143] 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 dc8da8f906b22608b96a29fb5965486ea281e3a7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:11 +0100 Subject: [PATCH 103/143] 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 cb44c26dbc2044e7c0228ee327479816f125392d Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:11 +0100 Subject: [PATCH 104/143] 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 22fa7ba1d60694d4e25251599a77054adb7d55ee Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:11 +0100 Subject: [PATCH 105/143] 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 11f9d1704c726115933730fd21b442d1602e8e12 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:12 +0100 Subject: [PATCH 106/143] 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 f0b952161434f3a8249c6df0d20b129944420dd3 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:12 +0100 Subject: [PATCH 107/143] 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 bd3355b57a4da552cfd2c224755d932447ffaabb Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:12 +0100 Subject: [PATCH 108/143] 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 5121eeb048d4e0bda5fd34bc6fc0e043f40fd6e5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:13 +0100 Subject: [PATCH 109/143] 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 a056e941940d2ef3d5111b84720d3c77693bd186 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:13 +0100 Subject: [PATCH 110/143] 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 28cbb13e084011bda8b9a4228a0136952a4e8196 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:13 +0100 Subject: [PATCH 111/143] 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 ec069d589c44bfe29743e0b7803cc54c12ac2b92 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:14 +0100 Subject: [PATCH 112/143] 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 32d818bcaa0f355f3d328b48b5645222a74ddb4c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:14 +0100 Subject: [PATCH 113/143] 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 e2111364b810a06c6be5037ce50a860d77a44bb5 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:14 +0100 Subject: [PATCH 114/143] 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 88865f93185afafe4535b263fcbe06daebf81caa Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:15 +0100 Subject: [PATCH 115/143] 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 adc3a132aa6042d03948f7bcc2cdb9681a78c798 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:15 +0100 Subject: [PATCH 116/143] 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 47ec95bc8acb0f0dc2927759f8ceac241a686069 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:15 +0100 Subject: [PATCH 117/143] 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 396c7cb7f4bf3d0cfe0faa2d31e1c131ee535a4f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:16 +0100 Subject: [PATCH 118/143] 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 08ce76119a5d28687dd4232efebd7706a776f37b Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Thu, 5 Feb 2026 15:33:16 +0100 Subject: [PATCH 119/143] 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 0aba487716bc294e8f2d3b97fd6a61363d40a670 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:02:25 +0100 Subject: [PATCH 120/143] 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 7d05779db6d4a4796eb41828e0d95d9eba570d7f Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:02:36 +0100 Subject: [PATCH 121/143] 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 c2b53614920eabac0bc93b3e9a94c74905898ed7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:47:36 +0100 Subject: [PATCH 122/143] 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 da2303628942187f17fa5aec611e7032a26b0f2a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 15:51:37 +0100 Subject: [PATCH 123/143] 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 c8d4114450de403949f19c1b943ab5c6e9e3d774 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Mon, 9 Feb 2026 17:43:42 +0100 Subject: [PATCH 124/143] 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 11e693e9e1190c24ea9073607571dfecac465ae8 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Wed, 11 Feb 2026 14:02:42 +0100 Subject: [PATCH 125/143] 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 2b890598226b113c96ffda36e02f00219d254722 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 14:18:12 +0100 Subject: [PATCH 126/143] 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 00a60514180e548710c8ff7566be0154a494b033 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 15:28:45 +0100 Subject: [PATCH 127/143] 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 5ec212a5b9b11c570baf6831413f936c7b429c05 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 15:59:51 +0100 Subject: [PATCH 128/143] 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 88c39cad4776a18365551836b6d61a867cf109c7 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 16:14:45 +0100 Subject: [PATCH 129/143] 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 46d895be2a0bd8820154f79d56a193b150511242 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 17 Feb 2026 16:23:00 +0100 Subject: [PATCH 130/143] 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 From d1ddaeb2b01ce65a2788941f04ea0b30812c22f1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Fri, 20 Feb 2026 10:01:05 +0100 Subject: [PATCH 131/143] add cgfcollector to ci pipeline --- .github/workflows/mcg-ci.yml | 8 +++++++- container/full-build | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/mcg-ci.yml b/.github/workflows/mcg-ci.yml index 776d638c..192ae645 100644 --- a/.github/workflows/mcg-ci.yml +++ b/.github/workflows/mcg-ci.yml @@ -196,4 +196,10 @@ jobs: with: image: metacg-devel:latest run: /opt/metacg/build/tools/cgdiff/test/unit/cgdifftests - + - name: Run cgfcollector tests + uses: maus007/docker-run-action-fork@207a4e2a8ebf7e4b985656ba990b1e53715dce2a + with: + image: metacg-devel:latest + run: | + cd /opt/metacg/build/cgfcollector + ctest . --verbose diff --git a/container/full-build b/container/full-build index 0adf7929..f5fcec2e 100644 --- a/container/full-build +++ b/container/full-build @@ -47,6 +47,7 @@ RUN cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Debug \ -DMETACG_BUILD_GRAPH_TOOLS=ON \ -DMETACG_BUILD_CGPATCH=ON \ -DMETACG_BUILD_GRAPH_TOOLS=ON \ + -DMETACG_BUILD_CGFCOLLECTOR=ON \ -DCAGE_USE_METAVIRT=ON \ -DMETACG_BUILD_PYMETACG=ON \ -DPython_ROOT_DIR=/opt/metacg/.venv \ From 0571de7b78060684b8f9c803b12db0450435e4b1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Fri, 20 Feb 2026 10:40:36 +0100 Subject: [PATCH 132/143] namespacify --- cgfcollector/include/Edge.h | 12 ++++++++---- cgfcollector/include/FortranUtil.h | 4 ++++ cgfcollector/include/Function.h | 4 ++++ cgfcollector/include/ParseTreeVisitor.h | 4 ++++ cgfcollector/include/PotentialFinalizer.h | 4 ++++ cgfcollector/include/Type.h | 4 ++++ cgfcollector/include/VariableTracking.h | 8 ++++++-- cgfcollector/src/Edge.cpp | 4 ++++ cgfcollector/src/FortranUtil.cpp | 4 ++++ cgfcollector/src/Main.cpp | 1 + cgfcollector/src/ParseTreeVisitor.cpp | 4 ++++ cgfcollector/src/VariableTracking.cpp | 4 ++++ 12 files changed, 51 insertions(+), 6 deletions(-) diff --git a/cgfcollector/include/Edge.h b/cgfcollector/include/Edge.h index 1ef03242..2766cbd0 100644 --- a/cgfcollector/include/Edge.h +++ b/cgfcollector/include/Edge.h @@ -15,6 +15,8 @@ #include #include +namespace metacg::cgfcollector { + struct Edge { std::string caller; std::string callee; @@ -25,7 +27,7 @@ struct Edge { bool operator<(const Edge& other) const { return caller < other.caller || (caller == other.caller && callee < other.callee); } -}; +}; // namespace metacg::cgfcollector struct EdgeSymbol { const Fortran::semantics::Symbol* caller; @@ -59,9 +61,9 @@ struct EdgeManager { * @param currentFunctionSymbol * @param symbol */ - std::vector getEdgesForFinalizers(const std::vector& types, - const Fortran::semantics::Symbol* currentFunctionSymbol, - const Fortran::semantics::Symbol* symbol); + [[nodiscard]] 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. * @@ -72,3 +74,5 @@ struct EdgeManager { void addEdgesForFinalizers(const std::vector& types, const Fortran::semantics::Symbol* currentFunctionSymbol, const Fortran::semantics::Symbol* symbol); }; + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/include/FortranUtil.h b/cgfcollector/include/FortranUtil.h index 035d516c..2a2a6ead 100644 --- a/cgfcollector/include/FortranUtil.h +++ b/cgfcollector/include/FortranUtil.h @@ -28,6 +28,8 @@ struct fmt::formatter { } }; +namespace metacg::cgfcollector { + /** * @brief Variant check if it holds any of the given types * @@ -141,3 +143,5 @@ const Fortran::semantics::Symbol* getTypeSymbolFromSymbol(const Fortran::semanti */ std::vector findTypeWithDerivedTypes(const std::vector& types, const Fortran::semantics::Symbol* symbol); + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/include/Function.h b/cgfcollector/include/Function.h index 5e9d8cd7..b9678497 100644 --- a/cgfcollector/include/Function.h +++ b/cgfcollector/include/Function.h @@ -9,6 +9,8 @@ #include #include +namespace metacg::cgfcollector { + struct Function { struct DummyArg { const Fortran::semantics::Symbol* symbol; @@ -27,3 +29,5 @@ struct Function { void addDummyArg(const Fortran::semantics::Symbol* sym) { dummyArgs.emplace_back(sym); } }; + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 1403f83a..33bd62c7 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -31,6 +31,8 @@ #include #include +namespace metacg::cgfcollector { + /** * @class ParseTreeVisitor * @brief Implements visitor methods to traverse parse tree and generate callgraph @@ -250,3 +252,5 @@ class ParseTreeVisitor { std::vector potentialFinalizers; }; + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/include/PotentialFinalizer.h b/cgfcollector/include/PotentialFinalizer.h index e3d6cf8b..4b65d0ae 100644 --- a/cgfcollector/include/PotentialFinalizer.h +++ b/cgfcollector/include/PotentialFinalizer.h @@ -11,6 +11,8 @@ #include #include +namespace metacg::cgfcollector { + struct PotentialFinalizer { std::size_t argPos; std::string procedureCalled; @@ -21,3 +23,5 @@ struct PotentialFinalizer { void addFinalizerEdge(const Edge& e) { finalizerEdges.emplace_back(e); } }; + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/include/Type.h b/cgfcollector/include/Type.h index ba90b8ef..7d58ca86 100644 --- a/cgfcollector/include/Type.h +++ b/cgfcollector/include/Type.h @@ -11,6 +11,8 @@ #include #include +namespace metacg::cgfcollector { + struct Type { const Fortran::semantics::Symbol* typeSymbol; const Fortran::semantics::Symbol* extendsFrom; @@ -22,3 +24,5 @@ struct Type { std::vector> operators; }; + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/include/VariableTracking.h b/cgfcollector/include/VariableTracking.h index 3441bbea..b10940c2 100644 --- a/cgfcollector/include/VariableTracking.h +++ b/cgfcollector/include/VariableTracking.h @@ -12,6 +12,8 @@ #include +namespace metacg::cgfcollector { + struct TrackedVar { const Fortran::semantics::Symbol* var; @@ -40,8 +42,8 @@ struct VariableTracking { * @param sourceName * @return trackedVar* or nullptr if not found */ - TrackedVar* getTrackedVarFromSourceName(const Fortran::semantics::Symbol* currentFunctionSymbol, - Fortran::semantics::SourceName sourceName); + [[nodiscard]] TrackedVar* getTrackedVarFromSourceName(const Fortran::semantics::Symbol* currentFunctionSymbol, + Fortran::semantics::SourceName sourceName); /** * @brief Search trackedVars for a canditate and set it as initialized. @@ -81,3 +83,5 @@ struct VariableTracking { std::vector& types; std::vector& functions; }; + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/src/Edge.cpp b/cgfcollector/src/Edge.cpp index 8ed29895..be9ba485 100644 --- a/cgfcollector/src/Edge.cpp +++ b/cgfcollector/src/Edge.cpp @@ -10,6 +10,8 @@ using namespace Fortran::semantics; using namespace Fortran::parser; using namespace metacg; +namespace metacg::cgfcollector { + std::vector EdgeManager::getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { std::vector edges; @@ -82,3 +84,5 @@ void EdgeManager::addEdges(const std::vector& newEdges, bool debug) addEdge(e, debug); } } + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/src/FortranUtil.cpp b/cgfcollector/src/FortranUtil.cpp index 7adbff2b..c79529bc 100644 --- a/cgfcollector/src/FortranUtil.cpp +++ b/cgfcollector/src/FortranUtil.cpp @@ -11,6 +11,8 @@ using namespace Fortran::parser; using namespace Fortran::common; using namespace metacg; +namespace metacg::cgfcollector { + bool compareSymbols(const Symbol* a, const Symbol* b) { if (a == b) return true; @@ -285,3 +287,5 @@ std::vector findTypeWithDerivedTypes(const std::vector& types return typesWithDerived; } + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/src/Main.cpp b/cgfcollector/src/Main.cpp index 014efb26..1ee80761 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 metacg::cgfcollector; using namespace Fortran::parser; static MCGManager& mcgManager = MCGManager::get(); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index f4df18c7..64116310 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -11,6 +11,8 @@ using namespace Fortran::semantics; using namespace Fortran::common; using namespace metacg; +namespace metacg::cgfcollector { + template void ParseTreeVisitor::handleFuncSubStmt(const FunctionStmt&); template void ParseTreeVisitor::handleFuncSubStmt(const SubroutineStmt&); @@ -735,3 +737,5 @@ void ParseTreeVisitor::Post(const UseStmt& u) { MCGLogger::logDebug("Finished Use module: {} ({})", useSymbol->name(), fmt::ptr(useSymbol)); } + +} // namespace metacg::cgfcollector diff --git a/cgfcollector/src/VariableTracking.cpp b/cgfcollector/src/VariableTracking.cpp index 17b23afd..9feac4dc 100644 --- a/cgfcollector/src/VariableTracking.cpp +++ b/cgfcollector/src/VariableTracking.cpp @@ -11,6 +11,8 @@ using namespace Fortran::semantics; using namespace metacg; +namespace metacg::cgfcollector { + 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; }); @@ -88,3 +90,5 @@ void VariableTracking::removeTrackedVars(const Symbol* procedureSymbol) { [&](const TrackedVar& t) { return t.procedure == procedureSymbol; }), trackedVars.end()); } + +} // namespace metacg::cgfcollector From 1ff47b0eb24d88ebce5b3b586881782258ec1f97 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Fri, 20 Feb 2026 10:43:37 +0100 Subject: [PATCH 133/143] more consistent whitespacing --- cgfcollector/include/ParseTreeVisitor.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 33bd62c7..129e337e 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -85,11 +85,15 @@ class ParseTreeVisitor { void Post(const A&) {} bool Pre(const Fortran::parser::MainProgram& p); + void Post(const Fortran::parser::MainProgram&); bool Pre(const Fortran::parser::FunctionSubprogram&); + void Post(const Fortran::parser::FunctionSubprogram&); + bool Pre(const Fortran::parser::SubroutineSubprogram&); + void Post(const Fortran::parser::SubroutineSubprogram&); /** @@ -102,8 +106,11 @@ class ParseTreeVisitor { void Post(const Fortran::parser::EntryStmt& e); 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&); /** @@ -187,8 +194,11 @@ class ParseTreeVisitor { // The following methods are for collecting defined operators in interface statements 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); /** From 46bacbb4b83982beeae7ccc86674e48beb6bb269 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Fri, 20 Feb 2026 10:53:26 +0100 Subject: [PATCH 134/143] removed FLANG_LITTLE_ENDIAN as it is platform specific and not required --- cmake/FlangLLVM.cmake | 2 -- 1 file changed, 2 deletions(-) diff --git a/cmake/FlangLLVM.cmake b/cmake/FlangLLVM.cmake index 59557789..a0c9659e 100644 --- a/cmake/FlangLLVM.cmake +++ b/cmake/FlangLLVM.cmake @@ -22,8 +22,6 @@ 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( From 17ed4c583c5b4ed638f4e6840bafd5696c72f75a Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Fri, 20 Feb 2026 11:09:30 +0100 Subject: [PATCH 135/143] CMakeLists variable naming --- cgfcollector/CMakeLists.txt | 8 ++++---- cgfcollector/test/multi/cmake_base.txt.in | 2 +- cgfcollector/test/multi/make_base.in | 2 +- cgfcollector/tools/cgfcollector_comp_wrapper.sh.in | 2 +- cgfcollector/tools/cgfcollector_wrapper.sh.in | 2 +- cgfcollector/tools/test_runner.sh.in | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 5e54cd99..34965675 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -50,11 +50,11 @@ function( set(INSTALL_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_BASENAME}.install") # build-time values for development - set(CGFCOLLECTOR_FILE_NAME "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.so") - set(CGFCOLLECTOR_CGDIFF "${CMAKE_BINARY_DIR}/tools/cgdiff/cgdiff") + set(CGFCOLLECTOR_FILE_NAME_LIB "${CMAKE_CURRENT_BINARY_DIR}/lib${PROJECT_NAME}.so") + set(CGFCOLLECTOR_CGDIFF_BIN "${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") + set(CGMERGE2_BIN "${CMAKE_BINARY_DIR}/tools/cgmerge2/cgmerge2") configure_file( "${TEMPLATE}" @@ -70,7 +70,7 @@ function( endif() # install-time values - set(CGFCOLLECTOR_FILE_NAME "${CMAKE_INSTALL_PREFIX}/lib/lib${PROJECT_NAME}.so") + set(CGFCOLLECTOR_FILE_NAME_LIB "${CMAKE_INSTALL_PREFIX}/lib/lib${PROJECT_NAME}.so") configure_file( "${TEMPLATE}" diff --git a/cgfcollector/test/multi/cmake_base.txt.in b/cgfcollector/test/multi/cmake_base.txt.in index 33500d99..bf9f35f7 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 "@CGFCOLLECTOR_WRAPPER@") set(CMAKE_Fortran_FLAGS "") set(CMAKE_Fortran_COMPILE_OBJECT " -norename -o ") -set(CMAKE_Fortran_LINK_EXECUTABLE "@CGMERGE2_EXECUTABLE@ ") +set(CMAKE_Fortran_LINK_EXECUTABLE "@CGMERGE2_BIN@ ") set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/test/multi/make_base.in b/cgfcollector/test/multi/make_base.in index 230b993d..60cc47c8 100644 --- a/cgfcollector/test/multi/make_base.in +++ b/cgfcollector/test/multi/make_base.in @@ -1,3 +1,3 @@ MAKEDEPEND = fortdepend FC = @CGFCOLLECTOR_WRAPPER@ -LD = @CGMERGE2_EXECUTABLE@ +LD = @CGMERGE2_BIN@ diff --git a/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in b/cgfcollector/tools/cgfcollector_comp_wrapper.sh.in index 37384e13..2a47bc98 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 "@CGFCOLLECTOR_FILE_NAME@" -plugin "genCG" "${options[@]}" "${source_files[@]}" + $flang_fc1_bin -fc1 -load "@CGFCOLLECTOR_FILE_NAME_LIB@" -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 34fe5858..325353f7 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 "@CGFCOLLECTOR_FILE_NAME@" -plugin "$plugin_name" "${flang_args[@]}" +$flang_bin -fc1 -load "@CGFCOLLECTOR_FILE_NAME_LIB@" -plugin "$plugin_name" "${flang_args[@]}" diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index 217c2bc3..b8fcce2e 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -81,7 +81,7 @@ function run_single_test() } fi - if @CGFCOLLECTOR_CGDIFF@ "$expected_output" "$actual_output"; then + if @CGFCOLLECTOR_CGDIFF_BIN@ "$expected_output" "$actual_output"; then echo "Passed" return 0 else From 4f762f94546f33cffaeab8bd287b35c1684a973e Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Fri, 20 Feb 2026 14:22:11 +0100 Subject: [PATCH 136/143] better option parsing and verbose option --- cgfcollector/include/Edge.h | 16 ++-- cgfcollector/src/Edge.cpp | 47 ++++++----- cgfcollector/src/Main.cpp | 84 +++++++------------ cgfcollector/src/ParseTreeVisitor.cpp | 5 +- cgfcollector/test/multi/cmake_base.txt.in | 2 +- cgfcollector/tools/cgfcollector_wrapper.sh.in | 28 ++++--- cgfcollector/tools/test_runner.sh.in | 19 ++++- 7 files changed, 100 insertions(+), 101 deletions(-) diff --git a/cgfcollector/include/Edge.h b/cgfcollector/include/Edge.h index 2766cbd0..23b6b90b 100644 --- a/cgfcollector/include/Edge.h +++ b/cgfcollector/include/Edge.h @@ -17,6 +17,8 @@ namespace metacg::cgfcollector { +struct PotentialFinalizer; + struct Edge { std::string caller; std::string callee; @@ -47,12 +49,12 @@ struct EdgeManager { EdgeManager(std::vector& edges) : edges(edges) {} - 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 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 addEdge(const EdgeSymbol& e); + void addEdge(const Fortran::semantics::Symbol* caller, const Fortran::semantics::Symbol* callee); + void addEdge(const Edge& e); + void addEdge(const std::string& caller, const std::string& callee); + void addEdges(const std::vector& newEdges); + void addEdges(const std::vector& newEdges); /** * @brief For a given symbol, returns a list of edges from the current function to all finalizers of that type. @@ -73,6 +75,8 @@ struct EdgeManager { */ void addEdgesForFinalizers(const std::vector& types, const Fortran::semantics::Symbol* currentFunctionSymbol, const Fortran::semantics::Symbol* symbol); + + void addEdgesForFinalizers(const PotentialFinalizer& e); }; } // namespace metacg::cgfcollector diff --git a/cgfcollector/src/Edge.cpp b/cgfcollector/src/Edge.cpp index be9ba485..6b476602 100644 --- a/cgfcollector/src/Edge.cpp +++ b/cgfcollector/src/Edge.cpp @@ -6,6 +6,8 @@ #include "Edge.h" +#include "PotentialFinalizer.h" + using namespace Fortran::semantics; using namespace Fortran::parser; using namespace metacg; @@ -37,51 +39,50 @@ std::vector EdgeManager::getEdgesForFinalizers(const std::vector& types, const Symbol* currentFunctionSymbol, const Symbol* symbol) { for (const EdgeSymbol& edge : getEdgesForFinalizers(types, currentFunctionSymbol, symbol)) { - addEdge(edge, false); + addEdge(edge); 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)); - if (debug) { - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), - mangleSymbol(e.callee), fmt::ptr(e.callee)); +void EdgeManager::addEdgesForFinalizers(const PotentialFinalizer& e) { + for (const Edge& edge : e.finalizerEdges) { + addEdge(edge); + MCGLogger::logDebug("Add edge for finalizer: {} -> {}", edge.caller, edge.callee); } } -void EdgeManager::addEdge(const Symbol* caller, const Symbol* callee, bool debug) { +void EdgeManager::addEdge(const EdgeSymbol& e) { + edges.emplace_back(mangleSymbol(e.caller), mangleSymbol(e.callee)); + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(e.caller), fmt::ptr(e.caller), + mangleSymbol(e.callee), fmt::ptr(e.callee)); +} + +void EdgeManager::addEdge(const Symbol* caller, const Symbol* callee) { edges.emplace_back(mangleSymbol(caller), mangleSymbol(callee)); - if (debug) { - MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(caller), fmt::ptr(caller), mangleSymbol(callee), - fmt::ptr(callee)); - } + MCGLogger::logDebug("Add edge: {} ({}) -> {} ({})", mangleSymbol(caller), fmt::ptr(caller), mangleSymbol(callee), + fmt::ptr(callee)); } -void EdgeManager::addEdge(const Edge& e, bool debug) { +void EdgeManager::addEdge(const Edge& e) { edges.emplace_back(e.caller, e.callee); - if (debug) { - MCGLogger::logDebug("Add edge: {} -> {}", e.caller, e.callee); - } + 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) { edges.emplace_back(caller, callee); - if (debug) { - MCGLogger::logDebug("Add edge: {} -> {}", caller, callee); - } + MCGLogger::logDebug("Add edge: {} -> {}", caller, callee); } -void EdgeManager::addEdges(const std::vector& newEdges, bool debug) { +void EdgeManager::addEdges(const std::vector& newEdges) { for (const Edge& e : newEdges) { - addEdge(e, debug); + addEdge(e); } } -void EdgeManager::addEdges(const std::vector& newEdges, bool debug) { +void EdgeManager::addEdges(const std::vector& newEdges) { for (const EdgeSymbol& e : newEdges) { - addEdge(e, debug); + addEdge(e); } } diff --git a/cgfcollector/src/Main.cpp b/cgfcollector/src/Main.cpp index 1ee80761..f2bd4f27 100644 --- a/cgfcollector/src/Main.cpp +++ b/cgfcollector/src/Main.cpp @@ -7,6 +7,7 @@ #include "ParseTreeVisitor.h" #include +#include using namespace metacg; using namespace metacg::graph; @@ -16,6 +17,15 @@ using namespace Fortran::parser; static MCGManager& mcgManager = MCGManager::get(); +static llvm::cl::OptionCategory CGCategory("Callgraph Plugin Options"); + +static llvm::cl::opt Dot("dot", llvm::cl::desc("Generate DOT output"), llvm::cl::cat(CGCategory), + llvm::cl::init(false)); +static llvm::cl::opt NoRename("no-rename", llvm::cl::desc("Do not rename output file"), llvm::cl::cat(CGCategory), + llvm::cl::init(false)); +static llvm::cl::opt Verbose("verbose", llvm::cl::desc("Enable verbose logging"), llvm::cl::cat(CGCategory), + llvm::cl::init(false)); + /** * @brief Create output file with given extension * @@ -52,11 +62,6 @@ void generateCG(std::optional& parseTree, llvm::StringRef currentFile) mcgManager.addToManagedGraphs("cg", std::make_unique(), true); Callgraph* cg = mcgManager.getCallgraph("cg"); -#ifndef NDEBUG - metacg::MCGLogger::instance().getConsole()->set_level(spdlog::level::debug); - metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); -#endif - ParseTreeVisitor visitor(cg, currentFile.str()); Fortran::parser::Walk(parseTree, visitor); visitor.postProcess(); @@ -96,62 +101,37 @@ std::string dumpCG() { class CollectCG : public Fortran::frontend::PluginParseTreeAction { public: void executeAction() override { - generateCG(getParsing().parseTree(), getCurrentFile()); - - std::string cgString = dumpCG(); - std::unique_ptr file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); - file->write(cgString.c_str(), cgString.size()); - } -}; + if (Verbose) { + metacg::MCGLogger::instance().getConsole()->set_level(spdlog::level::debug); + metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); + } -/** - * @class CollectCGwithDot - * @brief Like CollectCG but also generates a DOT file of callgraph - * - */ -class CollectCGwithDot : public Fortran::frontend::PluginParseTreeAction { - public: - void executeAction() override { generateCG(getParsing().parseTree(), getCurrentFile()); std::string cgString = dumpCG(); - std::unique_ptr file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); - file->write(cgString.c_str(), cgString.size()); - - // dot file - Callgraph* cg = mcgManager.getCallgraph("cg"); - if (cg == nullptr) { - MCGLogger::logError("No callgraph generated"); - return; + std::unique_ptr file; + if (!NoRename) { + file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); + } else { + file = ::createOutputFile(getInstance(), getCurrentFile(), ""); } + file->write(cgString.c_str(), cgString.size()); - dot::DotGenerator dotGen(cg); - dotGen.generate(); + if (Dot) { + Callgraph* cg = mcgManager.getCallgraph("cg"); + if (cg == nullptr) { + MCGLogger::logError("No callgraph generated"); + return; + } - std::unique_ptr dotfile = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); - std::string dotString = dotGen.getDotString(); - dotfile->write(dotString.c_str(), dotString.size()); - } -}; + dot::DotGenerator dotGen(cg); + dotGen.generate(); -/** - * @class CollectCGNoRename - * @brief Like CollectCG but does not rename output file - * - */ -class CollectCGNoRename : public Fortran::frontend::PluginParseTreeAction { - public: - void executeAction() override { - generateCG(getParsing().parseTree(), getCurrentFile()); - - std::string cgString = dumpCG(); - - std::unique_ptr file = ::createOutputFile(getInstance(), getCurrentFile(), ""); - file->write(cgString.c_str(), cgString.size()); + std::unique_ptr dotfile = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); + std::string dotString = dotGen.getDotString(); + dotfile->write(dotString.c_str(), dotString.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"); diff --git a/cgfcollector/src/ParseTreeVisitor.cpp b/cgfcollector/src/ParseTreeVisitor.cpp index 64116310..7fc8d6a7 100644 --- a/cgfcollector/src/ParseTreeVisitor.cpp +++ b/cgfcollector/src/ParseTreeVisitor.cpp @@ -63,10 +63,7 @@ void ParseTreeVisitor::postProcess() { if (!arg->hasBeenInitialized) continue; - for (const Edge& edge : pf.finalizerEdges) { - edgeM->addEdge(edge, false); - MCGLogger::logDebug("Add edge for potential finalizer: {} -> {}", edge.caller, edge.callee); - } + edgeM->addEdgesForFinalizers(pf); } // sort unique edges diff --git a/cgfcollector/test/multi/cmake_base.txt.in b/cgfcollector/test/multi/cmake_base.txt.in index bf9f35f7..7d6c912c 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 "@CGFCOLLECTOR_WRAPPER@") set(CMAKE_Fortran_FLAGS "") -set(CMAKE_Fortran_COMPILE_OBJECT " -norename -o ") +set(CMAKE_Fortran_COMPILE_OBJECT " -no-rename -o ") set(CMAKE_Fortran_LINK_EXECUTABLE "@CGMERGE2_BIN@ ") set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index 325353f7..443aa8d8 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -11,24 +11,28 @@ flang_bin=${CGFCOLLECTOR_FLANG_BIN:-"flang-new"} -flang_args=("$@") -plugin_name="genCG" +flang_args=() -if [[ "$1" == "-dot" ]]; then - plugin_name="genCGwithDot" - shift - flang_args=("$@") -fi +declare -A OPTION_MAP=( + ["-dot"]="-dot" + ["-no-rename"]="-no-rename" + ["-v"]="--verbose" + ["--verbose"]="--verbose" +) -if [[ "$1" == "-norename" ]]; then - plugin_name="genCGNoRename" +while [[ $# -gt 0 ]]; do + arg="$1" + if [[ -n "${OPTION_MAP[$arg]}" ]]; then + flang_args+=("-mllvm" "${OPTION_MAP[$arg]}") + else + flang_args+=("$arg") + fi shift - flang_args=("$@") -fi +done if ! command -v "$flang_bin" &>/dev/null; then echo "Error: $flang_bin not found in PATH." exit 1 fi -$flang_bin -fc1 -load "@CGFCOLLECTOR_FILE_NAME_LIB@" -plugin "$plugin_name" "${flang_args[@]}" +$flang_bin -fc1 -load "@CGFCOLLECTOR_FILE_NAME_LIB@" -plugin "genCG" "${flang_args[@]}" diff --git a/cgfcollector/tools/test_runner.sh.in b/cgfcollector/tools/test_runner.sh.in index b8fcce2e..de786528 100755 --- a/cgfcollector/tools/test_runner.sh.in +++ b/cgfcollector/tools/test_runner.sh.in @@ -17,6 +17,19 @@ if ! [ -d "$test_case_dir" ]; then exit 1 fi +cgfcollector_options=() +test_cases_to_run=() + +while [[ $# -gt 0 ]]; do + arg="$1" + if [[ "$arg" =~ ^-+ ]]; then + cgfcollector_options+=("$arg") + else + test_cases_to_run+=("$arg") + fi + shift +done + function find_test_dir() { local test_name="${1#*_}" @@ -75,7 +88,7 @@ function run_single_test() return 1 fi - @CGFCOLLECTOR_WRAPPER@ -dot -o "$actual_output" "${input_files[@]}" || { + @CGFCOLLECTOR_WRAPPER@ "${cgfcollector_options[@]}" -o "$actual_output" "${input_files[@]}" || { echo "Failed: could not generate CG" return 1 } @@ -90,10 +103,10 @@ function run_single_test() fi } -if [ $# -gt 0 ]; then +if [ ${#test_cases_to_run[@]} -gt 0 ]; then # specific test cases test_cases=() - for test_name in "$@"; do + for test_name in "${test_cases_to_run[@]}"; do test_dir=$(find_test_dir "$test_name") if [ -n "$test_dir" ]; then test_cases+=("$test_dir:$test_name") From 902692033fdfe27eac08fbbf1c8f102ef4d7638c Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 24 Feb 2026 13:17:51 +0100 Subject: [PATCH 137/143] visuel -> visual and also added some option parsing --- cgfcollector/CMakeLists.txt | 10 ++--- cgfcollector/tools/visual.cpp | 85 +++++++++++++++++++++++++++++++++++ cgfcollector/tools/visuel.cpp | 42 ----------------- 3 files changed, 90 insertions(+), 47 deletions(-) create mode 100644 cgfcollector/tools/visual.cpp delete mode 100644 cgfcollector/tools/visuel.cpp diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 34965675..2b4a27cb 100644 --- a/cgfcollector/CMakeLists.txt +++ b/cgfcollector/CMakeLists.txt @@ -28,12 +28,12 @@ configure_package_config_file( install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake DESTINATION lib/cmake) -# 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) +# visual program to generate dot file from graph +add_executable(${PROJECT_NAME}-visual ${CMAKE_CURRENT_SOURCE_DIR}/tools/visual.cpp) +add_metacg(${PROJECT_NAME}-visual) +add_spdlog_libraries(${PROJECT_NAME}-visual) install( - TARGETS ${PROJECT_NAME}-visuel + TARGETS ${PROJECT_NAME}-visual LIBRARY DESTINATION bin ARCHIVE DESTINATION bin ) diff --git a/cgfcollector/tools/visual.cpp b/cgfcollector/tools/visual.cpp new file mode 100644 index 00000000..0b8a95e7 --- /dev/null +++ b/cgfcollector/tools/visual.cpp @@ -0,0 +1,85 @@ +/** + * File: visual.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 +#include +#include +#include +#include + +static auto console = metacg::MCGLogger::instance().getConsole(); +static auto errConsole = metacg::MCGLogger::instance().getErrConsole(); + +void printUsage() { + std::cout << "Usage: visuel \n\n"; + std::cout << "Reads a call graph from the specified file and generates a DOT file for visualization.\n\n"; + std::cout << "Options:\n"; + std::cout << " -h, --help Show this help message\n"; + std::cout << " -o, --output Specify output DOT file name (default: callgraph.dot)\n"; +} + +int main(int argc, char* argv[]) { + if (argc < 2) { + printUsage(); + return EXIT_FAILURE; + } + + std::string inputFile; + std::string outputFile = "callgraph.dot"; + + for (int i = 1; i < argc; i++) { + std::string arg = argv[i]; + + if (arg == "-h" || arg == "--help") { + printUsage(); + return EXIT_SUCCESS; + } else if (arg == "-o" || arg == "--output") { + if (i + 1 >= argc) { + errConsole->error("Output file name not specified after {}", arg); + return EXIT_FAILURE; + } + outputFile = argv[++i]; + } else { + inputFile = arg; + } + } + + if (inputFile.empty()) { + errConsole->error("No input call graph file specified."); + printUsage(); + return EXIT_FAILURE; + } + + metacg::io::FileSource fs1(inputFile); + + auto mcgReader1 = metacg::io::createReader(fs1); + if (!mcgReader1) { + errConsole->error("Failed to create MCG reader for file: {}", inputFile); + return EXIT_FAILURE; + } + + auto cg = mcgReader1->read(); + if (!cg) { + errConsole->error("Failed to read call graph from file."); + return EXIT_FAILURE; + } + + metacg::io::dot::DotGenerator dotGen(cg.get()); + dotGen.generate(); + + std::ofstream outFile(outputFile); + if (!outFile.is_open()) { + errConsole->error("Could not open output file for writing: {}", outputFile); + return EXIT_FAILURE; + } + outFile << dotGen.getDotString(); + + outFile.close(); + + console->info("DOT file generated successfully: {}", outputFile); + + return EXIT_SUCCESS; +} diff --git a/cgfcollector/tools/visuel.cpp b/cgfcollector/tools/visuel.cpp deleted file mode 100644 index 01bc2c46..00000000 --- a/cgfcollector/tools/visuel.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#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 3ac9a600ad605560d2d9242995e664759a63659e Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 24 Feb 2026 14:09:04 +0100 Subject: [PATCH 138/143] removed visual tool --- cgfcollector/CMakeLists.txt | 10 ----- cgfcollector/tools/visual.cpp | 85 ----------------------------------- 2 files changed, 95 deletions(-) delete mode 100644 cgfcollector/tools/visual.cpp diff --git a/cgfcollector/CMakeLists.txt b/cgfcollector/CMakeLists.txt index 2b4a27cb..e72d207c 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) -# visual program to generate dot file from graph -add_executable(${PROJECT_NAME}-visual ${CMAKE_CURRENT_SOURCE_DIR}/tools/visual.cpp) -add_metacg(${PROJECT_NAME}-visual) -add_spdlog_libraries(${PROJECT_NAME}-visual) -install( - TARGETS ${PROJECT_NAME}-visual - LIBRARY DESTINATION bin - ARCHIVE DESTINATION bin -) - # generate wrapper scripts function( diff --git a/cgfcollector/tools/visual.cpp b/cgfcollector/tools/visual.cpp deleted file mode 100644 index 0b8a95e7..00000000 --- a/cgfcollector/tools/visual.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/** - * File: visual.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 -#include -#include -#include -#include - -static auto console = metacg::MCGLogger::instance().getConsole(); -static auto errConsole = metacg::MCGLogger::instance().getErrConsole(); - -void printUsage() { - std::cout << "Usage: visuel \n\n"; - std::cout << "Reads a call graph from the specified file and generates a DOT file for visualization.\n\n"; - std::cout << "Options:\n"; - std::cout << " -h, --help Show this help message\n"; - std::cout << " -o, --output Specify output DOT file name (default: callgraph.dot)\n"; -} - -int main(int argc, char* argv[]) { - if (argc < 2) { - printUsage(); - return EXIT_FAILURE; - } - - std::string inputFile; - std::string outputFile = "callgraph.dot"; - - for (int i = 1; i < argc; i++) { - std::string arg = argv[i]; - - if (arg == "-h" || arg == "--help") { - printUsage(); - return EXIT_SUCCESS; - } else if (arg == "-o" || arg == "--output") { - if (i + 1 >= argc) { - errConsole->error("Output file name not specified after {}", arg); - return EXIT_FAILURE; - } - outputFile = argv[++i]; - } else { - inputFile = arg; - } - } - - if (inputFile.empty()) { - errConsole->error("No input call graph file specified."); - printUsage(); - return EXIT_FAILURE; - } - - metacg::io::FileSource fs1(inputFile); - - auto mcgReader1 = metacg::io::createReader(fs1); - if (!mcgReader1) { - errConsole->error("Failed to create MCG reader for file: {}", inputFile); - return EXIT_FAILURE; - } - - auto cg = mcgReader1->read(); - if (!cg) { - errConsole->error("Failed to read call graph from file."); - return EXIT_FAILURE; - } - - metacg::io::dot::DotGenerator dotGen(cg.get()); - dotGen.generate(); - - std::ofstream outFile(outputFile); - if (!outFile.is_open()) { - errConsole->error("Could not open output file for writing: {}", outputFile); - return EXIT_FAILURE; - } - outFile << dotGen.getDotString(); - - outFile.close(); - - console->info("DOT file generated successfully: {}", outputFile); - - return EXIT_SUCCESS; -} From 146504d0c98f5f21dfdaba3c2754685fed3e1ef1 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 24 Feb 2026 14:25:40 +0100 Subject: [PATCH 139/143] Updated README.md regarding options parsing and more consistent options --- cgfcollector/README.md | 22 +++++++++---------- cgfcollector/test/multi/cmake_base.txt.in | 2 +- cgfcollector/tools/cgfcollector_wrapper.sh.in | 12 +++++----- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/cgfcollector/README.md b/cgfcollector/README.md index 0506eff3..f3c16439 100644 --- a/cgfcollector/README.md +++ b/cgfcollector/README.md @@ -8,7 +8,7 @@ Flang plugin and generates a call graph from source-level. For single file projects or projects not using modules use: ```sh -cgfcollector_wrapper.sh +cgfcollector_wrapper.sh [options] ``` For any other projects you need a build system. For this we provide another @@ -22,20 +22,20 @@ cgfcollector_comp_wrapper.sh You can also run the plugin directly with Flang: ```sh -flang -fc1 -load "libcgfcollector.so" -plugin "genCG" +flang -fc1 -load "libcgfcollector.so" -plugin "genCG" [options] ``` -There are three kinds of plugins: +Available options: -- `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. +- `--dot`: Additionally generate a DOT file. This is mostly used for debugging. +- `--no-rename`: Do not rename the output file to `.json`. +- `--verbose`: Print additional information during the generation process. 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. -- `test_runner.sh`: run tests. +- `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. +- `test_runner.sh`: Run tests. ## How to build @@ -51,7 +51,7 @@ Paste this into your CMakeLists.txt. ``` set(CMAKE_Fortran_COMPILER ) set(CMAKE_Fortran_FLAGS "") -set(CMAKE_Fortran_COMPILE_OBJECT " -dot -o ") +set(CMAKE_Fortran_COMPILE_OBJECT " --dot -o ") set(CMAKE_Fortran_LINK_EXECUTABLE " ") set(CMAKE_EXECUTABLE_SUFFIX .json) ``` @@ -74,7 +74,7 @@ you probably don't need this. ## Running test -run `test_runner.sh` +Run `test_runner.sh` NOTE: The test `test/multi/fortdepend_deps` has a dependency on [fortdepend](https://fortdepend.readthedocs.io/en/latest/) diff --git a/cgfcollector/test/multi/cmake_base.txt.in b/cgfcollector/test/multi/cmake_base.txt.in index 7d6c912c..6c44914d 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 "@CGFCOLLECTOR_WRAPPER@") set(CMAKE_Fortran_FLAGS "") -set(CMAKE_Fortran_COMPILE_OBJECT " -no-rename -o ") +set(CMAKE_Fortran_COMPILE_OBJECT " --no-rename -o ") set(CMAKE_Fortran_LINK_EXECUTABLE "@CGMERGE2_BIN@ ") set(CMAKE_EXECUTABLE_SUFFIX .json) diff --git a/cgfcollector/tools/cgfcollector_wrapper.sh.in b/cgfcollector/tools/cgfcollector_wrapper.sh.in index 443aa8d8..c3c64f87 100755 --- a/cgfcollector/tools/cgfcollector_wrapper.sh.in +++ b/cgfcollector/tools/cgfcollector_wrapper.sh.in @@ -3,19 +3,21 @@ # Wrapper script for running cgfcollector plugin with flang. # # Usage: -# cgfcollector_wrapper.sh [-dot|-norename] [flang options and source files] +# cgfcollector_wrapper.sh [options] [flang options and source files] # # Options: -# -dot # Generate call graph with dot format -# -norename # Do not rename output files +# --dot # Additionally generate a DOT file. This is mostly used for debugging. +# --no-rename # Do not rename the output file to `.json`. +# --verbose # Print additional information during the generation process. flang_bin=${CGFCOLLECTOR_FLANG_BIN:-"flang-new"} flang_args=() declare -A OPTION_MAP=( - ["-dot"]="-dot" - ["-no-rename"]="-no-rename" + ["-d"]="--dot" + ["--dot"]="--dot" + ["--no-rename"]="--no-rename" ["-v"]="--verbose" ["--verbose"]="--verbose" ) From 1be6138b1c0e675f1f8b2ceefcf612342d15c863 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 24 Feb 2026 15:07:48 +0100 Subject: [PATCH 140/143] clarified comment --- cgfcollector/include/ParseTreeVisitor.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cgfcollector/include/ParseTreeVisitor.h b/cgfcollector/include/ParseTreeVisitor.h index 129e337e..017359f2 100644 --- a/cgfcollector/include/ParseTreeVisitor.h +++ b/cgfcollector/include/ParseTreeVisitor.h @@ -238,14 +238,15 @@ class ParseTreeVisitor { // added to cg in postProcess step std::vector edges; - // all functions + // all functions. This vector collects all functions (+ dummy args) that were discovered during traversal of the parse + // tree. std::vector functions; // 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 + // all types. This vector collects all types that were discovered during traversal of the parse tree. std::vector types; // all interface operators. First is either a symbol of a DefinedOpName or From ba2759bee965a92e0ae986b5d59b71086aa71424 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 24 Feb 2026 16:03:21 +0100 Subject: [PATCH 141/143] simplified main --- cgfcollector/src/Main.cpp | 130 +++++++++++++++----------------------- 1 file changed, 50 insertions(+), 80 deletions(-) diff --git a/cgfcollector/src/Main.cpp b/cgfcollector/src/Main.cpp index f2bd4f27..2f85011f 100644 --- a/cgfcollector/src/Main.cpp +++ b/cgfcollector/src/Main.cpp @@ -26,71 +26,13 @@ static llvm::cl::opt NoRename("no-rename", llvm::cl::desc("Do not rename o static llvm::cl::opt Verbose("verbose", llvm::cl::desc("Enable verbose logging"), llvm::cl::cat(CGCategory), llvm::cl::init(false)); -/** - * @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); - 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; -} - -/** - * @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"); - - ParseTreeVisitor visitor(cg, currentFile.str()); - Fortran::parser::Walk(parseTree, visitor); - visitor.postProcess(); - - mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); -} - -/** - * @brief Dump callgraph as JSON string - * - * @return - */ -std::string dumpCG() { - Callgraph* cg = mcgManager.getCallgraph("cg"); - if (cg == nullptr) { - MCGLogger::logError("No callgraph generated"); - return ""; +void replaceExtension(std::string& filePath, const std::string& newExtension) { + size_t lastDotPos = filePath.find_last_of('.'); + if (lastDotPos != std::string::npos) { + filePath = filePath.substr(0, lastDotPos) + newExtension; + } else { + filePath += newExtension; } - - std::unique_ptr mcgWriter = createWriter(4); - if (!mcgWriter) { - MCGLogger::logError("Unable to create a writer"); - return ""; - }; - - JsonSink jsonSink; - mcgWriter->writeActiveGraph(jsonSink); - - return jsonSink.getJson().dump(); } /** @@ -106,30 +48,58 @@ class CollectCG : public Fortran::frontend::PluginParseTreeAction { metacg::MCGLogger::instance().getConsole()->set_pattern("%v"); } - generateCG(getParsing().parseTree(), getCurrentFile()); + // create and register callgraph + mcgManager.addToManagedGraphs("cg", std::make_unique(), true); + + Callgraph* cg = mcgManager.getCallgraph("cg"); + if (!cg) { + MCGLogger::logError("Failed to create callgraph"); + return; + } + + // traverse parse tree and generate callgraph + std::string currentFile = getCurrentFile().str(); + + ParseTreeVisitor visitor(cg, currentFile); + Fortran::parser::Walk(getParsing().parseTree(), visitor); + visitor.postProcess(); + + mcgManager.mergeIntoActiveGraph(metacg::MergeByName()); + + // create writer + auto mcgWriter = io::createWriter(4); + if (!mcgWriter) { + MCGLogger::logError("Unable to create a writer for format version {}", 4); + return; + } + + io::JsonSink jsonSink; + mcgWriter->write(cg, jsonSink); + + // determine output file name. Honors `-o` option. + std::string outputFile = getInstance().getFrontendOpts().outputFile; + + if (outputFile.empty()) { + outputFile = getCurrentFile().str(); + } - std::string cgString = dumpCG(); - std::unique_ptr file; if (!NoRename) { - file = ::createOutputFile(getInstance(), getCurrentFile(), "json"); - } else { - file = ::createOutputFile(getInstance(), getCurrentFile(), ""); + replaceExtension(outputFile, ".json"); } - file->write(cgString.c_str(), cgString.size()); - if (Dot) { - Callgraph* cg = mcgManager.getCallgraph("cg"); - if (cg == nullptr) { - MCGLogger::logError("No callgraph generated"); - return; - } + std::ofstream os(outputFile); + os << jsonSink.getJson() << std::endl; + // optionally generate dot output + if (Dot) { dot::DotGenerator dotGen(cg); dotGen.generate(); - std::unique_ptr dotfile = ::createOutputFile(getInstance(), getCurrentFile(), "dot"); - std::string dotString = dotGen.getDotString(); - dotfile->write(dotString.c_str(), dotString.size()); + auto dotOutputFile = outputFile; + replaceExtension(dotOutputFile, ".dot"); + + std::ofstream dotOs(dotOutputFile); + dotOs << dotGen.getDotString() << std::endl; } } }; From c93fd5679c4e2a8def9d93dd408f028ffd4ab2e3 Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 24 Feb 2026 16:05:55 +0100 Subject: [PATCH 142/143] add doc to replaceExtension --- cgfcollector/src/Main.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cgfcollector/src/Main.cpp b/cgfcollector/src/Main.cpp index 2f85011f..bb4f7b2a 100644 --- a/cgfcollector/src/Main.cpp +++ b/cgfcollector/src/Main.cpp @@ -26,6 +26,13 @@ static llvm::cl::opt NoRename("no-rename", llvm::cl::desc("Do not rename o static llvm::cl::opt Verbose("verbose", llvm::cl::desc("Enable verbose logging"), llvm::cl::cat(CGCategory), llvm::cl::init(false)); +/** + * @brief Replace the file extension of filePath with newExtension. If filePath does not have an extension, append + * newExtension. + * + * @param filePath + * @param newExtension + */ void replaceExtension(std::string& filePath, const std::string& newExtension) { size_t lastDotPos = filePath.find_last_of('.'); if (lastDotPos != std::string::npos) { From 872fd22a438343cd3b52ffa4bec4d5aabee9b0ec Mon Sep 17 00:00:00 2001 From: gomfol12 Date: Tue, 24 Feb 2026 17:49:55 +0100 Subject: [PATCH 143/143] full-build container add missing mlir and flang deps --- container/full-build | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/container/full-build b/container/full-build index f5fcec2e..d2ac9470 100644 --- a/container/full-build +++ b/container/full-build @@ -23,7 +23,8 @@ RUN wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted. apt-get update && \ apt-get install -y libllvm18 llvm-18 llvm-18-dev llvm-18-runtime \ clang-18 clang-tools-18 libclang-common-18-dev libclang-18-dev libclang1-18 \ - lld-18 + lld-18 \ + libmlir-18-dev mlir-18-tools flang-18 RUN bash -c "ln -s $(which clang-18) /usr/bin/clang" && \ bash -c "ln -s $(which clang++-18) /usr/bin/clang++" && \ bash -c "ln -s $(which llvm-config-18) /usr/bin/llvm-config" @@ -55,7 +56,9 @@ RUN cmake -S . -B build -GNinja -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_MODULE_PATH=/opt/metacg/.venv/share/Pytest/cmake \ -DMETACG_BUILD_PYMETACG_TESTS=ON \ -DCMAKE_VERBOSE_MAKEFILE=ON \ - -DLLVM_EXTERNAL_LIT=/usr/lib/llvm-18/build/utils/lit/lit.py + -DLLVM_EXTERNAL_LIT=/usr/lib/llvm-18/build/utils/lit/lit.py \ + -DMLIR_DIR=/usr/lib/llvm-18/lib/cmake/mlir \ + -DFlang_DIR=/usr/lib/llvm-18/lib/cmake/flang RUN cmake --build build --parallel