untested frontend, and without array support

This commit is contained in:
ridethepig 2023-05-05 21:24:02 +08:00
parent d87a5151eb
commit 7cd6471b9d
8 changed files with 1001 additions and 91 deletions

View File

@ -7,15 +7,15 @@
#include <sstream>
#include <string>
#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 {

View File

@ -1,13 +1,90 @@
#pragma once
#include <memory>
#include <vector>
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<Type> 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<IntegerType>(type)->isI32();
}
static bool isIntegerTypeI1(TypePtr_t &type) {
return isIntegerType(type) && std::dynamic_pointer_cast<IntegerType>(type)->isI1();
}
inline static TypePtr_t TYPE_I32 = std::make_shared<IntegerType>();
inline static TypePtr_t TYPE_I1 = std::make_shared<IntegerType>(IntegerType::IntTypes::I1);
inline static TypePtr_t TYPE_LABEL = std::make_shared<LabelType>();
inline static TypePtr_t TYPE_VOID = std::make_shared<VoidType>();
};
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<TypePtr_t> params_type;
FunctionType(TypePtr_t return_type) : Type(TypeTag::FunctionType), return_type(return_type) {}
FunctionType(TypePtr_t return_type, const std::vector<TypePtr_t> &params_type)
: Type(TypeTag::FunctionType), return_type(return_type), params_type(params_type) {}
};
} // namespace antlrSysY

View File

