Compare commits
9 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
f32ebef74c | 11 hours ago |
|
|
3a49f8e131 | 12 hours ago |
|
|
f34f36ed59 | 2 weeks ago |
|
|
4a4e0a2e0c | 2 weeks ago |
|
|
5756a2952c | 3 weeks ago |
|
|
dcaada11bc | 3 weeks ago |
|
|
f54091c010 | 3 weeks ago |
|
|
876ba72328 | 3 weeks ago |
|
|
c2759c27cd | 3 weeks ago |
Binary file not shown.
@ -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::VarContext* 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::VarContext* 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::VarContext*,
|
||||
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,145 @@
|
||||
#!/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"
|
||||
COMPILER="$BUILD_DIR/bin/compiler"
|
||||
SAVE_TREE=false
|
||||
TREE_DIR="$ROOT_DIR/test_tree"
|
||||
POSITIVE_CASES=(
|
||||
"$ROOT_DIR"/test/test_case/functional/*.sy
|
||||
"$ROOT_DIR"/test/test_case/performance/*.sy
|
||||
)
|
||||
NEGATIVE_CASES=(
|
||||
"$ROOT_DIR"/test/test_case/negative/*.sy
|
||||
)
|
||||
positive_total=0
|
||||
positive_passed=0
|
||||
positive_failed=0
|
||||
negative_total=0
|
||||
negative_passed=0
|
||||
negative_failed=0
|
||||
failed_cases=()
|
||||
|
||||
print_summary() {
|
||||
local total passed failed
|
||||
total=$((positive_total + negative_total))
|
||||
passed=$((positive_passed + negative_passed))
|
||||
failed=$((positive_failed + negative_failed))
|
||||
|
||||
echo
|
||||
echo "Summary:"
|
||||
echo " Positive cases: total=$positive_total, passed=$positive_passed, failed=$positive_failed"
|
||||
echo " Negative cases: total=$negative_total, passed=$negative_passed, failed=$negative_failed"
|
||||
echo " Overall: total=$total, passed=$passed, failed=$failed"
|
||||
|
||||
if (( ${#failed_cases[@]} > 0 )); then
|
||||
echo "Failed cases:"
|
||||
printf ' - %s\n' "${failed_cases[@]}"
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--save-tree)
|
||||
SAVE_TREE=true
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
echo "Usage: $0 [--save-tree]" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
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=ON
|
||||
|
||||
echo "[3/4] Building project..."
|
||||
cmake --build "$BUILD_DIR" -j "$(nproc)"
|
||||
|
||||
echo "[4/4] Running parse-tree tests in parse-only mode..."
|
||||
|
||||
if [[ "$SAVE_TREE" == true ]]; then
|
||||
rm -rf "$TREE_DIR"
|
||||
mkdir -p "$TREE_DIR"
|
||||
fi
|
||||
|
||||
for case_file in "${POSITIVE_CASES[@]}"; do
|
||||
((positive_total += 1))
|
||||
if [[ "$SAVE_TREE" == true ]]; then
|
||||
rel_path="${case_file#"$ROOT_DIR"/test/test_case/}"
|
||||
rel_dir="$(dirname "$rel_path")"
|
||||
stem="$(basename "${case_file%.sy}")"
|
||||
out_dir="$TREE_DIR/$rel_dir"
|
||||
out_file="$out_dir/$stem.tree"
|
||||
mkdir -p "$out_dir"
|
||||
if ! "$COMPILER" --emit-parse-tree "$case_file" >"$out_file" 2>/tmp/lab1_parse.err; then
|
||||
echo "FAIL: $case_file"
|
||||
cat /tmp/lab1_parse.err
|
||||
rm -f "$out_file"
|
||||
((positive_failed += 1))
|
||||
failed_cases+=("$case_file")
|
||||
else
|
||||
echo "PASS: $case_file -> $out_file"
|
||||
((positive_passed += 1))
|
||||
fi
|
||||
else
|
||||
if ! "$COMPILER" --emit-parse-tree "$case_file" >/dev/null 2>/tmp/lab1_parse.err; then
|
||||
echo "FAIL: $case_file"
|
||||
cat /tmp/lab1_parse.err
|
||||
((positive_failed += 1))
|
||||
failed_cases+=("$case_file")
|
||||
else
|
||||
echo "PASS: $case_file"
|
||||
((positive_passed += 1))
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if (( ${#NEGATIVE_CASES[@]} > 0 )); then
|
||||
echo
|
||||
echo "Running negative parse tests..."
|
||||
for case_file in "${NEGATIVE_CASES[@]}"; do
|
||||
((negative_total += 1))
|
||||
if "$COMPILER" --emit-parse-tree "$case_file" >/tmp/lab1_negative.out 2>/tmp/lab1_negative.err; then
|
||||
echo "FAIL: $case_file (expected parse failure, but parsing succeeded)"
|
||||
((negative_failed += 1))
|
||||
failed_cases+=("$case_file")
|
||||
else
|
||||
if grep -q '^\[error\] \[parse\]' /tmp/lab1_negative.err; then
|
||||
echo "PASS: $case_file -> expected parse error"
|
||||
((negative_passed += 1))
|
||||
else
|
||||
echo "FAIL: $case_file (did not report parse error as expected)"
|
||||
cat /tmp/lab1_negative.err
|
||||
((negative_failed += 1))
|
||||
failed_cases+=("$case_file")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
print_summary
|
||||
|
||||
if (( positive_failed + negative_failed > 0 )); then
|
||||
echo "Batch test finished with failures."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Batch test passed."
|
||||
@ -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
|
||||
|
||||
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);
|
||||
|
||||
@ -0,0 +1 @@
|
||||
3
|
||||
@ -0,0 +1,6 @@
|
||||
int a[3] = 1;
|
||||
float b[2] = 2.5;
|
||||
|
||||
int main() {
|
||||
return a[0] + a[1] + a[2] + b[0] + b[1];
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
const int a = 5 % 2.0;
|
||||
|
||||
int main() {
|
||||
return a;
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
int main( {
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
int main() {
|
||||
int a = 1
|
||||
return a;
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
else return 0;
|
||||
}
|
||||
Loading…
Reference in new issue