From 1011655d2ef76a0c0aa29dbbff091dab139198e3 Mon Sep 17 00:00:00 2001 From: Roland Reichwein Date: Sat, 24 Oct 2020 16:32:18 +0200 Subject: Add FlowGraph --- Makefile | 19 +++--- flowgraph/data.cpp | 1 + flowgraph/data.h | 51 ++++++++++++++++ flowgraph/graph.cpp | 1 + flowgraph/graph.h | 20 +++++++ flowgraph/node.cpp | 9 +++ flowgraph/node.h | 150 +++++++++++++++++++++++++++++++++++++++++++++++ flowgraph/storage.cpp | 8 +++ flowgraph/storage.h | 85 +++++++++++++++++++++++++++ test-asm.cpp | 132 ----------------------------------------- test-cpp.cpp | 78 ------------------------ test-cppbnf.cpp | 45 -------------- test-elf.cpp | 71 ---------------------- test-grammer.cpp | 65 -------------------- test-lexer.cpp | 39 ------------ test-minicc.cpp | 8 --- tests/test-asm.cpp | 132 +++++++++++++++++++++++++++++++++++++++++ tests/test-cpp.cpp | 78 ++++++++++++++++++++++++ tests/test-cppbnf.cpp | 45 ++++++++++++++ tests/test-elf.cpp | 71 ++++++++++++++++++++++ tests/test-flowgraph.cpp | 50 ++++++++++++++++ tests/test-grammer.cpp | 65 ++++++++++++++++++++ tests/test-lexer.cpp | 39 ++++++++++++ tests/test-minicc.cpp | 8 +++ 24 files changed, 825 insertions(+), 445 deletions(-) create mode 100644 flowgraph/data.cpp create mode 100644 flowgraph/data.h create mode 100644 flowgraph/graph.cpp create mode 100644 flowgraph/graph.h create mode 100644 flowgraph/node.cpp create mode 100644 flowgraph/node.h create mode 100644 flowgraph/storage.cpp create mode 100644 flowgraph/storage.h delete mode 100644 test-asm.cpp delete mode 100644 test-cpp.cpp delete mode 100644 test-cppbnf.cpp delete mode 100644 test-elf.cpp delete mode 100644 test-grammer.cpp delete mode 100644 test-lexer.cpp delete mode 100644 test-minicc.cpp create mode 100644 tests/test-asm.cpp create mode 100644 tests/test-cpp.cpp create mode 100644 tests/test-cppbnf.cpp create mode 100644 tests/test-elf.cpp create mode 100644 tests/test-flowgraph.cpp create mode 100644 tests/test-grammer.cpp create mode 100644 tests/test-lexer.cpp create mode 100644 tests/test-minicc.cpp diff --git a/Makefile b/Makefile index 38e5759..316e21b 100644 --- a/Makefile +++ b/Makefile @@ -63,19 +63,24 @@ PROGSRC=\ coff.cpp \ debug.cpp \ elf.cpp \ + flowgraph/data.cpp \ + flowgraph/graph.cpp \ + flowgraph/node.cpp \ + flowgraph/storage.cpp \ file.cpp \ grammer.cpp \ lexer.cpp \ minicc.cpp \ TESTSRC=\ - test-cpp.cpp \ - test-cppbnf.cpp \ - test-elf.cpp \ - test-grammer.cpp \ - test-lexer.cpp \ - test-minicc.cpp \ - test-asm.cpp \ + tests/test-cpp.cpp \ + tests/test-cppbnf.cpp \ + tests/test-elf.cpp \ + tests/test-flowgraph.cpp \ + tests/test-grammer.cpp \ + tests/test-lexer.cpp \ + tests/test-minicc.cpp \ + tests/test-asm.cpp \ googlemock/src/gmock-all.cpp \ googletest/src/gtest-all.cpp \ $(PROGSRC) diff --git a/flowgraph/data.cpp b/flowgraph/data.cpp new file mode 100644 index 0000000..9dd6ef8 --- /dev/null +++ b/flowgraph/data.cpp @@ -0,0 +1 @@ +#include "data.h" diff --git a/flowgraph/data.h b/flowgraph/data.h new file mode 100644 index 0000000..353567c --- /dev/null +++ b/flowgraph/data.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +namespace FlowGraph { + + // Explicitely not including size + enum class DataType + { + Size, + Int, + UInt, + Pointer, + Bool, + Char, + UChar, + }; + + class Storage; ///< forward declaration + + // Argument for Operations + // -> includes identity of data point, e.g. a local int variable + // Built up as a list of Data instances for global and local data points in parallel to FlowGraph + class Data + { + public: + Data(DataType type, std::shared_ptr storage):m_type(type) {} + DataType type() const { return m_type; } + private: + const DataType m_type; + std::shared_ptr m_storage; + }; + +} + +namespace GlobalData { + + // variable of simple or struct type + class Element + { + private: + size_t size; + std::vector data; // may be empty if uninitialized + }; + + class Segment: public std::vector + { + }; + +} diff --git a/flowgraph/graph.cpp b/flowgraph/graph.cpp new file mode 100644 index 0000000..e8b6b5e --- /dev/null +++ b/flowgraph/graph.cpp @@ -0,0 +1 @@ +#include "graph.h" diff --git a/flowgraph/graph.h b/flowgraph/graph.h new file mode 100644 index 0000000..265a3bd --- /dev/null +++ b/flowgraph/graph.h @@ -0,0 +1,20 @@ +#pragma once + +#include "node.h" + +#include +#include +#include +#include +#include + +namespace FlowGraph { + + class Graph: public std::vector> + { + public: + Graph() {} + }; + +} + diff --git a/flowgraph/node.cpp b/flowgraph/node.cpp new file mode 100644 index 0000000..fc55ef6 --- /dev/null +++ b/flowgraph/node.cpp @@ -0,0 +1,9 @@ +#include "node.h" + +#include "data.h" + +using namespace FlowGraph; + +Data FlowGraph::MakeLocalPointer(const std::string& name) { return Data(DataType::Pointer, std::make_shared(name)); } +Data FlowGraph::MakeLocalSize(const std::string& name) { return Data(DataType::Size, std::make_shared(name)); } + diff --git a/flowgraph/node.h b/flowgraph/node.h new file mode 100644 index 0000000..37af95a --- /dev/null +++ b/flowgraph/node.h @@ -0,0 +1,150 @@ +#pragma once + +#include "data.h" +#include "storage.h" + +namespace FlowGraph { + + // Node in Graph: Building block of the graph + // Abstracts actions of program flow, yet machine independent, + // to be converted later into machine code + // Basic elements: + // - Subroutine calls + // - Arithmetic/logical operations + // Arguments (Data instances) will be provided explicitly from outside + class Node + { + public: + virtual ~Node() {}; // force class to be polymorphic + }; + + // Memory on Heap: new and delete + class AllocateDynamic: public Node + { + public: + AllocateDynamic(Data& location, Data& size): m_location(location), m_size(size) {} + private: + Data m_location; // in/out: Pointer + Data m_size; // in: Size + }; + + class DeallocateDynamic: public Node + { + public: + DeallocateDynamic(Data& location) : m_location(location) {} // in + private: + Data m_location; // in: Pointer + }; + + Data MakeLocalPointer(const std::string& name); + Data MakeLocalSize(const std::string& name); + + class MemCopy: public Node + { + public: + MemCopy(Data& destination, Data& source, Data& size): m_destination(destination), m_source(source), m_size(size) {} + private: + Data m_destination; // Pointer + Data m_source; // Pointer + Data m_size; // in bytes + }; + + class Move: public Node + { + public: + Move(Data& destination, Data& source): m_destination(destination), m_source(source) {} + private: + Data m_destination; + Data m_source; + }; + + enum class JumpVariant + { + Unconditional, + GT, + LT, + GE, + LE, + EQ, + NE + }; + + // Unconditional Jump, conditional Jump + class Jump: public Node + { + public: + Jump(JumpVariant variant, Data& source0, Data& source1, std::shared_ptr destination): + m_variant(variant), + m_source0(source0), + m_source1(source1), + m_destination(destination) + {} + private: + JumpVariant m_variant; + Data m_source0; + Data m_source1; + std::shared_ptr m_destination; // successor on branch + }; + + // Call Subroutine + class Call: public Node + { + public: + Call(std::shared_ptr destination, std::vector arguments): m_destination(destination), m_arguments(arguments) {} + private: + std::shared_ptr m_destination; + std::vector m_arguments; + }; + + // Return from Subroutine + class Return: public Node + { + public: + Return(std::vector returnValues): m_returnValues(returnValues) {} + private: + std::vector m_returnValues; + }; + + enum class UnaryOperationType { + Increment, + Decrement, + Negate + }; + + class UnaryOperation: public Node + { + public: + UnaryOperation(Data& destination, Data& source): m_destination(destination), m_source(source) {} + private: + Data m_destination; + Data m_source; + }; + + enum class BinaryOperationType { + Add, + Subtract, + Multiply, + Divide, + Modulo, + ShiftRight, + ShiftLeft, + BitwiseAnd, + BitwiseOr, + BitwiseXor, + BitwiseNot, + LogicalAnd, + LogicalOr, + LogicalNot + }; + + class BinaryOperation: public Node + { + public: + BinaryOperation(Data& destination, Data& source0, Data& source1): m_destination(destination), m_source0(source0), m_source1(source1) {} + private: + Data m_destination; + Data m_source0; + Data m_source1; + }; + +} diff --git a/flowgraph/storage.cpp b/flowgraph/storage.cpp new file mode 100644 index 0000000..f78a65d --- /dev/null +++ b/flowgraph/storage.cpp @@ -0,0 +1,8 @@ +#include "storage.h" + +using namespace std::string_literals; + +FlowGraph::TemporaryStorage::TemporaryStorage(LocalScope& scope): + m_name("__local_"s + std::to_string(scope.getNewIndex())) +{} + diff --git a/flowgraph/storage.h b/flowgraph/storage.h new file mode 100644 index 0000000..c2fa7c5 --- /dev/null +++ b/flowgraph/storage.h @@ -0,0 +1,85 @@ +#pragma once + +#include "data.h" + +#include +#include +#include + +namespace FlowGraph { + + // Parameter to Data class, defining where the data is stored + // Explicitely not including size + // But including label/data/pointer + // -> includes identity of certain data point + class Storage + { + public: + virtual ~Storage() {} // force class to be polymorphic + }; + + class Constant: public Storage + { + public: + Constant(std::vector value) {} // little endian data + const std::vector& value() const { return m_value; } + private: + std::vector m_value; + }; + + class GlobalStorage : public Storage + { + public: + GlobalStorage(const std::string& name): m_name(name) {} + const std::string& name() const { return m_name; } + private: + std::string m_name; + }; + + class LocalStorage : public Storage + { + public: + LocalStorage(const std::string& name): m_name(name) {} + const std::string& name() const { return m_name; } + private: + std::string m_name; + }; + + // Provide a context for local temporaries name generation + class LocalScope + { + public: + size_t getNewIndex() { return m_index++; } + private: + size_t m_index{ 0 }; + }; + + // intermediate results, anonymous values + // use generated name + class TemporaryStorage : public Storage + { + public: + TemporaryStorage(LocalScope& scope); + const std::string& name() const { return m_name; } + private: + std::string m_name; + }; + + // dereferenced pointer + class PointeeStorage : public Storage + { + public: + PointeeStorage(const Data& pointer, const Data& offset): m_pointer(pointer), m_offset(offset) { + if (pointer.type() != DataType::Pointer) + throw std::runtime_error("Pointer argument must be a DataType::Pointer"); + if (offset.type() != DataType::Size) + throw std::runtime_error("Offset argument must be a DataType::Size"); + } + Data pointer() { return m_pointer; } + Data offset() { return m_offset; } + private: + Data m_pointer; + Data m_offset; + }; + +} diff --git a/test-asm.cpp b/test-asm.cpp deleted file mode 100644 index 2d3afa0..0000000 --- a/test-asm.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "asm/chunk.h" -#include "asm/assembler.h" -#include "asm/segment.h" -#include "asm/intel64/all_ops.h" - -#include "minicc.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std::string_literals; -namespace fs = std::filesystem; - -class AsmTest: public ::testing::Test -{ -protected: - AsmTest() { - //debug = true; - } - ~AsmTest() { - } - void SetUp(){ - } - void TearDown(){ - } -}; - -TEST_F(AsmTest, Intel64_add) { - Segment segment; - Asm::Args args{{Asm::Args::Register32("eax"), Asm::Args::Immediate32(1)}}; - segment.push_back(makeOp("add", args)); - - ASSERT_EQ(segment.size(), 1); - ASSERT_EQ(segment.getCode(), std::vector({0x05, 0x01, 0x00, 0x00, 0x00})); -} - -TEST_F(AsmTest, Intel64_int_0) { - Segment segment; - Asm::Args args{{Asm::Args::Immediate8(0)}}; - segment.push_back(makeOp("int", args)); - - ASSERT_EQ(segment.size(), 1); - ASSERT_EQ(segment.getCode(), std::vector{0xCE}); -} - -TEST_F(AsmTest, Intel64_int_1) { - Segment segment; - Asm::Args args{{Asm::Args::Immediate8(1)}}; - segment.push_back(makeOp("int", args)); - - ASSERT_EQ(segment.size(), 1); - ASSERT_EQ(segment.getCode(), std::vector{0xF1}); -} - -TEST_F(AsmTest, Intel64_int_5) { - Segment segment; - Asm::Args args{{Asm::Args::Immediate8(5)}}; - segment.push_back(makeOp("int", args)); - - ASSERT_EQ(segment.size(), 1); - ASSERT_EQ(segment.getCode(), std::vector({0xCD, 0x05})); -} - -TEST_F(AsmTest, Intel64_nop) { - Segment segment; - segment.push_back(makeOp("nop")); - - ASSERT_EQ(segment.size(), 1); - ASSERT_EQ(segment.getCode(), std::vector{0x90}); -} - -TEST_F(AsmTest, Intel64_ret) { - Segment segment; - segment.push_back(makeOp("ret")); - - ASSERT_EQ(segment.size(), 1); - ASSERT_EQ(segment.getCode(), std::vector{0xC3}); -} - -TEST_F(AsmTest, Intel64_multiple) { - Segment segment; - - segment.push_back(makeOp("nop")); - Asm::Args args0{{Asm::Args::Immediate8(5)}}; - segment.push_back(makeOp("int", args0)); - segment.push_back(makeOp("ret")); - segment.push_back(makeLabel("data1")); - segment.push_back(makeOp("ret")); - Asm::Args args1{{Asm::Args::Label("data1")}}; - segment.push_back(makeOp("jmp", args1)); - segment.push_back(makeData({1, 2, 3})); - - segment.insertAddresses(); - - ASSERT_EQ(segment.size(), 7); - ASSERT_EQ(segment.getCode(), std::vector( - { - 0x90, // nop - 0xCD, 0x05, // int 5 - 0xC3, // ret - // data1: - 0xC3, // ret - 0xE9, 0xFF, 0xFF, 0xFF, 0xFF, // jmp data1 - 0x01, 0x02, 0x03 // data - })); - - segment.optimize(); - - ASSERT_EQ(segment.size(), 7); - ASSERT_EQ(segment.getCode(), std::vector( - { - 0x90, // nop - 0xCD, 0x05, // int 5 - 0xC3, // ret - // data1: - 0xC3, // ret - 0xEB, 0xFF, // jmp data1 - 0x01, 0x02, 0x03 // data - })); -} diff --git a/test-cpp.cpp b/test-cpp.cpp deleted file mode 100644 index 513a3a5..0000000 --- a/test-cpp.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include "bnf.h" -#include "cpp.h" -#include "cppbnf.h" -#include "lexer.h" -#include "grammer.h" -#include "minicc.h" -#include "debug.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -class CppTest: public ::testing::Test -{ -protected: - CppTest() { - debug = true; - } - ~CppTest() { - } -}; - -TEST_F(CppTest, preprocessing_tokenize) { - CPP cpp; - auto pp_tokens = cpp.preprocessing_tokenize("int main() { return 1; }"); - - ASSERT_EQ(pp_tokens.size(), 9); - - auto tokens = cpp.tokens_from_pptokens(pp_tokens); - - ASSERT_EQ(tokens.size(), 9); - - auto nodes = cpp.analysis(tokens); - - ASSERT_EQ(nodes.size(), 60/*44*/); -} - -TEST_F(CppTest, preprocessing_tokenize_compile_error) { - CPP cpp; - auto ppTree = cpp.preprocessing_tokenize("in ma"); - - auto tokens = cpp.tokens_from_pptokens(ppTree); - - ASSERT_EQ(tokens.size(), 2); - - try { - auto nodes = cpp.analysis(tokens); - } catch (const std::exception& ex) { - EXPECT_EQ(ex.what(), "Compile error"s); - return; - } - - FAIL() << "Exception expected"; -} - -TEST(Cpp, compile) { - CPP cpp; - - cpp.compile("int main() { return 1 + 1; }"); -} - -TEST(Cpp, compile_2_times) { - CPP cpp; - - cpp.compile("int main() { return (1 + 2) * 2; }"); - cpp.compile("int main() { return 1 + 2 * 2; }"); -} - diff --git a/test-cppbnf.cpp b/test-cppbnf.cpp deleted file mode 100644 index e365574..0000000 --- a/test-cppbnf.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "bnf.h" -#include "cpp.h" -#include "cppbnf.h" -#include "lexer.h" -#include "grammer.h" -#include "minicc.h" -#include "debug.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -class CppBnfTest: public ::testing::Test -{ -protected: - CppBnfTest() { - //debug = true; - } - ~CppBnfTest() { - } -}; - -TEST_F(CppBnfTest, LexicalBnf) { - auto bnf = SubBNF(CPPBNF::GetCppBNFLex(), "preprocessing-token"); - - EXPECT_TRUE(CPPBNF::valid(bnf)); - EXPECT_TRUE(CPPBNF::validLex(bnf)); -} - -TEST_F(CppBnfTest, GrammarBnf) { - auto bnf = SubBNF(CPPBNF::GetCppBNFGram(), "translation-unit"); - - EXPECT_TRUE(CPPBNF::valid(bnf)); -} - diff --git a/test-elf.cpp b/test-elf.cpp deleted file mode 100644 index 0bf1d42..0000000 --- a/test-elf.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "elf.h" -#include "minicc.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std::string_literals; -namespace fs = std::filesystem; - -class ElfTest: public ::testing::Test -{ -protected: - ElfTest() { - //debug = true; - } - ~ElfTest() { - } - fs::path TempFilename(){return "tempfile.txt";} - - void SetUp(){ - std::error_code ec; - fs::remove(TempFilename(), ec); - } - void TearDown(){ - std::error_code ec; - fs::remove(TempFilename(), ec); - } -}; - -#if 0 -TEST_F(ElfTest, read) { -} -#endif - -TEST_F(ElfTest, write_code) { - Elf::Write(TempFilename(), - { - 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, // mov $0x3c,%rax # syscall 60 - 0x48, 0x31, 0xff, // xor %rdi,%rdi # exit code 0 - 0x0f, 0x05, // syscall - }, {}); - - ASSERT_TRUE(fs::exists(TempFilename())); - ASSERT_GT(fs::file_size(TempFilename()), 0); -} - -TEST_F(ElfTest, write_code_data) { - Elf::Write(TempFilename(), - { - 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, // mov $0x3c,%rax # syscall 60 - 0x48, 0x8b, 0x3c, 0x25, 0x00, 0x20, 0x40, // mov 0x402000,%rdi # use value from data segment as exit code - 0, - 0x0f, 0x05, // syscall - }, - {1, 0, 0, 0, 0, 0, 0, 0}); - - ASSERT_TRUE(fs::exists(TempFilename())); - ASSERT_GT(fs::file_size(TempFilename()), 0); -} diff --git a/test-grammer.cpp b/test-grammer.cpp deleted file mode 100644 index 1734da2..0000000 --- a/test-grammer.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "bnf.h" -#include "cpp.h" -#include "cppbnf.h" -#include "lexer.h" -#include "grammer.h" -#include "minicc.h" -#include "debug.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace std::string_literals; - -class GrammerTest: public ::testing::Test -{ -protected: - GrammerTest() { - //debug = true; - } - ~GrammerTest() { - } - - // Accessors for friend - size_t minimumSymbolsNeeded(Gram::Compiler& compiler, std::vector list) { - return compiler.minimumSymbolsNeeded(list); - } - size_t minimumSymbolsNeeded(Gram::Compiler& compiler, std::string s) { - return compiler.minimumSymbolsNeeded(s); - } -}; - -TEST_F(GrammerTest, minimumSymbolsNeeded) { - auto bnf = SubBNF(CPPBNF::GetCppBNFGram(), "translation-unit"); - - Gram::Compiler compiler(bnf, "translation-unit"); - - EXPECT_EQ(minimumSymbolsNeeded(compiler, std::vector{}), 0); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "translation-unit"), 0); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "logical-or-expression"), 1); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "assignment-expression"), 1); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "declaration"), 1); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "block-declaration"), 3); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "simple-declaration"), 2); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "asm-declaration"), 5); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "namespace-alias-definition"), 5); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "using-declaration"), 4); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "using-enum-declaration"), 4); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "using-directive"), 4); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "static_assert-declaration"), 5); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "alias-declaration"), 7); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "opaque-enum-declaration"), 3); - EXPECT_EQ(minimumSymbolsNeeded(compiler, "function-definition"), 4); -} - diff --git a/test-lexer.cpp b/test-lexer.cpp deleted file mode 100644 index 23983f1..0000000 --- a/test-lexer.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "bnf.h" -#include "cpp.h" -#include "cppbnf.h" -#include "lexer.h" -#include "grammer.h" -#include "minicc.h" -#include "debug.h" - -#include - -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -class LexerTest: public ::testing::Test { -protected: - LexerTest(){ - debug = false; - } - ~LexerTest() override {} -}; - -TEST_F(LexerTest, Lex) { - auto bnf{SubBNF(CPPBNF::GetCppBNFLex(), "preprocessing-token")}; - - Lex::Lexer lexer(bnf, "preprocessing-token"); - - std::vector tokens{lexer.Lex("int main() { return 1; }")}; - - ASSERT_EQ(tokens.size(), 9); -} diff --git a/test-minicc.cpp b/test-minicc.cpp deleted file mode 100644 index baf8b3f..0000000 --- a/test-minicc.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -int main(int argc, char* argv[]) { - ::testing::InitGoogleMock(&argc, argv); - return RUN_ALL_TESTS(); -} - diff --git a/tests/test-asm.cpp b/tests/test-asm.cpp new file mode 100644 index 0000000..2d3afa0 --- /dev/null +++ b/tests/test-asm.cpp @@ -0,0 +1,132 @@ +#include "asm/chunk.h" +#include "asm/assembler.h" +#include "asm/segment.h" +#include "asm/intel64/all_ops.h" + +#include "minicc.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::string_literals; +namespace fs = std::filesystem; + +class AsmTest: public ::testing::Test +{ +protected: + AsmTest() { + //debug = true; + } + ~AsmTest() { + } + void SetUp(){ + } + void TearDown(){ + } +}; + +TEST_F(AsmTest, Intel64_add) { + Segment segment; + Asm::Args args{{Asm::Args::Register32("eax"), Asm::Args::Immediate32(1)}}; + segment.push_back(makeOp("add", args)); + + ASSERT_EQ(segment.size(), 1); + ASSERT_EQ(segment.getCode(), std::vector({0x05, 0x01, 0x00, 0x00, 0x00})); +} + +TEST_F(AsmTest, Intel64_int_0) { + Segment segment; + Asm::Args args{{Asm::Args::Immediate8(0)}}; + segment.push_back(makeOp("int", args)); + + ASSERT_EQ(segment.size(), 1); + ASSERT_EQ(segment.getCode(), std::vector{0xCE}); +} + +TEST_F(AsmTest, Intel64_int_1) { + Segment segment; + Asm::Args args{{Asm::Args::Immediate8(1)}}; + segment.push_back(makeOp("int", args)); + + ASSERT_EQ(segment.size(), 1); + ASSERT_EQ(segment.getCode(), std::vector{0xF1}); +} + +TEST_F(AsmTest, Intel64_int_5) { + Segment segment; + Asm::Args args{{Asm::Args::Immediate8(5)}}; + segment.push_back(makeOp("int", args)); + + ASSERT_EQ(segment.size(), 1); + ASSERT_EQ(segment.getCode(), std::vector({0xCD, 0x05})); +} + +TEST_F(AsmTest, Intel64_nop) { + Segment segment; + segment.push_back(makeOp("nop")); + + ASSERT_EQ(segment.size(), 1); + ASSERT_EQ(segment.getCode(), std::vector{0x90}); +} + +TEST_F(AsmTest, Intel64_ret) { + Segment segment; + segment.push_back(makeOp("ret")); + + ASSERT_EQ(segment.size(), 1); + ASSERT_EQ(segment.getCode(), std::vector{0xC3}); +} + +TEST_F(AsmTest, Intel64_multiple) { + Segment segment; + + segment.push_back(makeOp("nop")); + Asm::Args args0{{Asm::Args::Immediate8(5)}}; + segment.push_back(makeOp("int", args0)); + segment.push_back(makeOp("ret")); + segment.push_back(makeLabel("data1")); + segment.push_back(makeOp("ret")); + Asm::Args args1{{Asm::Args::Label("data1")}}; + segment.push_back(makeOp("jmp", args1)); + segment.push_back(makeData({1, 2, 3})); + + segment.insertAddresses(); + + ASSERT_EQ(segment.size(), 7); + ASSERT_EQ(segment.getCode(), std::vector( + { + 0x90, // nop + 0xCD, 0x05, // int 5 + 0xC3, // ret + // data1: + 0xC3, // ret + 0xE9, 0xFF, 0xFF, 0xFF, 0xFF, // jmp data1 + 0x01, 0x02, 0x03 // data + })); + + segment.optimize(); + + ASSERT_EQ(segment.size(), 7); + ASSERT_EQ(segment.getCode(), std::vector( + { + 0x90, // nop + 0xCD, 0x05, // int 5 + 0xC3, // ret + // data1: + 0xC3, // ret + 0xEB, 0xFF, // jmp data1 + 0x01, 0x02, 0x03 // data + })); +} diff --git a/tests/test-cpp.cpp b/tests/test-cpp.cpp new file mode 100644 index 0000000..513a3a5 --- /dev/null +++ b/tests/test-cpp.cpp @@ -0,0 +1,78 @@ +#include "bnf.h" +#include "cpp.h" +#include "cppbnf.h" +#include "lexer.h" +#include "grammer.h" +#include "minicc.h" +#include "debug.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class CppTest: public ::testing::Test +{ +protected: + CppTest() { + debug = true; + } + ~CppTest() { + } +}; + +TEST_F(CppTest, preprocessing_tokenize) { + CPP cpp; + auto pp_tokens = cpp.preprocessing_tokenize("int main() { return 1; }"); + + ASSERT_EQ(pp_tokens.size(), 9); + + auto tokens = cpp.tokens_from_pptokens(pp_tokens); + + ASSERT_EQ(tokens.size(), 9); + + auto nodes = cpp.analysis(tokens); + + ASSERT_EQ(nodes.size(), 60/*44*/); +} + +TEST_F(CppTest, preprocessing_tokenize_compile_error) { + CPP cpp; + auto ppTree = cpp.preprocessing_tokenize("in ma"); + + auto tokens = cpp.tokens_from_pptokens(ppTree); + + ASSERT_EQ(tokens.size(), 2); + + try { + auto nodes = cpp.analysis(tokens); + } catch (const std::exception& ex) { + EXPECT_EQ(ex.what(), "Compile error"s); + return; + } + + FAIL() << "Exception expected"; +} + +TEST(Cpp, compile) { + CPP cpp; + + cpp.compile("int main() { return 1 + 1; }"); +} + +TEST(Cpp, compile_2_times) { + CPP cpp; + + cpp.compile("int main() { return (1 + 2) * 2; }"); + cpp.compile("int main() { return 1 + 2 * 2; }"); +} + diff --git a/tests/test-cppbnf.cpp b/tests/test-cppbnf.cpp new file mode 100644 index 0000000..e365574 --- /dev/null +++ b/tests/test-cppbnf.cpp @@ -0,0 +1,45 @@ +#include "bnf.h" +#include "cpp.h" +#include "cppbnf.h" +#include "lexer.h" +#include "grammer.h" +#include "minicc.h" +#include "debug.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class CppBnfTest: public ::testing::Test +{ +protected: + CppBnfTest() { + //debug = true; + } + ~CppBnfTest() { + } +}; + +TEST_F(CppBnfTest, LexicalBnf) { + auto bnf = SubBNF(CPPBNF::GetCppBNFLex(), "preprocessing-token"); + + EXPECT_TRUE(CPPBNF::valid(bnf)); + EXPECT_TRUE(CPPBNF::validLex(bnf)); +} + +TEST_F(CppBnfTest, GrammarBnf) { + auto bnf = SubBNF(CPPBNF::GetCppBNFGram(), "translation-unit"); + + EXPECT_TRUE(CPPBNF::valid(bnf)); +} + diff --git a/tests/test-elf.cpp b/tests/test-elf.cpp new file mode 100644 index 0000000..0bf1d42 --- /dev/null +++ b/tests/test-elf.cpp @@ -0,0 +1,71 @@ +#include "elf.h" +#include "minicc.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::string_literals; +namespace fs = std::filesystem; + +class ElfTest: public ::testing::Test +{ +protected: + ElfTest() { + //debug = true; + } + ~ElfTest() { + } + fs::path TempFilename(){return "tempfile.txt";} + + void SetUp(){ + std::error_code ec; + fs::remove(TempFilename(), ec); + } + void TearDown(){ + std::error_code ec; + fs::remove(TempFilename(), ec); + } +}; + +#if 0 +TEST_F(ElfTest, read) { +} +#endif + +TEST_F(ElfTest, write_code) { + Elf::Write(TempFilename(), + { + 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, // mov $0x3c,%rax # syscall 60 + 0x48, 0x31, 0xff, // xor %rdi,%rdi # exit code 0 + 0x0f, 0x05, // syscall + }, {}); + + ASSERT_TRUE(fs::exists(TempFilename())); + ASSERT_GT(fs::file_size(TempFilename()), 0); +} + +TEST_F(ElfTest, write_code_data) { + Elf::Write(TempFilename(), + { + 0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, // mov $0x3c,%rax # syscall 60 + 0x48, 0x8b, 0x3c, 0x25, 0x00, 0x20, 0x40, // mov 0x402000,%rdi # use value from data segment as exit code + 0, + 0x0f, 0x05, // syscall + }, + {1, 0, 0, 0, 0, 0, 0, 0}); + + ASSERT_TRUE(fs::exists(TempFilename())); + ASSERT_GT(fs::file_size(TempFilename()), 0); +} diff --git a/tests/test-flowgraph.cpp b/tests/test-flowgraph.cpp new file mode 100644 index 0000000..132af4b --- /dev/null +++ b/tests/test-flowgraph.cpp @@ -0,0 +1,50 @@ +#include "flowgraph/data.h" +#include "flowgraph/graph.h" +#include "flowgraph/node.h" +#include "flowgraph/storage.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::string_literals; +namespace fs = std::filesystem; + +using namespace FlowGraph; + +class FlowGraphTest: public ::testing::Test +{ +protected: + FlowGraphTest() { + //debug = true; + } + ~FlowGraphTest() { + } + void SetUp(){ + } + void TearDown(){ + } +}; + +TEST_F(FlowGraphTest, build_graph) { + Graph graph; + + Data pointer{ MakeLocalPointer("malloc1") }; + Data size{ MakeLocalSize("size1") }; + std::shared_ptr malloc1 {std::make_shared(pointer, size) }; + std::shared_ptr free1{ std::make_shared(pointer) }; + + graph.push_back(malloc1); + graph.push_back(free1); +} diff --git a/tests/test-grammer.cpp b/tests/test-grammer.cpp new file mode 100644 index 0000000..1734da2 --- /dev/null +++ b/tests/test-grammer.cpp @@ -0,0 +1,65 @@ +#include "bnf.h" +#include "cpp.h" +#include "cppbnf.h" +#include "lexer.h" +#include "grammer.h" +#include "minicc.h" +#include "debug.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std::string_literals; + +class GrammerTest: public ::testing::Test +{ +protected: + GrammerTest() { + //debug = true; + } + ~GrammerTest() { + } + + // Accessors for friend + size_t minimumSymbolsNeeded(Gram::Compiler& compiler, std::vector list) { + return compiler.minimumSymbolsNeeded(list); + } + size_t minimumSymbolsNeeded(Gram::Compiler& compiler, std::string s) { + return compiler.minimumSymbolsNeeded(s); + } +}; + +TEST_F(GrammerTest, minimumSymbolsNeeded) { + auto bnf = SubBNF(CPPBNF::GetCppBNFGram(), "translation-unit"); + + Gram::Compiler compiler(bnf, "translation-unit"); + + EXPECT_EQ(minimumSymbolsNeeded(compiler, std::vector{}), 0); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "translation-unit"), 0); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "logical-or-expression"), 1); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "assignment-expression"), 1); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "declaration"), 1); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "block-declaration"), 3); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "simple-declaration"), 2); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "asm-declaration"), 5); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "namespace-alias-definition"), 5); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "using-declaration"), 4); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "using-enum-declaration"), 4); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "using-directive"), 4); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "static_assert-declaration"), 5); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "alias-declaration"), 7); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "opaque-enum-declaration"), 3); + EXPECT_EQ(minimumSymbolsNeeded(compiler, "function-definition"), 4); +} + diff --git a/tests/test-lexer.cpp b/tests/test-lexer.cpp new file mode 100644 index 0000000..23983f1 --- /dev/null +++ b/tests/test-lexer.cpp @@ -0,0 +1,39 @@ +#include "bnf.h" +#include "cpp.h" +#include "cppbnf.h" +#include "lexer.h" +#include "grammer.h" +#include "minicc.h" +#include "debug.h" + +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class LexerTest: public ::testing::Test { +protected: + LexerTest(){ + debug = false; + } + ~LexerTest() override {} +}; + +TEST_F(LexerTest, Lex) { + auto bnf{SubBNF(CPPBNF::GetCppBNFLex(), "preprocessing-token")}; + + Lex::Lexer lexer(bnf, "preprocessing-token"); + + std::vector tokens{lexer.Lex("int main() { return 1; }")}; + + ASSERT_EQ(tokens.size(), 9); +} diff --git a/tests/test-minicc.cpp b/tests/test-minicc.cpp new file mode 100644 index 0000000..baf8b3f --- /dev/null +++ b/tests/test-minicc.cpp @@ -0,0 +1,8 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +int main(int argc, char* argv[]) { + ::testing::InitGoogleMock(&argc, argv); + return RUN_ALL_TESTS(); +} + -- cgit v1.2.3