#include "3rdparty/easylogging++.h" #include "common.h" #include "llir.h" #include "visitor.h" #include #include #include #include #include #include namespace antlrSysY { const std::array libfunc_list = {"getint", "getch", "getarray", "putint", "putch", "putarray", "starttime", "stoptime"}; // On generation, the array is flattened, thus we have to calculate them std::shared_ptr gen_arr_hierarchy( const std::shared_ptr array_type, const std::vector &const_array, int base, int length ) { int dim_n = array_type->element_count; int dim_size = length / dim_n; std::vector value_list; if (array_type->element_type->type_tag == Type::TypeTag::IntegerType) { sysy_assert(dim_size == 1); for (int i = 0; i < dim_n; ++i) { if (const_array[base + i]) { value_list.push_back(const_array[base + i]); } else { break; } } if (!value_list.empty()) return ConstantArr::make_shared("", value_list, array_type); else return nullptr; } else { int last_non_null = -1; for (int i = 0; i < dim_n; ++i) { auto sub_arr = gen_arr_hierarchy( std::dynamic_pointer_cast(array_type->element_type), const_array, dim_size * i, dim_size ); value_list.push_back(sub_arr); if (sub_arr) last_non_null = i; } if (last_non_null == -1) { return nullptr; } else { // if (last_non_null + 1 < value_list.size()) value_list.erase(value_list.begin() + last_non_null + 1, value_list.end()); return ConstantArr::make_shared("", value_list, array_type); } } } static void _build_arr_init_list( std::ostream &ostr, std::shared_ptr arr, std::shared_ptr arr_type ) { if (arr_type->element_type->type_tag == Type::TypeTag::IntegerType) { ostr << arr_type->to_IR_string(); ostr << " ["; for (int i = 0; i < arr->value_list.size(); ++i) { ostr << "i32 " << std::dynamic_pointer_cast(arr->value_list[i])->value; if (i < arr->value_list.size() - 1) { ostr << ", "; } } for (int i = 0; i < arr_type->element_count - arr->value_list.size(); ++i) { ostr << ", i32 0"; } ostr << "]"; } else { ostr << arr_type->to_IR_string() << " "; ostr << "["; for (int i = 0; i < arr->value_list.size(); ++i) { if (arr->value_list[i]) _build_arr_init_list( ostr, std::dynamic_pointer_cast(arr->value_list[i]), std::dynamic_pointer_cast(arr_type->element_type) ); else ostr << arr_type->element_type->to_IR_string() << " zeroinitializer"; if (i < arr->value_list.size() - 1) { ostr << ", "; } } for (int i = 0; i < arr_type->element_count - arr->value_list.size(); ++i) { ostr << ", " << arr_type->element_type->to_IR_string() << " zeroinitializer"; } 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 (auto inst : block->inst_list) { sysy_assert(inst->ir_seqno == -1); // multi-alloc is error switch (inst->tag) { // These are not to get a seqno case InstTag::Br: case InstTag::Ret: case InstTag::Store: break; // Call's seqno is dependent on its return type case InstTag::Call: if (inst->type->type_tag == Type::TypeTag::IntegerType) { inst->ir_seqno = reg_count++; } break; // These should have a seqno case InstTag::Add: case InstTag::Sub: case InstTag::Mod: case InstTag::Mul: case InstTag::Div: case InstTag::Lt: case InstTag::Le: case InstTag::Ge: case InstTag::Gt: case InstTag::Eq: case InstTag::Ne: case InstTag::And: case InstTag::Or: case InstTag::Load: case InstTag::GEP: case InstTag::Alloca: case InstTag::Phi: case InstTag::Zext: inst->ir_seqno = reg_count++; break; // These should not be generated in frontend stage default: panic("Unexpected instruction"); } } } // second pass, generate IR for (auto block_itr = block_list.begin(); block_itr != block_list.end(); ++block_itr) { auto block = *block_itr; if (block_itr != block_list.begin()) { ostr << block->ir_seqno << ":" << std::endl; } for (auto _inst : block->inst_list) { ostr << " "; VLOG(5) << "Build inst" << _inst->ir_seqno << ": " << _inst->to_string(); switch (_inst->tag) { case InstTag::Br: { auto inst = Value::as(_inst); assert(inst->ir_seqno == -1); ostr << "br "; if (inst->operand_list.size() == 1) { assert(Value::is(inst->operand_list[0])); auto bb_dest = Value::as(inst->operand_list[0]); assert(bb_dest->ir_seqno >= 0); ostr << "label %" << bb_dest->ir_seqno; } else { assert(Value::is(inst->operand_list[0])); assert(Type::isType(inst->operand_list[0]->type)); auto cond = Value::as(inst->operand_list[0]); auto bb_true = Value::as(inst->operand_list[1]); auto bb_false = Value::as(inst->operand_list[2]); if (!Type::asType(cond->type)->isI1()) { LOG(ERROR) << "Expect cond evals to i1: " << cond->to_string(); panic("Grammar check"); } assert(cond->ir_seqno >= 0); assert(bb_true->ir_seqno >= 0); assert(bb_false->ir_seqno >= 0); ostr << "i1 %" << cond->ir_seqno << ", label %" << bb_true->ir_seqno << ", label %" << bb_false->ir_seqno; } break; } case InstTag::Ret: { auto inst = Value::as(_inst); assert(inst->ir_seqno == -1); ostr << "ret "; if (inst->operand_list.size() == 0) { assert(Type::isType(inst->type)); ostr << "void"; } else { if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); assert(op0->ir_seqno >= 0); ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string(); } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string(); } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); assert(0); } } break; } case InstTag::Store: { auto inst = Value::as(_inst); assert(inst->ir_seqno == -1); ostr << "store "; if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); assert(op0->ir_seqno >= 0); ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno << ", "; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string() << ", "; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string() << ", "; } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); assert(0); } if (Value::is(inst->operand_list[1])) { auto op1 = Value::as(inst->operand_list[1]); ostr << op1->type->to_IR_string() << " @" << op1->name; } else if (Value::is(inst->operand_list[1])) { auto op1 = Value::as(inst->operand_list[1]); assert(op1->ir_seqno >= 0); ostr << op1->type->to_IR_string() << " %" << op1->ir_seqno; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string(); } else { LOG(ERROR) << "Unexpected type of op1: " << inst->operand_list[1]->to_string(); assert(0); } ostr << ", align 4"; break; } // Call's seqno is dependent on its return type case InstTag::Call: { auto inst = Value::as(_inst); LOG(DEBUG) << inst->operand_list[0]->to_string(); auto func = Value::as(inst->operand_list[0]); auto func_type = Type::asType(func->type); if (Type::isType(func_type->return_type)) { assert(inst->ir_seqno == -1); ostr << "call void @"; } else { assert(inst->ir_seqno >= 0); ostr << "%" << inst->ir_seqno << " = call i32 @"; } ostr << func->name << "("; if (inst->operand_list.size() > 1) { for (int i = 1; i < inst->operand_list.size(); ++i) { if (Value::is(inst->operand_list[i])) { auto op0 = Value::as(inst->operand_list[i]); assert(op0->ir_seqno >= 0); ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno; } else if (Value::is(inst->operand_list[i])) { auto op0 = Value::as(inst->operand_list[i]); ostr << op0->to_IR_string(); } else if (Value::is(inst->operand_list[i])) { auto op0 = Value::as(inst->operand_list[i]); ostr << op0->to_IR_string() << ", "; } else { LOG(ERROR) << "Unexpected type of op_i: " << inst->operand_list[i]->to_string(); assert(0); } if (i < inst->operand_list.size() - 1) ostr << ", "; } } ostr << ")"; break; } // These should have a seqno case InstTag::Add: case InstTag::Sub: case InstTag::Mod: case InstTag::Mul: case InstTag::Div: case InstTag::Lt: case InstTag::Le: case InstTag::Ge: case InstTag::Gt: case InstTag::Eq: case InstTag::Ne: case InstTag::And: case InstTag::Or: { auto inst = Value::as(_inst); ostr << "%" << inst->ir_seqno << " = " << inst->to_string() << " "; if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); assert(op0->ir_seqno >= 0); ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno << ", "; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string() << ", "; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string() << ", "; } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); assert(0); } if (Value::is(inst->operand_list[1])) { auto op1 = Value::as(inst->operand_list[1]); assert(op1->ir_seqno >= 0); ostr << "%" << op1->ir_seqno; } else if (Value::is(inst->operand_list[1])) { auto op1 = Value::as(inst->operand_list[1]); ostr << op1->value; } else if (Value::is(inst->operand_list[1])) { auto op1 = Value::as(inst->operand_list[1]); ostr << "%" << op1->ir_seqno; } else { LOG(ERROR) << "Unexpected type of op1: " << inst->operand_list[1]->to_string(); assert(0); } break; } case InstTag::Load: { auto inst = Value::as(_inst); assert(inst->ir_seqno != -1); assert(Type::isType(inst->operand_list[0]->type)); ostr << "%" << inst->ir_seqno << " = load " << inst->type->to_IR_string() << ", "; if (Value::is(inst->operand_list[0])) { auto op = Value::as(inst->operand_list[0]); ostr << op->type->to_IR_string() << " @" << op->name; } else if (Value::is(inst->operand_list[0])) { auto op = Value::as(inst->operand_list[0]); assert(op->ir_seqno >= 0); ostr << op->type->to_IR_string() << " %" << op->ir_seqno; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string(); } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[1]->to_string(); assert(0); } break; } case InstTag::GEP: { auto inst = Value::as(_inst); assert(inst->ir_seqno >= 0); auto pointer_type = Type::asType(inst->operand_list[0]->type); ostr << "%" << inst->ir_seqno << " = getelementptr " << pointer_type->pointed_type->to_IR_string() << ", "; if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string(); } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string(); } else { LOG(WARNING) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); assert(0); } for (int i = 1; i < inst->operand_list.size(); ++i) { ostr << ", " << inst->operand_list[i]->to_IR_string(); } break; } case InstTag::Alloca: { auto inst = Value::as(_inst); assert(inst->ir_seqno != -1); auto pointer_type = Type::asType(inst->type); ostr << "%" << inst->ir_seqno << " = alloca " << pointer_type->pointed_type->to_IR_string() << ", align 4"; ostr << " ;" << inst->name; break; } case InstTag::Zext: { auto inst = Value::as(_inst); assert(inst->ir_seqno >= 0); ostr << "%" << inst->ir_seqno << " = zext "; if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); assert(op0->ir_seqno >= 0); ostr << op0->type->to_IR_string() << " %" << op0->ir_seqno << "to "; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string() << "to "; } else if (Value::is(inst->operand_list[0])) { auto op0 = Value::as(inst->operand_list[0]); ostr << op0->to_IR_string() << "to "; } else { LOG(ERROR) << "Unexpected type of op0: " << inst->operand_list[0]->to_string(); assert(0); } ostr << "i32"; break; } case InstTag::Phi: { auto inst = Value::as(_inst); assert(inst->ir_seqno >= 0); ostr << "%" << inst->ir_seqno << " = phi " << inst->type->to_IR_string() << " "; for (int i = 0; i < inst->operand_list.size(); ++i) { auto op = inst->operand_list[i]; ostr << "["; if (Value::is(op)) { auto op0 = Value::as(op); ostr << "@" << op0->name; } else if (Value::is(op)) { auto op0 = Value::as(op); ostr << "%" << op0->ir_seqno; } else if (Value::is(op)) { auto op0 = Value::as(op); ostr << "%" << op0->ir_seqno; } else if (Value::is(op)) { auto op0 = Value::as(op); ostr << op0->value; } else { LOG(WARNING) << "Unexpected type of op: " << op->to_string(); assert(0); } auto pred = inst->parent_bb->predecessors.begin(); std::advance(pred, i); ostr << ", %" << (*pred)->ir_seqno << "]"; if (i < inst->operand_list.size() - 1) { ostr << ", "; } } break; } // These should not be generated in frontend stage default: panic("Unexpected instruction"); } ostr << "\n"; } } } void Visitor::llir_gen(std::ostream &ostr) { #pragma region GenLibFuncDecl for (auto &lib_func_name : libfunc_list) { VLOG(6) << "Gen LibFunc " << lib_func_name; auto lib_func = _func_tab.get_name(lib_func_name).value(); auto lib_func_type = std::dynamic_pointer_cast(lib_func->type); ostr << "declare" << " " << lib_func_type->return_type->to_IR_string() << " " << "@" << lib_func_name << "("; auto ¶m_list = lib_func->fparam_list; for (int i = 0; i < (int)param_list.size() - 1; ++i) { ostr << param_list[i]->type->to_IR_string() << ", "; } if (param_list.size()) ostr << param_list.back()->type->to_IR_string(); ostr << ")" << std::endl; } // ostr.flush(); #pragma endregion #pragma region GenGlobDecl for (auto &global_var : module.global_var_list) { // both int and arr have the same leading part VLOG(5) << "Gen Global " << global_var->name; ostr << "@" << global_var->name << " = " << "dso_local" << " " << (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); ostr << "i32" << " " << init_value->value << std::endl; } else if (global_var_type->pointed_type->type_tag == Type::TypeTag::ArrayType) { // 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); 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"; } ostr << ", align 4" << std::endl; } else { LOG(ERROR) << global_var->to_string(); panic("Invalid Global Declaration"); } } #pragma endregion #pragma region GenFunction for (auto &func : module.function_list) { if (func->is_libfunc()) { LOG(WARNING) << "Lib func"; continue; } int reg_count = 0; // allocate unique register name from %0 auto func_type = std::dynamic_pointer_cast(func->type); ostr << "define dso_local " << func_type->return_type->to_IR_string() << " @" << func->name; ostr << "("; auto ¶m_list = func->fparam_list; for (int i = 0; i < (int)param_list.size() - 1; ++i) { 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(); } ostr << ") {" << std::endl; _gen_blocks(ostr, func->bb_list, reg_count); ostr << "}" << std::endl; } #pragma endregion } } // namespace antlrSysY