|
|
#include "irgen/IRGen.h"
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
|
|
#include "SysYParser.h"
|
|
|
#include "ir/IR.h"
|
|
|
#include "utils/Log.h"
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
void VerifyFunctionStructure(const ir::Function& func) {
|
|
|
// 当前 IRGen 仍是单入口、顺序生成;这里在生成结束后补一层块终结校验。
|
|
|
for (const auto& bb : func.GetBlocks()) {
|
|
|
if (!bb || !bb->HasTerminator()) {
|
|
|
throw std::runtime_error(
|
|
|
FormatError("irgen", "基本块未正确终结: " +
|
|
|
(bb ? bb->GetName() : std::string("<null>"))));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
IRGenImpl::IRGenImpl(ir::Module& module, const SemanticContext& sema)
|
|
|
: module_(module),
|
|
|
sema_(sema),
|
|
|
func_(nullptr),
|
|
|
builder_(module.GetContext(), nullptr) {}
|
|
|
|
|
|
ir::AllocaInst* IRGenImpl::CreateEntryAllocaI32(const std::string& name) {
|
|
|
if (!func_) {
|
|
|
throw std::runtime_error(FormatError("irgen", "局部 alloca 必须位于函数内"));
|
|
|
}
|
|
|
auto* saved = builder_.GetInsertBlock();
|
|
|
builder_.SetInsertPoint(func_->GetEntry());
|
|
|
auto* slot = builder_.CreateAllocaI32(name);
|
|
|
builder_.SetInsertPoint(saved);
|
|
|
return slot;
|
|
|
}
|
|
|
|
|
|
ir::AllocaInst* IRGenImpl::CreateEntryAllocaArray(int count, const std::string& name) {
|
|
|
if (!func_) {
|
|
|
throw std::runtime_error(FormatError("irgen", "局部 alloca 必须位于函数内"));
|
|
|
}
|
|
|
auto* saved = builder_.GetInsertBlock();
|
|
|
builder_.SetInsertPoint(func_->GetEntry());
|
|
|
auto* slot = builder_.CreateAllocaArray(count, name);
|
|
|
builder_.SetInsertPoint(saved);
|
|
|
return slot;
|
|
|
}
|
|
|
|
|
|
// 预声明 SysY 运行时外部函数(putint / putch / getint / getch 等)。
|
|
|
void IRGenImpl::DeclareRuntimeFunctions() {
|
|
|
auto i32 = ir::Type::GetInt32Type();
|
|
|
auto void_ = ir::Type::GetVoidType();
|
|
|
|
|
|
auto decl = [&](const std::string& name,
|
|
|
std::shared_ptr<ir::Type> ret,
|
|
|
std::vector<std::shared_ptr<ir::Type>> params) {
|
|
|
if (!module_.FindFunction(name)) {
|
|
|
auto* f = module_.CreateFunction(name, ret, params);
|
|
|
f->SetExternal(true);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// 整数 I/O
|
|
|
decl("getint", i32, {});
|
|
|
decl("getch", i32, {});
|
|
|
decl("putint", void_, {i32});
|
|
|
decl("putch", void_, {i32});
|
|
|
// 数组 I/O
|
|
|
decl("getarray", i32, {ir::Type::GetPtrInt32Type()});
|
|
|
decl("putarray", void_, {i32, ir::Type::GetPtrInt32Type()});
|
|
|
// 时间
|
|
|
decl("starttime", void_, {});
|
|
|
decl("stoptime", void_, {});
|
|
|
}
|
|
|
|
|
|
// 编译单元 IR 生成:
|
|
|
// 1. 预声明 SysY runtime;
|
|
|
// 2. 处理全局变量/常量声明;
|
|
|
// 3. 生成各函数 IR。
|
|
|
std::any IRGenImpl::visitCompUnit(SysYParser::CompUnitContext* ctx) {
|
|
|
if (!ctx) {
|
|
|
throw std::runtime_error(FormatError("irgen", "缺少编译单元"));
|
|
|
}
|
|
|
|
|
|
DeclareRuntimeFunctions();
|
|
|
|
|
|
// 全局声明(func_ == nullptr 时 visitVarDef/visitConstDef 会走全局路径)
|
|
|
for (auto* decl : ctx->decl()) {
|
|
|
if (decl) decl->accept(this);
|
|
|
}
|
|
|
|
|
|
for (auto* func : ctx->funcDef()) {
|
|
|
if (func) func->accept(this);
|
|
|
}
|
|
|
return {};
|
|
|
}
|
|
|
|
|
|
// 函数 IR 生成当前实现了:
|
|
|
// 1. 获取函数名;
|
|
|
// 2. 支持 int 与 void 返回类型;
|
|
|
// 3. 支持 int 形参:入口处为每个参数 alloca + store;
|
|
|
// 4. 在 Module 中创建 Function;
|
|
|
// 5. 将 builder 插入点设置到入口基本块;
|
|
|
// 6. 继续生成函数体。
|
|
|
//
|
|
|
// 当前还没有实现:
|
|
|
// - float 参数/返回类型;
|
|
|
// - 数组类型形参;
|
|
|
// - FunctionType 这样的函数类型对象(参数类型目前只用 shared_ptr<Type>)。
|
|
|
|
|
|
std::any IRGenImpl::visitFuncDef(SysYParser::FuncDefContext* ctx) {
|
|
|
if (!ctx) {
|
|
|
throw std::runtime_error(FormatError("irgen", "缺少函数定义"));
|
|
|
}
|
|
|
if (!ctx->blockStmt()) {
|
|
|
throw std::runtime_error(FormatError("irgen", "函数体为空"));
|
|
|
}
|
|
|
if (!ctx->ID()) {
|
|
|
throw std::runtime_error(FormatError("irgen", "缺少函数名"));
|
|
|
}
|
|
|
if (!ctx->funcType()) {
|
|
|
throw std::runtime_error(FormatError("irgen", "缺少函数返回类型"));
|
|
|
}
|
|
|
|
|
|
std::shared_ptr<ir::Type> ret_type;
|
|
|
if (ctx->funcType()->INT()) {
|
|
|
ret_type = ir::Type::GetInt32Type();
|
|
|
} else if (ctx->funcType()->VOID()) {
|
|
|
ret_type = ir::Type::GetVoidType();
|
|
|
} else {
|
|
|
throw std::runtime_error(FormatError("irgen", "当前仅支持 int/void 返回类型"));
|
|
|
}
|
|
|
|
|
|
// 收集形参类型(支持 int 标量和 int 数组参数)。
|
|
|
std::vector<std::shared_ptr<ir::Type>> param_types;
|
|
|
std::vector<std::string> param_names;
|
|
|
std::vector<bool> param_is_array;
|
|
|
|
|
|
if (auto* fparams = ctx->funcFParams()) {
|
|
|
for (auto* fp : fparams->funcFParam()) {
|
|
|
if (!fp || !fp->btype() || !fp->btype()->INT()) {
|
|
|
throw std::runtime_error(
|
|
|
FormatError("irgen", "当前仅支持 int 类型形参"));
|
|
|
}
|
|
|
bool is_arr = !fp->LBRACK().empty();
|
|
|
param_is_array.push_back(is_arr);
|
|
|
param_types.push_back(is_arr ? ir::Type::GetPtrInt32Type()
|
|
|
: ir::Type::GetInt32Type());
|
|
|
param_names.push_back(fp->ID() ? fp->ID()->getText() : "");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func_ = module_.CreateFunction(ctx->ID()->getText(), ret_type, param_types);
|
|
|
auto* body_entry = func_->CreateBlock(NextBlockName());
|
|
|
builder_.SetInsertPoint(body_entry);
|
|
|
storage_map_.clear();
|
|
|
named_storage_.clear();
|
|
|
local_array_dims_.clear();
|
|
|
|
|
|
// 第二遍:处理形参(现在有插入点,可以生成 alloca 等)
|
|
|
auto* fparams = ctx->funcFParams();
|
|
|
for (size_t i = 0; i < param_names.size(); ++i) {
|
|
|
auto* arg = func_->GetArgument(i);
|
|
|
if (param_is_array[i]) {
|
|
|
// 数组参数:直接存入 named_storage_,维度用 EvalExpAsConst 获取
|
|
|
if (!param_names[i].empty()) {
|
|
|
named_storage_[param_names[i]] = arg;
|
|
|
std::vector<int> dims = {-1}; // 首维未知
|
|
|
if (fparams) {
|
|
|
auto fp_list = fparams->funcFParam();
|
|
|
if (i < fp_list.size()) {
|
|
|
for (auto* dim_exp : fp_list[i]->exp()) {
|
|
|
dims.push_back(EvalExpAsConst(dim_exp));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
local_array_dims_[param_names[i]] = dims;
|
|
|
}
|
|
|
} else {
|
|
|
// 标量参数:alloca + store
|
|
|
auto* slot = CreateEntryAllocaI32(module_.GetContext().NextTemp());
|
|
|
builder_.CreateStore(arg, slot);
|
|
|
if (!param_names[i].empty()) {
|
|
|
named_storage_[param_names[i]] = slot;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
short_circuit_slot_ = CreateEntryAllocaI32(module_.GetContext().NextTemp());
|
|
|
|
|
|
ctx->blockStmt()->accept(this);
|
|
|
|
|
|
// 入口块只用于静态栈槽分配,末尾统一跳到函数体起始块。
|
|
|
auto* saved = builder_.GetInsertBlock();
|
|
|
builder_.SetInsertPoint(func_->GetEntry());
|
|
|
if (!func_->GetEntry()->HasTerminator()) {
|
|
|
builder_.CreateBr(body_entry);
|
|
|
}
|
|
|
builder_.SetInsertPoint(saved);
|
|
|
|
|
|
// 对于 void 函数,若末尾块无 terminator,自动补 ret void。
|
|
|
if (ret_type->IsVoid()) {
|
|
|
auto* bb = builder_.GetInsertBlock();
|
|
|
if (bb && !bb->HasTerminator()) {
|
|
|
builder_.CreateRetVoid();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 语义正确性主要由 sema 保证,这里只兜底检查 IR 结构是否合法。
|
|
|
VerifyFunctionStructure(*func_);
|
|
|
short_circuit_slot_ = nullptr;
|
|
|
func_ = nullptr; // 回到全局作用域
|
|
|
return {};
|
|
|
}
|