diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e2da50a..4c3aad50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(bort) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(BORT_BUILD_TESTS OFF) +option(BORT_BUILD_TESTS "Enable testing (requires python3)") include_directories(include) @@ -106,5 +106,6 @@ target_link_libraries(${PROJECT_NAME} PRIVATE Boost::functional Boost::assert Boost::stacktrace Boost::range) if(${BORT_BUILD_TESTS}) + message(STATUS "Testing enabled") add_subdirectory(tests) endif() diff --git a/README.md b/README.md index c3556af3..ac1d1a65 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,9 @@ **bort** is a cross-platform Small-C language cross compiler for RISC-V architecture. ## Global TODOs -- Global variables -- for-loops +- global variables - goto - switch -- compound assignment (`+=`, `*=`, ...) - compound variable declaration (`int a = 5, b, c = d;`) - testing diff --git a/dev.sh b/dev.sh index f52469dd..5e792c62 100755 --- a/dev.sh +++ b/dev.sh @@ -35,6 +35,7 @@ fi cmake -S . \ -B build \ -G Ninja \ + -DBORT_BUILD_TESTS=ON \ -DCMAKE_CXX_COMPILER=clang++ \ -DCMAKE_C_COMPILER=clang \ -DCMAKE_CXX_FLAGS="-fsanitize=address,undefined -Wall -Wextra -pedantic" \ @@ -46,5 +47,5 @@ cp -f build/compile_commands.json . if [ $# -ne 0 ] && [ "$1" == "run" ]; then echo -e "----------------------------------\n" set -eux - ./build/bort --dump-ast --emit-ir --dump-codegen-info -o - ./tests/corpus/arrays.c + ./build/bort --dump-ast --emit-ir --dump-codegen-info -o - ./tests/corpus/loops.c fi diff --git a/include/bort/AST/ASTNode.hpp b/include/bort/AST/ASTNode.hpp index 8c40d9aa..4950a8a8 100644 --- a/include/bort/AST/ASTNode.hpp +++ b/include/bort/AST/ASTNode.hpp @@ -32,6 +32,8 @@ enum class NodeKind { IfStmt, WhileStmt, ReturnStmt, + BreakStmt, + ContinueStmt, ASTRoot, NUM_NODES }; diff --git a/include/bort/AST/BinOpExpr.hpp b/include/bort/AST/BinOpExpr.hpp index e982b908..0ab24a7a 100644 --- a/include/bort/AST/BinOpExpr.hpp +++ b/include/bort/AST/BinOpExpr.hpp @@ -16,6 +16,9 @@ class BinOpExpr final : public ExpressionNode { /// @todo bitwise operators return m_Op == TokenKind::Plus || m_Op == TokenKind::Minus || m_Op == TokenKind::Star || m_Op == TokenKind::Div || + m_Op == TokenKind::Mod || m_Op == TokenKind::LShift || + m_Op == TokenKind::RShift || m_Op == TokenKind::Amp || + m_Op == TokenKind::Pipe || m_Op == TokenKind::Xor || m_Op == TokenKind::Amp || m_Op == TokenKind::Pipe; } [[nodiscard]] constexpr auto isLogical() const -> bool { diff --git a/include/bort/AST/BreakStmt.hpp b/include/bort/AST/BreakStmt.hpp new file mode 100644 index 00000000..6010681d --- /dev/null +++ b/include/bort/AST/BreakStmt.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "bort/AST/ASTNode.hpp" + +namespace bort::ast { + +class BreakStmt final : public Statement { +private: + BreakStmt() + : Statement{ NodeKind::BreakStmt } { + } + +public: + friend class ASTRoot; +}; + +} // namespace bort::ast diff --git a/include/bort/AST/ContinueStmt.hpp b/include/bort/AST/ContinueStmt.hpp new file mode 100644 index 00000000..8fe36825 --- /dev/null +++ b/include/bort/AST/ContinueStmt.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "bort/AST/ASTNode.hpp" + +namespace bort::ast { + +class ContinueStmt final : public Statement { +private: + ContinueStmt() + : Statement{ NodeKind::ContinueStmt } { + } + +public: + friend class ASTRoot; +}; + +} // namespace bort::ast diff --git a/include/bort/AST/Visitors/ASTPrinter.hpp b/include/bort/AST/Visitors/ASTPrinter.hpp index cc1621b3..d35936b5 100644 --- a/include/bort/AST/Visitors/ASTPrinter.hpp +++ b/include/bort/AST/Visitors/ASTPrinter.hpp @@ -1,4 +1,6 @@ #pragma once +#include "bort/AST/BreakStmt.hpp" +#include "bort/AST/ContinueStmt.hpp" #include "bort/AST/IndexationExpr.hpp" #include "bort/AST/InitializerList.hpp" #include "bort/AST/UnaryOpExpr.hpp" @@ -32,6 +34,8 @@ class ASTPrinter : public StructureAwareASTVisitor { void visit(const Ref& ifStmtNode) override; void visit(const Ref& whileStmtNode) override; void visit(const Ref& returnStmtNode) override; + void visit(const Ref& breakStmtNode) override; + void visit(const Ref& continueStmtNode) override; void visit(const Ref& functionCallExpr) override; void push(); diff --git a/include/bort/AST/Visitors/ASTVisitor.hpp b/include/bort/AST/Visitors/ASTVisitor.hpp index c37a4e60..8c2eb5a0 100644 --- a/include/bort/AST/Visitors/ASTVisitor.hpp +++ b/include/bort/AST/Visitors/ASTVisitor.hpp @@ -23,6 +23,8 @@ class ExpressionStmt; class IfStmt; class WhileStmt; class ReturnStmt; +class BreakStmt; +class ContinueStmt; class ASTVisitorBase { public: @@ -80,6 +82,12 @@ class StructureAwareASTVisitor : public ASTVisitorBase { virtual void visit(const Ref& /* charNode */) { // leaf } + virtual void visit(const Ref& /* breakNode */) { + // leaf + } + virtual void visit(const Ref& /* continueNode */) { + // leaf + } virtual void visit(const Ref& varDeclNode); virtual void visit(const Ref& initializerListNode); virtual void visit(const Ref& indexationExpr); diff --git a/include/bort/AST/Visitors/TypePropagationVisitor.hpp b/include/bort/AST/Visitors/TypePropagationVisitor.hpp index 8234f482..3ff065c1 100644 --- a/include/bort/AST/Visitors/TypePropagationVisitor.hpp +++ b/include/bort/AST/Visitors/TypePropagationVisitor.hpp @@ -1,4 +1,5 @@ #pragma once +#include "bort/AST/ASTNode.hpp" #include "bort/AST/BinOpExpr.hpp" #include "bort/AST/IndexationExpr.hpp" #include "bort/AST/UnaryOpExpr.hpp" @@ -32,6 +33,7 @@ class TypePropagationVisitor : public StructureAwareASTVisitor { void visit(const Ref& indexationNode) override; void visit(const Ref& varDeclNode) override; + void assertLvalue(const Ref& node); void promoteAssignmentOperands(const TypeRef& lhsTy, TypeRef& rhsTy, const ASTDebugInfo& debugInfo); diff --git a/include/bort/AST/Visitors/Utils.hpp b/include/bort/AST/Visitors/Utils.hpp index 2839219f..d42dd57a 100644 --- a/include/bort/AST/Visitors/Utils.hpp +++ b/include/bort/AST/Visitors/Utils.hpp @@ -2,6 +2,8 @@ #include "bort/AST/ASTNode.hpp" #include "bort/AST/BinOpExpr.hpp" +#include "bort/AST/BreakStmt.hpp" +#include "bort/AST/ContinueStmt.hpp" #include "bort/AST/ExpressionStmt.hpp" #include "bort/AST/FunctionCallExpr.hpp" #include "bort/AST/FunctionDecl.hpp" @@ -83,6 +85,14 @@ auto callHandler(const Ref& node, F&& visit) { bort_assert_nomsg(dynCastRef(node)); return visit(dynCastRef(node)); break; + case NodeKind::BreakStmt: + bort_assert_nomsg(dynCastRef(node)); + return visit(dynCastRef(node)); + break; + case NodeKind::ContinueStmt: + bort_assert_nomsg(dynCastRef(node)); + return visit(dynCastRef(node)); + break; case NodeKind::ASTRoot: bort_assert_nomsg(dynCastRef(node)); return visit(dynCastRef(node)); diff --git a/include/bort/Codegen/RISCVCodegen.hpp b/include/bort/Codegen/RISCVCodegen.hpp index e35f92bd..10316185 100644 --- a/include/bort/Codegen/RISCVCodegen.hpp +++ b/include/bort/Codegen/RISCVCodegen.hpp @@ -86,6 +86,16 @@ struct RVInstInfo final : public ir::Metadata { std::string InstName; }; +struct RVOpAdditionalInfo final : public ir::Metadata { + explicit RVOpAdditionalInfo(bool isSingleOp) + : IsSingleOp{ isSingleOp } { + } + + [[nodiscard]] auto toString() const -> std::string override; + + bool IsSingleOp; +}; + struct RARSMacroDefinitions final : public ir::Metadata { [[nodiscard]] auto toString() const -> std::string override; diff --git a/include/bort/IR/IRCodegen.hpp b/include/bort/IR/IRCodegen.hpp index c9db2c32..559d96ec 100644 --- a/include/bort/IR/IRCodegen.hpp +++ b/include/bort/IR/IRCodegen.hpp @@ -1,6 +1,8 @@ #pragma once #include "bort/AST/ASTNode.hpp" #include "bort/AST/BinOpExpr.hpp" +#include "bort/AST/BreakStmt.hpp" +#include "bort/AST/ContinueStmt.hpp" #include "bort/AST/FunctionCallExpr.hpp" #include "bort/AST/IfStmt.hpp" #include "bort/AST/IndexationExpr.hpp" @@ -47,11 +49,14 @@ class IRCodegen { auto visit(const Ref& ifStmtNode) -> ValueRef; auto visit(const Ref& whileStmtNode) -> ValueRef; auto visit(const Ref& returnStmt) -> ValueRef; + auto visit(const Ref& breakStmt) -> ValueRef; + auto visit(const Ref& continueStmt) -> ValueRef; auto visit(const Ref& funcCallExpr) -> ValueRef; auto genBranchFromCondition(const Ref& cond, bool negate = false) -> Ref; - auto genArrayPtr(const ValueRef& arr) -> std::pair, ValueRef>; + auto genArrayPtr(const ValueRef& arr) + -> std::pair, ValueRef>; template requires std::is_base_of_v diff --git a/include/bort/IR/Metadata.hpp b/include/bort/IR/Metadata.hpp index 1fbdac7b..8342dfe0 100644 --- a/include/bort/IR/Metadata.hpp +++ b/include/bort/IR/Metadata.hpp @@ -40,6 +40,11 @@ class MDList { return node; } + template + void remove() { + m_Registry.erase(typeid(T)); + } + auto nodes() { return std::views::values(m_Registry); } diff --git a/include/bort/IR/OpInst.hpp b/include/bort/IR/OpInst.hpp index 14e89658..f1033a23 100644 --- a/include/bort/IR/OpInst.hpp +++ b/include/bort/IR/OpInst.hpp @@ -31,6 +31,9 @@ class OpInst final : public Instruction { void setSrc2(ValueRef value) { m_Operands[Src2Idx] = std::move(value); } + void setOp(TokenKind op) { + m_Op = op; + } static constexpr int Src1Idx{ 1 }; static constexpr int Src2Idx{ 2 }; diff --git a/include/bort/IR/Value.hpp b/include/bort/IR/Value.hpp index 9db2b21e..969e32aa 100644 --- a/include/bort/IR/Value.hpp +++ b/include/bort/IR/Value.hpp @@ -40,6 +40,11 @@ class Value { return m_MDList->get(); } + template + void removeMDNode() { + m_MDList->remove(); + } + template auto getMDNode() const -> const T* { return m_MDList->get(); diff --git a/include/bort/Lex/Tokens.def b/include/bort/Lex/Tokens.def index 159ca195..91888072 100644 --- a/include/bort/Lex/Tokens.def +++ b/include/bort/Lex/Tokens.def @@ -38,11 +38,24 @@ PUNCT(Plus, "+") PUNCT(Minus, "-") PUNCT(Star, "*") PUNCT(Div, "/") +PUNCT(Mod, "%") PUNCT(Pipe, "|") +PUNCT(Xor, "^") +PUNCT(LShift, "<<") +PUNCT(RShift, ">>") +PUNCT(Not, "!") +PUNCT(Tilde, "~") +PUNCT(AmpAmp, "&&") +PUNCT(PipePipe, "||") PUNCT(PlusAssign, "+=") PUNCT(MinusAssign, "-=") -PUNCT(TimesAssign, "*=") +PUNCT(StarAssign, "*=") PUNCT(DivAssign, "/=") +PUNCT(AmpAssign, "&=") +PUNCT(XorAssign, "^=") +PUNCT(PipeAssign, "|=") +PUNCT(LShiftAssign, "<<=") +PUNCT(RShiftAssign, ">>=") PUNCT(Less, "<") PUNCT(Greater, ">") PUNCT(LessEqual, "<=") @@ -54,7 +67,10 @@ KEYWORD(char) KEYWORD(const) KEYWORD(if) KEYWORD(else) +KEYWORD(break) +KEYWORD(continue) KEYWORD(while) +KEYWORD(for) KEYWORD(return) KEYWORD(sizeof) diff --git a/include/bort/Parse/Parser.hpp b/include/bort/Parse/Parser.hpp index 35b10fbc..8e772ac4 100644 --- a/include/bort/Parse/Parser.hpp +++ b/include/bort/Parse/Parser.hpp @@ -1,5 +1,7 @@ #pragma once #include "bort/AST/ASTNode.hpp" +#include "bort/AST/BreakStmt.hpp" +#include "bort/AST/ContinueStmt.hpp" #include "bort/AST/ExpressionNode.hpp" #include "bort/AST/FunctionCallExpr.hpp" #include "bort/AST/FunctionDecl.hpp" @@ -46,12 +48,11 @@ class Parser { /// parenExpr \n /// -> '(' expression ')' \n /// @todo type-casts -> '(' declspec ')' - auto parseParenExpr() -> Unique; + auto parseParenExpr() -> Ref; /// identifier \n - /// -> identifier - variable \n - /// -> identifier '(' expr, ... ')' - function call \n - /// -> identifier indexationExpr \n - /// -> identifier ('++' | '--') + /// -> varExpr \n + /// -> functionCallExpr \n + /// -> identifier indexationExpr auto parseIdentifierExpr() -> Unique; /// value expression \n /// -> number \n @@ -59,29 +60,28 @@ class Parser { /// -> sizeofExpr \n /// -> unaryOpExpr \n /// -> lvalue - auto parseValueExpression() -> Unique; + auto parseValueExpression() -> Ref; /// sizeofExpr -> 'sizeof' (parenExpr | '(' declspec ')' ) auto parseSizeofExpr() -> Unique; - /// lvalue \n - /// -> identifier - auto tryParseLValue() -> std::optional>; + /// varExpr -> identifier + auto parseVarExpr() -> Unique; /// unaryOpExpr -> unaryOp valueExpression auto parseUnaryOpExpr() -> Unique; /// expression - /// -> valueExpression (binOp valueExpression ...) - auto parseExpression() -> Unique; + /// -> valueExpression binOpRhs + auto parseExpression() -> Ref; /// binOpRhs /// -> bipOp valueExpression (binOpRhs ...) - auto parseBinOpRhs(Unique lhs, + auto parseBinOpRhs(Ref lhs, int32_t prevPrecedence = 0) - -> Unique; + -> Ref; /// declspec -> ( 'int' | 'void' | 'char' ) ('*'...) /// @todo type qualifiers auto parseDeclspec() -> TypeRef; - /// declaration -> declspec (varDecl | functionDecl) + /// declaration -> declspec (varDecl ';' | functionDecl) auto parseDeclarationStatement() -> Ref; - /// varDecl -> declspec identifier ';' - /// @todo declspec (identifier ('=' expr)?, ...) ';' + /// varDecl -> declspec identifier initializerExpr? + /// @todo declspec (identifier ('=' expr)?, ...) auto parseVarDecl(TypeRef type, const Token& nameTok) -> Ref; /// initializerList -> '{' number, ... '}' | stringLiteral @@ -93,14 +93,22 @@ class Parser { auto parseFunctionCallExpr(const Token& nameTok) -> Unique; /// indexationExpr -> nameTok '[' expr ']' - /// desugared into pointer arithmetic auto parseIndexationExpr(const Token& nameTok) -> Unique; /// statement \n /// -> expression ';' \n + /// -> declarationStatement ';' \n /// -> block \n /// -> ifStatement \n + /// -> whileStatement \n + /// -> returnStatement \n + /// -> breakStatement \n + /// -> continueStatement auto parseStatement() -> Ref; + /// breakStatement -> 'break' ';' + auto parseBreakStatement() -> Ref; + /// continueStatement -> 'continue' ';' + auto parseContinueStatement() -> Ref; /// block /// -> '{' statement... '}' auto parseBlock() -> Unique; @@ -108,6 +116,8 @@ class Parser { auto parseIfStatement() -> Ref; /// whileStatement -> 'while' parenExpr block auto parseWhileStatement() -> Ref; + /// forStatement -> 'for' '(' varDecl ';' expr? ';' expr? ')' block + auto parseForStatement() -> Ref; /// returnStatement -> 'return' (expr)? ';' auto parseReturnStatement() -> Ref; @@ -141,6 +151,7 @@ class Parser { Ref m_ASTRoot; bool m_ASTInvalid{ false }; bool m_DiagnosticSilenced{ false }; + bool m_InsideLoop{ false }; }; } // namespace bort diff --git a/launch.json b/launch.json index 66fa3702..aa2ed9f1 100644 --- a/launch.json +++ b/launch.json @@ -8,7 +8,9 @@ "program": "${workspaceFolder}/build/bort", "args": [ "--emit-ir", - "${workspaceFolder}/tests/corpus/functions.c" + "-o", + "-", + "${workspaceFolder}/tests/corpus/loops.c" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/src/AST/Visitors/ASTPrinter.cpp b/src/AST/Visitors/ASTPrinter.cpp index 39c1d775..32ce2505 100644 --- a/src/AST/Visitors/ASTPrinter.cpp +++ b/src/AST/Visitors/ASTPrinter.cpp @@ -36,6 +36,8 @@ static constexpr cul::BiMap s_NodeKindNames{ [](auto&& selector) { .Case(NodeKind::IfStmt, "IfStmt") .Case(NodeKind::WhileStmt, "WhileStmt") .Case(NodeKind::ReturnStmt, "ReturnStmt") + .Case(NodeKind::BreakStmt, "BreakStmt") + .Case(NodeKind::ContinueStmt, "ContinueStmt") .Case(NodeKind::Block, "Block") .Case(NodeKind::ASTRoot, "ASTRoot"); } }; @@ -191,4 +193,13 @@ void ASTPrinter::visit(const Ref& returnStmtNode) { dumpNodeInfo(returnStmtNode); dump("Expression", returnStmtNode->getExpression()); } + +void ASTPrinter::visit(const Ref& breakStmtNode) { + dumpNodeInfo(breakStmtNode); +} + +void ASTPrinter::visit(const Ref& continueStmtNode) { + dumpNodeInfo(continueStmtNode); +} + } // namespace bort::ast diff --git a/src/AST/Visitors/TypePropagationVisitor.cpp b/src/AST/Visitors/TypePropagationVisitor.cpp index 5cfb406b..b0d371ac 100644 --- a/src/AST/Visitors/TypePropagationVisitor.cpp +++ b/src/AST/Visitors/TypePropagationVisitor.cpp @@ -1,6 +1,8 @@ #include "bort/AST/Visitors/TypePropagationVisitor.hpp" #include "bort/AST/ASTDebugInfo.hpp" +#include "bort/AST/ASTNode.hpp" #include "bort/AST/NumberExpr.hpp" +#include "bort/AST/UnaryOpExpr.hpp" #include "bort/AST/Visitors/ASTVisitor.hpp" #include "bort/Basic/Casts.hpp" #include "bort/CLI/IO.hpp" @@ -126,6 +128,8 @@ void TypePropagationVisitor::visit(const Ref& binopNode) { } else if (binopNode->isLogical()) { binopNode->setType(IntType::get()); } else if (binopNode->getOp() == TokenKind::Assign) { + assertLvalue(binopNode->getLHS()); + auto lhsTy{ binopNode->getLHS()->getType() }; auto rhsTy{ binopNode->getRHS()->getType() }; @@ -141,6 +145,7 @@ void TypePropagationVisitor::visit(const Ref& unaryOpNode) { switch (unaryOpNode->getOp()) { case TokenKind::Amp: + assertLvalue(unaryOpNode->getOperand()); // for arrays it's address of first element if (auto arrOpType{ dynCastRef(type) }) { type = PointerType::get(arrOpType->getBaseType()); @@ -159,6 +164,10 @@ void TypePropagationVisitor::visit(const Ref& unaryOpNode) { throw FatalSemanticError(); } break; + case TokenKind::PlusPlus: + case TokenKind::MinusMinus: + assertLvalue(unaryOpNode->getOperand()); + break; default: break; } @@ -218,4 +227,21 @@ void TypePropagationVisitor::promoteAssignmentOperands( } } +void TypePropagationVisitor::assertLvalue(const Ref& node) { + if (node->getKind() == NodeKind::VariableExpr || + node->getKind() == NodeKind::IndexationExpr) { + return; + } + + if (auto unOpNode{ dynCastRef(node) }) { + if (unOpNode->getOp() == TokenKind::Star) { + return; + } + } + + Diagnostic::emitError(getASTRoot()->getNodeDebugInfo(node).token, + "Expected lvalue"); + throw FatalSemanticError(); +} + } // namespace bort::ast diff --git a/src/CLI/Main.cpp b/src/CLI/Main.cpp index d5cd83d5..a22937ea 100644 --- a/src/CLI/Main.cpp +++ b/src/CLI/Main.cpp @@ -6,9 +6,28 @@ #include #include #include +#ifdef WIN32 +#include +#endif auto main(int argc, char* argv[]) -> int { +/// @todo: Make custom wrappers for fmt color formatters to make color +/// optional +#ifdef WIN32 + HANDLE hOut{ GetStdHandle(STD_OUTPUT_HANDLE) }; + HANDLE hErr{ GetStdHandle(STD_ERROR_HANDLE) }; + DWORD dwMode{}; + + GetConsoleMode(hOut, &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hOut, dwMode); + + GetConsoleMode(hErr, &dwMode); + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + SetConsoleMode(hErr, dwMode); +#endif + bort::CLIOptions cliOptions; cxxopts::Options cliParser{ "bort", "Small-C to RISC-V compiler" }; @@ -29,6 +48,8 @@ auto main(int argc, char* argv[]) -> int { // clang-format on cliParser.parse_positional("inputs"); + cliParser.positional_help("inputs"); + cliParser.show_positional_help(); auto result{ cliParser.parse(argc, argv) }; diff --git a/src/Codegen/RISCVCodegen.cpp b/src/Codegen/RISCVCodegen.cpp index 2291ce2c..2c63fea2 100644 --- a/src/Codegen/RISCVCodegen.cpp +++ b/src/Codegen/RISCVCodegen.cpp @@ -49,6 +49,10 @@ auto RVInstInfo::toString() const -> std::string { return fmt::format("rv_ii .inst_name={}", InstName); } +auto RVOpAdditionalInfo::toString() const -> std::string { + return fmt::format("rv_oai .is_single_op={}", IsSingleOp); +} + auto RARSMacroDefinitions::toString() const -> std::string { return fmt::format("rars_macros .num_defined={}", Macros.size()); } @@ -80,11 +84,44 @@ class PreprocessPass : public InstructionVisitorBase { if (opInst->getOp() == TokenKind::Plus || opInst->getOp() == TokenKind::Amp || - opInst->getOp() == TokenKind::Pipe) { - // add can be immediate + opInst->getOp() == TokenKind::Pipe || + opInst->getOp() == TokenKind::Xor || + opInst->getOp() == TokenKind::LShift || + opInst->getOp() == TokenKind::RShift) { + // these can be immediate break; } } + + // in RISC-V boolean stuff should be expressed through slt/sge/seqz + + auto addModInst{ [this](TokenKind op, auto&& inst) { + m_CurrentBBIter->insertAfter( + m_CurrentInstIter, + makeRef(op, inst->getDestination(), + inst->getDestination())); + } }; + if (opInst->getOp() == TokenKind::LessEqual) { + opInst->setOp(TokenKind::Greater); + addModInst(TokenKind::Not, opInst); + } else if (opInst->getOp() == TokenKind::GreaterEqual) { + opInst->setOp(TokenKind::Less); + addModInst(TokenKind::Not, opInst); + } else if (opInst->getOp() == TokenKind::Equals || + opInst->getOp() == TokenKind::NotEquals) { + if (opInst->getSrc2()) { + // c = a xor b + // c = (c ==/!= 0) + auto oldOp{ opInst->getOp() }; + opInst->setOp(TokenKind::Xor); + auto newInst{ makeRef(oldOp, opInst->getDestination(), + opInst->getDestination(), + nullptr) }; + newInst->addMDNode(RVOpAdditionalInfo{ true }); + m_CurrentBBIter->insertAfter(m_CurrentInstIter, + std::move(newInst)); + } + } } void visit(const Ref& unaryInst) override { @@ -193,14 +230,26 @@ class InstructionChoicePass : public InstructionVisitorBase { .Case(TokenKind::Minus, "sub") .Case(TokenKind::Star, "mul") .Case(TokenKind::Div, "div") + .Case(TokenKind::Mod, "rem") .Case(TokenKind::Less, "slt") .Case(TokenKind::Greater, "sgt") + .Case(TokenKind::Equals, "seqz") + .Case(TokenKind::NotEquals, "snez") .Case(TokenKind::Amp, "and") - .Case(TokenKind::Pipe, "or"); + .Case(TokenKind::Pipe, "or") + .Case(TokenKind::Xor, "xor") + .Case(TokenKind::Xor, "sll") + .Case(TokenKind::Xor, "sra"); } }; bort_assert(s_OpInstNames.Find(opInst->getOp()).has_value(), "Unknown op name"); + if (opInst->getOp() == TokenKind::Equals || + opInst->getOp() == TokenKind::NotEquals) { + bort_assert(!opInst->getSrc2(), + "Preprocess should leave only ==/!= nullptr"); + } + RVInstInfo info{ std::string{ s_OpInstNames.Find(opInst->getOp()).value() } }; if (isaRef(opInst->getSrc2())) { @@ -211,7 +260,8 @@ class InstructionChoicePass : public InstructionVisitorBase { void visit(const Ref& unaryInst) override { static constexpr cul::BiMap s_UnaryInstNames{ [](auto&& selector) { - return selector.Case(TokenKind::Minus, "neg"); + return selector.Case(TokenKind::Minus, "neg") + .Case(TokenKind::Not, "not"); } }; bort_assert(s_UnaryInstNames.Find(unaryInst->getOp()).has_value(), diff --git a/src/Codegen/RISCVPrinter.cpp b/src/Codegen/RISCVPrinter.cpp index ce3c6887..d30ea6e8 100644 --- a/src/Codegen/RISCVPrinter.cpp +++ b/src/Codegen/RISCVPrinter.cpp @@ -71,14 +71,21 @@ void Printer::run(ir::Module& module) { void Printer::visit(const Ref& opInst) { /// @todo MD node for immediatness, signedness, etc... auto* II{ opInst->getMDNode() }; + auto* OAI{ opInst->getMDNode() }; auto opName{ II->InstName }; auto src1Reg{ dynCastRef(opInst->getSrc()) }; auto src2Reg{ dynCastRef(opInst->getSrc2()) }; auto dstReg{ dynCastRef(opInst->getDestination()) }; - fmt::println(m_Stream, "{} {}, {}, {}", opName, - formatMachineValue(opInst->getDestination()), - formatMachineValue(opInst->getSrc()), - formatMachineValue(opInst->getSrc2())); + if (OAI && OAI->IsSingleOp) { + fmt::println(m_Stream, "{} {}, {}", opName, + formatMachineValue(opInst->getDestination()), + formatMachineValue(opInst->getSrc())); + } else { + fmt::println(m_Stream, "{} {}, {}, {}", opName, + formatMachineValue(opInst->getDestination()), + formatMachineValue(opInst->getSrc()), + formatMachineValue(opInst->getSrc2())); + } } void Printer::visit(const Ref& unaryInst) { diff --git a/src/IR/IRCodegen.cpp b/src/IR/IRCodegen.cpp index 6f29ad55..aea4aa4b 100644 --- a/src/IR/IRCodegen.cpp +++ b/src/IR/IRCodegen.cpp @@ -50,6 +50,18 @@ class StoreSync : public Metadata { ValueRef m_Loc; }; +struct BrToLoopEndMDTag : public MDTag { + explicit BrToLoopEndMDTag() + : MDTag{ "br_to_loop_end" } { + } +}; + +struct BrToLoopStartMDTag : public MDTag { + explicit BrToLoopStartMDTag() + : MDTag{ "br_to_loop_cond" } { + } +}; + auto IRCodegen::genBranchFromCondition( const Ref& cond, bool negate) -> Ref { @@ -154,6 +166,15 @@ auto IRCodegen::visit(const Ref& unaryOpExpr) case TokenKind::Plus: result = addInstruction(makeRef(dst, operand)); break; + case TokenKind::PlusPlus: + result = addInstruction(makeRef( + TokenKind::Plus, operand, operand, IntegralConstant::getChar(1))); + break; + case TokenKind::MinusMinus: + result = + addInstruction(makeRef(TokenKind::Minus, operand, operand, + IntegralConstant::getChar(-1))); + break; default: result = addInstruction( makeRef(unaryOpExpr->getOp(), dst, operand)); @@ -291,17 +312,17 @@ auto IRCodegen::visit(const Ref& ifStmtNode) -> ValueRef { genBranchFromCondition(ifStmtNode->getCondition())) }; bort_assert_nomsg(thenBr); - pushBB("_false"); + pushBB("_false_if"); genericVisit(ifStmtNode->getElseBlock()); auto endBr{ addInstruction(makeRef()) }; bort_assert_nomsg(endBr); - pushBB("_true"); + pushBB("_true_if"); auto lastBBIt{ m_Module.getLastBBIt() }; thenBr->setTarget(&*lastBBIt); genericVisit(ifStmtNode->getThenBlock()); - pushBB("_end"); + pushBB("_end_if"); lastBBIt = m_Module.getLastBBIt(); endBr->setTarget(&*lastBBIt); return nullptr; @@ -319,19 +340,34 @@ void IRCodegen::pushBB(std::string postfix, std::string name) { auto IRCodegen::visit(const Ref& whileStmtNode) -> ValueRef { - pushBB("_cond"); + pushBB("_cond_loop"); auto& condBB{ *m_Module.getLastBBIt() }; auto endBr{ addInstruction( genBranchFromCondition(whileStmtNode->getCondition(), true)) }; - pushBB("_body"); + pushBB("_body_loop"); genericVisit(whileStmtNode->getBody()); auto loopBr{ addInstruction(makeRef()) }; loopBr->setTarget(&condBB); - pushBB("_end"); - endBr->setTarget(&*m_Module.getLastBBIt()); + pushBB("_end_loop"); + auto& endBB{ *m_Module.getLastBBIt() }; + endBr->setTarget(&endBB); + + for (auto&& bb : *m_Module.getLastFunctionIt()) { + for (auto&& inst : bb) { + if (auto br{ dynCastRef(inst) }) { + if (br->getMDNode()) { + br->setTarget(&endBB); + br->removeMDNode(); + } else if (br->getMDNode()) { + br->setTarget(&condBB); + br->removeMDNode(); + } + } + } + } return nullptr; } @@ -376,4 +412,18 @@ void IRCodegen::processNewInst(const Ref& instruction) { } } +auto IRCodegen::visit(const Ref& /*breakStmt*/) + -> ValueRef { + auto newInst{ addInstruction(makeRef()) }; + newInst->addMDNode(BrToLoopEndMDTag{}); + return nullptr; +} + +auto IRCodegen::visit(const Ref& /*continueStmt*/) + -> ValueRef { + auto newInst{ addInstruction(makeRef()) }; + newInst->addMDNode(BrToLoopStartMDTag{}); + return nullptr; +} + } // namespace bort::ir diff --git a/src/IR/IRPrinter.cpp b/src/IR/IRPrinter.cpp index f1733006..b640950e 100644 --- a/src/IR/IRPrinter.cpp +++ b/src/IR/IRPrinter.cpp @@ -105,7 +105,10 @@ static auto styleInst(T&& name) { static constexpr cul::BiMap s_UnaryInstNames{ [](auto&& selector) { return selector.Case(TokenKind::Minus, "neg") .Case(TokenKind::Amp, "addr") - .Case(TokenKind::Star, "deref"); + .Case(TokenKind::Star, "deref") + .Case(TokenKind::PlusPlus, "inc") + .Case(TokenKind::MinusMinus, "dec") + .Case(TokenKind::Not, "not"); } }; static constexpr cul::BiMap s_OpInstNames{ [](auto&& selector) { @@ -113,10 +116,18 @@ static constexpr cul::BiMap s_OpInstNames{ [](auto&& selector) { .Case(TokenKind::Minus, "sub") .Case(TokenKind::Star, "mul") .Case(TokenKind::Div, "div") + .Case(TokenKind::Mod, "rem") .Case(TokenKind::Less, "slt") .Case(TokenKind::Greater, "sgt") + .Case(TokenKind::GreaterEqual, "sge") + .Case(TokenKind::LessEqual, "sle") + .Case(TokenKind::Equals, "seq") + .Case(TokenKind::NotEquals, "sne") .Case(TokenKind::Amp, "and") - .Case(TokenKind::Pipe, "or"); + .Case(TokenKind::Pipe, "or") + .Case(TokenKind::Xor, "xor") + .Case(TokenKind::LShift, "sl") + .Case(TokenKind::RShift, "sr"); } }; namespace bort { diff --git a/src/Parse/Parser.cpp b/src/Parse/Parser.cpp index 1c80018f..51710ce9 100644 --- a/src/Parse/Parser.cpp +++ b/src/Parse/Parser.cpp @@ -3,6 +3,7 @@ #include "bort/AST/ASTNode.hpp" #include "bort/AST/BinOpExpr.hpp" #include "bort/AST/Block.hpp" +#include "bort/AST/ContinueStmt.hpp" #include "bort/AST/ExpressionNode.hpp" #include "bort/AST/ExpressionStmt.hpp" #include "bort/AST/FunctionCallExpr.hpp" @@ -15,6 +16,7 @@ #include "bort/AST/VariableExpr.hpp" #include "bort/AST/WhileStmt.hpp" #include "bort/Basic/Assert.hpp" +#include "bort/Basic/Casts.hpp" #include "bort/Basic/Ref.hpp" #include "bort/CLI/IO.hpp" #include "bort/Frontend/Symbol.hpp" @@ -26,17 +28,20 @@ namespace bort { static constexpr auto s_BinopPrecedence{ frozen::make_unordered_map({ - { TokenKind::Plus, 20 }, - { TokenKind::Minus, 20 }, - { TokenKind::Star, 40 }, - { TokenKind::Div, 40 }, - { TokenKind::Less, 10 }, - { TokenKind::Greater, 10 }, - { TokenKind::LessEqual, 10 }, - { TokenKind::GreaterEqual, 10 }, - { TokenKind::Amp, 9 }, - { TokenKind::Pipe, 8 }, - { TokenKind::Assign, 5 }, + { TokenKind::Star, 400 }, { TokenKind::Div, 400 }, + { TokenKind::Mod, 400 }, { TokenKind::Plus, 200 }, + { TokenKind::Minus, 200 }, { TokenKind::LShift, 150 }, + { TokenKind::RShift, 150 }, { TokenKind::Less, 100 }, + { TokenKind::Greater, 100 }, { TokenKind::LessEqual, 100 }, + { TokenKind::GreaterEqual, 100 }, { TokenKind::Equals, 95 }, + { TokenKind::NotEquals, 95 }, { TokenKind::Amp, 90 }, + { TokenKind::Xor, 80 }, { TokenKind::Pipe, 70 }, + { TokenKind::AmpAmp, 65 }, { TokenKind::PipePipe, 60 }, + { TokenKind::Assign, 50 }, { TokenKind::PlusAssign, 50 }, + { TokenKind::MinusAssign, 50 }, { TokenKind::StarAssign, 50 }, + { TokenKind::DivAssign, 50 }, { TokenKind::AmpAssign, 50 }, + { TokenKind::XorAssign, 50 }, { TokenKind::PipeAssign, 50 }, + { TokenKind::LShiftAssign, 50 }, { TokenKind::RShiftAssign, 50 }, }) }; @@ -53,7 +58,7 @@ auto Parser::buildAST() -> Ref { break; } - auto node{ parseStatement() }; + auto node{ parseDeclarationStatement() }; if (!node) { break; } @@ -81,7 +86,7 @@ auto Parser::parseNumberExpr() -> Unique { ast::ASTDebugInfo{ token }, value, std::move(type)); } -auto Parser::parseParenExpr() -> Unique { +auto Parser::parseParenExpr() -> Ref { bort_assert(curTok().is(TokenKind::LParen), "Expected '('"); consumeToken(); @@ -119,21 +124,21 @@ auto Parser::parseIdentifierExpr() -> Unique { makeRef(std::string{ identifierTok.getStringView() })); } -auto Parser::tryParseLValue() - -> std::optional> { - if (curTok().is(TokenKind::Identifier)) { - return parseIdentifierExpr(); - } - return std::nullopt; +auto Parser::parseVarExpr() -> Unique { + bort_assert(curTok().is(TokenKind::Identifier), "Expected identifier"); + auto varTok{ curTok() }; + consumeToken(); + + return m_ASTRoot->registerNode( + ast::ASTDebugInfo{ varTok }, + makeRef(std::string{ varTok.getStringView() })); } -auto Parser::parseValueExpression() - -> std::unique_ptr { - if (auto lvalue{ tryParseLValue() }) { - return std::move(*lvalue); - } +auto Parser::parseValueExpression() -> Ref { switch (curTok().getKind()) { + case TokenKind::Identifier: + return parseIdentifierExpr(); case TokenKind::LParen: return parseParenExpr(); case TokenKind::NumericLiteral: @@ -177,7 +182,9 @@ auto Parser::parseSizeofExpr() -> Unique { auto Parser::parseUnaryOpExpr() -> Unique { if (!curTok().isOneOf(TokenKind::Plus, TokenKind::Minus, TokenKind::Amp, - TokenKind::Star)) { + TokenKind::Star, TokenKind::PlusPlus, + TokenKind::MinusMinus, TokenKind::Not, + TokenKind::Tilde)) { Diagnostic::emitError(curTok(), "Expected unary operator in value expression"); return invalidNode(); @@ -185,22 +192,17 @@ auto Parser::parseUnaryOpExpr() -> Unique { auto op{ curTok() }; consumeToken(); - Unique operand; - if (op.is(TokenKind::Amp)) { - auto lvalueOpt{ tryParseLValue() }; - if (!lvalueOpt) { - Diagnostic::emitError(curTok(), "Expected lvalue"); - return invalidNode(); - } - operand = std::move(*lvalueOpt); - } else { - operand = parseValueExpression(); + + // same in IR + if (op.is(TokenKind::Tilde)) { + op.setKind(TokenKind::Not); } + return m_ASTRoot->registerNode( - ast::ASTDebugInfo{ op }, std::move(operand), op.getKind()); + ast::ASTDebugInfo{ op }, parseValueExpression(), op.getKind()); } -auto Parser::parseExpression() -> std::unique_ptr { +auto Parser::parseExpression() -> Ref { auto lhs{ parseValueExpression() }; if (!lhs) { return invalidNode(); @@ -215,9 +217,9 @@ static auto getTokPrecedence(const Token& tok) -> int32_t { return -1; } -auto Parser::parseBinOpRhs(std::unique_ptr lhs, +auto Parser::parseBinOpRhs(Ref lhs, int32_t prevPrecedence) - -> std::unique_ptr { + -> Ref { while (true) { // precedence or -1 if not a binop auto tokPrecedence{ getTokPrecedence(curTok()) }; @@ -227,7 +229,7 @@ auto Parser::parseBinOpRhs(std::unique_ptr lhs, auto binOp{ curTok() }; consumeToken(); - auto rhs{ parseValueExpression() }; + Ref rhs{ parseValueExpression() }; if (!rhs) { return invalidNode(); } @@ -244,9 +246,53 @@ auto Parser::parseBinOpRhs(std::unique_ptr lhs, } // now: (lhs binOp rhs) lookahead unparsed - lhs = m_ASTRoot->registerNode( - ast::ASTDebugInfo{ binOp }, std::move(lhs), std::move(rhs), - binOp.getKind()); + + // these are the same in IR + if (binOp.is(TokenKind::AmpAmp)) { + binOp.setKind(TokenKind::Amp); + } + if (binOp.is(TokenKind::PipePipe)) { + binOp.setKind(TokenKind::Pipe); + } + + // try to desugar compound assignment + auto getAssignTok{ [](TokenKind tok) -> std::optional { + switch (tok) { + case TokenKind::PlusAssign: + return TokenKind::Plus; + case TokenKind::MinusAssign: + return TokenKind::Minus; + case TokenKind::StarAssign: + return TokenKind::Star; + case TokenKind::DivAssign: + return TokenKind::Div; + case TokenKind::AmpAssign: + return TokenKind::Amp; + case TokenKind::XorAssign: + return TokenKind::Xor; + case TokenKind::PipeAssign: + return TokenKind::Div; + case TokenKind::LShiftAssign: + return TokenKind::LShift; + case TokenKind::RShiftAssign: + return TokenKind::RShift; + default: + return std::nullopt; + } + } }; + auto assignTok{ getAssignTok(binOp.getKind()) }; + + if (assignTok) { + auto actualOp{ m_ASTRoot->registerNode( + ast::ASTDebugInfo{ binOp }, lhs, std::move(rhs), *assignTok) }; + lhs = m_ASTRoot->registerNode( + ast::ASTDebugInfo{ binOp }, std::move(lhs), std::move(actualOp), + TokenKind::Assign); + } else { + lhs = m_ASTRoot->registerNode( + ast::ASTDebugInfo{ binOp }, std::move(lhs), std::move(rhs), + binOp.getKind()); + } } } @@ -294,7 +340,15 @@ auto Parser::parseDeclarationStatement() -> Ref { return parseFunctionDecl(type, nameTok); } - return parseVarDecl(type, nameTok); + auto varDecl{ parseVarDecl(type, nameTok) }; + + if (curTok().isNot(TokenKind::Semicolon)) { + Diagnostic::emitError(curTok(), "Expected ';'"); + return invalidNode(); + } + consumeToken(); + + return varDecl; } auto Parser::parseVarDecl(TypeRef type, @@ -333,12 +387,6 @@ auto Parser::parseVarDecl(TypeRef type, } } - if (curTok().isNot(TokenKind::Semicolon)) { - Diagnostic::emitError(curTok(), "Expected ';'"); - return invalidNode(); - } - consumeToken(); - if (type->getKind() == TypeKind::Void) { Diagnostic::emitError(nameTok, "Variable of incomplete type 'void'"); return invalidNode(); @@ -485,32 +533,6 @@ auto Parser::parseIndexationExpr(const Token& nameTok) ast::ASTDebugInfo{ nameTok }, makeRef(std::string{ nameTok.getStringView() })) }; - // Ref varAddr{ - // m_ASTRoot->registerNode( - // ast::ASTDebugInfo{ indexationStartTok }, std::move(varExpr), - // TokenKind::Amp) - // }; - // - // auto varAddrDeref{ m_ASTRoot->registerNode( - // ast::ASTDebugInfo{ indexationStartTok }, varAddr, - // TokenKind::Star) }; - // - // auto sizeofExpr{ m_ASTRoot->registerNode( - // ast::ASTDebugInfo{ indexationStartTok }, std::move(varAddrDeref), - // TokenKind::KW_sizeof) }; - // - // auto indexMultiplication{ m_ASTRoot->registerNode( - // ast::ASTDebugInfo{ indexationStartTok }, std::move(sizeofExpr), - // std::move(expr), TokenKind::Star) }; - // - // auto pointerAddition{ m_ASTRoot->registerNode( - // ast::ASTDebugInfo{ indexationStartTok }, varAddr, - // std::move(indexMultiplication), TokenKind::Plus) }; - // - // auto dereference{ m_ASTRoot->registerNode( - // ast::ASTDebugInfo{ indexationStartTok }, - // std::move(pointerAddition), TokenKind::Star) }; - return m_ASTRoot->registerNode( ast::ASTDebugInfo{ indexationStartTok }, std::move(varExpr), std::move(idxExpr)); @@ -532,6 +554,12 @@ auto Parser::parseStatement() -> Ref { return parseWhileStatement(); case TokenKind::KW_return: return parseReturnStatement(); + case TokenKind::KW_for: + return parseForStatement(); + case TokenKind::KW_break: + return parseBreakStatement(); + case TokenKind::KW_continue: + return parseContinueStatement(); default: break; } @@ -548,6 +576,48 @@ auto Parser::parseStatement() -> Ref { ast::ASTDebugInfo{ stmtTok }, std::move(expression)); } +auto Parser::parseBreakStatement() -> Ref { + bort_assert(curTok().is(TokenKind::KW_break), "Expected 'break'"); + + if (!m_InsideLoop) { + Diagnostic::emitError(curTok(), "'break' outside of loop"); + return invalidNode(); + } + + auto breakTok{ curTok() }; + consumeToken(); + + if (curTok().isNot(TokenKind::Semicolon)) { + Diagnostic::emitError(curTok(), "Expected ';'"); + return invalidNode(); + } + consumeToken(); + + return m_ASTRoot->registerNode( + ast::ASTDebugInfo{ breakTok }); +} + +auto Parser::parseContinueStatement() -> Ref { + bort_assert(curTok().is(TokenKind::KW_continue), "Expected 'continue'"); + + if (!m_InsideLoop) { + Diagnostic::emitError(curTok(), "'break' outside of loop"); + return invalidNode(); + } + + auto continueTok{ curTok() }; + consumeToken(); + + if (curTok().isNot(TokenKind::Semicolon)) { + Diagnostic::emitError(curTok(), "Expected ';'"); + return invalidNode(); + } + consumeToken(); + + return m_ASTRoot->registerNode( + ast::ASTDebugInfo{ continueTok }); +} + auto Parser::parseBlock() -> Unique { bort_assert(curTok().is(TokenKind::LBrace), "Expected '{'"); auto block{ m_ASTRoot->registerNode( @@ -613,7 +683,10 @@ auto Parser::parseWhileStatement() -> Ref { return invalidNode(); } auto condition{ parseParenExpr() }; + + m_InsideLoop = true; auto body{ parseBlock() }; + m_InsideLoop = false; return m_ASTRoot->registerNode( ast::ASTDebugInfo{ whileTok }, std::move(condition), @@ -639,4 +712,83 @@ auto Parser::parseReturnStatement() -> Ref { return m_ASTRoot->registerNode({ kwTok }, expr); } +/// desugared into +/// { +/// init; +/// while (condition) { +/// ... +/// update; +/// } +auto Parser::parseForStatement() -> Ref { + bort_assert(curTok().is(TokenKind::KW_for), "Expected 'for'"); + auto forTok{ curTok() }; + consumeToken(); + + if (curTok().isNot(TokenKind::LParen)) { + Diagnostic::emitError(curTok(), "Expected '('"); + return invalidNode(); + } + consumeToken(); + + Ref init; + if (curTok().isNot(TokenKind::Semicolon)) { + // a little trick + init = dynCastRef(parseDeclarationStatement()); + if (!init) { + Diagnostic::emitError(curTok(), "Expected variable declaration"); + return invalidNode(); + } + } + + Ref condition; + if (curTok().isNot(TokenKind::Semicolon)) { + condition = parseExpression(); + if (curTok().isNot(TokenKind::Semicolon)) { + Diagnostic::emitError(curTok(), "Expected ';'"); + return invalidNode(); + } + consumeToken(); + } + + if (!condition) { + condition = m_ASTRoot->registerNode( + ast::ASTDebugInfo{ curTok() }, 1, IntType::get()); + } + + Ref update; + auto updateTok{ curTok() }; + if (curTok().isNot(TokenKind::Semicolon)) { + update = parseExpression(); + } + + if (curTok().isNot(TokenKind::RParen)) { + Diagnostic::emitError(curTok(), "Expected ')'"); + return invalidNode(); + } + consumeToken(); + + if (curTok().isNot(TokenKind::LBrace)) { + Diagnostic::emitError(curTok(), "Expected '{{'"); + return invalidNode(); + } + + m_InsideLoop = true; + auto body{ parseBlock() }; + m_InsideLoop = false; + + if (update) { + body->pushChild(m_ASTRoot->registerNode( + ast::ASTDebugInfo{ updateTok }, std::move(update))); + } + + auto outerBlock{ m_ASTRoot->registerNode( + ast::ASTDebugInfo{ curTok() }) }; + outerBlock->pushChild(init); + outerBlock->pushChild(m_ASTRoot->registerNode( + ast::ASTDebugInfo{ forTok }, std::move(condition), + std::move(body))); + + return outerBlock; +} + } // namespace bort diff --git a/tests/corpus/expression.c b/tests/corpus/expression.c index 34c411ed..41e5d736 100644 --- a/tests/corpus/expression.c +++ b/tests/corpus/expression.c @@ -3,7 +3,8 @@ int main() { var = 1; char cvar; cvar = 1; - if (var + cvar < 2) { + cvar ^= 1; + if (!(var + cvar == 2)) { var = 1; } else { var = 2; diff --git a/tests/corpus/loops.c b/tests/corpus/loops.c index 763d2be0..e1f676e4 100644 --- a/tests/corpus/loops.c +++ b/tests/corpus/loops.c @@ -2,6 +2,19 @@ int main() { int i; i = 0; while (i < 1000000) { - i = i + 1; + i += 1; + + if (i % 2374 == 0) { + break; + } + } + + for (int j = 0; j < 1000000; ++j) { + if (i % 2 == 0) { + i -= 3; + continue; + } + + i = i - 1; } }