fix(ir): 规范ir实现
This commit is contained in:
@@ -69,3 +69,8 @@ java -jar third_party/antlr-4.13.2-complete.jar \
|
|||||||
# 仅输出语法树
|
# 仅输出语法树
|
||||||
./build/bin/compiler --emit-parse-tree test/test_case/simple_add.sy
|
./build/bin/compiler --emit-parse-tree test/test_case/simple_add.sy
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 7. 关于 AST 的建议
|
||||||
|
|
||||||
|
同学们也可以自行设计一层抽象语法树(AST),将 ANTLR 语法树先转换为 AST,再进入后续阶段。
|
||||||
|
这样可以减少对具体文法细节的依赖,使语义分析、IR 生成和后续扩展更清晰。这里不做具体要求。
|
||||||
|
|||||||
@@ -57,6 +57,9 @@ Lab2 的目标是在该示例基础上扩展语义覆盖范围,逐步把更多
|
|||||||
|
|
||||||
说明:当前阶段变量统一采用内存模型:先 `alloca` 分配栈槽,再通过 `store/load` 读写。即使变量由常量初始化(如 `int a = 1;`),也会先 `store` 到栈槽,而不是直接把变量替换成 SSA 值。后续实验中,同学可按需求再重构。
|
说明:当前阶段变量统一采用内存模型:先 `alloca` 分配栈槽,再通过 `store/load` 读写。即使变量由常量初始化(如 `int a = 1;`),也会先 `store` 到栈槽,而不是直接把变量替换成 SSA 值。后续实验中,同学可按需求再重构。
|
||||||
|
|
||||||
|
此外,当前 IR 还维护了最基本的 use-def 关系:每个 `Value` 会记录哪些 `Instruction` 使用了它。
|
||||||
|
这对后续做数据流分析、死代码删除、常量传播等优化会很有帮助;但目前相关实现,接口仍不完整,后续实验中还需要同学继续补充和完善。
|
||||||
|
|
||||||
## 6. 构建与运行
|
## 6. 构建与运行
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -6,7 +6,18 @@
|
|||||||
namespace ir {
|
namespace ir {
|
||||||
|
|
||||||
Function::Function(std::string name, std::shared_ptr<Type> ret_type)
|
Function::Function(std::string name, std::shared_ptr<Type> ret_type)
|
||||||
: Value(std::move(ret_type), std::move(name)),
|
: Value(std::move(ret_type), std::move(name)) {
|
||||||
entry_(std::make_unique<BasicBlock>("entry")) {}
|
entry_ = CreateBlock("entry");
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicBlock* Function::CreateBlock(const std::string& name) {
|
||||||
|
auto block = std::make_unique<BasicBlock>(name);
|
||||||
|
auto* ptr = block.get();
|
||||||
|
blocks_.push_back(std::move(block));
|
||||||
|
if (!entry_) {
|
||||||
|
entry_ = ptr;
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
|
|||||||
25
src/ir/IR.h
25
src/ir/IR.h
@@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -13,6 +14,7 @@ namespace ir {
|
|||||||
class Type;
|
class Type;
|
||||||
class ConstantInt;
|
class ConstantInt;
|
||||||
class Instruction;
|
class Instruction;
|
||||||
|
class BasicBlock;
|
||||||
|
|
||||||
// IR 上下文:集中管理类型、常量等共享资源,便于复用与扩展。
|
// IR 上下文:集中管理类型、常量等共享资源,便于复用与扩展。
|
||||||
class Context {
|
class Context {
|
||||||
@@ -82,9 +84,13 @@ class Instruction : public Value {
|
|||||||
Instruction(Opcode op, std::shared_ptr<Type> ty, std::string name = "")
|
Instruction(Opcode op, std::shared_ptr<Type> ty, std::string name = "")
|
||||||
: Value(std::move(ty), std::move(name)), opcode_(op) {}
|
: Value(std::move(ty), std::move(name)), opcode_(op) {}
|
||||||
Opcode opcode() const { return opcode_; }
|
Opcode opcode() const { return opcode_; }
|
||||||
|
bool IsTerminator() const { return opcode_ == Opcode::Ret; }
|
||||||
|
BasicBlock* parent() const { return parent_; }
|
||||||
|
void set_parent(BasicBlock* parent) { parent_ = parent; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Opcode opcode_;
|
Opcode opcode_;
|
||||||
|
BasicBlock* parent_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BinaryInst : public Instruction {
|
class BinaryInst : public Instruction {
|
||||||
@@ -137,13 +143,21 @@ class BasicBlock {
|
|||||||
public:
|
public:
|
||||||
explicit BasicBlock(std::string name) : name_(std::move(name)) {}
|
explicit BasicBlock(std::string name) : name_(std::move(name)) {}
|
||||||
const std::string& name() const { return name_; }
|
const std::string& name() const { return name_; }
|
||||||
|
bool HasTerminator() const {
|
||||||
|
return !instructions_.empty() && instructions_.back()->IsTerminator();
|
||||||
|
}
|
||||||
const std::vector<std::unique_ptr<Instruction>>& instructions() const {
|
const std::vector<std::unique_ptr<Instruction>>& instructions() const {
|
||||||
return instructions_;
|
return instructions_;
|
||||||
}
|
}
|
||||||
template <typename T, typename... Args>
|
template <typename T, typename... Args>
|
||||||
T* Append(Args&&... args) {
|
T* Append(Args&&... args) {
|
||||||
|
if (HasTerminator()) {
|
||||||
|
throw std::runtime_error("BasicBlock 已有 terminator,不能继续追加指令: " +
|
||||||
|
name_);
|
||||||
|
}
|
||||||
auto inst = std::make_unique<T>(std::forward<Args>(args)...);
|
auto inst = std::make_unique<T>(std::forward<Args>(args)...);
|
||||||
auto* ptr = inst.get();
|
auto* ptr = inst.get();
|
||||||
|
ptr->set_parent(this);
|
||||||
instructions_.push_back(std::move(inst));
|
instructions_.push_back(std::move(inst));
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
@@ -157,11 +171,16 @@ class Function : public Value {
|
|||||||
public:
|
public:
|
||||||
// 允许显式指定返回类型,便于后续扩展多种函数签名。
|
// 允许显式指定返回类型,便于后续扩展多种函数签名。
|
||||||
Function(std::string name, std::shared_ptr<Type> ret_type);
|
Function(std::string name, std::shared_ptr<Type> ret_type);
|
||||||
BasicBlock* entry() { return entry_.get(); }
|
BasicBlock* CreateBlock(const std::string& name);
|
||||||
const BasicBlock* entry() const { return entry_.get(); }
|
BasicBlock* entry() { return entry_; }
|
||||||
|
const BasicBlock* entry() const { return entry_; }
|
||||||
|
const std::vector<std::unique_ptr<BasicBlock>>& blocks() const {
|
||||||
|
return blocks_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<BasicBlock> entry_;
|
BasicBlock* entry_ = nullptr;
|
||||||
|
std::vector<std::unique_ptr<BasicBlock>> blocks_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Module {
|
class Module {
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ bool IsArithmeticType(const std::shared_ptr<Type>& ty) {
|
|||||||
return ty && ty->kind() == Type::Kind::Int32;
|
return ty && ty->kind() == Type::Kind::Int32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsPtrInt32Type(const std::shared_ptr<Type>& ty) {
|
||||||
|
return ty && ty->kind() == Type::Kind::PtrInt32;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
ConstantInt* IRBuilder::CreateConstInt(int v) {
|
ConstantInt* IRBuilder::CreateConstInt(int v) {
|
||||||
@@ -54,6 +58,12 @@ LoadInst* IRBuilder::CreateLoad(Value* ptr, const std::string& name) {
|
|||||||
if (!insertBlock_) {
|
if (!insertBlock_) {
|
||||||
throw std::runtime_error("IRBuilder 未设置插入点");
|
throw std::runtime_error("IRBuilder 未设置插入点");
|
||||||
}
|
}
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("IRBuilder::CreateLoad 缺少 ptr");
|
||||||
|
}
|
||||||
|
if (!IsPtrInt32Type(ptr->type())) {
|
||||||
|
throw std::runtime_error("IRBuilder::CreateLoad 当前只支持从 i32* 加载");
|
||||||
|
}
|
||||||
return insertBlock_->Append<LoadInst>(ptr, name);
|
return insertBlock_->Append<LoadInst>(ptr, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +71,18 @@ StoreInst* IRBuilder::CreateStore(Value* val, Value* ptr) {
|
|||||||
if (!insertBlock_) {
|
if (!insertBlock_) {
|
||||||
throw std::runtime_error("IRBuilder 未设置插入点");
|
throw std::runtime_error("IRBuilder 未设置插入点");
|
||||||
}
|
}
|
||||||
|
if (!val) {
|
||||||
|
throw std::runtime_error("IRBuilder::CreateStore 缺少 val");
|
||||||
|
}
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("IRBuilder::CreateStore 缺少 ptr");
|
||||||
|
}
|
||||||
|
if (!IsArithmeticType(val->type())) {
|
||||||
|
throw std::runtime_error("IRBuilder::CreateStore 当前只支持存储 i32");
|
||||||
|
}
|
||||||
|
if (!IsPtrInt32Type(ptr->type())) {
|
||||||
|
throw std::runtime_error("IRBuilder::CreateStore 当前只支持写入 i32*");
|
||||||
|
}
|
||||||
return insertBlock_->Append<StoreInst>(val, ptr);
|
return insertBlock_->Append<StoreInst>(val, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +90,9 @@ ReturnInst* IRBuilder::CreateRet(Value* v) {
|
|||||||
if (!insertBlock_) {
|
if (!insertBlock_) {
|
||||||
throw std::runtime_error("IRBuilder 未设置插入点");
|
throw std::runtime_error("IRBuilder 未设置插入点");
|
||||||
}
|
}
|
||||||
|
if (!v) {
|
||||||
|
throw std::runtime_error("IRBuilder::CreateRet 缺少返回值");
|
||||||
|
}
|
||||||
return insertBlock_->Append<ReturnInst>(v);
|
return insertBlock_->Append<ReturnInst>(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace ir {
|
namespace ir {
|
||||||
@@ -41,6 +42,13 @@ static const char* OpcodeToString(Opcode op) {
|
|||||||
return "?";
|
return "?";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string ValueToString(const Value* v) {
|
||||||
|
if (auto* ci = dynamic_cast<const ConstantInt*>(v)) {
|
||||||
|
return std::to_string(ci->value());
|
||||||
|
}
|
||||||
|
return v ? v->name() : "<null>";
|
||||||
|
}
|
||||||
|
|
||||||
void IRPrinter::Print(const Module& module) {
|
void IRPrinter::Print(const Module& module) {
|
||||||
for (const auto& func : module.functions()) {
|
for (const auto& func : module.functions()) {
|
||||||
std::cout << "define " << TypeToString(*func->type()) << " @"
|
std::cout << "define " << TypeToString(*func->type()) << " @"
|
||||||
@@ -60,7 +68,8 @@ void IRPrinter::Print(const Module& module) {
|
|||||||
auto* bin = static_cast<const BinaryInst*>(inst);
|
auto* bin = static_cast<const BinaryInst*>(inst);
|
||||||
std::cout << " " << bin->name() << " = " << OpcodeToString(bin->opcode())
|
std::cout << " " << bin->name() << " = " << OpcodeToString(bin->opcode())
|
||||||
<< " " << TypeToString(*bin->lhs()->type()) << " "
|
<< " " << TypeToString(*bin->lhs()->type()) << " "
|
||||||
<< bin->lhs()->name() << ", " << bin->rhs()->name() << "\n";
|
<< ValueToString(bin->lhs()) << ", "
|
||||||
|
<< ValueToString(bin->rhs()) << "\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Opcode::Alloca: {
|
case Opcode::Alloca: {
|
||||||
@@ -71,19 +80,19 @@ void IRPrinter::Print(const Module& module) {
|
|||||||
case Opcode::Load: {
|
case Opcode::Load: {
|
||||||
auto* load = static_cast<const LoadInst*>(inst);
|
auto* load = static_cast<const LoadInst*>(inst);
|
||||||
std::cout << " " << load->name() << " = load i32, i32* "
|
std::cout << " " << load->name() << " = load i32, i32* "
|
||||||
<< load->ptr()->name() << "\n";
|
<< ValueToString(load->ptr()) << "\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Opcode::Store: {
|
case Opcode::Store: {
|
||||||
auto* store = static_cast<const StoreInst*>(inst);
|
auto* store = static_cast<const StoreInst*>(inst);
|
||||||
std::cout << " store i32 " << store->value()->name() << ", i32* "
|
std::cout << " store i32 " << ValueToString(store->value()) << ", i32* "
|
||||||
<< store->ptr()->name() << "\n";
|
<< ValueToString(store->ptr()) << "\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Opcode::Ret: {
|
case Opcode::Ret: {
|
||||||
auto* ret = static_cast<const ReturnInst*>(inst);
|
auto* ret = static_cast<const ReturnInst*>(inst);
|
||||||
std::cout << " ret " << TypeToString(*ret->value()->type()) << " "
|
std::cout << " ret " << TypeToString(*ret->value()->type()) << " "
|
||||||
<< ret->value()->name() << "\n";
|
<< ValueToString(ret->value()) << "\n";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,40 @@
|
|||||||
// - 指令操作数与结果类型管理,支持打印与优化
|
// - 指令操作数与结果类型管理,支持打印与优化
|
||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace ir {
|
namespace ir {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
bool IsArithmeticType(const std::shared_ptr<Type>& ty) {
|
||||||
|
return ty && ty->kind() == Type::Kind::Int32;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPtrInt32Type(const std::shared_ptr<Type>& ty) {
|
||||||
|
return ty && ty->kind() == Type::Kind::PtrInt32;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
BinaryInst::BinaryInst(Opcode op, std::shared_ptr<Type> ty, Value* lhs,
|
BinaryInst::BinaryInst(Opcode op, std::shared_ptr<Type> ty, Value* lhs,
|
||||||
Value* rhs, std::string name)
|
Value* rhs, std::string name)
|
||||||
: Instruction(op, std::move(ty), std::move(name)), lhs_(lhs), rhs_(rhs) {
|
: Instruction(op, std::move(ty), std::move(name)), lhs_(lhs), rhs_(rhs) {
|
||||||
|
if (op != Opcode::Add) {
|
||||||
|
throw std::runtime_error("BinaryInst 当前只支持 Add");
|
||||||
|
}
|
||||||
|
if (!lhs_ || !rhs_) {
|
||||||
|
throw std::runtime_error("BinaryInst 缺少操作数");
|
||||||
|
}
|
||||||
|
if (!type_ || !lhs_->type() || !rhs_->type()) {
|
||||||
|
throw std::runtime_error("BinaryInst 缺少类型信息");
|
||||||
|
}
|
||||||
|
if (lhs_->type()->kind() != rhs_->type()->kind() ||
|
||||||
|
type_->kind() != lhs_->type()->kind()) {
|
||||||
|
throw std::runtime_error("BinaryInst 类型不匹配");
|
||||||
|
}
|
||||||
|
if (!IsArithmeticType(type_)) {
|
||||||
|
throw std::runtime_error("BinaryInst 当前只支持 i32");
|
||||||
|
}
|
||||||
if (lhs_) {
|
if (lhs_) {
|
||||||
lhs_->AddUser(this);
|
lhs_->AddUser(this);
|
||||||
}
|
}
|
||||||
@@ -18,9 +47,10 @@ BinaryInst::BinaryInst(Opcode op, std::shared_ptr<Type> ty, Value* lhs,
|
|||||||
|
|
||||||
ReturnInst::ReturnInst(Value* val)
|
ReturnInst::ReturnInst(Value* val)
|
||||||
: Instruction(Opcode::Ret, Type::Void(), ""), value_(val) {
|
: Instruction(Opcode::Ret, Type::Void(), ""), value_(val) {
|
||||||
if (value_) {
|
if (!value_) {
|
||||||
value_->AddUser(this);
|
throw std::runtime_error("ReturnInst 缺少返回值");
|
||||||
}
|
}
|
||||||
|
value_->AddUser(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
AllocaInst::AllocaInst(std::string name)
|
AllocaInst::AllocaInst(std::string name)
|
||||||
@@ -28,19 +58,31 @@ AllocaInst::AllocaInst(std::string name)
|
|||||||
|
|
||||||
LoadInst::LoadInst(Value* ptr, std::string name)
|
LoadInst::LoadInst(Value* ptr, std::string name)
|
||||||
: Instruction(Opcode::Load, Type::Int32(), std::move(name)), ptr_(ptr) {
|
: Instruction(Opcode::Load, Type::Int32(), std::move(name)), ptr_(ptr) {
|
||||||
if (ptr_) {
|
if (!ptr_) {
|
||||||
ptr_->AddUser(this);
|
throw std::runtime_error("LoadInst 缺少 ptr");
|
||||||
}
|
}
|
||||||
|
if (!IsPtrInt32Type(ptr_->type())) {
|
||||||
|
throw std::runtime_error("LoadInst 当前只支持从 i32* 加载");
|
||||||
|
}
|
||||||
|
ptr_->AddUser(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
StoreInst::StoreInst(Value* val, Value* ptr)
|
StoreInst::StoreInst(Value* val, Value* ptr)
|
||||||
: Instruction(Opcode::Store, Type::Void(), ""), value_(val), ptr_(ptr) {
|
: Instruction(Opcode::Store, Type::Void(), ""), value_(val), ptr_(ptr) {
|
||||||
if (value_) {
|
if (!value_) {
|
||||||
|
throw std::runtime_error("StoreInst 缺少 value");
|
||||||
|
}
|
||||||
|
if (!ptr_) {
|
||||||
|
throw std::runtime_error("StoreInst 缺少 ptr");
|
||||||
|
}
|
||||||
|
if (!IsArithmeticType(value_->type())) {
|
||||||
|
throw std::runtime_error("StoreInst 当前只支持存储 i32");
|
||||||
|
}
|
||||||
|
if (!IsPtrInt32Type(ptr_->type())) {
|
||||||
|
throw std::runtime_error("StoreInst 当前只支持写入 i32*");
|
||||||
|
}
|
||||||
value_->AddUser(this);
|
value_->AddUser(this);
|
||||||
}
|
|
||||||
if (ptr_) {
|
|
||||||
ptr_->AddUser(this);
|
ptr_->AddUser(this);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
|
|||||||
@@ -6,10 +6,6 @@
|
|||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
|
|
||||||
bool IRGenImpl::GenStmt(SysYParser::StmtContext& stmt) {
|
bool IRGenImpl::GenStmt(SysYParser::StmtContext& stmt) {
|
||||||
if (stmt.varDecl()) {
|
|
||||||
GenVarDecl(*stmt.varDecl());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (stmt.returnStmt()) {
|
if (stmt.returnStmt()) {
|
||||||
GenReturnStmt(*stmt.returnStmt());
|
GenReturnStmt(*stmt.returnStmt());
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
Reference in New Issue
Block a user