From 8ea0c571e437d7ae7c3064d41ff5374c170e3d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20H=C3=B6lscher?= Date: Mon, 16 May 2022 13:38:48 +0200 Subject: [PATCH 1/2] Added some simple UnitTests --- CMakeLists.txt | 14 ++++++ UnitTest/CMakeLists.txt | 52 +++++++++++++++++++++ UnitTest/UnitTest.cpp | 101 ++++++++++++++++++++++++++++++++++++++++ helper.sh | 2 +- include/AbstractCache.h | 43 ++++++++--------- include/AbstractState.h | 64 ++++++++++++++----------- 6 files changed, 226 insertions(+), 50 deletions(-) create mode 100644 UnitTest/CMakeLists.txt create mode 100644 UnitTest/UnitTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bbc8701..a550b68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,19 @@ cmake_minimum_required(VERSION 3.13.4) project(RTSA-lab01-CacheAnalysis) +#=============================================================================== +# 0. Add GoogleTest +#=============================================================================== +include(FetchContent) +FetchContent_Declare( + googletest + URL + URL https://github.com/google/googletest/archive/05CC6081FCBD0071053DE78238E136B3.zip +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + #=============================================================================== # 1. VERIFY LLVM INSTALLATION DIR # This is just a bit of a sanity checking. @@ -108,4 +121,5 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") # available for the sub-projects. #=============================================================================== add_subdirectory(CacheAnalysisPass) # Use your pass name here. +add_subdirectory(UnitTest) #add_subdirectory(lib) \ No newline at end of file diff --git a/UnitTest/CMakeLists.txt b/UnitTest/CMakeLists.txt new file mode 100644 index 0000000..ea179d5 --- /dev/null +++ b/UnitTest/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 3.13.4) +project(UnitTest) + +#=============================================================================== +# 1. LOAD LLVM CONFIGURATION +#=============================================================================== +# Set this to a valid LLVM installation dir +set(LT_LLVM_INSTALL_DIR "" CACHE PATH "LLVM installation directory") + +# Add the location of LLVMConfig.cmake to CMake search paths (so that +# find_package can locate it) +list(APPEND CMAKE_PREFIX_PATH "${LT_LLVM_INSTALL_DIR}/lib/cmake/llvm/") + +# FIXME: This is a warkaround for #25. Remove once resolved and use +# find_package(LLVM 11.0.0 REQUIRED CONFIG) instead. +find_package(LLVM REQUIRED CONFIG) + +# UnitTest includes headers from LLVM - update the include paths accordingly +include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}, "${CMAKE_CURRENT_SOURCE_DIR}/../include") + +#=============================================================================== +# 2. LLVM-TUTOR BUILD CONFIGURATION +#=============================================================================== +# Use the same C++ standard as LLVM does +set(CMAKE_CXX_STANDARD 14 CACHE STRING "") + +# LLVM is normally built without RTTI. Be consistent with that. +if(NOT LLVM_ENABLE_RTTI) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") +endif() + +#=============================================================================== +# 3. ADD THE TARGET +#=============================================================================== +enable_testing() + +add_executable(UnitTest + # List your source files here. + UnitTest.cpp +) + + +# Allow undefined symbols in shared objects on Darwin (this is the default +# behaviour on Linux) +target_link_libraries(UnitTest + "$<$:-undefined dynamic_lookup>") + target_link_libraries( + UnitTest + gtest_main +) +include(GoogleTest) +gtest_discover_tests(UnitTest) \ No newline at end of file diff --git a/UnitTest/UnitTest.cpp b/UnitTest/UnitTest.cpp new file mode 100644 index 0000000..1cd62e0 --- /dev/null +++ b/UnitTest/UnitTest.cpp @@ -0,0 +1,101 @@ +#include +#include + +#include "../include/AbstractCache.h" + +void fillSet(AbstractState &In, unsigned int Set) { + for (int I = 0; I < 4; I++) { + In.Sets[Set].Associativity[I].Blocks.push_front(I); + } +} + +// Joined Set should remain the same +TEST(UnitTest, MustJoinSameStates) { + AbstractCache AC; + AC.addEmptyNode(0); + fillSet(AC.Nodes[0], 0); + AC.addEmptyNode(1); + fillSet(AC.Nodes[1], 0); + + AC.Nodes[0].mustJoin(AC.Nodes[1]); + for (auto B : AC.Nodes[0].Sets[0].Associativity) { + uint Age = B.first; + EXPECT_EQ(AC.Nodes[0].Sets[0].Associativity[Age].Blocks.front(), + AC.Nodes[1].Sets[0].Associativity[Age].Blocks.front()); + } +} + +void counterFillSet(AbstractState &In, unsigned int Set) { + for (int I = 0; I < 4; I++) { + In.Sets[Set].Associativity[I].Blocks.push_front((I + 1) % 4); + } +} + +// Resulting state should be +// Set[0]: +// Age[0]: +// Age[1]: 1 +// Age[2]: 2 +// Age[3]: 0 3 +TEST(UnitTest, MustJoinDifferentStates) { + AbstractCache AC; + AC.addEmptyNode(0); + counterFillSet(AC.Nodes[0], 0); + AC.addEmptyNode(1); + fillSet(AC.Nodes[1], 0); + std::cout << "==Before:==\n"; + AC.Nodes[0].dumpSet(0); + AC.Nodes[1].dumpSet(0); + + AC.Nodes[0].mustJoin(AC.Nodes[1]); + std::cout << "\n==After:==\n"; + AC.Nodes[0].dumpSet(0); + for (auto B : AC.Nodes[0].Sets[0].Associativity) { + uint Age = B.first; + switch (Age) { + case 0: + EXPECT_TRUE(AC.Nodes[0].Sets[0].Associativity[Age].Blocks.empty()); + break; + case 1: + EXPECT_EQ(AC.Nodes[0].Sets[0].Associativity[Age].Blocks.front(), 1); + break; + case 2: + EXPECT_EQ(AC.Nodes[0].Sets[0].Associativity[Age].Blocks.front(), 2); + break; + case 3: + // this test is not very exact the Set 0 with age 3 should contain (0,3) + // in arbitrary order + EXPECT_EQ(AC.Nodes[0].Sets[0].Associativity[Age].Blocks.size(), 2); + break; + default: + // should never be reached! + EXPECT_TRUE(false); + } + } +} + +void fillSetHighNumbers(AbstractState &In, unsigned int Set) { + for (int I = 0; I < 4; I++) { + In.Sets[Set].Associativity[I].Blocks.push_front(10+I); + } +} + +// resulting state should be empty +TEST(UnitTest, MustJoinDisjunctStates) { + AbstractCache AC; + AC.addEmptyNode(0); + fillSetHighNumbers(AC.Nodes[0], 0); + AC.addEmptyNode(1); + fillSet(AC.Nodes[1], 0); + std::cout << "==Before:==\n"; + AC.Nodes[0].dumpSet(0); + AC.Nodes[1].dumpSet(0); + + AC.Nodes[0].mustJoin(AC.Nodes[1]); + std::cout << "\n==After:==\n"; + AC.Nodes[0].dumpSet(0); + for (auto B : AC.Nodes[0].Sets[0].Associativity) { + uint Age = B.first; + EXPECT_TRUE(AC.Nodes[0].Sets[0].Associativity[Age].Blocks.empty()); + } +} \ No newline at end of file diff --git a/helper.sh b/helper.sh index c4138d6..04e1414 100755 --- a/helper.sh +++ b/helper.sh @@ -11,7 +11,7 @@ config () { mkdir build cd build echo "==== Configuring cmake ====" - cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DLT_LLVM_INSTALL_DIR=$LLVM_DIR ../CacheAnalysisPass/ + cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 -DLT_LLVM_INSTALL_DIR=/usr .. cd .. cp build/compile_commands.json compile_commands.json echo "==== Done! ====" diff --git a/include/AbstractCache.h b/include/AbstractCache.h index cbec453..7829e23 100644 --- a/include/AbstractCache.h +++ b/include/AbstractCache.h @@ -1,4 +1,3 @@ - #ifndef ABSTRACHTCACHESTATE_H #define ABSTRACHTCACHESTATE_H @@ -11,9 +10,6 @@ #include #include -#include -#include - #include "AbstractState.h" #include "Address.h" #include "ConcreteState.h" @@ -292,34 +288,34 @@ public: // everything is public, because IDGAF removeNestedLoops(LoopBody, OrigNodeToUnrolledNode); if (Verbose && FoundLoopBody) { - llvm::outs() << "Found LoopHead @: " << NodeNr << "\n"; - llvm::outs() << "With LoopTail @: " << LoopTail << "\n"; - llvm::outs() << "With Body: {\n"; + std::cout << "Found LoopHead @: " << NodeNr << "\n"; + std::cout << "With LoopTail @: " << LoopTail << "\n"; + std::cout << "With Body: {\n"; int I = 1; for (auto Node : LoopBody) { - llvm::outs() << Node << ", "; + std::cout << Node << ", "; if (!(I++ % 5)) { - llvm::outs() << "\n"; + std::cout << "\n"; } } - llvm::outs() << "}\n"; - llvm::outs() << "Unrolled States: {\n"; + std::cout << "}\n"; + std::cout << "Unrolled States: {\n"; I = 1; for (auto Node : LoopBody) { - llvm::outs() << OrigNodeToUnrolledNode[Node] << ", "; + std::cout << OrigNodeToUnrolledNode[Node] << ", "; if (!(I++ % 5)) { - llvm::outs() << "\n"; + std::cout << "\n"; } } - llvm::outs() << "}\n"; + std::cout << "}\n"; I = 1; - llvm::outs() << "OrigNodeToUnrolledNode: {\n"; + std::cout << "OrigNodeToUnrolledNode: {\n"; for (auto Nr : OrigNodeToUnrolledNode) { - llvm::outs() << Nr.first << "->" << Nr.second << ", "; + std::cout << Nr.first << "->" << Nr.second << ", "; if (!(I++ % 3)) - llvm::outs() << "\n"; + std::cout << "\n"; } - llvm::outs() << "}\n"; + std::cout << "}\n"; } } } @@ -400,19 +396,19 @@ public: // everything is public, because IDGAF * */ void dumpEdges() { - llvm::outs() << "Dumping Edges:\n"; + std::cout << "Dumping Edges:\n"; for (auto const &E : Edges) { - llvm::outs() << E.first; + std::cout << E.first; bool FirstPrint = true; for (unsigned int To : E.second) { if (FirstPrint) { - llvm::outs() << " -> " << To; + std::cout << " -> " << To; FirstPrint = false; } else { - llvm::outs() << ", " << To; + std::cout << ", " << To; } } - llvm::outs() << "\n"; + std::cout << "\n"; } } @@ -453,5 +449,6 @@ public: // everything is public, because IDGAF Nodes[E.first].dump(); } } + }; // namespace #endif // ABSTRACHTCACHESTATE_H \ No newline at end of file diff --git a/include/AbstractState.h b/include/AbstractState.h index 46149d6..25214f8 100644 --- a/include/AbstractState.h +++ b/include/AbstractState.h @@ -12,8 +12,6 @@ #include #include -#include - #include "Address.h" // Forward declarations @@ -107,8 +105,8 @@ public: // everything is public, because IDGAF void setUnrolled(unsigned int In) { Unrolled = In; } bool operator==(AbstractState In) { - for (int Index; Index < 16; Index++) { - for (int Age; Age < 4; Age++) { + for (int Index = 0; Index < 16; Index++) { + for (int Age = 0; Age < 4; Age++) { for (auto E1 : Sets[Index].Associativity[Age].Blocks) { // find E1 in In States Set and Age. if (std::find(In.Sets[Index].Associativity[Age].Blocks.begin(), @@ -203,46 +201,60 @@ public: // everything is public, because IDGAF } } if (Verbose) { - llvm::outs() << "Before:\n"; + std::cout << "Before:\n"; this->dump(); } // update this with PreAddr this->update(PreAddr); if (Verbose) { - llvm::outs() << "Update Tag: " << PreAddr.Tag << "\n"; - llvm::outs() << "Update Set: " << PreAddr.Index << "\n"; - llvm::outs() << "After:\n"; + std::cout << "Update Tag: " << PreAddr.Tag << "\n"; + std::cout << "Update Set: " << PreAddr.Index << "\n"; + std::cout << "After:\n"; this->dump(); } } - void dump() { - llvm::outs() << Addr << " {\n"; - llvm::outs() << "Unrolled: " << Unrolled << "\n"; - llvm::outs() << "Computed: " << Computed << "\n"; - llvm::outs() << "Predecessors: "; - for (auto PreNr : Predecessors) { - llvm::outs() << PreNr << " "; - } - llvm::outs() << "\n"; + void dumpSet(unsigned int Set) { + std::cout << Addr << " {\n"; - llvm::outs() << "Successors: "; - for (auto SuccNr : Successors) { - llvm::outs() << SuccNr << " "; + std::cout << "Set[" << Set << "]: \n"; + for (auto EntryPair : this->Sets[Set].Associativity) { + std::cout << " Age[" << EntryPair.first << "]: "; + for (auto Block : EntryPair.second.Blocks) { + std::cout << Block << " "; + } + std::cout << "\n"; + } + std::cout << "}\n"; + } + + void dump() { + std::cout << Addr << " {\n"; + std::cout << "Unrolled: " << Unrolled << "\n"; + std::cout << "Computed: " << Computed << "\n"; + std::cout << "Predecessors: "; + for (auto PreNr : Predecessors) { + std::cout << PreNr << " "; } - llvm::outs() << "\n"; + std::cout << "\n"; + + std::cout << "Successors: "; + for (auto SuccNr : Successors) { + std::cout << SuccNr << " "; + } + std::cout << "\n"; for (auto SetPair : Sets) { - llvm::outs() << "Set[" << SetPair.first << "]: \n"; + std::cout << "Set[" << SetPair.first << "]: \n"; for (auto EntryPair : SetPair.second.Associativity) { - llvm::outs() << " Age[" << EntryPair.first << "]: "; + std::cout << " Age[" << EntryPair.first << "]: "; for (auto Block : EntryPair.second.Blocks) { - llvm::outs() << Block << " "; + std::cout << Block << " "; } - llvm::outs() << "\n"; + std::cout << "\n"; } } - llvm::outs() << "}\n"; + std::cout << "}\n"; } }; // namespace From 3264029256016e06256ecf56bd9ccd10b0c4fb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nils=20H=C3=B6lscher?= Date: Mon, 16 May 2022 13:44:23 +0200 Subject: [PATCH 2/2] Updated readme for UnitTests --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 7c2eb29..5a0e609 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ This is my personally preferred IDE setup for C/C++ and by no means needed to ac clangd, Clang-Format, CodeLLDB, +C++ TestMate, Docker and Remote Development @@ -151,6 +152,12 @@ You can also set the following variables in the CacheAnalysisPass/CacheAnalysisP Helpful to understand what the program does but not very so much for the actual exercise. +## UnitTest + +The best way to see what your function does is to use the UnitTest.cpp. +With "C++ TestMate" install you can simply run or debug the test from the side panel in VS Code (Flask Icon). +The "C++ TestMate" is not installed in the VM as I just added this feature now. +Please feel free to add more test cases to your liking in UnitTest.cpp. ## Use the Helper script Again if you work on a Pool PC use poolhelper.sh insted of the helper.sh script.