untested frontend, and without array support
This commit is contained in:
parent
d87a5151eb
commit
7cd6471b9d
@ -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 {
|
||||
|
||||
@ -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> ¶ms_type)
|
||||
: Type(TypeTag::FunctionType), return_type(return_type), params_type(params_type) {}
|
||||
};
|
||||
|
||||
} // namespace antlrSysY
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
32
src/main.cpp
32
src/main.cpp
@ -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
94
src/visit_factory.cpp
Normal 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
|
||||
552
src/visitor.cpp
552
src/visitor.cpp
@ -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
|
||||
Loading…
Reference in New Issue
Block a user