From 391b529960351efbe97d3c91ca569a15762859e9 Mon Sep 17 00:00:00 2001 From: ridethepig Date: Sun, 14 May 2023 19:44:51 +0800 Subject: [PATCH] At least, they all compiles --- .vscode/launch.json | 2 +- include/llir_instruction.h | 19 +++---- include/llir_type.h | 7 +-- include/llir_value.h | 20 ++++--- scripts/compiler.py | 9 ++-- scripts/manual-compile.py | 17 ++++-- scripts/sylib-gen.sh | 3 ++ scripts/tester.py | 6 +-- src/main.cpp | 3 +- src/visitor.cpp | 106 ++++++++++++++++++++++++------------- src/visitor_factory.cpp | 10 ++++ src/visitor_llir_gen.cpp | 101 ++++++++++++++++++++++++----------- tools/sylib/libsysy_x64.a | Bin 0 -> 9616 bytes tools/sylib/libsysy_x86.a | Bin 9616 -> 6544 bytes 14 files changed, 203 insertions(+), 100 deletions(-) create mode 100644 scripts/sylib-gen.sh create mode 100644 tools/sylib/libsysy_x64.a diff --git a/.vscode/launch.json b/.vscode/launch.json index bb7292e..944d877 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "request": "launch", "name": "Debug", "program": "${workspaceFolder}/build/sysy", - "args": ["build/ManualTest/a.c", "-S", "-o", "sss"], + "args": ["testcases/functional/64_calculator.sy", "-S", "-o", "build/manual-test/21_my.ll"], "cwd": "${workspaceFolder}" } ] diff --git a/include/llir_instruction.h b/include/llir_instruction.h index 331fd54..516200e 100644 --- a/include/llir_instruction.h +++ b/include/llir_instruction.h @@ -90,6 +90,7 @@ 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) { + assert(value); operand_list.push_back(value); operand_list.push_back(pointer); } @@ -198,7 +199,7 @@ public: aim_to = pointer; } else { - LOG(WARNING) << "Unexpected pointer type" << pointer->to_string(); + // LOG(WARNING) << "Unexpected pointer type " << pointer->to_string(); aim_to = nullptr; } element_type = extract_type(pointer, indices); @@ -215,19 +216,19 @@ public: static TypePtr_t extract_type(ValuePtr_t pointer, const std::vector &indices) { auto pointer_type = std::dynamic_pointer_cast(pointer->type); auto pointed_type = pointer_type->pointed_type; - // ptr->array - if (pointed_type->type_tag == Type::TypeTag::ArrayType) { - for (int i = 1; i < indices.size(); ++i) { - pointed_type = std::dynamic_pointer_cast(pointed_type)->element_type; - } - } if (pointed_type->type_tag == Type::TypeTag::IntegerType) { return pointed_type; } - else { - LOG(WARNING) << "not returning an int-type"; + else if (Type::isType(pointed_type)){ + for (int i = 1; i < indices.size(); ++i) { + pointed_type = std::dynamic_pointer_cast(pointed_type)->element_type; + } return pointed_type; } + else { + LOG(ERROR) << "unexpected type: " << pointer->to_string(); + assert(0); + } } }; diff --git a/include/llir_type.h b/include/llir_type.h index 5a3b446..f87d54f 100644 --- a/include/llir_type.h +++ b/include/llir_type.h @@ -34,7 +34,7 @@ public: template static bool isType(std::shared_ptr ptr) { if (ptr) { - return dynamic_cast(ptr.get()) != nullptr; + return dynamic_cast(ptr.get()) != nullptr; } else { return false; @@ -136,10 +136,7 @@ public: return "Pointer->" + pointed_type->to_string(); } virtual std::string to_IR_string() override { - if (pointed_type->type_tag == TypeTag::IntegerType) { - return "i32*"; - } - panic("Unknown"); + return pointed_type->to_IR_string() + "*"; } }; diff --git a/include/llir_value.h b/include/llir_value.h index ecba172..4e4a048 100644 --- a/include/llir_value.h +++ b/include/llir_value.h @@ -3,6 +3,7 @@ #include "3rdparty/easylogging++.h" #include "common.h" #include "llir_type.h" +#include #include #include #include @@ -23,9 +24,10 @@ public: if (ptr.get()) { // auto &r = *ptr.get(); // return typeid(r) == typeid(TT); - return dynamic_cast(ptr.get()) != nullptr; + return dynamic_cast(ptr.get()) != nullptr; } LOG(WARNING) << "Comparing with nullptr"; + assert(0); return false; } @@ -55,6 +57,12 @@ class FParam : public Value { public: int ir_seqno = -1; FParam(const std::string &name, TypePtr_t type) : Value(name, type) {} + virtual std::string to_string() override { + return type->to_string() + " @" + name + " %" + std::to_string(ir_seqno); + } + virtual std::string to_IR_string() override { + return type->to_IR_string() + " %" + std::to_string(ir_seqno); + } }; typedef std::list>::iterator BasicBlockListNode_t; @@ -114,7 +122,7 @@ public: return str; } virtual std::string to_IR_string() override { - return to_string(); + return type->to_IR_string() + " " + std::to_string(value); } }; @@ -157,10 +165,10 @@ class GlobalVar : public Value { public: ConstantPtr_t init_value; bool is_const; - GlobalVar(const std::string &name, ConstantPtr_t init_value, bool is_const) - : Value(name, PointerType::make_shared(init_value->type)), init_value(init_value), is_const(is_const) {} - static std::shared_ptr make_shared(const std::string &name, ConstantPtr_t init_value, bool is_const) { - return std::make_shared(name, init_value, is_const); + GlobalVar(const std::string &name, TypePtr_t type, ConstantPtr_t init_value, bool is_const) + : Value(name, PointerType::make_shared(type)), init_value(init_value), is_const(is_const) {} + static std::shared_ptr make_shared(const std::string &name, TypePtr_t type, ConstantPtr_t init_value, bool is_const) { + return std::make_shared(name, type ,init_value, is_const); } virtual std::string to_IR_string() override { std::string str = type->to_IR_string() + " @" + name; diff --git a/scripts/compiler.py b/scripts/compiler.py index 7cd0b36..ee17515 100644 --- a/scripts/compiler.py +++ b/scripts/compiler.py @@ -79,7 +79,7 @@ class Compiler: Print_C.print_procedure(f"Generating {self.scheme}.s") # subprocess.run(f"llc -O3 -march=arm -mcpu=cortex-a72 -float-abi=hard -filetype=asm {ir} -o {asm}".split(), stdout=log_file, stderr=log_file, bufsize=1) - completed = subprocess.run(f"llc -O3 --relocation-model=pic {ir} -o {asm}".split(), stdout=log_file, stderr=log_file, bufsize=1) + completed = subprocess.run(f"llc --march=x86 --relocation-model=pic {ir} -o {asm}".split(), stdout=log_file, stderr=log_file, bufsize=1) if completed.returncode != 0: Print_C.print_error(f"Generating {self.scheme}.s failed! See {log} | {ir}") self.count_error += 1 @@ -96,7 +96,7 @@ class Compiler: Print_C.print_procedure(f"Generating {self.scheme}.o") # subprocess.run(f"as -march=armv7-a -mfloat-abi=hard {asm} -o {obj}".split(), stdout=log_file, stderr=log_file, bufsize=1) - completed = subprocess.run(f"as {asm} -o {obj}".split(), stdout=log_file, stderr=log_file, bufsize=1) + completed = subprocess.run(f"as --32 {asm} -o {obj}".split(), stdout=log_file, stderr=log_file, bufsize=1) if completed.returncode != 0: Print_C.print_error(f"Generating {self.scheme}.o failed! See {log}") self.count_error += 1 @@ -111,7 +111,7 @@ class Compiler: Print_C.print_procedure(f"Generating {self.scheme}") # subprocess.run(f"clang -Ofast -marm -march=armv7-a -mfpu=neon -mfloat-abi=hard {obj} {lib} -o {bin}".split(), stdout=log_file, stderr=log_file, bufsize=1) - completed = subprocess.run(f"clang -Ofast -fPIE {obj} {lib} -o {bin}".split(), stdout=log_file, stderr=log_file, bufsize=1) + completed = subprocess.run(f"clang -m32 -Ofast -fPIE {obj} {lib} -o {bin}".split(), stdout=log_file, stderr=log_file, bufsize=1) if completed.returncode != 0: Print_C.print_error(f"Linking {self.scheme} failed! See {log}") self.count_error += 1 @@ -155,6 +155,9 @@ class Compiler: for testcase in self.testcases: Print_C.print_subheader(f"[Compiling {self.scheme} | {testcase}]") self.sy_to_ir(frontend_instr=frontend_instr, testcase=testcase, category=category) + self.ir_to_asm(testcase=testcase) + self.asm_to_obj(testcase=testcase) + self.obj_to_bin(testcase=testcase) if self.count_error >= 1: Print_C.print_error(f"Test script stopped due to too many errors") return diff --git a/scripts/manual-compile.py b/scripts/manual-compile.py index b59c37e..951b43a 100644 --- a/scripts/manual-compile.py +++ b/scripts/manual-compile.py @@ -6,8 +6,15 @@ print("compile {}".format(sys.argv[1])) fullpath = sys.argv[1] libpath = sys.argv[2] (filename, extname) = os.path.splitext(sys.argv[1]) - -os.system(f"clang -emit-llvm -S {fullpath} -o {filename}.ll -O0 -fno-builtin") -os.system(f"llc{filename}.ll -o {filename}.s") -os.system(f"as {filename}.s -o {filename}.o") -os.system(f"clang {filename}.o {libpath} -o {filename}") \ No newline at end of file +''' +python3 scripts/manual-compile.py build/manual-test/55_sort_test1.sy tools/sylib/libsysy_x86.a +''' +# os.system(f"clang -emit-llvm -S {fullpath} -o {filename}.ll -O0 -fno-builtin") +print(f"clang -x c -c -m32 -emit-llvm -S {fullpath} -o {filename}.ll -O0 -fno-builtin") +os.system(f"clang -x c -c -m32 -emit-llvm -S {fullpath} -o {filename}.ll -O0 -fno-builtin") +print(f"llc --march=x86 --relocation-model=pic {filename}.ll -o {filename}.s") +os.system(f"llc --march=x86 --relocation-model=pic {filename}.ll -o {filename}.s") +print(f"as --32 {filename}.s -o {filename}.o") +os.system(f"as --32 {filename}.s -o {filename}.o") +print(f"clang -m32 -fPIE {filename}.o {libpath} -o {filename}") +os.system(f"clang -m32 -fPIE {filename}.o {libpath} -o {filename}") \ No newline at end of file diff --git a/scripts/sylib-gen.sh b/scripts/sylib-gen.sh new file mode 100644 index 0000000..0d219e9 --- /dev/null +++ b/scripts/sylib-gen.sh @@ -0,0 +1,3 @@ +#!/bin/bash +clang -c -m32 -fPIC tools/sylib/sylib.c -include tools/sylib/sylib.h -o tools/sylib/sylib.o +ar rcs tools/sylib/libsysy_x86.a tools/sylib/sylib.o \ No newline at end of file diff --git a/scripts/tester.py b/scripts/tester.py index d1aabcb..407a800 100644 --- a/scripts/tester.py +++ b/scripts/tester.py @@ -88,7 +88,7 @@ my_scheme = {"scheme": "my", "emit_llvm_ir": False} llir_scheme = {"scheme": "llir", - "frontend_instr": "clang -x c -c -fPIE -S -emit-llvm -include {header} {sy} -o {ir}", + "frontend_instr": "clang -x c -c -fPIE -m32 -S -emit-llvm -include {header} {sy} -o {ir}", "emit_llvm_ir": True} # gcc -c -fPIC -o sylib.o -include sylib.h sylib.c && ar rcs libsysy_x86.a sylib.o @@ -123,8 +123,8 @@ if __name__ == '__main__': subprocess.run("rm -rf build/log/run_log".split()) subprocess.run("rm -rf build/log/test_result.log".split()) - # Tester(my_scheme, True, testcases).compile_rubbish(category=category) - Tester(llir_scheme, True, testcases).compile_reference(category=category) + Tester(my_scheme, True, testcases).compile_rubbish(category=category) + # Tester(llir_scheme, True, testcases).compile_reference(category=category) # for scheme in all_schemes: # tester = Tester(scheme, is_trivial=True) diff --git a/src/main.cpp b/src/main.cpp index ebd9c22..b45c6ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -60,6 +60,7 @@ int main(int argc, const char **argv) { 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"); + defaultConf.set(el::Level::Info, el::ConfigurationType::Format, "%level %loc %msg"); el::Loggers::addFlag(el::LoggingFlag::ColoredTerminalOutput); el::Loggers::reconfigureLogger("default", defaultConf); #pragma endregion @@ -85,7 +86,7 @@ int main(int argc, const char **argv) { auto tree = parser.program(); Visitor visitor(lexer); visitor.visitProgram(tree); - auto llir_file = source_file + ".ll"; + auto llir_file = output_file.substr(0, output_file.rfind(".")) + ".ll"; std::ofstream ofs_llir_file(llir_file); if (!ofs_llir_file.good()) { LOG(ERROR) << "Failed to create/overwrite LLIR output file " << llir_file; diff --git a/src/visitor.cpp b/src/visitor.cpp index cb55e21..8478756 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -2,9 +2,11 @@ #include "3rdparty/easylogging++.h" #include "SysyLexer.h" #include "common.h" +#include "llir_instruction.h" #include "llir_type.h" #include "llir_value.h" #include +#include #include #include #include @@ -159,16 +161,16 @@ std::any Visitor::visitConstDef(SysyParser::ConstDefContext *ctx) { base_ptr = build_InstGEP(base_ptr, {CONST0, CONST0}, _state.current_bb); } // we store the elements in 1-dim shape - build_InstStore(array_value[0], base_ptr, _state.current_bb); + build_InstStore(array_value[0] ? array_value[0] : CONST0, base_ptr, _state.current_bb); for (int i = 1; i < array_value.size(); ++i) { auto ptr = build_InstGEP(base_ptr, {ConstantInt::make_shared(i)}, _state.current_bb); - build_InstStore(array_value[i], ptr, _state.current_bb); + build_InstStore(array_value[i] ? array_value[i] : CONST0, ptr, _state.current_bb); } } else { // global const array auto const_arr = ConstantArr::make_shared("const_arr", array_value, array_type); - auto global_var = GlobalVar::make_shared(const_name, const_arr, true); + auto global_var = GlobalVar::make_shared(const_name, array_type, const_arr, true); module.global_var_list.push_back(global_var); _scope_tab.push_name(const_name, global_var); } @@ -191,12 +193,12 @@ std::any Visitor::visitVarDef(SysyParser::VarDefContext *ctx) { _state.isGlobalIint = true; auto result = std::any_cast>(visitInitVal(ctx->initVal())); _state.isGlobalIint = false; - auto global_var_int = std::make_shared(var_name, result, false); + auto global_var_int = GlobalVar::make_shared(var_name, result->type,result, false); module.global_var_list.push_back(global_var_int); _scope_tab.push_name(var_name, global_var_int); } else { - auto global_var_int = std::make_shared(var_name, CONST0, false); + auto global_var_int = GlobalVar::make_shared(var_name, CONST0->type,CONST0, false); module.global_var_list.push_back(global_var_int); _scope_tab.push_name(var_name, global_var_int); } @@ -236,10 +238,10 @@ std::any Visitor::visitVarDef(SysyParser::VarDefContext *ctx) { base_ptr = build_InstGEP(base_ptr, {CONST0, CONST0}, _state.current_bb); } // TODO: BAAU-2021 calls memset in `libc` (not sysylib) to optimize this process - build_InstStore(array_value[0], base_ptr, _state.current_bb); + build_InstStore(array_value[0] ? array_value[0] : CONST0, base_ptr, _state.current_bb); for (int i = 1; i < array_value.size(); ++i) { auto ptr = build_InstGEP(base_ptr, {ConstantInt::make_shared(i)}, _state.current_bb); - build_InstStore(array_value[i], ptr, _state.current_bb); + build_InstStore(array_value[i] ? array_value[i] : CONST0, ptr, _state.current_bb); } } // If there is no init expr, let it be @@ -256,12 +258,12 @@ std::any Visitor::visitVarDef(SysyParser::VarDefContext *ctx) { sysy_assert(_state.arr_dim_index == 0); _state.isGlobalIint = false; auto const_arr = ConstantArr::make_shared("var_arr", array_value, array_type); - auto global_var = GlobalVar::make_shared(var_name, const_arr, false); + auto global_var = GlobalVar::make_shared(var_name, const_arr->type, const_arr, false); module.global_var_list.push_back(global_var); _scope_tab.push_name(var_name, global_var); } else { - auto global_var = GlobalVar::make_shared(var_name, CONST0, false); + auto global_var = GlobalVar::make_shared(var_name, array_type, nullptr, false); module.global_var_list.push_back(global_var); _scope_tab.push_name(var_name, global_var); } @@ -485,11 +487,19 @@ std::any Visitor::visitRelExp(SysyParser::RelExpContext *ctx) { // 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)); + auto rel_exp = any_to_Value(visitRelExp(ctx->relExp())); + bool need_zext = false; if (ctx->eqExp()) { auto eq_exp = any_to_Value(visitEqExp(ctx->eqExp())); - // sysy_assert(TypeHelper::isIntegerTypeI1(eq_exp->type)); + if (TypeHelper::isIntegerTypeI32(eq_exp->type) || TypeHelper::isIntegerTypeI32(rel_exp->type)) { + need_zext = true; + } + if (need_zext && TypeHelper::isIntegerTypeI1(eq_exp->type)) { + eq_exp = build_InstZext(eq_exp, _state.current_bb); + } + if (need_zext && TypeHelper::isIntegerTypeI1(rel_exp->type)) { + rel_exp = build_InstZext(rel_exp, _state.current_bb); + } if (ctx->EQ()) { rel_exp = build_InstBinary(InstTag::Eq, eq_exp, rel_exp, _state.current_bb); } @@ -604,9 +614,12 @@ std::any Visitor::visitUnaryExp(SysyParser::UnaryExpContext *ctx) { 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())); + auto rparam = rparams[i]; + auto fparam = fparams[i]; + _state.isRealParam = true; + if (Type::isType(fparam->type)) _state.isRealParam = false; + auto exp = any_to_Value(visitExp(rparam->exp())); + _state.isRealParam = false; args.push_back(exp); } } @@ -645,7 +658,7 @@ std::any Visitor::visitPrimaryExp(SysyParser::PrimaryExpContext *ctx) { else if (ctx->lVal()) { if (_state.isRealParam) { _state.isRealParam = false; - LOG(WARNING) << "isRealParam"; + VLOG(5) << "isRealParam:" << ctx->getStart()->getLine(); return visitLVal(ctx->lVal()); } else { @@ -657,7 +670,7 @@ std::any Visitor::visitPrimaryExp(SysyParser::PrimaryExpContext *ctx) { } // @retval: InstLoad else { - LOG(WARNING) << "lval type is pointer: " << lval->type->type_tag; + // LOG(WARNING) << "lval type is pointer: " << lval->type->type_tag; // should be InstAlloca auto ptr_type = std::dynamic_pointer_cast(lval->type); return build_InstLoad(lval, ptr_type->pointed_type, _state.current_bb); @@ -716,7 +729,8 @@ std::any Visitor::visitLVal(SysyParser::LValContext *ctx) { return lval; } else { - LOG(WARNING) << "Unexpected array referece"; + LOG(WARNING) << "Unexpected array referece" << lval->to_string() + << " : "<< ctx->getStart()->getLine(); // array index, perhaps // @retval: InstGEP auto gep = lval; @@ -728,7 +742,6 @@ std::any Visitor::visitLVal(SysyParser::LValContext *ctx) { } }; case Type::TypeTag::PointerType: { - sysy_assert(0); if (ctx->exp().empty()) { // ??? Pointer // @retval: InstLoad @@ -739,19 +752,26 @@ std::any Visitor::visitLVal(SysyParser::LValContext *ctx) { } else { // fparam array, whose first dim is represented by a pointer - auto pointed_type = std::dynamic_pointer_cast(lval->type)->pointed_type; + auto pointed_type = Type::asType(lval->type)->pointed_type; auto inst_load = build_InstLoad(lval, pointed_type, _state.current_bb); - - LOG(INFO) << lval->to_string() << " " << lval->type->to_string(); - auto ptr = build_InstGEP(inst_load, {CONST0}, _state.current_bb); - pointed_type = std::dynamic_pointer_cast(ptr->type)->pointed_type; - LOG(INFO) << pointed_type->to_string(); - sysy_assert(pointed_type->type_tag == Type::TypeTag::ArrayType); - ValuePtr_t offset = ConstantInt::make_shared(0); auto exp_list = ctx->exp(); + if (exp_list.size() == 1) { + auto exp_val = any_to_Value(visitExp(exp_list[0])); + auto ptr = build_InstGEP(inst_load, {exp_val}, _state.current_bb); + pointed_type = Type::asType(ptr->type)->pointed_type; + assert(Type::isType(pointed_type)); + return ptr; + } + // LOG(INFO) << lval->to_string() << " " << lval->type->to_string(); + auto ptr = build_InstGEP(inst_load, {CONST0}, _state.current_bb); + pointed_type = Type::asType(ptr->type)->pointed_type; + ValuePtr_t offset = ConstantInt::make_shared(0); + // LOG(INFO) << pointed_type->to_string(); + assert(Type::isType(pointed_type)); + // calculate offset by hand: offset = (offset + exp[i]) * dim_size[i] for (int i = 0; i < exp_list.size() - 1; ++i) { - sysy_assert(typeid(pointed_type) == typeid(std::shared_ptr)); + sysy_assert(Type::isType(pointed_type)); auto array_type = std::dynamic_pointer_cast(pointed_type); auto exp_val = any_to_Value(visitExp(exp_list[i])); auto dim_size = ConstantInt::make_shared(array_type->element_count); @@ -846,12 +866,20 @@ std::any Visitor::visitFuncDef(SysyParser::FuncDefContext *ctx) { visitBlock(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); + // there may be an empty bb in the end + + // avoid duplicate ret + if (func_obj->bb_list.back()->inst_list.size() == 0 && func_obj->bb_list.back()->name == "after_ret") { + func_obj->bb_list.pop_back(); } - else { - build_InstReturn(CONST0, _state.current_bb); + if (func_obj->bb_list.back()->inst_list.size()==0 + || !Value::isValueType(func_obj->bb_list.back()->inst_list.back())){ + if (func_ret_type->type_tag == Type::TypeTag::VoidType) { + build_InstReturn(_state.current_bb); + } + else { + build_InstReturn(CONST0, _state.current_bb); + } } return {}; } @@ -880,16 +908,18 @@ std::any Visitor::visitFuncFParam(SysyParser::FuncFParamContext *ctx) { else { // array type std::vector dim_list = {0}; // the first dim must be empty, though + TypePtr_t array_type = TypeHelper::TYPE_I32; for (auto exp_ctx : ctx->exp()) { _state.isConstInt = true; auto exp_val = std::dynamic_pointer_cast(any_to_Value(visitExp(exp_ctx))); _state.isConstInt = false; dim_list.push_back(exp_val->value); + array_type = std::make_shared(array_type, exp_val->value); } - auto array_type = ArrayType::build_from_list(dim_list); - // auto true_array_type = std::make_shared(array_type); - // return {true_array_type}; - return {array_type}; + // auto array_type = ArrayType::build_from_list(dim_list); + auto true_array_type = std::make_shared(array_type); + return {true_array_type}; + // return {array_type}; } } @@ -995,6 +1025,8 @@ std::any Visitor::visitReturnStmt(SysyParser::ReturnStmtContext *ctx) { else { build_InstReturn(_state.current_bb); } + auto new_bb = build_BasicBlock("after_ret", _state.current_func, _state.current_bb->itr); + _state.current_bb = new_bb; return {}; } diff --git a/src/visitor_factory.cpp b/src/visitor_factory.cpp index 7ba0d1d..f1e4a66 100644 --- a/src/visitor_factory.cpp +++ b/src/visitor_factory.cpp @@ -2,6 +2,7 @@ #include "llir_type.h" #include "llir_value.h" #include "visitor.h" +#include #include namespace antlrSysY { @@ -65,6 +66,15 @@ std::shared_ptr build_InstBinary( else { panic("Invalid Binary Operation"); } + assert(Type::isType(op1->type)); + assert(Type::isType(op2->type)); + if (!(Type::asType(op1->type)->int_type == Type::asType(op2->type)->int_type)) { + if (!(Value::isValueType(op1) && Value::asValueType(op1)->value == 0) + && !(Value::isValueType(op2) && Value::asValueType(op2)->value == 0)) { + LOG(ERROR) << op1->to_string() << ",\t" << op2->to_string(); + assert(0); + } + } parent_bb->inst_list.push_back(inst_binary); return inst_binary; } diff --git a/src/visitor_llir_gen.cpp b/src/visitor_llir_gen.cpp index ca1cb94..5e66b9c 100644 --- a/src/visitor_llir_gen.cpp +++ b/src/visitor_llir_gen.cpp @@ -81,6 +81,7 @@ static void _build_arr_init_list( ostr << "]"; } else { + ostr << arr_type->to_IR_string() << " "; ostr << "["; for (int i = 0; i < arr->value_list.size(); ++i) { if (arr->value_list[i]) @@ -102,11 +103,12 @@ static void _build_arr_init_list( } } -static void _gen_blocks(const std::list &block_list, int reg_count) { - std::stringstream ostr; +static void _gen_blocks(std::ostream &ostr, const std::list &block_list, int reg_count) { // first pass, allocate sequence number for (auto block_itr = block_list.begin(); block_itr != block_list.end(); ++block_itr) { auto block = *block_itr; + sysy_assert(block->ir_seqno == -1); // multi-alloc is error + block->ir_seqno = reg_count++; for (int j = 0; j < block->inst_list.size(); ++j) { auto inst = block->inst_list[j]; sysy_assert(inst->ir_seqno == -1); // multi-alloc is error @@ -143,10 +145,6 @@ static void _gen_blocks(const std::list &block_list, int reg_co default: panic("Unexpected instruction"); } } - sysy_assert(block->ir_seqno == -1); // multi-alloc is error - if (block_itr != block_list.begin()) { - block->ir_seqno = reg_count++; - } } // second pass, generate IR for (auto block_itr = block_list.begin(); block_itr != block_list.end(); ++block_itr) { @@ -156,12 +154,11 @@ static void _gen_blocks(const std::list &block_list, int reg_co } for (int j = 0; j < block->inst_list.size(); ++j) { auto _inst = block->inst_list[j]; - + VLOG(5) << "Build inst" << _inst->ir_seqno << ": " << _inst->to_string(); switch (_inst->tag) { case InstTag::Br: { auto inst = Value::asValueType(_inst); assert(inst->ir_seqno == -1); - std::stringstream ostr; ostr << "br "; if (inst->operand_list.size() == 1) { assert(Value::isValueType(inst->operand_list[0])); @@ -202,7 +199,11 @@ static void _gen_blocks(const std::list &block_list, int reg_co } else if (Value::isValueType(inst->operand_list[0])) { auto op0 = Value::asValueType(inst->operand_list[0]); - ostr << op0->to_string(); + ostr << op0->to_IR_string(); + } + else if (Value::isValueType(inst->operand_list[0])) { + auto op0 = Value::asValueType(inst->operand_list[0]); + ostr << op0->to_IR_string(); } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); @@ -215,8 +216,7 @@ static void _gen_blocks(const std::list &block_list, int reg_co auto inst = Value::asValueType(_inst); assert(inst->ir_seqno == -1); ostr << "store "; - assert(Type::isType(inst->operand_list[0]->type)); - assert(Type::isType(inst->operand_list[1]->type)); + if (Value::isValueType(inst->operand_list[0])) { auto op0 = Value::asValueType(inst->operand_list[0]); assert(op0->ir_seqno >= 0); @@ -224,7 +224,11 @@ static void _gen_blocks(const std::list &block_list, int reg_co } else if (Value::isValueType(inst->operand_list[0])) { auto op0 = Value::asValueType(inst->operand_list[0]); - ostr << "i32 " << op0->value << ", "; + ostr << op0->to_IR_string() << ", "; + } + else if (Value::isValueType(inst->operand_list[0])) { + auto op0 = Value::asValueType(inst->operand_list[0]); + ostr << op0->to_IR_string() << ", "; } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); @@ -239,6 +243,10 @@ static void _gen_blocks(const std::list &block_list, int reg_co assert(op1->ir_seqno >= 0); ostr << op1->type->to_IR_string() << " %" << op1->ir_seqno; } + else if (Value::isValueType(inst->operand_list[0])) { + auto op0 = Value::asValueType(inst->operand_list[0]); + ostr << op0->to_IR_string(); + } else { LOG(ERROR) << "Unexpected type of op1: " << inst->operand_list[1]->to_string(); assert(0); @@ -247,7 +255,8 @@ static void _gen_blocks(const std::list &block_list, int reg_co } // Call's seqno is dependent on its return type case InstTag::Call: { - auto inst = Value::asValueType(_inst); + auto inst = Value::asValueType(_inst); + LOG(DEBUG) << inst->operand_list[0]->to_string(); auto func = Value::asValueType(inst->operand_list[0]); auto func_type = Type::asType(func->type); if (Type::isType(func_type->return_type)) { @@ -268,7 +277,11 @@ static void _gen_blocks(const std::list &block_list, int reg_co } else if (Value::isValueType(inst->operand_list[i])) { auto op0 = Value::asValueType(inst->operand_list[i]); - ostr << op0->to_string(); + ostr << op0->to_IR_string(); + } + else if (Value::isValueType(inst->operand_list[i])) { + auto op0 = Value::asValueType(inst->operand_list[i]); + ostr << op0->to_IR_string() << ", "; } else { LOG(ERROR) << "Unexpected type of op_i: " << inst->operand_list[i]->to_string(); @@ -295,7 +308,7 @@ static void _gen_blocks(const std::list &block_list, int reg_co case InstTag::And: case InstTag::Or: { auto inst = Value::asValueType(_inst); - ostr << "%" << inst->ir_seqno << " = " << inst->to_string(); + ostr << "%" << inst->ir_seqno << " = " << inst->to_string() << " "; if (Value::isValueType(inst->operand_list[0])) { auto op0 = Value::asValueType(inst->operand_list[0]); assert(op0->ir_seqno >= 0); @@ -303,7 +316,11 @@ static void _gen_blocks(const std::list &block_list, int reg_co } else if (Value::isValueType(inst->operand_list[0])) { auto op0 = Value::asValueType(inst->operand_list[0]); - ostr << op0->to_string() << ", "; + ostr << op0->to_IR_string() << ", "; + } + else if (Value::isValueType(inst->operand_list[0])) { + auto op0 = Value::asValueType(inst->operand_list[0]); + ostr << op0->to_IR_string() << ", "; } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); @@ -318,6 +335,10 @@ static void _gen_blocks(const std::list &block_list, int reg_co auto op1 = Value::asValueType(inst->operand_list[1]); ostr << op1->value; } + else if (Value::isValueType(inst->operand_list[1])) { + auto op1 = Value::asValueType(inst->operand_list[1]); + ostr << "%" << op1->ir_seqno; + } else { LOG(ERROR) << "Unexpected type of op1: " << inst->operand_list[1]->to_string(); assert(0); @@ -338,6 +359,10 @@ static void _gen_blocks(const std::list &block_list, int reg_co assert(op1->ir_seqno >= 0); ostr << op1->type->to_IR_string() << " %" << op1->ir_seqno; } + else if (Value::isValueType(inst->operand_list[0])) { + auto op0 = Value::asValueType(inst->operand_list[0]); + ostr << op0->to_IR_string(); + } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[1]->to_string(); assert(0); @@ -348,7 +373,6 @@ static void _gen_blocks(const std::list &block_list, int reg_co auto inst = Value::asValueType(_inst); assert(inst->ir_seqno >= 0); - std::stringstream ostr; auto pointer_type = Type::asType(inst->operand_list[0]->type); ostr << "%" << inst->ir_seqno << " = getelementptr " << pointer_type->pointed_type->to_IR_string() << ", "; if (Value::isValueType(inst->operand_list[0])) { @@ -359,6 +383,10 @@ static void _gen_blocks(const std::list &block_list, int reg_co auto op0 = Value::asValueType(inst->operand_list[0]); ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno; } + else if (Value::isValueType(inst->operand_list[0])) { + auto op0 = Value::asValueType(inst->operand_list[0]); + ostr << op0->to_IR_string(); + } else { LOG(WARNING) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); assert(0); @@ -371,22 +399,26 @@ static void _gen_blocks(const std::list &block_list, int reg_co case InstTag::Alloca: { auto inst = Value::asValueType(_inst); assert(inst->ir_seqno != -1); - ostr << "%" << inst->ir_seqno << " = alloca i32, align 4"; + auto pointer_type = Type::asType(inst->type); + ostr << "%" << inst->ir_seqno << " = alloca " << pointer_type->pointed_type->to_IR_string() << ", align 4"; break; } case InstTag::Zext: { auto inst = Value::asValueType(_inst); assert(inst->ir_seqno >= 0); - std::stringstream ostr; ostr << "%" << inst->ir_seqno << " = zext "; if (Value::isValueType(inst->operand_list[0])) { auto op0 = Value::asValueType(inst->operand_list[0]); assert(op0->ir_seqno >= 0); - ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno << ", "; + ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno << "to "; } else if (Value::isValueType(inst->operand_list[0])) { auto op0 = Value::asValueType(inst->operand_list[0]); - ostr << op0->to_string() << ", "; + ostr << op0->to_IR_string() << "to "; + } + else if (Value::isValueType(inst->operand_list[0])) { + auto op0 = Value::asValueType(inst->operand_list[0]); + ostr << op0->to_IR_string() << "to "; } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); @@ -398,6 +430,7 @@ static void _gen_blocks(const std::list &block_list, int reg_co // These should not be generated in frontend stage default: panic("Unexpected instruction"); } + ostr << "\n"; } } } @@ -427,7 +460,7 @@ void Visitor::llir_gen(std::ostream &ostr) { LOG(DEBUG) << "Gen Global " << global_var->name; ostr << "@" << global_var->name << " = " << "dso_local" - << " " << (global_var->is_const ? "const" : "global") << " "; + << " " << (global_var->is_const ? "constant" : "global") << " "; auto global_var_type = std::dynamic_pointer_cast(global_var->type); if (global_var_type->pointed_type->type_tag == Type::TypeTag::IntegerType) { auto init_value = std::dynamic_pointer_cast(global_var->init_value); @@ -438,11 +471,15 @@ void Visitor::llir_gen(std::ostream &ostr) { // sysy_assert(global_var->) auto array_type = std::dynamic_pointer_cast(global_var_type->pointed_type); auto init_value = std::dynamic_pointer_cast(global_var->init_value); - auto hierarchy_array = gen_arr_hierarchy(array_type, init_value->value_list, 0, init_value->value_list.size()); - if (hierarchy_array) { - // LOG(DEBUG) << hierarchy_array->to_string(); - ostr << array_type->to_IR_string() << " "; - _build_arr_init_list(ostr, hierarchy_array, array_type); + if (init_value != nullptr) { + auto hierarchy_array = gen_arr_hierarchy(array_type, init_value->value_list, 0, init_value->value_list.size()); + if (hierarchy_array) { + // LOG(DEBUG) << hierarchy_array->to_string(); + _build_arr_init_list(ostr, hierarchy_array, array_type); + } + else { + ostr << array_type->to_IR_string() << " zeroinitializer"; + } } else { ostr << array_type->to_IR_string() << " zeroinitializer"; @@ -468,11 +505,15 @@ void Visitor::llir_gen(std::ostream &ostr) { ostr << "("; auto ¶m_list = func->fparam_list; for (int i = 0; i < (int)param_list.size() - 1; ++i) { - ostr << param_list[i]->type->to_IR_string() << " %" << reg_count++ << ", "; + param_list[i]->ir_seqno = reg_count++; + ostr << param_list[i]->to_IR_string() << ", "; + } + if (param_list.size()) { + param_list.back()->ir_seqno = reg_count++; + ostr << param_list.back()->to_IR_string(); } - if (param_list.size()) ostr << param_list.back()->type->to_IR_string(); ostr << ") {" << std::endl; - _gen_blocks(func->bb_list, reg_count); + _gen_blocks(ostr, func->bb_list, reg_count); ostr << "}" << std::endl; } #pragma endregion diff --git a/tools/sylib/libsysy_x64.a b/tools/sylib/libsysy_x64.a new file mode 100644 index 0000000000000000000000000000000000000000..8a5af1dbe16b368708ddd1b79951db29b8785a7d GIT binary patch literal 9616 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 literal 0 HcmV?d00001 diff --git a/tools/sylib/libsysy_x86.a b/tools/sylib/libsysy_x86.a index 8a5af1dbe16b368708ddd1b79951db29b8785a7d..3944ca07aabf97a81da7a2e3274ba08d12509d6c 100644 GIT binary patch literal 6544 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