@ -1,31 +1,186 @@
#pragma once
#include <memory>
#include <shared_mutex>
#include <string>
#include <vector>
#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<std::shared_ptr<FParam>> fparam_list;
std::vector<std::shared_ptr<BasicBlock>> bb_list;
Function(const std::string &name, TypePtr_t ret_type) : Value(name, std::make_shared<FunctionType>(ret_type)) {}
std::shared_ptr<FunctionType> get_type() {
return std::dynamic_pointer_cast<FunctionType>(type);
}
};
class BasicBlock : public Value {
public:
std::vector<std::shared_ptr<Instruction>> inst_list;
std::shared_ptr<Function> parent;
std::vector<std::shared_ptr<BasicBlock>> successors;
std::vector<std::shared_ptr<BasicBlock>> predecessors;
BasicBlock(const std::string &name, std::shared_ptr<Function> 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<Value> ValuePtr_t;
typedef std::shared_ptr<BasicBlock> BasicBlockPtr_t;
typedef std::shared_ptr<Function> FunctionPtr_t;
class Instruction : public Value {
public:
InstTag tag;
std::shared_ptr<BasicBlock> parent_bb;
std::vector<std::shared_ptr<Value>> operand_list;
Instruction(InstTag inst_tag, TypePtr_t type, std::shared_ptr<BasicBlock> 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<BasicBlock> parent_bb)
: Instruction(InstTag::Alloca, std::make_shared<PointerType>(alloc_type), parent_bb), isInit(isInit) {}
};
class InstStore : public Instruction {
public:
InstStore(std::shared_ptr<Value> value, std::shared_ptr<Value> pointer, std::shared_ptr<BasicBlock> 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> value, TypePtr_t type, std::shared_ptr<BasicBlock> 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<Value> op1, std::shared_ptr<Value> op2,
std::shared_ptr<BasicBlock> 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<Value> op, std::shared_ptr<BasicBlock> 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<ValuePtr_t> &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

View File

@ -8,7 +8,8 @@ template <typename T_V> class ScopeTable {
private:
std::vector<ScopeTableEntry_t> _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<T_V> get_name(const std::string &name) {
for (auto i = _scope_list.size() - 1; i >= 0; --i) {
std::optional<T_V> 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<T_V> get_name(const std::string &name, int level) {
std::optional<T_V> 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;
}
};
}
} // namespace antlrSysY

View File

@ -5,52 +5,104 @@
#include <any>
#include <cstddef>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <memory>
#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<ConstantInt> build_ConstantInt(const std::string &name, int value);
std::shared_ptr<BasicBlock> build_BasicBlock(const std::string &name, std::shared_ptr<Function> parent);
std::shared_ptr<InstLoad> build_InstLoad(std::shared_ptr<Value> value, TypePtr_t type,
std::shared_ptr<BasicBlock> parent_bb);
std::shared_ptr<InstStore> build_InstStore(std::shared_ptr<Value> value, std::shared_ptr<Value> pointer,
std::shared_ptr<BasicBlock> parent_bb);
std::shared_ptr<InstAlloca> build_InstAlloca(TypePtr_t type, bool isInit, std::shared_ptr<BasicBlock> parent_bb);
std::shared_ptr<InstBinary> build_InstBinary(InstTag inst_tag, std::shared_ptr<Value> op1, std::shared_ptr<Value> op2,
std::shared_ptr<BasicBlock> parent_bb);
std::shared_ptr<InstZext> build_InstZext(std::shared_ptr<Value> op, std::shared_ptr<BasicBlock> parent_bb);
std::shared_ptr<InstBranch> build_InstBranch(ValuePtr_t cond, BasicBlockPtr_t true_block, BasicBlockPtr_t false_block,
BasicBlockPtr_t parent_bb);
std::shared_ptr<InstBranch> build_InstBranch(BasicBlockPtr_t target_block, BasicBlockPtr_t parent_bb);
std::shared_ptr<InstReturn> build_InstReturn(ValuePtr_t ret_val, BasicBlockPtr_t parent_bb);
std::shared_ptr<InstReturn> build_InstReturn(BasicBlockPtr_t parent_bb);
std::shared_ptr<InstCall> build_InstCall(FunctionPtr_t func, const std::vector<ValuePtr_t> &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<Function> current_func = {};
std::shared_ptr<BasicBlock> current_bb = {};
struct loop_record {
BasicBlockPtr_t cond, body, next;
int id;
};
int loop_stmt_count = 0;
std::vector<loop_record> loop_stack = {};
};
struct Module {
std::vector<std::shared_ptr<Function>> functions;
};
private:
ScopeTable<std::shared_ptr<Value>> _scope_tab;
ScopeTable<std::shared_ptr<Function>> _func_tab; // var can have same name as func
VisitorState _state = {};
Module _module = {};
std::map<std::string, size_t> token_type_map;
std::shared_ptr<ConstantInt> CONST0;
public:
Visitor(SysyLexer &lexer) {
for (auto item : lexer.getTokenTypeMap()) {
token_type_map[std::string(item.first)] = item.second;
}
CONST0 = std::make_shared<ConstantInt>("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

View File

@ -2,6 +2,9 @@
#include <iostream>
#include <string>
#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);

94
src/visit_factory.cpp Normal file
View File

@ -0,0 +1,94 @@
#include "common.h"
#include "llir_type.h"
#include "llir_value.h"
#include "visitor.h"
#include <memory>
namespace antlrSysY {
std::shared_ptr<ConstantInt> build_ConstantInt(const std::string &name, int value) {
auto lala = std::make_shared<ConstantInt>(name, value);
return lala;
}
std::shared_ptr<BasicBlock> build_BasicBlock(const std::string &name, std::shared_ptr<Function> parent) {
auto basic_block = std::make_shared<BasicBlock>(name, parent);
parent->bb_list.push_back(basic_block);
return basic_block;
}
std::shared_ptr<InstLoad> build_InstLoad(std::shared_ptr<Value> value, TypePtr_t type,
std::shared_ptr<BasicBlock> parent_bb) {
auto inst_load = std::make_shared<InstLoad>(value, type, parent_bb);
parent_bb->inst_list.push_back(inst_load);
return inst_load;
}
std::shared_ptr<InstStore> build_InstStore(std::shared_ptr<Value> value, std::shared_ptr<Value> pointer,
std::shared_ptr<BasicBlock> parent_bb) {
auto inst_store = std::make_shared<InstStore>(value, pointer, parent_bb);
parent_bb->inst_list.push_back(inst_store);
return inst_store;
}
std::shared_ptr<InstAlloca> build_InstAlloca(TypePtr_t type, bool isInit, std::shared_ptr<BasicBlock> parent_bb) {
auto inst_alloca = std::make_shared<InstAlloca>(type, isInit, parent_bb);
parent_bb->inst_list.push_back(inst_alloca);
return inst_alloca;
}
std::shared_ptr<InstBinary> build_InstBinary(InstTag inst_tag, std::shared_ptr<Value> op1, std::shared_ptr<Value> op2,
std::shared_ptr<BasicBlock> parent_bb) {
std::shared_ptr<InstBinary> inst_binary;
if (InstTag::Lt <= inst_tag && inst_tag <= InstTag::Or) {
inst_binary = std::make_shared<InstBinary>(inst_tag, TypeHelper::TYPE_I1, op1, op2, parent_bb);
}
else if (InstTag::Add <= inst_tag && inst_tag <= InstTag::Div) {
inst_binary = std::make_shared<InstBinary>(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<InstZext> build_InstZext(std::shared_ptr<Value> op, std::shared_ptr<BasicBlock> parent_bb) {
auto inst_zext = std::make_shared<InstZext>(op, parent_bb);
parent_bb->inst_list.push_back(inst_zext);
return inst_zext;
}
std::shared_ptr<InstBranch> build_InstBranch(ValuePtr_t cond, BasicBlockPtr_t true_block, BasicBlockPtr_t false_block,
BasicBlockPtr_t parent_bb) {
auto inst = std::make_shared<InstBranch>(cond, true_block, false_block, parent_bb);
parent_bb->inst_list.push_back(inst);
return inst;
}
std::shared_ptr<InstBranch> build_InstBranch(BasicBlockPtr_t target_block, BasicBlockPtr_t parent_bb) {
auto inst = std::make_shared<InstBranch>(target_block, parent_bb);
parent_bb->inst_list.push_back(inst);
return inst;
}
std::shared_ptr<InstReturn> build_InstReturn(ValuePtr_t ret_val, BasicBlockPtr_t parent_bb) {
auto inst = std::make_shared<InstReturn>(ret_val, parent_bb);
parent_bb->inst_list.push_back(inst);
return inst;
}
std::shared_ptr<InstReturn> build_InstReturn(BasicBlockPtr_t parent_bb) {
auto inst = std::make_shared<InstReturn>(parent_bb);
parent_bb->inst_list.push_back(inst);
return inst;
}
std::shared_ptr<InstCall> build_InstCall(FunctionPtr_t func, const std::vector<ValuePtr_t> &args,
BasicBlockPtr_t parent_bb) {
auto inst = std::make_shared<InstCall>(func, args, parent_bb);
parent_bb->inst_list.push_back(inst);
return inst;
}
} // namespace antlrSysY

View File

@ -1,11 +1,46 @@
#include "visitor.h"
#include "common.h"
#include "llir_type.h"
#include "llir_value.h"
#include <any>
#include <memory>
#include <string>
#include <vector>
// 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<std::shared_ptr<_my_type_>>(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<std::shared_ptr<ConstantInt>>(visit(ctx->constInitVal()));
// _scope_tab.push_name(name, Value *const &&value)
_scope_tab.push_name(name, result);
auto result = std::any_cast<int>(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<int>(visit(ctx->initVal()));
_state.isGlobalInt = false;
}
auto global_var_int = std::make_shared<GlobalVarInt>(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<int>(visit(ctx->addExp()));
_state.isConstInt = false;
LOG(DEBUG) << "ConstExp Eval to " << result;
auto constint = std::make_shared<ConstantInt>("", 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<int>(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<IntegerType>(add_exp->type)->isI1()) {
add_exp = build_InstZext(add_exp, _state.current_bb);
}
if (std::dynamic_pointer_cast<IntegerType>(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<int>(visit(ctx->unaryExp()));
int result = std::any_cast<int>(visitUnaryExp(ctx->unaryExp()));
if (ctx->mulExp()) {
int mul_result = std::any_cast<int>(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<IntegerType>(unary_exp->type)->isI1()) {
unary_exp = build_InstZext(unary_exp, _state.current_bb);
}
if (std::dynamic_pointer_cast<IntegerType>(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<IntegerType>(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<ValuePtr_t> 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<std::shared_ptr<Value>>(visit(ctx->lVal()));
auto constint = std::dynamic_pointer_cast<ConstantInt>(value);
}
else if (ctx->lVal()) {
auto value = any_to_Value(visit(ctx->lVal()));
auto constint = std::dynamic_pointer_cast<ConstantInt>(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<PointerType>(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<int>(visit(ctx->intConst()));
if (_state.isConstInt) {
return visit(ctx->intConst());
return int_const;
}
else {
return std::dynamic_pointer_cast<Value>(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<PointerType>(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<Function>(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<TypePtr_t>(visit(fparam));
auto fparam_name = fparam->getText();
auto fparam_ptr = std::make_shared<FParam>(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