diff options
| author | Roland Reichwein <mail@reichwein.it> | 2020-11-14 22:06:10 +0100 | 
|---|---|---|
| committer | Roland Reichwein <mail@reichwein.it> | 2020-11-14 22:06:10 +0100 | 
| commit | 9e7f4c9d43b310c280cd6432cd4150411f4b914e (patch) | |
| tree | c7be57a91602c228f05ff1cab2f186dcc6635733 | |
| parent | 009e450626194299ee4b5ccb8463ac64e127fde6 (diff) | |
Added system tests
| -rw-r--r-- | Makefile | 11 | ||||
| -rw-r--r-- | asm/intel64/encode.cpp | 141 | ||||
| -rw-r--r-- | cpp.cpp | 22 | ||||
| -rw-r--r-- | flowgraph/node.cpp | 13 | ||||
| -rw-r--r-- | flowgraph/node.h | 24 | ||||
| -rw-r--r-- | flowgraph/storage.h | 18 | ||||
| -rw-r--r-- | systemtest/config/unix.exp | 19 | ||||
| -rw-r--r-- | systemtest/mcc-execute.tests/exitcodes.exp | 17 | ||||
| -rw-r--r-- | systemtest/mcc-execute.tests/test-addition.cpp (renamed from systemtest/mcc-execute.tests/test1.cpp) | 0 | ||||
| -rw-r--r-- | systemtest/mcc-execute.tests/test-return-1.cpp (renamed from systemtest/mcc-execute.tests/test.cpp) | 0 | ||||
| -rw-r--r-- | tests/test-cpp.cpp | 6 | ||||
| -rw-r--r-- | tests/test-flowgraph.cpp | 14 | 
12 files changed, 186 insertions, 99 deletions
| @@ -98,12 +98,12 @@ TESTSRC=\  SRC=$(PROGSRC) mcc.cpp  all: test-$(PROJECTNAME) mcc -	./test-$(PROJECTNAME) --gtest_filter='CppTest.compile' +	./test-$(PROJECTNAME) # --gtest_filter='CppTest.compile_1'  systemtest: -	#./mcc systemtest/mcc-execute.tests/test.cpp -	./mcc systemtest/mcc-execute.tests/test1.cpp -	runtest --all --srcdir systemtest --tool mcc +	./mcc systemtest/mcc-execute.tests/test-return-1.cpp +	./mcc systemtest/mcc-execute.tests/test-addition.cpp +	runtest --srcdir systemtest --tool mcc # --all  # testsuite ----------------------------------------------  test-$(PROJECTNAME): $(TESTSRC:.cpp=.o) @@ -133,7 +133,8 @@ ADD_DEP=Makefile  clean:  	-rm -f test-$(PROJECTNAME) mcc tempfile.txt  	-find . -name '*.o' -o -name '*.d' -o -name '*.gcno' -o -name '*.gcda' | xargs rm -f -	-rm -f systemtest/mcc-execute.tests/test1 +	-rm -f systemtest/mcc-execute.tests/test-return-1 +	-rm -f systemtest/mcc-execute.tests/test-addition  	-rm -f *.log *.sum  zip: clean diff --git a/asm/intel64/encode.cpp b/asm/intel64/encode.cpp index 702efaf..b26bf9c 100644 --- a/asm/intel64/encode.cpp +++ b/asm/intel64/encode.cpp @@ -13,61 +13,94 @@ void Asm::toMachineCode(const FlowGraph::Graph& graph, Segment& segment)   segment.clear();   for (const std::shared_ptr<FlowGraph::Node>& node: graph) { -  try { -   FlowGraph::BinaryOperation& op {dynamic_cast<FlowGraph::BinaryOperation&>(*node)}; - -   auto operands {op.operands()}; -   // TODO: ignore destination (0) for now - -   if (operands[1].type() != FlowGraph::DataType::Int) { -    std::runtime_error("Bad type for operand 1: "s + std::to_string(int(operands[1].type()))); -   } - -   if (operands[2].type() != FlowGraph::DataType::Int) { -    std::runtime_error("Bad type for operand 2: "s + std::to_string(int(operands[2].type()))); -   } - -   if (!operands[1].storage()) -    throw std::runtime_error("ICE: Operand 1 storage is 0"); -   if (!operands[2].storage()) -    throw std::runtime_error("ICE: Operand 2 storage is 0"); - -   uint32_t immediate1{}; -   try { -    FlowGraph::Constant& value1 {dynamic_cast<FlowGraph::Constant&>(*operands[1].storage())}; -    if (value1.value().size() < sizeof(uint32_t)) -     throw std::runtime_error("ICE: Int data from operand 1 needs at least 4 bytes, got "s + std::to_string(value1.value().size())); - -    immediate1 = boost::endian::little_to_native(*(reinterpret_cast<const uint32_t*>(value1.value().data()))); -   } catch (const std::bad_cast& ex) { -    std::runtime_error("Bad value for operand 1: Constant expected"); -   } -    -   uint32_t immediate2{}; -   try { -    FlowGraph::Constant& value2 {dynamic_cast<FlowGraph::Constant&>(*operands[2].storage())}; -    if (value2.value().size() < sizeof(uint32_t)) -     throw std::runtime_error("ICE: Int data from operand 2 needs at least 4 bytes, got "s + std::to_string(value2.value().size())); - -    immediate2 = boost::endian::little_to_native(*(reinterpret_cast<const uint32_t*>(value2.value().data()))); -   } catch (const std::bad_cast& ex) { -    std::runtime_error("Bad value for operand 2: Constant expected"); +  if (node.get()) { +   auto& node_deref = *node.get(); +   if (typeid(node_deref) == typeid(FlowGraph::UnaryOperation)) { +    FlowGraph::UnaryOperation& op {dynamic_cast<FlowGraph::UnaryOperation&>(*node)}; +    auto operands {op.operands()}; + +    if (operands[1].type() != FlowGraph::DataType::Int) { +     std::runtime_error("Bad type for operand 1: "s + std::to_string(int(operands[1].type()))); +    } +     +    if (!operands[1].storage()) +     throw std::runtime_error("ICE: Operand 1 storage is 0"); + +    uint32_t immediate1{}; +    try { +     FlowGraph::Constant& value1 {dynamic_cast<FlowGraph::Constant&>(*operands[1].storage())}; +     if (value1.value().size() < sizeof(uint32_t)) +      throw std::runtime_error("ICE: Int data from operand 1 needs at least 4 bytes, got "s + std::to_string(value1.value().size())); + +     immediate1 = boost::endian::little_to_native(*(reinterpret_cast<const uint32_t*>(value1.value().data()))); +    } catch (const std::bad_cast& ex) { +     std::runtime_error("Bad value for operand 1: Constant expected"); +    } +     +    if (op.type() == FlowGraph::UnaryOperationType::Store) { +     Asm::Args args1{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate1)}}; +     segment.push_back(makeOp("mov", args1)); +    } else +     throw std::runtime_error("ICE: Asm: Unsupported unary operation type: "s + std::to_string(static_cast<int>(op.type()))); +     +   } else if (typeid(node_deref) == typeid(FlowGraph::BinaryOperation)) { +    FlowGraph::BinaryOperation& op {dynamic_cast<FlowGraph::BinaryOperation&>(*node)}; + +    auto operands {op.operands()}; +    // TODO: ignore destination (0) for now + +    if (operands[1].type() != FlowGraph::DataType::Int) { +     std::runtime_error("Bad type for operand 1: "s + std::to_string(int(operands[1].type()))); +    } + +    if (operands[2].type() != FlowGraph::DataType::Int) { +     std::runtime_error("Bad type for operand 2: "s + std::to_string(int(operands[2].type()))); +    } + +    if (!operands[1].storage()) +     throw std::runtime_error("ICE: Operand 1 storage is 0"); +    if (!operands[2].storage()) +     throw std::runtime_error("ICE: Operand 2 storage is 0"); + +    uint32_t immediate1{}; +    try { +     FlowGraph::Constant& value1 {dynamic_cast<FlowGraph::Constant&>(*operands[1].storage())}; +     if (value1.value().size() < sizeof(uint32_t)) +      throw std::runtime_error("ICE: Int data from operand 1 needs at least 4 bytes, got "s + std::to_string(value1.value().size())); + +     immediate1 = boost::endian::little_to_native(*(reinterpret_cast<const uint32_t*>(value1.value().data()))); +    } catch (const std::bad_cast& ex) { +     std::runtime_error("Bad value for operand 1: Constant expected"); +    } +     +    uint32_t immediate2{}; +    try { +     FlowGraph::Constant& value2 {dynamic_cast<FlowGraph::Constant&>(*operands[2].storage())}; +     if (value2.value().size() < sizeof(uint32_t)) +      throw std::runtime_error("ICE: Int data from operand 2 needs at least 4 bytes, got "s + std::to_string(value2.value().size())); + +     immediate2 = boost::endian::little_to_native(*(reinterpret_cast<const uint32_t*>(value2.value().data()))); +    } catch (const std::bad_cast& ex) { +     std::runtime_error("Bad value for operand 2: Constant expected"); +    } + +    Asm::Args args1{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate1)}}; +    segment.push_back(makeOp("mov", args1)); +     +    Asm::Args args2{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate2)}}; + +    if (op.type() == FlowGraph::BinaryOperationType::Add) +     segment.push_back(makeOp("add", args2)); +    else if (op.type() == FlowGraph::BinaryOperationType::Multiply) +     segment.push_back(makeOp("mul", args2)); +    else +     throw std::runtime_error("ICE: Asm: Unsupported binary operation type: "s + std::to_string(static_cast<int>(op.type()))); + +   } else { +    throw std::runtime_error("ICE: Encoding: Unsupported node");     } - -   Asm::Args args1{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate1)}}; -   segment.push_back(makeOp("mov", args1)); -    -   Asm::Args args2{{Asm::Args::Register32("edi"), Asm::Args::Immediate32(immediate2)}}; - -   if (op.type() == FlowGraph::BinaryOperationType::Add) -    segment.push_back(makeOp("add", args2)); -   else if (op.type() == FlowGraph::BinaryOperationType::Multiply) -    segment.push_back(makeOp("mul", args2)); -   else -    throw std::runtime_error("ICE: Asm: Unsupported binary operation type: "s + std::to_string(static_cast<int>(FlowGraph::BinaryOperationType::Multiply))); - -  } catch (const std::bad_cast& ex) { -   std::runtime_error("ICE: Encoding: Unsupported node: "s + ex.what()); +  } else { +   throw std::runtime_error("ICE: encode: flowgraph node is null");    }   }  } @@ -315,6 +315,7 @@ bool CPP::childTypesOfChildMatch(index_t index, index_t child_index, const std::   return childTypesOfNodeMatch(m_nodes[index].child_ids[child_index], pattern);  } +// Get value for specified node, at bnf rule index child_index  std::any CPP::getValue(index_t node_id, index_t child_index)  {   size_t num_values_on_top {m_nodes[node_id].child_ids.size()}; @@ -506,9 +507,19 @@ std::unordered_map<std::string, std::function<std::any(index_t)>> CPP::getNodeEv    { "expression", [&](index_t index) -> std::any     {      if (childTypesOfNodeMatch(index, {"assignment-expression"})) { -     std::shared_ptr<FlowGraph::Node> node {std::any_cast<std::shared_ptr<FlowGraph::Node>>(getValue(index, 0))}; -     mCPPContext.graph.push_back(node); -     return getValue(index, 0); +     if (getValue(index, 0).type() == typeid(FlowGraph::Data)) { // got Data -> make trivial Node out of it and return it +      FlowGraph::LocalScope scope; // TODO: move to context! +      FlowGraph::Data destination{FlowGraph::MakeTemporaryInt(scope)}; +      FlowGraph::Data source {std::any_cast<FlowGraph::Data>(getValue(index, 0))}; + +      std::shared_ptr<FlowGraph::Node> node {std::make_shared<FlowGraph::UnaryOperation>(FlowGraph::UnaryOperationType::Store, destination, source)}; +      mCPPContext.graph.push_back(node); +      return node; +     } else { +      std::shared_ptr<FlowGraph::Node> node {std::any_cast<std::shared_ptr<FlowGraph::Node>>(getValue(index, 0))}; +      mCPPContext.graph.push_back(node); +      return getValue(index, 0); +     }      }      throw std::runtime_error("ICE: Unsupported childs: "s + ruleString(index)); // TODO     } @@ -592,9 +603,10 @@ void CPP::link()   mCode = std::vector<uint8_t>{              0x48, 0xc7, 0xc0, 0x3c, 0x00, 0x00, 0x00, // mov    $0x3c,%rax     # syscall 60              0x48, 0x31, 0xff,                         // xor    %rdi,%rdi      # exit code 0 - } + mSegment.getCode() + std::vector<uint8_t>{ // add to edi + } + mSegment.getCode() + std::vector<uint8_t>{       //                       # leave exit code in edi              0x0f, 0x05,                               // syscall - }; + } + ;  }  // phases of translation, according to standard diff --git a/flowgraph/node.cpp b/flowgraph/node.cpp index 795a252..9b68d74 100644 --- a/flowgraph/node.cpp +++ b/flowgraph/node.cpp @@ -14,17 +14,22 @@ Data FlowGraph::MakeConstantInt(int i)   return Data(DataType::Int, std::make_shared<Constant>(value));  } -Data FlowGraph::MakeLocalPointer(const std::string& name) +Data FlowGraph::MakeLocalPointer(FlowGraph::LocalScope& scope, const std::string& name)  { - return Data(DataType::Pointer, std::make_shared<LocalStorage>(name)); + return Data(DataType::Pointer, std::make_shared<LocalStorage>(scope, name));  } -Data FlowGraph::MakeLocalSize(const std::string& name) +Data FlowGraph::MakeLocalSize(FlowGraph::LocalScope& scope, const std::string& name)  { - return Data(DataType::Size, std::make_shared<LocalStorage>(name)); + return Data(DataType::Size, std::make_shared<LocalStorage>(scope, name));  }  Data FlowGraph::MakeTemporaryInt(FlowGraph::LocalScope& scope)  {   return Data(DataType::Int, std::make_shared<TemporaryStorage>(scope));  } + +LocalScope& CreateScopeOp::scope() +{ + return m_scope; +} diff --git a/flowgraph/node.h b/flowgraph/node.h index 9ae5479..98c684d 100644 --- a/flowgraph/node.h +++ b/flowgraph/node.h @@ -43,8 +43,8 @@ namespace FlowGraph {   };   Data MakeConstantInt(int i); - Data MakeLocalPointer(const std::string& name); - Data MakeLocalSize(const std::string& name); + Data MakeLocalPointer(FlowGraph::LocalScope& scope, const std::string& name); + Data MakeLocalSize(FlowGraph::LocalScope& scope, const std::string& name);   Data MakeTemporaryInt(LocalScope& scope);   class MemCopy: public Node @@ -114,7 +114,8 @@ namespace FlowGraph {   {    Increment,    Decrement, -  Negate +  Negate, +  Store // just take Data as-is to store it at destination   };   class UnaryOperation: public Node @@ -159,4 +160,21 @@ namespace FlowGraph {    BinaryOperationType m_type;   }; + // Open a new scope, with stack frame + class CreateScopeOp: public Node + { + public: +  CreateScopeOp() {} +  LocalScope& scope(); + private: +  LocalScope m_scope; + }; + + // Close current scope, closing stack frame + class DestroyScopeOp: public Node + { + public: +  DestroyScopeOp() {} + }; +  } // namespace FlowGraph diff --git a/flowgraph/storage.h b/flowgraph/storage.h index 28aae1e..fd3c085 100644 --- a/flowgraph/storage.h +++ b/flowgraph/storage.h @@ -37,15 +37,6 @@ namespace FlowGraph {    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   { @@ -56,6 +47,15 @@ namespace FlowGraph {    size_t m_index{ 0 };   }; + class LocalStorage : public Storage + { + public: +  LocalStorage(LocalScope& scope, const std::string& name): m_name(name) {} +  const std::string& name() const { return m_name; } + private: +  std::string m_name; + }; +   // intermediate results, anonymous values   // use generated name   class TemporaryStorage : public Storage diff --git a/systemtest/config/unix.exp b/systemtest/config/unix.exp index e69de29..2de93b0 100644 --- a/systemtest/config/unix.exp +++ b/systemtest/config/unix.exp @@ -0,0 +1,19 @@ +proc runtest_exit_code { test_name command_line exit_code } { + +  spawn $command_line +   +  expect eof +   +  lassign [wait] pid spawnid os_error_flag value +   +  if {$os_error_flag == 0} { +    if {$value == $exit_code} { +      pass "$test_name: Returned expected value $value" +    } else { +      fail "$test_name: Returned bad value $value, expected: $exit_code" +    } +  } else { +    fail "$test_name: errno: $value" +  } + +} diff --git a/systemtest/mcc-execute.tests/exitcodes.exp b/systemtest/mcc-execute.tests/exitcodes.exp index 6e8b632..48d19a1 100644 --- a/systemtest/mcc-execute.tests/exitcodes.exp +++ b/systemtest/mcc-execute.tests/exitcodes.exp @@ -1,18 +1,5 @@  # https://www.embecosm.com/appnotes/ean8/ean8-howto-dejagnu-1.0.html -spawn systemtest/mcc-execute.tests/test1 - -expect eof - -lassign [wait] pid spawnid os_error_flag value - -if {$os_error_flag == 0} { -  if {$value == 3} { -    pass "exit status: $value" -  } else { -    fail "exit status: $value" -  } -} else { -  fail "errno: $value" -} +runtest_exit_code "Return 1" "systemtest/mcc-execute.tests/test-return-1" 1 +runtest_exit_code "Addition" "systemtest/mcc-execute.tests/test-addition" 3 diff --git a/systemtest/mcc-execute.tests/test1.cpp b/systemtest/mcc-execute.tests/test-addition.cpp index 3a03f6f..3a03f6f 100644 --- a/systemtest/mcc-execute.tests/test1.cpp +++ b/systemtest/mcc-execute.tests/test-addition.cpp diff --git a/systemtest/mcc-execute.tests/test.cpp b/systemtest/mcc-execute.tests/test-return-1.cpp index 40cbb54..40cbb54 100644 --- a/systemtest/mcc-execute.tests/test.cpp +++ b/systemtest/mcc-execute.tests/test-return-1.cpp diff --git a/tests/test-cpp.cpp b/tests/test-cpp.cpp index 39e1513..5755516 100644 --- a/tests/test-cpp.cpp +++ b/tests/test-cpp.cpp @@ -86,6 +86,12 @@ TEST_F(CppTest, compile) {   cpp.compile("int main() { return 1 + 2; }");  } +TEST_F(CppTest, compile_1) { + CPP cpp; +  + cpp.compile("int main() { return 1; }"); +} +  TEST_F(CppTest, compile_2_times) {   CPP cpp; diff --git a/tests/test-flowgraph.cpp b/tests/test-flowgraph.cpp index f0cc204..021d6dd 100644 --- a/tests/test-flowgraph.cpp +++ b/tests/test-flowgraph.cpp @@ -40,12 +40,18 @@ protected:  TEST_F(FlowGraphTest, build_graph) {   Graph graph; - Data pointer{ MakeLocalPointer("malloc1") }; - Data size{ MakeLocalSize("size1") }; - std::shared_ptr<Node> malloc1 {std::make_shared<AllocateDynamic>(pointer, size) }; - std::shared_ptr<Node> free1{ std::make_shared<DeallocateDynamic>(pointer) }; + std::shared_ptr<CreateScopeOp> createScope{std::make_shared<CreateScopeOp>()}; + graph.push_back(createScope); + Data pointer{ MakeLocalPointer(createScope->scope(), "malloc1") }; + Data size{ MakeLocalSize(createScope->scope(), "size1") }; + std::shared_ptr<Node> malloc1 {std::make_shared<AllocateDynamic>(pointer, size) };   graph.push_back(malloc1); +  + std::shared_ptr<Node> free1{ std::make_shared<DeallocateDynamic>(pointer) };   graph.push_back(free1); +  + std::shared_ptr<Node> destroyScope{std::make_shared<DestroyScopeOp>()}; + graph.push_back(destroyScope);  } | 
