diff --git a/include/common.h b/include/common.h index ca0390f..98043df 100644 --- a/include/common.h +++ b/include/common.h @@ -7,15 +7,15 @@ #include #include -#define panic(message) \ - do { \ - throw GrammarException(__FILE__, __LINE__, message); \ +#define panic(message) \ + do { \ + throw GrammarException(__FILE__, __LINE__, (message)); \ } while (0) -#define sysy_assert(cond) \ - do { \ - if (!(cond)) \ - GrammarException(__FILE__, __LINE__, #cond); \ +#define sysy_assert(cond) \ + do { \ + if (!(cond)) \ + throw GrammarException(__FILE__, __LINE__, #cond); \ } while (0) namespace antlrSysY { @@ -25,11 +25,11 @@ public: GrammarException() : message("Unknown Grammar Exception") {} GrammarException(const std::string &what_arg) : message(what_arg){}; GrammarException(const char *what_arg) : message(what_arg){}; - GrammarException(const char *filename, int line, - const std::string &what_arg) { - std::stringstream ss; - ss << filename << ":" << line << ": " << what_arg; - ss >> message; + GrammarException(const char *filename, int line, const char *what_arg) { + std::ostringstream ss; + ss << filename << ":" << line << ": " + << "\x1b[031m" << what_arg << "\x1b[0m"; + message = ss.str(); }; virtual const char *what() const noexcept { diff --git a/include/llir_type.h b/include/llir_type.h index f8790d8..faebbdc 100644 --- a/include/llir_type.h +++ b/include/llir_type.h @@ -1,13 +1,90 @@ #pragma once +#include +#include namespace antlrSysY { -enum class Type { - IntegerType, - FunctionType, - ArrayType, - PointerType, - VectorType, +class Type { +public: + enum class TypeTag { + IntegerType, + FunctionType, + ArrayType, + PointerType, + VectorType, + VoidType, + LabelType, + }; + TypeTag type_tag; + Type(TypeTag type_tag) : type_tag(type_tag) {} + virtual ~Type() = default; +}; + +class IntegerType : public Type { +public: + enum class IntTypes { I32, I1 }; + IntTypes int_type = IntTypes::I32; + IntegerType(IntTypes int_type = IntTypes::I32) : Type(TypeTag::IntegerType), int_type(int_type) {} + bool isI32() { + return int_type == IntTypes::I32; + } + bool isI1() { + return int_type == IntTypes::I1; + } +}; + +class VoidType : public Type { +public: + VoidType() : Type(TypeTag::VoidType) {} +}; + +class LabelType : public Type { +public: + LabelType() : Type(TypeTag::LabelType) {} +}; + +typedef std::shared_ptr TypePtr_t; + +class TypeHelper { +public: + static bool isIntegerType(TypePtr_t type) { + return (type->type_tag == Type::TypeTag::IntegerType); + } + static bool isIntegerTypeI32(TypePtr_t type) { + return isIntegerType(type) && std::dynamic_pointer_cast(type)->isI32(); + } + static bool isIntegerTypeI1(TypePtr_t &type) { + return isIntegerType(type) && std::dynamic_pointer_cast(type)->isI1(); + } + inline static TypePtr_t TYPE_I32 = std::make_shared(); + inline static TypePtr_t TYPE_I1 = std::make_shared(IntegerType::IntTypes::I1); + inline static TypePtr_t TYPE_LABEL = std::make_shared(); + inline static TypePtr_t TYPE_VOID = std::make_shared(); +}; + +class ArrayType : public Type { +public: + TypePtr_t element_type = TypeHelper::TYPE_I32; + int element_count = 0; + ArrayType() : Type(TypeTag::ArrayType) {} + ArrayType(TypePtr_t element_type, int element_count) + : Type(TypeTag::ArrayType), element_count(element_count), element_type(element_type) {} +}; + +class PointerType : public Type { +public: + TypePtr_t pointed_type = TypeHelper::TYPE_I32; + PointerType() : Type(TypeTag::PointerType) {} + PointerType(TypePtr_t pointed_type) : Type(TypeTag::PointerType), pointed_type(pointed_type) {} +}; + +class FunctionType : public Type { +public: + TypePtr_t return_type = TypeHelper::TYPE_I32; + std::vector params_type; + FunctionType(TypePtr_t return_type) : Type(TypeTag::FunctionType), return_type(return_type) {} + FunctionType(TypePtr_t return_type, const std::vector ¶ms_type) + : Type(TypeTag::FunctionType), return_type(return_type), params_type(params_type) {} }; } // namespace antlrSysY \ No newline at end of file diff --git a/include/llir_value.h b/include/llir_value.h index 1071c9a..c9c240b 100644 --- a/include/llir_value.h +++ b/include/llir_value.h @@ -1,31 +1,186 @@ #pragma once +#include +#include #include #include #include "llir_type.h" namespace antlrSysY { + class Value { public: std::string name; - Type type; - Value(const std::string &name, const Type &type) : name(name), type(type) {} + TypePtr_t type; + Value(const std::string &name, TypePtr_t type) : name(name), type(type) {} virtual ~Value() = default; }; -class Function : public Value {}; +class User : public Value { +public: +}; -class BasicBlock : public Value {}; +class BasicBlock; +class Instruction; + +class FParam : public Value { +public: + FParam(const std::string &name, TypePtr_t type) : Value(name, type) {} +}; + +class Function : public Value { +public: + std::vector> fparam_list; + std::vector> bb_list; + Function(const std::string &name, TypePtr_t ret_type) : Value(name, std::make_shared(ret_type)) {} + std::shared_ptr get_type() { + return std::dynamic_pointer_cast(type); + } +}; + +class BasicBlock : public Value { +public: + std::vector> inst_list; + std::shared_ptr parent; + std::vector> successors; + std::vector> predecessors; + BasicBlock(const std::string &name, std::shared_ptr parent) : Value(name, TypeHelper::TYPE_LABEL) { + this->parent = parent; + } +}; class ConstantInt : public Value { public: int value; - int is_bool; // bool 0 or int 1 - ConstantInt(const std::string &name, int value, int is_bool) - : Value(name, Type::IntegerType), value(value), is_bool(is_bool) {} + ConstantInt(const std::string &name, int value) : Value(name, TypeHelper::TYPE_I32), value(value) {} }; class ConstantArr : public Value {}; +class GlobalVarInt : public Value { +public: + int init_value; + GlobalVarInt(const std::string &name, int init_value) : Value(name, TypeHelper::TYPE_I32), init_value(init_value) {} +}; + +enum class InstTag { + Add, + Sub, + Mod, + Mul, + Div, + Lt, + Le, + Ge, + Gt, + Eq, + Ne, + And, + Or, + Br, + Call, + Ret, + Alloca, + Load, + Store, + GEP, + Zext, + Phi, + MemPhi, + LoadDep, + InsertEle, + ExtractEle +}; + +typedef std::shared_ptr ValuePtr_t; +typedef std::shared_ptr BasicBlockPtr_t; +typedef std::shared_ptr FunctionPtr_t; +class Instruction : public Value { +public: + InstTag tag; + std::shared_ptr parent_bb; + std::vector> operand_list; + Instruction(InstTag inst_tag, TypePtr_t type, std::shared_ptr parent_bb) + : Value("", type), tag(inst_tag), parent_bb(parent_bb) {} +}; + +class InstAlloca : public Instruction { +public: + bool isInit = false; + InstAlloca(TypePtr_t alloc_type, bool isInit, std::shared_ptr parent_bb) + : Instruction(InstTag::Alloca, std::make_shared(alloc_type), parent_bb), isInit(isInit) {} +}; + +class InstStore : public Instruction { +public: + InstStore(std::shared_ptr value, std::shared_ptr pointer, std::shared_ptr parent_bb) + : Instruction(InstTag::Store, TypeHelper::TYPE_VOID, parent_bb) { + operand_list.push_back(value); + operand_list.push_back(pointer); + } +}; + +class InstLoad : public Instruction { +public: + InstLoad(std::shared_ptr value, TypePtr_t type, std::shared_ptr parent_bb) + : Instruction(InstTag::Load, type, parent_bb) { + operand_list.push_back(value); + } +}; + +class InstBinary : public Instruction { +public: + InstBinary(InstTag inst_tag, TypePtr_t val_type, std::shared_ptr op1, std::shared_ptr op2, + std::shared_ptr parent_bb) + : Instruction(inst_tag, val_type, parent_bb) { + operand_list.push_back(op1); + operand_list.push_back(op2); + } +}; + +class InstZext : public Instruction { +public: + InstZext(std::shared_ptr op, std::shared_ptr parent_bb) + : Instruction(InstTag::Zext, TypeHelper::TYPE_I32, parent_bb) { + operand_list.push_back(op); + } +}; + +class InstBranch : public Instruction { +public: + // conditional branch + InstBranch(ValuePtr_t cond, BasicBlockPtr_t true_block, BasicBlockPtr_t false_block, BasicBlockPtr_t parent_bb) + : Instruction(InstTag::Br, TypeHelper::TYPE_VOID, parent_bb) { + this->operand_list.push_back(cond); + this->operand_list.push_back(true_block); + this->operand_list.push_back(false_block); + } + // unconditional branch + InstBranch(BasicBlockPtr_t target_block, BasicBlockPtr_t parent_bb) + : Instruction(InstTag::Br, TypeHelper::TYPE_VOID, parent_bb) { + this->operand_list.push_back(target_block); + } +}; + +class InstReturn : public Instruction { +public: + InstReturn(ValuePtr_t ret_val, BasicBlockPtr_t parent_bb) + : Instruction(InstTag::Ret, TypeHelper::TYPE_VOID, parent_bb) { + this->operand_list.push_back(ret_val); + } + InstReturn(BasicBlockPtr_t parent_bb) : Instruction(InstTag::Ret, TypeHelper::TYPE_VOID, parent_bb) {} +}; + +class InstCall : public Instruction { +public: + InstCall(FunctionPtr_t func, const std::vector &args, BasicBlockPtr_t parent_bb) + : Instruction(InstTag::Call, func->get_type()->return_type, parent_bb) { + operand_list.push_back(func); + for (auto arg : args) { + operand_list.push_back(arg); + } + } +}; + } // namespace antlrSysY \ No newline at end of file diff --git a/include/scopetable.h b/include/scopetable.h index b8b99d3..9080276 100644 --- a/include/scopetable.h +++ b/include/scopetable.h @@ -8,7 +8,8 @@ template class ScopeTable { private: std::vector _scope_list; - + bool _pre_enter = false; // use this for adding formal param to scope, which + // is also in the top block scope of a function public: // On init, there is already an empty scope ScopeTable() { @@ -16,7 +17,14 @@ public: } ~ScopeTable() {} - void enter_scope() { + void enter_scope(bool pre_enter = false) { + if (_pre_enter) { + _pre_enter = false; + sysy_assert(!pre_enter); + return; + } + if (pre_enter) + _pre_enter = true; _scope_list.push_back(ScopeTableEntry_t()); }; @@ -34,18 +42,18 @@ public: current_scope[name] = value; }; - std::optional get_name(const std::string &name) { - for (auto i = _scope_list.size() - 1; i >= 0; --i) { + std::optional get_name(const std::string &name) const { + for (int i = _scope_list.size() - 1; i >= 0; --i) { if (_scope_list[i].count(name)) { - return {_scope_list[i][name]}; + return {_scope_list[i].at(name)}; } } return {}; }; - std::optional get_name(const std::string &name, int level) { + std::optional get_name(const std::string &name, int level) const { if (_scope_list[level].count(name)) { - return {_scope_list[level][name]}; + return {_scope_list[level].at(name)}; } return {}; }; @@ -53,4 +61,4 @@ public: return _scope_list.size() - 1; } }; -} \ No newline at end of file +} // namespace antlrSysY \ No newline at end of file diff --git a/include/visitor.h b/include/visitor.h index b166bf5..62313f0 100644 --- a/include/visitor.h +++ b/include/visitor.h @@ -5,52 +5,104 @@ #include #include #include +#include #include #include #include -#include #include "antlrgen/SysyLexer.h" #include "antlrgen/SysyParser.h" #include "common.h" +#include "llir_type.h" #include "llir_value.h" #include "scopetable.h" namespace antlrSysY { +#pragma region FactoryFunctionDeclaration +std::shared_ptr build_ConstantInt(const std::string &name, int value); + +std::shared_ptr build_BasicBlock(const std::string &name, std::shared_ptr parent); + +std::shared_ptr build_InstLoad(std::shared_ptr value, TypePtr_t type, + std::shared_ptr parent_bb); +std::shared_ptr build_InstStore(std::shared_ptr value, std::shared_ptr pointer, + std::shared_ptr parent_bb); + +std::shared_ptr build_InstAlloca(TypePtr_t type, bool isInit, std::shared_ptr parent_bb); + +std::shared_ptr build_InstBinary(InstTag inst_tag, std::shared_ptr op1, std::shared_ptr op2, + std::shared_ptr parent_bb); + +std::shared_ptr build_InstZext(std::shared_ptr op, std::shared_ptr parent_bb); + +std::shared_ptr build_InstBranch(ValuePtr_t cond, BasicBlockPtr_t true_block, BasicBlockPtr_t false_block, + BasicBlockPtr_t parent_bb); + +std::shared_ptr build_InstBranch(BasicBlockPtr_t target_block, BasicBlockPtr_t parent_bb); + +std::shared_ptr build_InstReturn(ValuePtr_t ret_val, BasicBlockPtr_t parent_bb); + +std::shared_ptr build_InstReturn(BasicBlockPtr_t parent_bb); + +std::shared_ptr build_InstCall(FunctionPtr_t func, const std::vector &args, + BasicBlockPtr_t parent_bb); +#pragma endregion + class Visitor : public antlrSysY::SysyBaseVisitor { private: struct VisitorState { - + bool isGlobalInt = false; bool isConstInt = false; + bool isRealParam = false; + bool isCondExp = false; + std::shared_ptr current_func = {}; + std::shared_ptr current_bb = {}; + + struct loop_record { + BasicBlockPtr_t cond, body, next; + int id; + }; + int loop_stmt_count = 0; + std::vector loop_stack = {}; + }; + + struct Module { + std::vector> functions; }; private: ScopeTable> _scope_tab; + ScopeTable> _func_tab; // var can have same name as func VisitorState _state = {}; + Module _module = {}; std::map token_type_map; + std::shared_ptr CONST0; public: Visitor(SysyLexer &lexer) { for (auto item : lexer.getTokenTypeMap()) { token_type_map[std::string(item.first)] = item.second; } + CONST0 = std::make_shared("CONST0", 0); } std::any visitProgram(SysyParser::ProgramContext *ctx) override { // before visiting, we add some lib functions return this->SysyBaseVisitor::visitProgram(ctx); } - std::any visitCompUnit(SysyParser::CompUnitContext *ctx) override { - return this->visitChildren(ctx); - } + // std::any visitCompUnit(SysyParser::CompUnitContext *ctx) override; - std::any visitDecl(SysyParser::DeclContext *ctx) override { - return this->visitChildren(ctx); - } + // std::any visitDecl(SysyParser::DeclContext *ctx) override; std::any visitConstDecl(SysyParser::ConstDeclContext *ctx) override; + // std::any visitVarDecl(SysyParser::VarDeclContext *ctx) override; + + std::any visitVarDef(SysyParser::VarDefContext *ctx) override; + + std::any visitInitVal(SysyParser::InitValContext *ctx) override; + // constDef : IDENT ('[' constExp ']')* '=' constInitVal ';' std::any visitConstDef(SysyParser::ConstDefContext *ctx) override; @@ -75,6 +127,46 @@ public: std::any visitIntConst(SysyParser::IntConstContext *ctx) override; std::any visitLVal(SysyParser::LValContext *) override; + + std::any visitFuncDef(SysyParser::FuncDefContext *ctx) override; + + std::any visitFuncFParams(SysyParser::FuncFParamsContext *ctx) override; + + std::any visitFuncFParam(SysyParser::FuncFParamContext *ctx) override; + + std::any visitBlock(SysyParser::BlockContext *ctx) override; + + // std::any visitBlockItem(SysyParser::BlockItemContext *ctx) override; + + std::any visitAssignStmt(SysyParser::AssignStmtContext *ctx) override; + + // std::any visitExpStmt(SysyParser::ExpStmtContext *ctx) override; + + // std::any visitBlockStmt(SysyParser::BlockStmtContext *ctx) override; + + std::any visitIfStmt(SysyParser::IfStmtContext *ctx) override; + + std::any visitWhileStmt(SysyParser::WhileStmtContext *ctx) override; + + std::any visitBreakStmt(SysyParser::BreakStmtContext *ctx) override; + + std::any visitContinueStmt(SysyParser::ContinueStmtContext *ctx) override; + + std::any visitReturnStmt(SysyParser::ReturnStmtContext *ctx) override; + + // std::any visitCond(SysyParser::CondContext *ctx) override; + + // std::any visitFuncRParams(SysyParser::FuncRParamsContext *ctx) override; + + // std::any visitFuncRParam(SysyParser::FuncRParamContext *ctx) override; + + std::any visitRelExp(SysyParser::RelExpContext *ctx) override; + + std::any visitEqExp(SysyParser::EqExpContext *ctx) override; + + std::any visitLAndExp(SysyParser::LAndExpContext *ctx) override; + + std::any visitLOrExp(SysyParser::LOrExpContext *ctx) override; }; } // namespace antlrSysY \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1c86d15..9dcd838 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,6 +2,9 @@ #include #include +#include "3rdparty/easylogging++.h" +#include "BaseErrorListener.h" +#include "Exceptions.h" #include "antlr4-runtime.h" #include "antlrgen/SysyLexer.h" #include "antlrgen/SysyParser.h" @@ -15,17 +18,22 @@ INITIALIZE_EASYLOGGINGPP using namespace antlrSysY; using namespace antlr4; +class AbortErrorListener : public BaseErrorListener { +public: + void syntaxError(Recognizer *recognizer, Token *offendingSymbol, size_t line, size_t charPositionInLine, + const std::string &msg, std::exception_ptr e) override { + throw ParseCancellationException("line " + std::to_string(line) + ":" + std::to_string(charPositionInLine) + " " + + msg); + } +}; + int main(int argc, const char **argv) { #pragma region ArgParse argparse::ArgumentParser arg_parser("Catfood's SysY Compiler"); arg_parser.add_argument("source").help("A single SysY source file"); - arg_parser.add_argument("-S") - .implicit_value(true) - .help("Useless but required by the contest") - .required(); + arg_parser.add_argument("-S").implicit_value(true).help("Useless but required by the contest").required(); arg_parser.add_argument("-o").help("Output file name").required(); - arg_parser.add_argument("-O1").implicit_value(true).default_value(false).help( - "Performance mode"); + arg_parser.add_argument("-O1").implicit_value(true).default_value(false).help("Performance mode"); try { arg_parser.parse_args(argc, argv); } catch (const std::runtime_error &err) { @@ -40,6 +48,16 @@ int main(int argc, const char **argv) { // std::endl; #pragma endregion +#pragma region Logger + el::Configurations defaultConf; + defaultConf.setToDefault(); + defaultConf.set(el::Level::Debug, el::ConfigurationType::Format, "%level %loc %msg"); + defaultConf.set(el::Level::Warning, el::ConfigurationType::Format, "%level %loc %msg"); + defaultConf.set(el::Level::Error, el::ConfigurationType::Format, "%level %loc %msg"); + el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); + el::Loggers::reconfigureLogger("default", defaultConf); +#pragma endregion + std::ifstream ifs_source_file(source_file); ANTLRInputStream input(ifs_source_file); SysyLexer lexer(&input); @@ -52,6 +70,8 @@ int main(int argc, const char **argv) { #endif SysyParser parser(&tokens); + parser.removeErrorListeners(); + parser.addErrorListener(new AbortErrorListener()); auto tree = parser.program(); Visitor visitor(lexer); visitor.visitProgram(tree); diff --git a/src/visit_factory.cpp b/src/visit_factory.cpp new file mode 100644 index 0000000..1aadf62 --- /dev/null +++ b/src/visit_factory.cpp @@ -0,0 +1,94 @@ +#include "common.h" +#include "llir_type.h" +#include "llir_value.h" +#include "visitor.h" +#include + +namespace antlrSysY { + +std::shared_ptr build_ConstantInt(const std::string &name, int value) { + auto lala = std::make_shared(name, value); + return lala; +} + +std::shared_ptr build_BasicBlock(const std::string &name, std::shared_ptr parent) { + auto basic_block = std::make_shared(name, parent); + parent->bb_list.push_back(basic_block); + return basic_block; +} + +std::shared_ptr build_InstLoad(std::shared_ptr value, TypePtr_t type, + std::shared_ptr parent_bb) { + auto inst_load = std::make_shared(value, type, parent_bb); + parent_bb->inst_list.push_back(inst_load); + return inst_load; +} + +std::shared_ptr build_InstStore(std::shared_ptr value, std::shared_ptr pointer, + std::shared_ptr parent_bb) { + auto inst_store = std::make_shared(value, pointer, parent_bb); + parent_bb->inst_list.push_back(inst_store); + return inst_store; +} + +std::shared_ptr build_InstAlloca(TypePtr_t type, bool isInit, std::shared_ptr parent_bb) { + auto inst_alloca = std::make_shared(type, isInit, parent_bb); + parent_bb->inst_list.push_back(inst_alloca); + return inst_alloca; +} + +std::shared_ptr build_InstBinary(InstTag inst_tag, std::shared_ptr op1, std::shared_ptr op2, + std::shared_ptr parent_bb) { + std::shared_ptr inst_binary; + if (InstTag::Lt <= inst_tag && inst_tag <= InstTag::Or) { + inst_binary = std::make_shared(inst_tag, TypeHelper::TYPE_I1, op1, op2, parent_bb); + } + else if (InstTag::Add <= inst_tag && inst_tag <= InstTag::Div) { + inst_binary = std::make_shared(inst_tag, TypeHelper::TYPE_I32, op1, op2, parent_bb); + } + else { + panic("Invalid Binary Operation"); + } + parent_bb->inst_list.push_back(inst_binary); + return inst_binary; +} + +std::shared_ptr build_InstZext(std::shared_ptr op, std::shared_ptr parent_bb) { + auto inst_zext = std::make_shared(op, parent_bb); + parent_bb->inst_list.push_back(inst_zext); + return inst_zext; +} + +std::shared_ptr build_InstBranch(ValuePtr_t cond, BasicBlockPtr_t true_block, BasicBlockPtr_t false_block, + BasicBlockPtr_t parent_bb) { + auto inst = std::make_shared(cond, true_block, false_block, parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; +} + +std::shared_ptr build_InstBranch(BasicBlockPtr_t target_block, BasicBlockPtr_t parent_bb) { + auto inst = std::make_shared(target_block, parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; +} + +std::shared_ptr build_InstReturn(ValuePtr_t ret_val, BasicBlockPtr_t parent_bb) { + auto inst = std::make_shared(ret_val, parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; +} + +std::shared_ptr build_InstReturn(BasicBlockPtr_t parent_bb) { + auto inst = std::make_shared(parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; +} + +std::shared_ptr build_InstCall(FunctionPtr_t func, const std::vector &args, + BasicBlockPtr_t parent_bb) { + auto inst = std::make_shared(func, args, parent_bb); + parent_bb->inst_list.push_back(inst); + return inst; +} + +} // namespace antlrSysY \ No newline at end of file diff --git a/src/visitor.cpp b/src/visitor.cpp index 0812e5c..f16cdad 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -1,11 +1,46 @@ #include "visitor.h" #include "common.h" +#include "llir_type.h" #include "llir_value.h" #include #include +#include +#include + +// Some virtual methods are comment out in the class definition, since they do +// nothing +// beyond base class implement namespace antlrSysY { +#define ANY2VALUE(_my_type_) \ + if (fuck_any.type() == typeid(std::shared_ptr<_my_type_>)) { \ + auto value = std::any_cast>(fuck_any); \ + return value; \ + } + +ValuePtr_t any_to_Value(const std::any &fuck_any) { + ANY2VALUE(Value) + ANY2VALUE(User) + ANY2VALUE(FParam) + ANY2VALUE(Function) + ANY2VALUE(BasicBlock) + ANY2VALUE(ConstantInt) + ANY2VALUE(ConstantArr) + ANY2VALUE(GlobalVarInt) + ANY2VALUE(Instruction) + ANY2VALUE(InstAlloca) + ANY2VALUE(InstStore) + ANY2VALUE(InstLoad) + ANY2VALUE(InstBinary) + ANY2VALUE(InstZext) + ANY2VALUE(InstBranch) + ANY2VALUE(InstReturn) + ANY2VALUE(InstCall) + LOG(ERROR) << fuck_any.type().name(); + panic("Unreachable"); +} + std::any Visitor::visitConstDecl(SysyParser::ConstDeclContext *ctx) { for (auto constDef : ctx->constDef()) { visitConstDef(constDef); @@ -19,40 +54,87 @@ std::any Visitor::visitConstDef(SysyParser::ConstDefContext *ctx) { LOG(DEBUG) << "Visiting ConstDef " << name; if (_scope_tab.get_name(name, _scope_tab.get_level()).has_value()) throw GrammarException("Duplicate const def"); + + // not array if (ctx->constExp().empty()) { - // not array define if (ctx->constInitVal() != nullptr) { - auto result = std::any_cast>(visit(ctx->constInitVal())); - // _scope_tab.push_name(name, Value *const &&value) - _scope_tab.push_name(name, result); + auto result = std::any_cast(visit(ctx->constInitVal())); + auto constint = build_ConstantInt(name, result); + _scope_tab.push_name(name, constint); } } return {}; } +std::any Visitor::visitVarDef(SysyParser::VarDefContext *ctx) { + auto name = ctx->IDENT()->getText(); + LOG(DEBUG) << "Visiting VarDef " << name; + if (_scope_tab.get_name(name, _scope_tab.get_level()).has_value()) + panic("Duplicate const def"); + // Not Array + if (ctx->constExp().empty()) { + // global variable + if (_scope_tab.get_level() == 0) { + // global variable must be initialized, either specified or zeroed + int result = 0; + if (ctx->initVal()) { + _state.isGlobalInt = true; + result = std::any_cast(visit(ctx->initVal())); + _state.isGlobalInt = false; + } + auto global_var_int = std::make_shared(name, result); + _scope_tab.push_name(name, global_var_int); + } + // local variable + else { + auto alloca = build_InstAlloca(TypeHelper::TYPE_I32, false, _state.current_bb); + _scope_tab.push_name(name, alloca); + if (ctx->initVal()) { + auto init_val = any_to_Value(visitInitVal(ctx->initVal())); + build_InstStore(init_val, alloca, _state.current_bb); + } + } + } + else { + // Array + panic("To be implemented"); + } + return {}; +} + +std::any Visitor::visitInitVal(SysyParser::InitValContext *ctx) { + if (ctx->exp()) { + if (_state.isGlobalInt) + _state.isConstInt = true; + auto retval = visit(ctx->exp()); + if (_state.isGlobalInt) + _state.isConstInt = false; + return retval; + } + // Array + panic("To be implemented"); +} + std::any Visitor::visitConstInitVal(SysyParser::ConstInitValContext *ctx) { if (ctx->constExp() != nullptr /*&& ctx->arrDim.size() == 0*/) { return visit(ctx->constExp()); } - return {}; + panic("To be implemented"); } std::any Visitor::visitConstExp(SysyParser::ConstExpContext *ctx) { if (ctx->addExp() == nullptr) - panic("missing element"); + panic("Unreachable"); _state.isConstInt = true; int result = std::any_cast(visit(ctx->addExp())); _state.isConstInt = false; LOG(DEBUG) << "ConstExp Eval to " << result; - auto constint = std::make_shared("", result, 1); - return {constint}; + return {result}; } // addExp: mulExp | addExp ('+' | '-') mulExp; std::any Visitor::visitAddExp(SysyParser::AddExpContext *ctx) { if (_state.isConstInt) { - if (ctx->mulExp() == nullptr) - panic("missing element"); int result = std::any_cast(visit(ctx->mulExp())); int add_result = 0; if (ctx->addExp()) { @@ -65,34 +147,154 @@ std::any Visitor::visitAddExp(SysyParser::AddExpContext *ctx) { panic("missing operator"); } return {result}; - } else { - panic("To be implemented"); } - return {}; + else { + auto mul_exp = any_to_Value(visitMulExp(ctx->mulExp())); + if (ctx->addExp()) { + auto add_exp = any_to_Value(visitAddExp(ctx->addExp())); + if (std::dynamic_pointer_cast(add_exp->type)->isI1()) { + add_exp = build_InstZext(add_exp, _state.current_bb); + } + if (std::dynamic_pointer_cast(mul_exp->type)->isI1()) { + mul_exp = build_InstZext(mul_exp, _state.current_bb); + } + if (ctx->ADD()) { + mul_exp = build_InstBinary(InstTag::Add, add_exp, mul_exp, _state.current_bb); + } + else if (ctx->SUB()) { + mul_exp = build_InstBinary(InstTag::Sub, add_exp, mul_exp, _state.current_bb); + } + else + panic("Unreachable"); + } + return mul_exp; + } } std::any Visitor::visitMulExp(SysyParser::MulExpContext *ctx) { if (_state.isConstInt) { - if (ctx->unaryExp() == nullptr) - panic("missing element"); - int result = std::any_cast(visit(ctx->unaryExp())); + int result = std::any_cast(visitUnaryExp(ctx->unaryExp())); if (ctx->mulExp()) { int mul_result = std::any_cast(visit(ctx->mulExp())); if (ctx->MUL()) { result = mul_result * result; - } else if (ctx->DIV()) { + } + else if (ctx->DIV()) { result = mul_result / result; - } else if (ctx->MOD()) { + } + else if (ctx->MOD()) { result = mul_result % result; - } else - panic("missing operator"); + } + else + panic("Unreachable"); } return {result}; - } else { - panic("To be implemented"); } + else { + auto unary_exp = any_to_Value(visitUnaryExp(ctx->unaryExp())); + if (ctx->mulExp()) { + auto mul_exp = any_to_Value(visitMulExp(ctx->mulExp())); + if (std::dynamic_pointer_cast(unary_exp->type)->isI1()) { + unary_exp = build_InstZext(unary_exp, _state.current_bb); + } + if (std::dynamic_pointer_cast(mul_exp->type)->isI1()) { + mul_exp = build_InstZext(mul_exp, _state.current_bb); + } + if (ctx->MUL()) { + unary_exp = build_InstBinary(InstTag::Mul, mul_exp, unary_exp, _state.current_bb); + } + else if (ctx->DIV()) { + unary_exp = build_InstBinary(InstTag::Div, mul_exp, unary_exp, _state.current_bb); + } + else if (ctx->MOD()) { + unary_exp = build_InstBinary(InstTag::Mod, mul_exp, unary_exp, _state.current_bb); + } + else + panic("Unreachable"); + } + return {unary_exp}; + } +} +// relExp: addExp | relExp ('<' | '>' | '<=' | '>=') addExp; +std::any Visitor::visitRelExp(SysyParser::RelExpContext *ctx) { + auto add_exp = any_to_Value(visitAddExp(ctx->addExp())); + // should always has type I32 + sysy_assert(TypeHelper::isIntegerTypeI32(add_exp->type)); + if (ctx->relExp()) { + auto rel_exp = any_to_Value(visitRelExp(ctx->relExp())); + sysy_assert(TypeHelper::isIntegerTypeI32(rel_exp->type)); + if (ctx->relExp()->LE()) { + add_exp = build_InstBinary(InstTag::Le, rel_exp, add_exp, _state.current_bb); + } + else if (ctx->relExp()->LT()) { + add_exp = build_InstBinary(InstTag::Lt, rel_exp, add_exp, _state.current_bb); + } + else if (ctx->relExp()->GE()) { + add_exp = build_InstBinary(InstTag::Ge, rel_exp, add_exp, _state.current_bb); + } + else if (ctx->relExp()->GT()) { + add_exp = build_InstBinary(InstTag::Gt, rel_exp, add_exp, _state.current_bb); + } + else + panic("Unreachable"); + } + return add_exp; +} + +// eqExp: relExp | eqExp ('==' | '!=') relExp; +std::any Visitor::visitEqExp(SysyParser::EqExpContext *ctx) { + auto rel_exp = any_to_Value(visitRelExp(ctx->relExp())); + sysy_assert(TypeHelper::isIntegerTypeI1(rel_exp->type)); + if (ctx->eqExp()) { + auto eq_exp = any_to_Value(visitEqExp(ctx->eqExp())); + sysy_assert(TypeHelper::isIntegerTypeI1(eq_exp->type)); + if (ctx->EQ()) { + rel_exp = build_InstBinary(InstTag::Eq, eq_exp, rel_exp, _state.current_bb); + } + else if (ctx->NE()) { + rel_exp = build_InstBinary(InstTag::Ne, eq_exp, rel_exp, _state.current_bb); + } + else + panic("Unreachable"); + } + return rel_exp; +} + +// Notes about SideEffect: except for || and &&, other sub-expression evaluations are unsequenced +// as long as they are calculated before the operator +// lAndExp: eqExp | lAndExp '&&' eqExp; +// Luckily, there is only one path to lOrExp, which is `stmt` -> `cond` -> `lOrExp` +// Thus, there is no need to care about what if it appears in a arithmetic expression +std::any Visitor::visitLAndExp(SysyParser::LAndExpContext *ctx) { + auto eq_exp_list = ctx->eqExp(); + for (int i = 0; i < eq_exp_list.size(); ++i) { + auto next_block = build_BasicBlock("", _state.current_func); + auto eq_exp = any_to_Value(visitEqExp(eq_exp_list[i])); + sysy_assert(TypeHelper::isIntegerTypeI1(eq_exp->type)); // expect a boolean + auto condition = build_InstBinary(InstTag::Ne, eq_exp, CONST0, _state.current_bb); + build_InstBranch(condition, next_block, ctx->false_block, _state.current_bb); + _state.current_bb = next_block; + } + build_InstBranch(ctx->true_block, _state.current_bb); return {}; } + +// @retval: Bool +// lOrExp: lAndExp ('||' lAndExp)*; +std::any Visitor::visitLOrExp(SysyParser::LOrExpContext *ctx) { + auto n_and_exp = ctx->lAndExp().size(); + for (int i = 0; i < n_and_exp - 1; ++i) { + auto next_block = build_BasicBlock("", _state.current_func); + ctx->lAndExp(i)->true_block = ctx->true_block; + ctx->lAndExp(i)->false_block = next_block; + visitLAndExp(ctx->lAndExp(i)); + _state.current_bb = next_block; + } + ctx->lAndExp(n_and_exp - 1)->true_block = ctx->true_block; + ctx->lAndExp(n_and_exp - 1)->false_block = ctx->false_block; + return {}; +} + // unaryExp: primaryExp | IDENT '(' (funcRParams)? ')' | unaryOp unaryExp; std::any Visitor::visitUnaryExp(SysyParser::UnaryExpContext *ctx) { if (_state.isConstInt) { @@ -105,69 +307,331 @@ std::any Visitor::visitUnaryExp(SysyParser::UnaryExpContext *ctx) { else if (ctx->unaryOp()->NOT()) result = !result; else - panic("Missing operator"); + panic("Unreachable"); return {result}; - } else if (ctx->primaryExp()) { + } + else if (ctx->primaryExp()) { return visitPrimaryExp(ctx->primaryExp()); - } else if (ctx->IDENT()) { + } + else if (ctx->IDENT()) { panic("Unexpected func call in const expr"); } - panic("missing element"); - } else { - panic("To be implemented"); + panic("Unreachable"); + } + else { + if (ctx->unaryExp()) { + auto _result = visitUnaryExp(ctx->unaryExp()); + auto unary_exp = any_to_Value(_result); + sysy_assert(unary_exp->type->type_tag == Type::TypeTag::IntegerType); + if (std::dynamic_pointer_cast(unary_exp->type)->isI1()) { + unary_exp = build_InstZext(unary_exp, _state.current_bb); + } + if (ctx->unaryOp()->NOT()) { + // should eval to i1 + sysy_assert(_state.isCondExp); + return build_InstBinary(InstTag::Eq, unary_exp, CONST0, _state.current_bb); + } + else if (ctx->unaryOp()->ADD()) { + return unary_exp; + } + else if (ctx->unaryOp()->SUB()) { + return build_InstBinary(InstTag::Sub, CONST0, unary_exp, _state.current_bb); + } + } + else if (ctx->IDENT()) { + // Fucntion call + // TODO: buildCall/isRealParam + // TODO: Handle string & putf() + auto func_name = ctx->IDENT()->getText(); + LOG(DEBUG) << "Calling Func: " << func_name; + auto _result = _func_tab.get_name(func_name); + sysy_assert(_result.has_value()); + auto func = _result.value(); + std::vector args; + // Directly parse RParams + if (ctx->funcRParams()) { + auto rparams = ctx->funcRParams()->funcRParam(); + const auto &fparams = func->fparam_list; + for (int i = 0; i < rparams.size(); ++i) { + auto rparam = rparams[i]; + auto fparam = fparams[i]; + auto exp = any_to_Value(visitExp(rparam->exp())); + args.push_back(exp); + } + } + return build_InstCall(func, args, _state.current_bb); + } + else if (ctx->primaryExp()) { + return visitPrimaryExp(ctx->primaryExp()); + } + panic("Unreachable"); } - return {}; } // primaryExp: ('(' exp ')') | lVal | number; std::any Visitor::visitPrimaryExp(SysyParser::PrimaryExpContext *ctx) { + // @retval: int if (_state.isConstInt) { if (ctx->exp()) { return visitExp(ctx->exp()); - } else if (ctx->lVal()) { - auto value = std::any_cast>(visit(ctx->lVal())); - auto constint = std::dynamic_pointer_cast(value); + } + else if (ctx->lVal()) { + auto value = any_to_Value(visit(ctx->lVal())); + auto constint = std::dynamic_pointer_cast(value); + // actually, it is only a type assertion return constint->value; - } else if (ctx->number()) { + } + else if (ctx->number()) { return visitNumber(ctx->number()); } - panic("missing element"); - } else { - panic("To be implemented"); + panic("Unreachable"); + } + else { + if (ctx->exp()) { + return visitExp(ctx->exp()); + } + else if (ctx->lVal()) { + if (_state.isRealParam) { + _state.isRealParam = false; + LOG(WARNING) << "isRealParam"; + return visitLVal(ctx->lVal()); + } + else { + auto child_ret = visitLVal(ctx->lVal()); + auto lval = any_to_Value(child_ret); + // @retval: ConstantInt + if (lval->type->type_tag == Type::TypeTag::IntegerType) { + return lval; + } + // @retval: InstLoad + else { + LOG(WARNING) << "lval type is pointer"; + // should be InstAlloca + auto ptr_type = std::dynamic_pointer_cast(lval->type); + return build_InstLoad(lval, ptr_type->pointed_type, _state.current_bb); + } + } + } + // @retval: int + else if (ctx->number()) { + return visitNumber(ctx->number()); + } + panic("Unreachable"); } return {}; } +// @retval: int std::any Visitor::visitNumber(SysyParser::NumberContext *ctx) { - sysy_assert(ctx->intConst()); + auto int_const = std::any_cast(visit(ctx->intConst())); if (_state.isConstInt) { - return visit(ctx->intConst()); + return int_const; + } + else { + return std::dynamic_pointer_cast(build_ConstantInt("", int_const)); } - panic("To be implemented"); } +// @retval: int std::any Visitor::visitIntConst(SysyParser::IntConstContext *ctx) { if (ctx->DECIMAL_CONST()) { return std::stoi(ctx->DECIMAL_CONST()->getText(), nullptr, 10); - } else if (ctx->HEXADECIMAL_CONST()) { + } + else if (ctx->HEXADECIMAL_CONST()) { return std::stoi(ctx->HEXADECIMAL_CONST()->getText(), nullptr, 16); - - } else if (ctx->OCTAL_CONST()) { + } + else if (ctx->OCTAL_CONST()) { return std::stoi(ctx->OCTAL_CONST()->getText(), nullptr, 8); } - panic("missing element"); + return {}; } +// lVal: IDENT ('[' exp ']')*; + std::any Visitor::visitLVal(SysyParser::LValContext *ctx) { auto name = ctx->IDENT()->getText(); LOG(DEBUG) << "Eval to lVal " << name; auto _lval = _scope_tab.get_name(name); sysy_assert(_lval.has_value()); auto lval = _lval.value(); - if (lval->type == Type::IntegerType) { + // @retval: ConstantInt + if (lval->type->type_tag == Type::TypeTag::IntegerType) { return {lval}; } + if (lval->type->type_tag == Type::TypeTag::PointerType) { + auto ptr_type = std::dynamic_pointer_cast(lval->type); + if (ptr_type->pointed_type->type_tag == Type::TypeTag::IntegerType) { + // @retval: InstAlloca + if (ctx->exp().empty()) { + return lval; + } + else { + panic("To be implemented"); + } + } + else { + panic("To be implemented"); + } + } + panic("Unreachable"); +} + +std::any Visitor::visitFuncDef(SysyParser::FuncDefContext *ctx) { + auto func_name = ctx->IDENT()->getText(); + LOG(DEBUG) << "Visit FuncDef " << func_name; + auto func_ret_type = TypeHelper::TYPE_VOID; + if (ctx->funcType()->INT()) { + func_ret_type = TypeHelper::TYPE_I32; + } + // param list will get collected as well as locally allocated in FuncFParam + auto func_obj = std::make_shared(func_name, func_ret_type); + _module.functions.push_back(func_obj); + _func_tab.push_name(func_name, func_obj); + auto basic_block = build_BasicBlock(func_name + "_ENTRY", func_obj); + _scope_tab.enter_scope(true); + _state.current_func = func_obj; + _state.current_bb = basic_block; + if (ctx->funcFParams()) { + visitFuncFParams(ctx->funcFParams()); + } + visit(ctx->block()); + // add return + // _scope_tab.leave_scope(); + // TODO: avoid duplicate ret + if (func_ret_type->type_tag == Type::TypeTag::VoidType) { + build_InstReturn(_state.current_bb); + } + else { + build_InstReturn(CONST0, _state.current_bb); + } + return {}; +} + +std::any Visitor::visitFuncFParams(SysyParser::FuncFParamsContext *ctx) { + for (auto fparam : ctx->funcFParam()) { + auto fparam_type = std::any_cast(visit(fparam)); + auto fparam_name = fparam->getText(); + auto fparam_ptr = std::make_shared(fparam_name, fparam_type); + _state.current_func->fparam_list.push_back(fparam_ptr); + if (fparam->LBRACKET().empty()) { + auto alloca = build_InstAlloca(TypeHelper::TYPE_I32, false, _state.current_bb); + auto store = build_InstStore(fparam_ptr, alloca, _state.current_bb); + _scope_tab.push_name(fparam_name, alloca); + _state.current_func->fparam_list.push_back(fparam_ptr); + } + else { + panic("To be implemented"); + } + } + return {}; +} + +std::any Visitor::visitFuncFParam(SysyParser::FuncFParamContext *ctx) { + if (ctx->LBRACKET().empty()) { + return {TypeHelper::TYPE_I32}; + } panic("To be implemented"); } +std::any Visitor::visitBlock(SysyParser::BlockContext *ctx) { + _scope_tab.enter_scope(); + for (auto block_item : ctx->blockItem()) { + visit(block_item); + } + _scope_tab.leave_scope(); + return {}; +} + +/* +* nobody needs the value of stmt, so return nothing + +stmt: lVal '=' exp ';' # assignStmt + | (exp)? ';' # expStmt // do nothing + | block # blockStmt // do nothing + | 'if' '(' cond ')' stmt ('else' stmt)? # ifStmt + | 'while' '(' cond ')' stmt # whileStmt + | 'break' ';' # breakStmt + | 'continue' ';' # continueStmt + | 'return' (exp)? ';' # returnStmt; +*/ + +std::any Visitor::visitAssignStmt(SysyParser::AssignStmtContext *ctx) { + auto lval = any_to_Value(visit(ctx->lVal())); + auto rhs = any_to_Value(visit(ctx->exp())); + auto store = build_InstStore(rhs, lval, _state.current_bb); + return {}; +} + +// TODO: Remove RETURN in else stmt +std::any Visitor::visitIfStmt(SysyParser::IfStmtContext *ctx) { + auto true_block = build_BasicBlock("_then", _state.current_func); + auto next_block = build_BasicBlock("_next", _state.current_func); + auto false_block = next_block; + if (ctx->ELSE()) { + false_block = build_BasicBlock("_else", _state.current_func); + } + ctx->cond()->lOrExp()->true_block = true_block; + ctx->cond()->lOrExp()->false_block = false_block; + visitCond(ctx->cond()); + + _state.current_bb = true_block; + visit(ctx->stmt(0)); + build_InstBranch(next_block, _state.current_bb); // use current_bb, god knows what happened + if (ctx->ELSE()) { + _state.current_bb = false_block; + visit(ctx->stmt(1)); + build_InstBranch(next_block, _state.current_bb); + } + _state.current_bb = next_block; + return {}; +} + +// TODO: backpatching? I am not sure whether it is necessary +std::any Visitor::visitWhileStmt(SysyParser::WhileStmtContext *ctx) { + auto while_id = std::to_string(_state.loop_stmt_count); + auto cond_block = build_BasicBlock("_loop_cond_" + while_id, _state.current_func); + auto body_block = build_BasicBlock("_loop_body_" + while_id, _state.current_func); + auto next_block = build_BasicBlock("_loop_exit_" + while_id, _state.current_func); + build_InstBranch(cond_block, _state.current_bb); + _state.loop_stack.push_back({cond_block, body_block, next_block, _state.loop_stmt_count++}); + // condition + ctx->cond()->lOrExp()->true_block = body_block; + ctx->cond()->lOrExp()->false_block = next_block; + _state.current_bb = cond_block; + visitCond(ctx->cond()); + // body + _state.current_bb = body_block; + visit(ctx->stmt()); + build_InstBranch(cond_block, _state.current_bb); + // exit + _state.loop_stack.pop_back(); + _state.current_bb = next_block; + return {}; +} + +std::any Visitor::visitBreakStmt(SysyParser::BreakStmtContext *ctx) { + sysy_assert(!_state.loop_stack.empty()); + build_InstBranch(_state.loop_stack.back().next, _state.current_bb); + _state.current_bb = build_BasicBlock("_after_break", _state.current_func); + return {}; +} + +std::any Visitor::visitContinueStmt(SysyParser::ContinueStmtContext *ctx) { + sysy_assert(!_state.loop_stack.empty()); + build_InstBranch(_state.loop_stack.back().cond, _state.current_bb); + _state.current_bb = build_BasicBlock("_after_continue", _state.current_func); + return {}; +} + +std::any Visitor::visitReturnStmt(SysyParser::ReturnStmtContext *ctx) { + if (ctx->exp()) { + auto exp = any_to_Value(visitExp(ctx->exp())); + build_InstReturn(exp, _state.current_bb); + } + else { + build_InstReturn(_state.current_bb); + } + return {}; +} + } // namespace antlrSysY \ No newline at end of file