parent
f34f36ed59
commit
3a49f8e131
@ -1,30 +1,77 @@
|
||||
// 基于语法树的语义检查与名称绑定。
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "SysYParser.h"
|
||||
#include "ir/IR.h"
|
||||
|
||||
enum class SymbolKind { Object, Function };
|
||||
|
||||
struct ConstantData {
|
||||
enum class Kind { Int, Float };
|
||||
|
||||
Kind kind = Kind::Int;
|
||||
int int_value = 0;
|
||||
float float_value = 0.0f;
|
||||
|
||||
static ConstantData FromInt(int value);
|
||||
static ConstantData FromFloat(float value);
|
||||
|
||||
bool IsInt() const { return kind == Kind::Int; }
|
||||
bool IsFloat() const { return kind == Kind::Float; }
|
||||
int AsInt() const;
|
||||
float AsFloat() const;
|
||||
ConstantData CastTo(const std::shared_ptr<ir::Type>& dst_type) const;
|
||||
std::shared_ptr<ir::Type> GetType() const;
|
||||
};
|
||||
|
||||
struct SymbolInfo {
|
||||
std::string name;
|
||||
SymbolKind kind = SymbolKind::Object;
|
||||
std::shared_ptr<ir::Type> type;
|
||||
bool is_const = false;
|
||||
bool is_global = false;
|
||||
bool is_parameter = false;
|
||||
bool is_array_parameter = false;
|
||||
bool is_builtin = false;
|
||||
|
||||
SysYParser::ConstDefContext* const_def = nullptr;
|
||||
SysYParser::VarDefContext* var_def = nullptr;
|
||||
SysYParser::FuncDefContext* func_def = nullptr;
|
||||
|
||||
bool has_const_value = false;
|
||||
ConstantData const_value{};
|
||||
};
|
||||
|
||||
class SemanticContext {
|
||||
public:
|
||||
void BindVarUse(SysYParser::LValContext* use,
|
||||
SysYParser::VarDefContext* decl) {
|
||||
var_uses_[use] = decl;
|
||||
}
|
||||
SymbolInfo* CreateSymbol(SymbolInfo symbol);
|
||||
|
||||
void BindConstDef(SysYParser::ConstDefContext* node, const SymbolInfo* symbol);
|
||||
void BindVarDef(SysYParser::VarDefContext* node, const SymbolInfo* symbol);
|
||||
void BindFuncDef(SysYParser::FuncDefContext* node, const SymbolInfo* symbol);
|
||||
void BindLVal(SysYParser::LValContext* node, const SymbolInfo* symbol);
|
||||
void BindCall(SysYParser::UnaryExpContext* node, const SymbolInfo* symbol);
|
||||
void SetExprType(const void* node, std::shared_ptr<ir::Type> type);
|
||||
|
||||
SysYParser::VarDefContext* ResolveVarUse(
|
||||
const SysYParser::LValContext* use) const {
|
||||
auto it = var_uses_.find(use);
|
||||
return it == var_uses_.end() ? nullptr : it->second;
|
||||
}
|
||||
const SymbolInfo* ResolveConstDef(const SysYParser::ConstDefContext* node) const;
|
||||
const SymbolInfo* ResolveVarDef(const SysYParser::VarDefContext* node) const;
|
||||
const SymbolInfo* ResolveFuncDef(const SysYParser::FuncDefContext* node) const;
|
||||
const SymbolInfo* ResolveLVal(const SysYParser::LValContext* node) const;
|
||||
const SymbolInfo* ResolveCall(const SysYParser::UnaryExpContext* node) const;
|
||||
std::shared_ptr<ir::Type> ResolveExprType(const void* node) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<const SysYParser::LValContext*,
|
||||
SysYParser::VarDefContext*>
|
||||
var_uses_;
|
||||
std::vector<std::unique_ptr<SymbolInfo>> owned_symbols_;
|
||||
std::unordered_map<const SysYParser::ConstDefContext*, const SymbolInfo*> const_defs_;
|
||||
std::unordered_map<const SysYParser::VarDefContext*, const SymbolInfo*> var_defs_;
|
||||
std::unordered_map<const SysYParser::FuncDefContext*, const SymbolInfo*> func_defs_;
|
||||
std::unordered_map<const SysYParser::LValContext*, const SymbolInfo*> lvals_;
|
||||
std::unordered_map<const SysYParser::UnaryExpContext*, const SymbolInfo*> calls_;
|
||||
std::unordered_map<const void*, std::shared_ptr<ir::Type>> expr_types_;
|
||||
};
|
||||
|
||||
// 目前仅检查:
|
||||
// - 变量先声明后使用
|
||||
// - 局部变量不允许重复定义
|
||||
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit);
|
||||
|
||||
@ -1,17 +1,20 @@
|
||||
// 极简符号表:记录局部变量定义点。
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "SysYParser.h"
|
||||
#include "sem/Sema.h"
|
||||
|
||||
class SymbolTable {
|
||||
public:
|
||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
||||
bool Contains(const std::string& name) const;
|
||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
||||
void EnterScope();
|
||||
void ExitScope();
|
||||
|
||||
bool Declare(const std::string& name, const SymbolInfo* symbol);
|
||||
const SymbolInfo* Lookup(const std::string& name) const;
|
||||
const SymbolInfo* LookupCurrent(const std::string& name) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
||||
std::vector<std::unordered_map<std::string, const SymbolInfo*>> scopes_;
|
||||
};
|
||||
|
||||
@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
shopt -s nullglob
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BUILD_DIR="$ROOT_DIR/build"
|
||||
ANTLR_DIR="$BUILD_DIR/generated/antlr4"
|
||||
JAR_PATH="$ROOT_DIR/third_party/antlr-4.13.2-complete.jar"
|
||||
GRAMMAR_PATH="$ROOT_DIR/src/antlr4/SysY.g4"
|
||||
OUT_ROOT="$ROOT_DIR/test/test_result/lab2_ir_batch"
|
||||
|
||||
RUN_FUNCTIONAL=true
|
||||
RUN_PERFORMANCE=true
|
||||
DO_BUILD=true
|
||||
|
||||
functional_total=0
|
||||
functional_passed=0
|
||||
functional_failed=0
|
||||
performance_total=0
|
||||
performance_passed=0
|
||||
performance_failed=0
|
||||
failed_cases=()
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: ./solution/run_lab2_batch.sh [options]
|
||||
|
||||
Options:
|
||||
--no-build Skip ANTLR generation and project rebuild
|
||||
--functional-only Run only test/test_case/functional/*.sy
|
||||
--performance-only Run only test/test_case/performance/*.sy
|
||||
--output-dir <dir> Set output directory for generated IR and logs
|
||||
--help Show this help message
|
||||
EOF
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
local total passed failed
|
||||
total=$((functional_total + performance_total))
|
||||
passed=$((functional_passed + performance_passed))
|
||||
failed=$((functional_failed + performance_failed))
|
||||
|
||||
echo
|
||||
echo "Summary:"
|
||||
echo " Functional cases: total=$functional_total, passed=$functional_passed, failed=$functional_failed"
|
||||
echo " Performance cases: total=$performance_total, passed=$performance_passed, failed=$performance_failed"
|
||||
echo " Overall: total=$total, passed=$passed, failed=$failed"
|
||||
|
||||
if (( ${#failed_cases[@]} > 0 )); then
|
||||
echo "Failed cases:"
|
||||
printf ' - %s\n' "${failed_cases[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_case() {
|
||||
local case_file=$1
|
||||
local group=$2
|
||||
local stem out_dir log_file
|
||||
|
||||
stem="$(basename "${case_file%.sy}")"
|
||||
out_dir="$OUT_ROOT/$group"
|
||||
log_file="$out_dir/$stem.verify.log"
|
||||
mkdir -p "$out_dir"
|
||||
|
||||
if [[ "$group" == "functional" ]]; then
|
||||
((functional_total += 1))
|
||||
else
|
||||
((performance_total += 1))
|
||||
fi
|
||||
|
||||
if ./scripts/verify_ir.sh "$case_file" "$out_dir" --run >"$log_file" 2>&1; then
|
||||
echo "PASS: $case_file"
|
||||
if [[ "$group" == "functional" ]]; then
|
||||
((functional_passed += 1))
|
||||
else
|
||||
((performance_passed += 1))
|
||||
fi
|
||||
else
|
||||
echo "FAIL: $case_file"
|
||||
cat "$log_file"
|
||||
if [[ "$group" == "functional" ]]; then
|
||||
((functional_failed += 1))
|
||||
else
|
||||
((performance_failed += 1))
|
||||
fi
|
||||
failed_cases+=("$case_file")
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--no-build)
|
||||
DO_BUILD=false
|
||||
;;
|
||||
--functional-only)
|
||||
RUN_FUNCTIONAL=true
|
||||
RUN_PERFORMANCE=false
|
||||
;;
|
||||
--performance-only)
|
||||
RUN_FUNCTIONAL=false
|
||||
RUN_PERFORMANCE=true
|
||||
;;
|
||||
--output-dir)
|
||||
shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "Missing value for --output-dir" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
if [[ "$1" = /* ]]; then
|
||||
OUT_ROOT="$1"
|
||||
else
|
||||
OUT_ROOT="$ROOT_DIR/$1"
|
||||
fi
|
||||
;;
|
||||
--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [[ "$RUN_FUNCTIONAL" == false && "$RUN_PERFORMANCE" == false ]]; then
|
||||
echo "No test set selected." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$DO_BUILD" == true ]]; then
|
||||
echo "[1/4] Generating ANTLR sources..."
|
||||
mkdir -p "$ANTLR_DIR"
|
||||
java -jar "$JAR_PATH" \
|
||||
-Dlanguage=Cpp \
|
||||
-visitor -no-listener \
|
||||
-Xexact-output-dir \
|
||||
-o "$ANTLR_DIR" \
|
||||
"$GRAMMAR_PATH"
|
||||
|
||||
echo "[2/4] Configuring CMake..."
|
||||
cmake -S "$ROOT_DIR" -B "$BUILD_DIR" -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF
|
||||
|
||||
echo "[3/4] Building project..."
|
||||
cmake --build "$BUILD_DIR" -j "$(nproc)"
|
||||
fi
|
||||
|
||||
echo "[4/4] Running IR batch tests..."
|
||||
|
||||
if [[ "$RUN_FUNCTIONAL" == true ]]; then
|
||||
for case_file in "$ROOT_DIR"/test/test_case/functional/*.sy; do
|
||||
run_case "$case_file" "functional"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "$RUN_PERFORMANCE" == true ]]; then
|
||||
for case_file in "$ROOT_DIR"/test/test_case/performance/*.sy; do
|
||||
run_case "$case_file" "performance"
|
||||
done
|
||||
fi
|
||||
|
||||
print_summary
|
||||
|
||||
if (( functional_failed + performance_failed > 0 )); then
|
||||
echo "Batch test finished with failures."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Batch test passed."
|
||||
@ -1,11 +1,19 @@
|
||||
// GlobalValue 占位实现:
|
||||
// - 具体的全局初始化器、打印和链接语义需要自行补全
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
namespace ir {
|
||||
|
||||
GlobalValue::GlobalValue(std::shared_ptr<Type> ty, std::string name)
|
||||
: User(std::move(ty), std::move(name)) {}
|
||||
: Value(std::move(ty), std::move(name)) {}
|
||||
|
||||
GlobalVariable::GlobalVariable(std::string name, std::shared_ptr<Type> value_type,
|
||||
ConstantValue* initializer, bool is_constant)
|
||||
: GlobalValue(Type::GetPointerType(value_type), std::move(name)),
|
||||
value_type_(std::move(value_type)),
|
||||
initializer_(initializer),
|
||||
is_constant_(is_constant) {}
|
||||
|
||||
Argument::Argument(std::shared_ptr<Type> ty, std::string name, size_t index,
|
||||
Function* parent)
|
||||
: Value(std::move(ty), std::move(name)), index_(index), parent_(parent) {}
|
||||
|
||||
} // namespace ir
|
||||
|
||||
@ -1,89 +1,178 @@
|
||||
// IR 构建工具:
|
||||
// - 管理插入点(当前基本块/位置)
|
||||
// - 提供创建各类指令的便捷接口,降低 IRGen 复杂度
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "utils/Log.h"
|
||||
|
||||
namespace ir {
|
||||
IRBuilder::IRBuilder(Context& ctx, BasicBlock* bb)
|
||||
: ctx_(ctx), insert_block_(bb) {}
|
||||
namespace {
|
||||
|
||||
void RequireInsertBlock(BasicBlock* bb) {
|
||||
if (!bb) {
|
||||
throw std::runtime_error("IRBuilder 未设置插入点");
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> InferLoadType(Value* ptr) {
|
||||
if (!ptr || !ptr->GetType() || !ptr->GetType()->IsPointer()) {
|
||||
throw std::runtime_error("CreateLoad 需要指针");
|
||||
}
|
||||
return ptr->GetType()->GetElementType();
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> InferGEPResultType(Value* base_ptr,
|
||||
const std::vector<Value*>& indices) {
|
||||
if (!base_ptr || !base_ptr->GetType() || !base_ptr->GetType()->IsPointer()) {
|
||||
throw std::runtime_error("CreateGEP 需要指针基址");
|
||||
}
|
||||
auto current = base_ptr->GetType()->GetElementType();
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
auto* index = indices[i];
|
||||
(void)index;
|
||||
if (!current) {
|
||||
throw std::runtime_error("CreateGEP 遇到空类型");
|
||||
}
|
||||
if (i == 0) {
|
||||
continue;
|
||||
}
|
||||
if (current->IsArray()) {
|
||||
current = current->GetElementType();
|
||||
continue;
|
||||
}
|
||||
if (current->IsPointer()) {
|
||||
current = current->GetElementType();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Type::GetPointerType(current);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
IRBuilder::IRBuilder(Context& ctx, BasicBlock* bb) : ctx_(ctx), insert_block_(bb) {}
|
||||
|
||||
void IRBuilder::SetInsertPoint(BasicBlock* bb) { insert_block_ = bb; }
|
||||
|
||||
BasicBlock* IRBuilder::GetInsertBlock() const { return insert_block_; }
|
||||
|
||||
ConstantInt* IRBuilder::CreateConstInt(int v) {
|
||||
// 常量不需要挂在基本块里,由 Context 负责去重与生命周期。
|
||||
return ctx_.GetConstInt(v);
|
||||
ConstantInt* IRBuilder::CreateConstInt(int v) { return ctx_.GetConstInt(v); }
|
||||
|
||||
ConstantFloat* IRBuilder::CreateConstFloat(float v) { return ctx_.GetConstFloat(v); }
|
||||
|
||||
ConstantValue* IRBuilder::CreateZero(std::shared_ptr<Type> type) {
|
||||
if (!type) {
|
||||
throw std::runtime_error("CreateZero 缺少类型");
|
||||
}
|
||||
if (type->IsInt1() || type->IsInt32()) {
|
||||
return CreateConstInt(0);
|
||||
}
|
||||
if (type->IsFloat32()) {
|
||||
return CreateConstFloat(0.0f);
|
||||
}
|
||||
return ctx_.CreateOwnedConstant<ConstantZero>(type);
|
||||
}
|
||||
|
||||
BinaryInst* IRBuilder::CreateBinary(Opcode op, Value* lhs, Value* rhs,
|
||||
const std::string& name) {
|
||||
if (!insert_block_) {
|
||||
throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
|
||||
}
|
||||
if (!lhs) {
|
||||
throw std::runtime_error(
|
||||
FormatError("ir", "IRBuilder::CreateBinary 缺少 lhs"));
|
||||
}
|
||||
if (!rhs) {
|
||||
throw std::runtime_error(
|
||||
FormatError("ir", "IRBuilder::CreateBinary 缺少 rhs"));
|
||||
RequireInsertBlock(insert_block_);
|
||||
if (!lhs || !rhs) {
|
||||
throw std::runtime_error("CreateBinary 缺少操作数");
|
||||
}
|
||||
return insert_block_->Append<BinaryInst>(op, lhs->GetType(), lhs, rhs, name);
|
||||
}
|
||||
|
||||
BinaryInst* IRBuilder::CreateAdd(Value* lhs, Value* rhs,
|
||||
const std::string& name) {
|
||||
return CreateBinary(Opcode::Add, lhs, rhs, name);
|
||||
AllocaInst* IRBuilder::CreateAlloca(std::shared_ptr<Type> allocated_type,
|
||||
const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
auto* parent = insert_block_->GetParent();
|
||||
if (!parent || !parent->GetEntry()) {
|
||||
throw std::runtime_error("CreateAlloca 需要所在函数入口块");
|
||||
}
|
||||
return parent->GetEntry()->Append<AllocaInst>(std::move(allocated_type), name);
|
||||
}
|
||||
|
||||
AllocaInst* IRBuilder::CreateAllocaI32(const std::string& name) {
|
||||
if (!insert_block_) {
|
||||
throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
|
||||
}
|
||||
return insert_block_->Append<AllocaInst>(Type::GetPtrInt32Type(), name);
|
||||
return CreateAlloca(Type::GetInt32Type(), name);
|
||||
}
|
||||
|
||||
LoadInst* IRBuilder::CreateLoad(Value* ptr, const std::string& name) {
|
||||
if (!insert_block_) {
|
||||
throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
|
||||
}
|
||||
if (!ptr) {
|
||||
throw std::runtime_error(
|
||||
FormatError("ir", "IRBuilder::CreateLoad 缺少 ptr"));
|
||||
}
|
||||
return insert_block_->Append<LoadInst>(Type::GetInt32Type(), ptr, name);
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<LoadInst>(ptr, InferLoadType(ptr), name);
|
||||
}
|
||||
|
||||
StoreInst* IRBuilder::CreateStore(Value* val, Value* ptr) {
|
||||
if (!insert_block_) {
|
||||
throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
|
||||
}
|
||||
if (!val) {
|
||||
throw std::runtime_error(
|
||||
FormatError("ir", "IRBuilder::CreateStore 缺少 val"));
|
||||
}
|
||||
if (!ptr) {
|
||||
throw std::runtime_error(
|
||||
FormatError("ir", "IRBuilder::CreateStore 缺少 ptr"));
|
||||
}
|
||||
return insert_block_->Append<StoreInst>(Type::GetVoidType(), val, ptr);
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<StoreInst>(val, ptr);
|
||||
}
|
||||
|
||||
ReturnInst* IRBuilder::CreateRet(Value* v) {
|
||||
if (!insert_block_) {
|
||||
throw std::runtime_error(FormatError("ir", "IRBuilder 未设置插入点"));
|
||||
}
|
||||
if (!v) {
|
||||
throw std::runtime_error(
|
||||
FormatError("ir", "IRBuilder::CreateRet 缺少返回值"));
|
||||
CompareInst* IRBuilder::CreateICmp(ICmpPred pred, Value* lhs, Value* rhs,
|
||||
const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<CompareInst>(pred, lhs, rhs, name);
|
||||
}
|
||||
|
||||
CompareInst* IRBuilder::CreateFCmp(FCmpPred pred, Value* lhs, Value* rhs,
|
||||
const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<CompareInst>(pred, lhs, rhs, name);
|
||||
}
|
||||
|
||||
BranchInst* IRBuilder::CreateBr(BasicBlock* target) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<BranchInst>(target);
|
||||
}
|
||||
|
||||
CondBranchInst* IRBuilder::CreateCondBr(Value* cond, BasicBlock* true_block,
|
||||
BasicBlock* false_block) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<CondBranchInst>(cond, true_block, false_block);
|
||||
}
|
||||
|
||||
CallInst* IRBuilder::CreateCall(Function* callee, const std::vector<Value*>& args,
|
||||
const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
std::string actual_name = name;
|
||||
if (callee && callee->GetReturnType()->IsVoid()) {
|
||||
actual_name.clear();
|
||||
}
|
||||
return insert_block_->Append<ReturnInst>(Type::GetVoidType(), v);
|
||||
return insert_block_->Append<CallInst>(callee, args, actual_name);
|
||||
}
|
||||
|
||||
GetElementPtrInst* IRBuilder::CreateGEP(Value* base_ptr,
|
||||
const std::vector<Value*>& indices,
|
||||
const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<GetElementPtrInst>(
|
||||
base_ptr, indices, InferGEPResultType(base_ptr, indices), name);
|
||||
}
|
||||
|
||||
CastInst* IRBuilder::CreateSIToFP(Value* value, const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<CastInst>(Opcode::SIToFP, value,
|
||||
Type::GetFloatType(), name);
|
||||
}
|
||||
|
||||
CastInst* IRBuilder::CreateFPToSI(Value* value, const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<CastInst>(Opcode::FPToSI, value,
|
||||
Type::GetInt32Type(), name);
|
||||
}
|
||||
|
||||
CastInst* IRBuilder::CreateZExt(Value* value, std::shared_ptr<Type> dst_type,
|
||||
const std::string& name) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<CastInst>(Opcode::ZExt, value, std::move(dst_type),
|
||||
name);
|
||||
}
|
||||
|
||||
ReturnInst* IRBuilder::CreateRet(Value* value) {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return value ? insert_block_->Append<ReturnInst>(value)
|
||||
: insert_block_->Append<ReturnInst>();
|
||||
}
|
||||
|
||||
ReturnInst* IRBuilder::CreateRetVoid() {
|
||||
RequireInsertBlock(insert_block_);
|
||||
return insert_block_->Append<ReturnInst>();
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
|
||||
@ -1,31 +1,141 @@
|
||||
// 当前仅支持 void、i32 和 i32*。
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ir {
|
||||
|
||||
Type::Type(Kind k) : kind_(k) {}
|
||||
Type::Type(Kind kind) : kind_(kind) {}
|
||||
|
||||
Type::Type(Kind kind, std::shared_ptr<Type> element_type)
|
||||
: kind_(kind), element_type_(std::move(element_type)) {}
|
||||
|
||||
Type::Type(Kind kind, std::shared_ptr<Type> element_type, size_t array_size)
|
||||
: kind_(kind),
|
||||
element_type_(std::move(element_type)),
|
||||
array_size_(array_size) {}
|
||||
|
||||
Type::Type(std::shared_ptr<Type> return_type,
|
||||
std::vector<std::shared_ptr<Type>> params)
|
||||
: kind_(Kind::Function),
|
||||
return_type_(std::move(return_type)),
|
||||
param_types_(std::move(params)) {}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetVoidType() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Void);
|
||||
static const auto type = std::make_shared<Type>(Kind::Void);
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetInt1Type() {
|
||||
static const auto type = std::make_shared<Type>(Kind::Int1);
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetInt32Type() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Int32);
|
||||
static const auto type = std::make_shared<Type>(Kind::Int32);
|
||||
return type;
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetFloatType() {
|
||||
static const auto type = std::make_shared<Type>(Kind::Float32);
|
||||
return type;
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> Type::GetPointerType(std::shared_ptr<Type> element_type) {
|
||||
if (!element_type) {
|
||||
throw std::runtime_error("GetPointerType 缺少 element_type");
|
||||
}
|
||||
return std::make_shared<Type>(Kind::Pointer, std::move(element_type));
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> Type::GetArrayType(std::shared_ptr<Type> element_type,
|
||||
size_t array_size) {
|
||||
if (!element_type) {
|
||||
throw std::runtime_error("GetArrayType 缺少 element_type");
|
||||
}
|
||||
return std::make_shared<Type>(Kind::Array, std::move(element_type), array_size);
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> Type::GetFunctionType(
|
||||
std::shared_ptr<Type> return_type,
|
||||
std::vector<std::shared_ptr<Type>> param_types) {
|
||||
if (!return_type) {
|
||||
throw std::runtime_error("GetFunctionType 缺少 return_type");
|
||||
}
|
||||
return std::make_shared<Type>(std::move(return_type), std::move(param_types));
|
||||
}
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetPtrInt32Type() {
|
||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::PtrInt32);
|
||||
static const auto type = GetPointerType(GetInt32Type());
|
||||
return type;
|
||||
}
|
||||
|
||||
Type::Kind Type::GetKind() const { return kind_; }
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetElementType() const { return element_type_; }
|
||||
|
||||
size_t Type::GetArraySize() const { return array_size_; }
|
||||
|
||||
const std::shared_ptr<Type>& Type::GetReturnType() const { return return_type_; }
|
||||
|
||||
const std::vector<std::shared_ptr<Type>>& Type::GetParamTypes() const {
|
||||
return param_types_;
|
||||
}
|
||||
|
||||
bool Type::IsVoid() const { return kind_ == Kind::Void; }
|
||||
|
||||
bool Type::IsInt1() const { return kind_ == Kind::Int1; }
|
||||
|
||||
bool Type::IsInt32() const { return kind_ == Kind::Int32; }
|
||||
|
||||
bool Type::IsPtrInt32() const { return kind_ == Kind::PtrInt32; }
|
||||
bool Type::IsFloat32() const { return kind_ == Kind::Float32; }
|
||||
|
||||
bool Type::IsPointer() const { return kind_ == Kind::Pointer; }
|
||||
|
||||
bool Type::IsArray() const { return kind_ == Kind::Array; }
|
||||
|
||||
bool Type::IsFunction() const { return kind_ == Kind::Function; }
|
||||
|
||||
bool Type::IsScalar() const { return IsInt1() || IsInt32() || IsFloat32(); }
|
||||
|
||||
bool Type::IsInteger() const { return IsInt1() || IsInt32(); }
|
||||
|
||||
bool Type::IsNumeric() const { return IsInteger() || IsFloat32(); }
|
||||
|
||||
bool Type::IsPtrInt32() const {
|
||||
return IsPointer() && element_type_ && element_type_->IsInt32();
|
||||
}
|
||||
|
||||
bool Type::Equals(const Type& other) const {
|
||||
if (kind_ != other.kind_) {
|
||||
return false;
|
||||
}
|
||||
switch (kind_) {
|
||||
case Kind::Void:
|
||||
case Kind::Int1:
|
||||
case Kind::Int32:
|
||||
case Kind::Float32:
|
||||
return true;
|
||||
case Kind::Pointer:
|
||||
return element_type_ && other.element_type_ &&
|
||||
element_type_->Equals(*other.element_type_);
|
||||
case Kind::Array:
|
||||
return array_size_ == other.array_size_ && element_type_ &&
|
||||
other.element_type_ && element_type_->Equals(*other.element_type_);
|
||||
case Kind::Function:
|
||||
if (!return_type_ || !other.return_type_ ||
|
||||
!return_type_->Equals(*other.return_type_) ||
|
||||
param_types_.size() != other.param_types_.size()) {
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 0; i < param_types_.size(); ++i) {
|
||||
if (!param_types_[i] || !other.param_types_[i] ||
|
||||
!param_types_[i]->Equals(*other.param_types_[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ir
|
||||
|
||||
@ -1,69 +1,479 @@
|
||||
#include "irgen/IRGen.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "SysYParser.h"
|
||||
#include "ir/IR.h"
|
||||
#include "utils/Log.h"
|
||||
|
||||
void IRGenImpl::GenBlock(SysYParser::BlockContext& block) {
|
||||
for (auto* item : block.blockItem()) {
|
||||
if (item) {
|
||||
if (GenBlockItem(*item)) {
|
||||
// 当前语法要求 return 为块内最后一条语句;命中后可停止生成。
|
||||
break;
|
||||
namespace {
|
||||
|
||||
using ir::Type;
|
||||
|
||||
size_t ScalarCount(const std::shared_ptr<Type>& type) {
|
||||
return type->IsArray() ? type->GetArraySize() * ScalarCount(type->GetElementType()) : 1;
|
||||
}
|
||||
|
||||
std::shared_ptr<Type> ScalarLeafType(const std::shared_ptr<Type>& type) {
|
||||
auto current = type;
|
||||
while (current->IsArray()) {
|
||||
current = current->GetElementType();
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
ConstantData ZeroForType(const std::shared_ptr<Type>& type) {
|
||||
return type->IsFloat32() ? ConstantData::FromFloat(0.0f)
|
||||
: ConstantData::FromInt(0);
|
||||
}
|
||||
|
||||
ConstantData ParseNumberValue(const std::string& text) {
|
||||
if (text.find_first_of(".pPeE") == std::string::npos) {
|
||||
return ConstantData::FromInt(static_cast<int>(std::strtoll(text.c_str(), nullptr, 0)));
|
||||
}
|
||||
return ConstantData::FromFloat(std::strtof(text.c_str(), nullptr));
|
||||
}
|
||||
|
||||
bool SameType(const std::shared_ptr<Type>& lhs, const std::shared_ptr<Type>& rhs) {
|
||||
return lhs && rhs && lhs->Equals(*rhs);
|
||||
}
|
||||
|
||||
ConstantData EvalGlobalConstAddExp(
|
||||
SysYParser::AddExpContext& add,
|
||||
const std::unordered_map<std::string, ConstantData>& const_values);
|
||||
|
||||
ConstantData EvalGlobalConstPrimary(
|
||||
SysYParser::PrimaryContext& primary,
|
||||
const std::unordered_map<std::string, ConstantData>& const_values) {
|
||||
if (primary.Number()) {
|
||||
return ParseNumberValue(primary.Number()->getText());
|
||||
}
|
||||
if (primary.exp()) {
|
||||
return EvalGlobalConstAddExp(*primary.exp()->addExp(), const_values);
|
||||
}
|
||||
if (primary.lVal() && primary.lVal()->Ident() && primary.lVal()->exp().empty()) {
|
||||
auto found = const_values.find(primary.lVal()->Ident()->getText());
|
||||
if (found == const_values.end()) {
|
||||
throw std::runtime_error(
|
||||
FormatError("irgen", "全局初始化器引用了非常量符号: " +
|
||||
primary.lVal()->Ident()->getText()));
|
||||
}
|
||||
return found->second;
|
||||
}
|
||||
throw std::runtime_error(
|
||||
FormatError("irgen", "全局初始化器暂不支持该常量表达式"));
|
||||
}
|
||||
|
||||
ConstantData EvalGlobalConstUnaryExp(
|
||||
SysYParser::UnaryExpContext& unary,
|
||||
const std::unordered_map<std::string, ConstantData>& const_values) {
|
||||
if (unary.primary()) {
|
||||
return EvalGlobalConstPrimary(*unary.primary(), const_values);
|
||||
}
|
||||
if (unary.unaryExp()) {
|
||||
ConstantData value = EvalGlobalConstUnaryExp(*unary.unaryExp(), const_values);
|
||||
const std::string op = unary.unaryOp()->getText();
|
||||
if (op == "+") {
|
||||
return value;
|
||||
}
|
||||
if (op == "-") {
|
||||
return value.IsFloat() ? ConstantData::FromFloat(-value.AsFloat())
|
||||
: ConstantData::FromInt(-value.AsInt());
|
||||
}
|
||||
if (op == "!") {
|
||||
return ConstantData::FromInt(value.IsFloat() ? (value.AsFloat() == 0.0f)
|
||||
: (value.AsInt() == 0));
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(FormatError("irgen", "全局初始化器不支持函数调用"));
|
||||
}
|
||||
|
||||
ConstantData EvalGlobalConstMulExp(
|
||||
SysYParser::MulExpContext& mul,
|
||||
const std::unordered_map<std::string, ConstantData>& const_values) {
|
||||
ConstantData acc = EvalGlobalConstUnaryExp(*mul.unaryExp(0), const_values);
|
||||
for (size_t i = 1; i < mul.unaryExp().size(); ++i) {
|
||||
ConstantData rhs = EvalGlobalConstUnaryExp(*mul.unaryExp(i), const_values);
|
||||
const std::string op = mul.children[2 * i - 1]->getText();
|
||||
if (op == "%") {
|
||||
acc = ConstantData::FromInt(acc.AsInt() % rhs.AsInt());
|
||||
continue;
|
||||
}
|
||||
auto result_type =
|
||||
(acc.GetType()->IsFloat32() || rhs.GetType()->IsFloat32()) ? Type::GetFloatType()
|
||||
: Type::GetInt32Type();
|
||||
acc = acc.CastTo(result_type);
|
||||
rhs = rhs.CastTo(result_type);
|
||||
if (result_type->IsFloat32()) {
|
||||
float value = op == "*" ? acc.AsFloat() * rhs.AsFloat()
|
||||
: acc.AsFloat() / rhs.AsFloat();
|
||||
acc = ConstantData::FromFloat(value);
|
||||
} else {
|
||||
int value = op == "*" ? acc.AsInt() * rhs.AsInt()
|
||||
: acc.AsInt() / rhs.AsInt();
|
||||
acc = ConstantData::FromInt(value);
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
ConstantData EvalGlobalConstAddExp(
|
||||
SysYParser::AddExpContext& add,
|
||||
const std::unordered_map<std::string, ConstantData>& const_values) {
|
||||
ConstantData acc = EvalGlobalConstMulExp(*add.mulExp(0), const_values);
|
||||
for (size_t i = 1; i < add.mulExp().size(); ++i) {
|
||||
ConstantData rhs = EvalGlobalConstMulExp(*add.mulExp(i), const_values);
|
||||
auto result_type =
|
||||
(acc.GetType()->IsFloat32() || rhs.GetType()->IsFloat32()) ? Type::GetFloatType()
|
||||
: Type::GetInt32Type();
|
||||
acc = acc.CastTo(result_type);
|
||||
rhs = rhs.CastTo(result_type);
|
||||
if (result_type->IsFloat32()) {
|
||||
float value = add.children[2 * i - 1]->getText() == "+"
|
||||
? acc.AsFloat() + rhs.AsFloat()
|
||||
: acc.AsFloat() - rhs.AsFloat();
|
||||
acc = ConstantData::FromFloat(value);
|
||||
} else {
|
||||
int value = add.children[2 * i - 1]->getText() == "+"
|
||||
? acc.AsInt() + rhs.AsInt()
|
||||
: acc.AsInt() - rhs.AsInt();
|
||||
acc = ConstantData::FromInt(value);
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
void FlattenInitValue(const std::shared_ptr<Type>& type, SysYParser::InitValContext& init,
|
||||
std::vector<SysYParser::InitValContext*>& leaves,
|
||||
size_t& cursor, size_t start) {
|
||||
if (!type->IsArray()) {
|
||||
if (cursor >= leaves.size()) {
|
||||
throw std::runtime_error(FormatError("irgen", "初始化器过长"));
|
||||
}
|
||||
leaves[cursor++] = &init;
|
||||
return;
|
||||
}
|
||||
if (init.exp()) {
|
||||
if (cursor >= leaves.size()) {
|
||||
throw std::runtime_error(FormatError("irgen", "初始化器过长"));
|
||||
}
|
||||
leaves[cursor++] = &init;
|
||||
return;
|
||||
}
|
||||
auto elem_type = type->GetElementType();
|
||||
const size_t elem_span = ScalarCount(elem_type);
|
||||
for (auto* child : init.initVal()) {
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
if (child->L_BRACE()) {
|
||||
size_t rel = cursor - start;
|
||||
if (rel % elem_span != 0) {
|
||||
cursor += elem_span - (rel % elem_span);
|
||||
}
|
||||
size_t child_start = cursor;
|
||||
FlattenInitValue(elem_type, *child, leaves, cursor, child_start);
|
||||
cursor = child_start + elem_span;
|
||||
} else {
|
||||
if (cursor >= leaves.size()) {
|
||||
throw std::runtime_error(FormatError("irgen", "初始化器过长"));
|
||||
}
|
||||
leaves[cursor++] = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool IRGenImpl::GenBlockItem(SysYParser::BlockItemContext& item) {
|
||||
if (item.decl()) {
|
||||
GenDecl(*item.decl());
|
||||
return false;
|
||||
void FlattenConstInitValue(const std::shared_ptr<Type>& type,
|
||||
SysYParser::ConstInitValContext& init,
|
||||
std::vector<SysYParser::ConstInitValContext*>& leaves,
|
||||
size_t& cursor, size_t start) {
|
||||
if (!type->IsArray()) {
|
||||
if (cursor >= leaves.size()) {
|
||||
throw std::runtime_error(FormatError("irgen", "初始化器过长"));
|
||||
}
|
||||
leaves[cursor++] = &init;
|
||||
return;
|
||||
}
|
||||
if (item.stmt()) {
|
||||
return GenStmt(*item.stmt());
|
||||
if (init.constExp()) {
|
||||
if (cursor >= leaves.size()) {
|
||||
throw std::runtime_error(FormatError("irgen", "初始化器过长"));
|
||||
}
|
||||
leaves[cursor++] = &init;
|
||||
return;
|
||||
}
|
||||
auto elem_type = type->GetElementType();
|
||||
const size_t elem_span = ScalarCount(elem_type);
|
||||
for (auto* child : init.constInitVal()) {
|
||||
if (!child) {
|
||||
continue;
|
||||
}
|
||||
if (child->L_BRACE()) {
|
||||
size_t rel = cursor - start;
|
||||
if (rel % elem_span != 0) {
|
||||
cursor += elem_span - (rel % elem_span);
|
||||
}
|
||||
size_t child_start = cursor;
|
||||
FlattenConstInitValue(elem_type, *child, leaves, cursor, child_start);
|
||||
cursor = child_start + elem_span;
|
||||
} else {
|
||||
if (cursor >= leaves.size()) {
|
||||
throw std::runtime_error(FormatError("irgen", "初始化器过长"));
|
||||
}
|
||||
leaves[cursor++] = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void IRGenImpl::GenGlobals(SysYParser::CompUnitContext& cu) {
|
||||
for (auto* decl : cu.decl()) {
|
||||
if (!decl) {
|
||||
continue;
|
||||
}
|
||||
if (decl->constDecl()) {
|
||||
for (auto* def : decl->constDecl()->constDef()) {
|
||||
auto* symbol = sema_.ResolveConstDef(def);
|
||||
auto* global = module_.CreateGlobal(
|
||||
symbol->name, symbol->type,
|
||||
BuildGlobalConstInitializer(symbol->type, def->constInitVal()), true);
|
||||
globals_[symbol->name] = {global, symbol->type, false, true, true};
|
||||
if (symbol->has_const_value) {
|
||||
global_const_values_[symbol->name] = symbol->const_value;
|
||||
}
|
||||
}
|
||||
} else if (decl->varDecl()) {
|
||||
for (auto* def : decl->varDecl()->varDef()) {
|
||||
auto* symbol = sema_.ResolveVarDef(def);
|
||||
auto* global =
|
||||
module_.CreateGlobal(symbol->name, symbol->type,
|
||||
BuildGlobalInitializer(symbol->type, def->initVal()), false);
|
||||
globals_[symbol->name] = {global, symbol->type, false, true, false};
|
||||
}
|
||||
}
|
||||
}
|
||||
throw std::runtime_error(FormatError("irgen", "暂不支持的语句或声明"));
|
||||
}
|
||||
|
||||
void IRGenImpl::GenDecl(SysYParser::DeclContext& decl) {
|
||||
if (decl.constDecl()) {
|
||||
GenConstDecl(*decl.constDecl());
|
||||
return;
|
||||
}
|
||||
if (decl.varDecl()) {
|
||||
GenVarDecl(*decl.varDecl());
|
||||
return;
|
||||
}
|
||||
throw std::runtime_error(FormatError("irgen", "暂不支持的声明类型"));
|
||||
throw std::runtime_error(FormatError("irgen", "未知声明类型"));
|
||||
}
|
||||
|
||||
void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
|
||||
if (!decl.bType() || !decl.bType()->Int()) {
|
||||
throw std::runtime_error(FormatError("irgen", "当前 IR 仅支持 int 标量局部变量"));
|
||||
void IRGenImpl::GenConstDecl(SysYParser::ConstDeclContext& decl) {
|
||||
for (auto* def : decl.constDef()) {
|
||||
auto* symbol = sema_.ResolveConstDef(def);
|
||||
if (!symbol) {
|
||||
throw std::runtime_error(FormatError("irgen", "const 声明缺少语义绑定"));
|
||||
}
|
||||
|
||||
auto* slot =
|
||||
builder_.CreateAlloca(symbol->type, module_.GetContext().NextTemp());
|
||||
if (symbol->type->IsArray()) {
|
||||
EmitLocalConstArrayInit(slot, symbol->type, *def->constInitVal());
|
||||
} else {
|
||||
ir::Value* value = GenAddExpr(*def->constInitVal()->constExp()->addExp());
|
||||
value = CastValue(value, symbol->type);
|
||||
builder_.CreateStore(value, slot);
|
||||
}
|
||||
DeclareLocal(symbol->name, {slot, symbol->type, false, false, true});
|
||||
}
|
||||
}
|
||||
|
||||
void IRGenImpl::GenVarDecl(SysYParser::VarDeclContext& decl) {
|
||||
for (auto* def : decl.varDef()) {
|
||||
if (!def) {
|
||||
continue;
|
||||
auto* symbol = sema_.ResolveVarDef(def);
|
||||
if (!symbol) {
|
||||
throw std::runtime_error(FormatError("irgen", "变量声明缺少语义绑定"));
|
||||
}
|
||||
if (storage_map_.find(def) != storage_map_.end()) {
|
||||
throw std::runtime_error(FormatError("irgen", "声明重复生成存储槽位"));
|
||||
|
||||
auto* slot =
|
||||
builder_.CreateAlloca(symbol->type, module_.GetContext().NextTemp());
|
||||
if (symbol->type->IsArray()) {
|
||||
if (def->initVal()) {
|
||||
EmitLocalArrayInit(slot, symbol->type, *def->initVal());
|
||||
}
|
||||
} else {
|
||||
ir::Value* init = symbol->type->IsFloat32()
|
||||
? static_cast<ir::Value*>(builder_.CreateConstFloat(0.0f))
|
||||
: static_cast<ir::Value*>(builder_.CreateConstInt(0));
|
||||
if (auto* init_val = def->initVal()) {
|
||||
init = GenExpr(*init_val->exp());
|
||||
init = CastValue(init, symbol->type);
|
||||
}
|
||||
builder_.CreateStore(init, slot);
|
||||
}
|
||||
if (!def->constExp().empty()) {
|
||||
throw std::runtime_error(
|
||||
FormatError("irgen", "当前 IR 仅支持 int 标量局部变量"));
|
||||
DeclareLocal(symbol->name, {slot, symbol->type, false, false, false});
|
||||
}
|
||||
}
|
||||
|
||||
void IRGenImpl::EmitArrayStore(ir::Value* base_ptr,
|
||||
const std::shared_ptr<Type>& array_type,
|
||||
size_t flat_index, ir::Value* value) {
|
||||
auto indices = FlatIndexToIndices(array_type, flat_index);
|
||||
std::vector<ir::Value*> gep_indices;
|
||||
gep_indices.push_back(builder_.CreateConstInt(0));
|
||||
for (int index : indices) {
|
||||
gep_indices.push_back(builder_.CreateConstInt(index));
|
||||
}
|
||||
auto* addr =
|
||||
builder_.CreateGEP(base_ptr, gep_indices, module_.GetContext().NextTemp());
|
||||
builder_.CreateStore(CastValue(value, addr->GetType()->GetElementType()), addr);
|
||||
}
|
||||
|
||||
void IRGenImpl::ZeroInitializeLocalArray(ir::Value* base_ptr,
|
||||
const std::shared_ptr<Type>& array_type) {
|
||||
const auto scalar_type = ScalarLeafType(array_type);
|
||||
for (size_t i = 0; i < CountScalars(array_type); ++i) {
|
||||
ir::Value* zero = scalar_type->IsFloat32()
|
||||
? static_cast<ir::Value*>(builder_.CreateConstFloat(0.0f))
|
||||
: static_cast<ir::Value*>(builder_.CreateConstInt(0));
|
||||
EmitArrayStore(base_ptr, array_type, i, zero);
|
||||
}
|
||||
}
|
||||
|
||||
void IRGenImpl::EmitLocalArrayInit(ir::Value* base_ptr,
|
||||
const std::shared_ptr<Type>& array_type,
|
||||
SysYParser::InitValContext& init) {
|
||||
ZeroInitializeLocalArray(base_ptr, array_type);
|
||||
std::vector<SysYParser::InitValContext*> leaves(CountScalars(array_type), nullptr);
|
||||
size_t cursor = 0;
|
||||
FlattenInitValue(array_type, init, leaves, cursor, 0);
|
||||
for (size_t i = 0; i < leaves.size(); ++i) {
|
||||
if (!leaves[i] || !leaves[i]->exp()) {
|
||||
continue;
|
||||
}
|
||||
EmitArrayStore(base_ptr, array_type, i, GenExpr(*leaves[i]->exp()));
|
||||
}
|
||||
}
|
||||
|
||||
void IRGenImpl::EmitLocalConstArrayInit(ir::Value* base_ptr,
|
||||
const std::shared_ptr<Type>& array_type,
|
||||
SysYParser::ConstInitValContext& init) {
|
||||
ZeroInitializeLocalArray(base_ptr, array_type);
|
||||
std::vector<SysYParser::ConstInitValContext*> leaves(CountScalars(array_type),
|
||||
nullptr);
|
||||
size_t cursor = 0;
|
||||
FlattenConstInitValue(array_type, init, leaves, cursor, 0);
|
||||
for (size_t i = 0; i < leaves.size(); ++i) {
|
||||
if (!leaves[i] || !leaves[i]->constExp()) {
|
||||
continue;
|
||||
}
|
||||
EmitArrayStore(base_ptr, array_type, i, GenAddExpr(*leaves[i]->constExp()->addExp()));
|
||||
}
|
||||
}
|
||||
|
||||
auto* slot = builder_.CreateAllocaI32(module_.GetContext().NextTemp());
|
||||
storage_map_[def] = slot;
|
||||
ir::ConstantValue* IRGenImpl::BuildGlobalInitializer(const std::shared_ptr<Type>& type,
|
||||
SysYParser::InitValContext* init) {
|
||||
if (!init) {
|
||||
return builder_.CreateZero(type);
|
||||
}
|
||||
if (!type->IsArray()) {
|
||||
auto value = EvalGlobalConstAddExp(*init->exp()->addExp(), global_const_values_)
|
||||
.CastTo(type);
|
||||
return type->IsFloat32()
|
||||
? static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstFloat(value.AsFloat()))
|
||||
: static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstInt(value.AsInt()));
|
||||
}
|
||||
|
||||
ir::Value* init = builder_.CreateConstInt(0);
|
||||
if (auto* init_val = def->initVal()) {
|
||||
if (!init_val->exp()) {
|
||||
throw std::runtime_error(
|
||||
FormatError("irgen", "当前 IR 仅支持表达式初始化"));
|
||||
const auto scalar_type = ScalarLeafType(type);
|
||||
std::vector<ConstantData> flat(CountScalars(type), ZeroForType(scalar_type));
|
||||
if (init->L_BRACE()) {
|
||||
std::vector<SysYParser::InitValContext*> leaves(flat.size(), nullptr);
|
||||
size_t cursor = 0;
|
||||
FlattenInitValue(type, *init, leaves, cursor, 0);
|
||||
for (size_t i = 0; i < leaves.size(); ++i) {
|
||||
if (leaves[i] && leaves[i]->exp()) {
|
||||
flat[i] = EvalGlobalConstAddExp(*leaves[i]->exp()->addExp(), global_const_values_)
|
||||
.CastTo(scalar_type);
|
||||
}
|
||||
init = GenExpr(*init_val->exp());
|
||||
}
|
||||
builder_.CreateStore(init, slot);
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
std::function<ir::ConstantValue*(const std::shared_ptr<Type>&)> build =
|
||||
[&](const std::shared_ptr<Type>& current) -> ir::ConstantValue* {
|
||||
if (!current->IsArray()) {
|
||||
ConstantData value = flat[offset++].CastTo(current);
|
||||
return current->IsFloat32()
|
||||
? static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstFloat(value.AsFloat()))
|
||||
: static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstInt(value.AsInt()));
|
||||
}
|
||||
std::vector<ir::ConstantValue*> elements;
|
||||
bool all_zero = true;
|
||||
for (size_t i = 0; i < current->GetArraySize(); ++i) {
|
||||
auto* child = build(current->GetElementType());
|
||||
all_zero = all_zero && child->IsZeroValue();
|
||||
elements.push_back(child);
|
||||
}
|
||||
if (all_zero) {
|
||||
return module_.GetContext().CreateOwnedConstant<ir::ConstantZero>(current);
|
||||
}
|
||||
return module_.GetContext().CreateOwnedConstant<ir::ConstantArray>(current,
|
||||
elements);
|
||||
};
|
||||
return build(type);
|
||||
}
|
||||
|
||||
ir::ConstantValue* IRGenImpl::BuildGlobalConstInitializer(
|
||||
const std::shared_ptr<Type>& type, SysYParser::ConstInitValContext* init) {
|
||||
if (!type->IsArray()) {
|
||||
auto value =
|
||||
EvalGlobalConstAddExp(*init->constExp()->addExp(), global_const_values_)
|
||||
.CastTo(type);
|
||||
return type->IsFloat32()
|
||||
? static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstFloat(value.AsFloat()))
|
||||
: static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstInt(value.AsInt()));
|
||||
}
|
||||
|
||||
const auto scalar_type = ScalarLeafType(type);
|
||||
std::vector<ConstantData> flat(CountScalars(type), ZeroForType(scalar_type));
|
||||
std::vector<SysYParser::ConstInitValContext*> leaves(flat.size(), nullptr);
|
||||
size_t cursor = 0;
|
||||
FlattenConstInitValue(type, *init, leaves, cursor, 0);
|
||||
for (size_t i = 0; i < leaves.size(); ++i) {
|
||||
if (leaves[i] && leaves[i]->constExp()) {
|
||||
flat[i] =
|
||||
EvalGlobalConstAddExp(*leaves[i]->constExp()->addExp(), global_const_values_)
|
||||
.CastTo(scalar_type);
|
||||
}
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
std::function<ir::ConstantValue*(const std::shared_ptr<Type>&)> build =
|
||||
[&](const std::shared_ptr<Type>& current) -> ir::ConstantValue* {
|
||||
if (!current->IsArray()) {
|
||||
ConstantData value = flat[offset++].CastTo(current);
|
||||
return current->IsFloat32()
|
||||
? static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstFloat(value.AsFloat()))
|
||||
: static_cast<ir::ConstantValue*>(
|
||||
module_.GetContext().GetConstInt(value.AsInt()));
|
||||
}
|
||||
std::vector<ir::ConstantValue*> elements;
|
||||
bool all_zero = true;
|
||||
for (size_t i = 0; i < current->GetArraySize(); ++i) {
|
||||
auto* child = build(current->GetElementType());
|
||||
all_zero = all_zero && child->IsZeroValue();
|
||||
elements.push_back(child);
|
||||
}
|
||||
if (all_zero) {
|
||||
return module_.GetContext().CreateOwnedConstant<ir::ConstantZero>(current);
|
||||
}
|
||||
return module_.GetContext().CreateOwnedConstant<ir::ConstantArray>(current,
|
||||
elements);
|
||||
};
|
||||
return build(type);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,38 @@
|
||||
// 维护局部变量声明的注册与查找。
|
||||
|
||||
#include "sem/SymbolTable.h"
|
||||
|
||||
void SymbolTable::Add(const std::string& name,
|
||||
SysYParser::VarDefContext* decl) {
|
||||
table_[name] = decl;
|
||||
#include <stdexcept>
|
||||
|
||||
void SymbolTable::EnterScope() { scopes_.emplace_back(); }
|
||||
|
||||
void SymbolTable::ExitScope() {
|
||||
if (scopes_.empty()) {
|
||||
throw std::runtime_error("作用域栈为空,无法退出");
|
||||
}
|
||||
scopes_.pop_back();
|
||||
}
|
||||
|
||||
bool SymbolTable::Declare(const std::string& name, const SymbolInfo* symbol) {
|
||||
if (scopes_.empty()) {
|
||||
EnterScope();
|
||||
}
|
||||
auto& scope = scopes_.back();
|
||||
return scope.emplace(name, symbol).second;
|
||||
}
|
||||
|
||||
bool SymbolTable::Contains(const std::string& name) const {
|
||||
return table_.find(name) != table_.end();
|
||||
const SymbolInfo* SymbolTable::Lookup(const std::string& name) const {
|
||||
for (auto it = scopes_.rbegin(); it != scopes_.rend(); ++it) {
|
||||
auto found = it->find(name);
|
||||
if (found != it->end()) {
|
||||
return found->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SysYParser::VarDefContext* SymbolTable::Lookup(const std::string& name) const {
|
||||
auto it = table_.find(name);
|
||||
return it == table_.end() ? nullptr : it->second;
|
||||
const SymbolInfo* SymbolTable::LookupCurrent(const std::string& name) const {
|
||||
if (scopes_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto found = scopes_.back().find(name);
|
||||
return found == scopes_.back().end() ? nullptr : found->second;
|
||||
}
|
||||
|
||||
@ -1,4 +1,66 @@
|
||||
// SysY 运行库实现:
|
||||
// - 按实验/评测规范提供 I/O 等函数实现
|
||||
// - 与编译器生成的目标代码链接,支撑运行时行为
|
||||
#include "sylib.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static float ReadFloatToken(void) {
|
||||
char buffer[128] = {0};
|
||||
if (scanf("%127s", buffer) != 1) {
|
||||
return 0.0f;
|
||||
}
|
||||
return strtof(buffer, NULL);
|
||||
}
|
||||
|
||||
int getint(void) {
|
||||
int value = 0;
|
||||
scanf("%d", &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
int getch(void) {
|
||||
return getchar();
|
||||
}
|
||||
|
||||
float getfloat(void) { return ReadFloatToken(); }
|
||||
|
||||
int getarray(int a[]) {
|
||||
int n = getint();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
a[i] = getint();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
int getfarray(float a[]) {
|
||||
int n = getint();
|
||||
for (int i = 0; i < n; ++i) {
|
||||
a[i] = getfloat();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void putint(int x) { printf("%d", x); }
|
||||
|
||||
void putch(int x) { putchar(x); }
|
||||
|
||||
void putfloat(float x) { printf("%a", x); }
|
||||
|
||||
void putarray(int n, int a[]) {
|
||||
printf("%d:", n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
printf(" %d", a[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void putfarray(int n, float a[]) {
|
||||
printf("%d:", n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
printf(" %a", a[i]);
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
|
||||
void starttime(void) {}
|
||||
|
||||
void stoptime(void) {}
|
||||
|
||||
@ -1,4 +1,16 @@
|
||||
// SysY 运行库头文件:
|
||||
// - 声明运行库函数原型(供编译器生成 call 或链接阶段引用)
|
||||
// - 与 sylib.c 配套,按规范逐步补齐声明
|
||||
#pragma once
|
||||
|
||||
int getint(void);
|
||||
int getch(void);
|
||||
float getfloat(void);
|
||||
int getarray(int a[]);
|
||||
int getfarray(float a[]);
|
||||
|
||||
void putint(int x);
|
||||
void putch(int x);
|
||||
void putfloat(float x);
|
||||
void putarray(int n, int a[]);
|
||||
void putfarray(int n, float a[]);
|
||||
|
||||
void starttime(void);
|
||||
void stoptime(void);
|
||||
|
||||
Loading…
Reference in new issue