Compare commits
18 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
2472624927 | 7 days ago |
|
|
184d5c7cb5 | 2 weeks ago |
|
|
69f2cdf11a | 3 weeks ago |
|
|
8f0c2e3919 | 2 months ago |
|
|
8dd9db7a74 | 2 months ago |
|
|
207df228fc | 2 months ago |
|
|
408f24c05f | 2 months ago |
|
|
4bb7007fd3 | 2 months ago |
|
|
4f0036900a | 3 months ago |
|
|
d8b30279e3 | 3 months ago |
|
|
86ce9bcf50 | 3 months ago |
|
|
63953bd4ae | 3 months ago |
|
|
9e981ce28b | 3 months ago |
|
|
dbc1d08957 | 3 months ago |
|
|
6b5e184cf6 | 3 months ago |
|
|
3cfd47fb71 | 3 months ago |
|
|
8465f2ddd5 | 3 months ago |
|
|
d3498162b4 | 3 months ago |
@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nudt-compiler-cpp"
|
||||||
|
version = "0.1.0"
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "nudt-compiler-cpp"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
@ -1,17 +1,42 @@
|
|||||||
// 极简符号表:记录局部变量定义点。
|
// 符号表:记录局部变量/常量/参数定义。
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "SysYParser.h"
|
#include "SysYParser.h"
|
||||||
|
|
||||||
|
enum class BaseTypeKind { Int, Float, Void };
|
||||||
|
|
||||||
|
struct TypeDesc {
|
||||||
|
BaseTypeKind base = BaseTypeKind::Int;
|
||||||
|
std::vector<int> dims; // 为空表示标量;数组维度允许首维为 -1 表示形参不定长
|
||||||
|
bool is_const = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SymbolKind { Var, Const, Param };
|
||||||
|
|
||||||
|
struct SymbolEntry {
|
||||||
|
SymbolKind kind = SymbolKind::Var;
|
||||||
|
SysYParser::VarDefContext* var_decl = nullptr;
|
||||||
|
SysYParser::ConstDefContext* const_decl = nullptr;
|
||||||
|
SysYParser::FuncFParamContext* param_decl = nullptr;
|
||||||
|
TypeDesc type; // 记录类型信息
|
||||||
|
bool is_const = false;
|
||||||
|
std::optional<int> const_value;
|
||||||
|
};
|
||||||
|
|
||||||
class SymbolTable {
|
class SymbolTable {
|
||||||
public:
|
public:
|
||||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
void EnterScope();
|
||||||
bool Contains(const std::string& name) const;
|
void ExitScope();
|
||||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
|
||||||
|
bool ContainsInCurrentScope(const std::string& name) const;
|
||||||
|
void Add(const std::string& name, const SymbolEntry& entry);
|
||||||
|
const SymbolEntry* Lookup(const std::string& name) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
std::vector<std::unordered_map<std::string, SymbolEntry>> scopes_;
|
||||||
};
|
};
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Reconfigure with IR pipeline enabled, build, then run Lab2 test script.
|
||||||
|
RESULT_FILE="test/test_result/run_lab2_result.log"
|
||||||
|
mkdir -p "$(dirname \"$RESULT_FILE\")"
|
||||||
|
: > "$RESULT_FILE"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "[run_lab2] start: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
echo "[run_lab2] logging to: $RESULT_FILE"
|
||||||
|
|
||||||
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF
|
||||||
|
cmake --build build -j "$(nproc)"
|
||||||
|
CASE_DIR=test/test_case bash scripts/test_lab2.sh
|
||||||
|
|
||||||
|
echo "[run_lab2] end: $(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
} 2>&1 | tee "$RESULT_FILE"
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Lab2 quick/full verification helper.
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/test_lab2.sh
|
||||||
|
# Optional env vars:
|
||||||
|
# COMPILER=./build/bin/compiler
|
||||||
|
# CASE_DIR=test/test_case
|
||||||
|
# OUT_DIR=test/test_result/lab2_ir
|
||||||
|
# LOG_FILE=test/test_result/lab2_test.log
|
||||||
|
|
||||||
|
COMPILER="${COMPILER:-./build/bin/compiler}"
|
||||||
|
CASE_DIR="${CASE_DIR:-test/test_case}"
|
||||||
|
OUT_DIR="${OUT_DIR:-test/test_result/lab2_ir}"
|
||||||
|
LOG_FILE="${LOG_FILE:-test/test_result/lab2_test.log}"
|
||||||
|
VERIFY_SCRIPT="./scripts/verify_ir.sh"
|
||||||
|
|
||||||
|
if [[ ! -x "$COMPILER" ]]; then
|
||||||
|
echo "compiler not found or not executable: $COMPILER" >&2
|
||||||
|
echo "build first:" >&2
|
||||||
|
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release" >&2
|
||||||
|
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -x "$VERIFY_SCRIPT" ]]; then
|
||||||
|
echo "verify script not found or not executable: $VERIFY_SCRIPT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$CASE_DIR" ]]; then
|
||||||
|
echo "case dir not found: $CASE_DIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
|
# Preflight: ensure compiler supports IR emission (not parse-only build).
|
||||||
|
probe_input="$CASE_DIR/simple_add.sy"
|
||||||
|
probe_err="$OUT_DIR/.lab2_probe.err"
|
||||||
|
if [[ -f "$probe_input" ]]; then
|
||||||
|
set +e
|
||||||
|
"$COMPILER" --emit-ir "$probe_input" > /dev/null 2> "$probe_err"
|
||||||
|
probe_rc=$?
|
||||||
|
set -e
|
||||||
|
if [[ $probe_rc -ne 0 ]] && grep -Eiq "parse-only|IR/汇编输出已禁用" "$probe_err"; then
|
||||||
|
echo "detected parse-only compiler build, cannot run Lab2 IR tests." >&2
|
||||||
|
echo "rebuild with IR enabled:" >&2
|
||||||
|
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF" >&2
|
||||||
|
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||||
|
rm -f "$probe_err"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
rm -f "$probe_err"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$LOG_FILE")"
|
||||||
|
: > "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[Lab2] start test" | tee -a "$LOG_FILE"
|
||||||
|
echo "compiler : $COMPILER" | tee -a "$LOG_FILE"
|
||||||
|
echo "cases : $CASE_DIR" | tee -a "$LOG_FILE"
|
||||||
|
echo "out dir : $OUT_DIR" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[Step 1] single sample check: simple_add.sy" | tee -a "$LOG_FILE"
|
||||||
|
sample_input="$(find "$CASE_DIR" -type f -name "simple_add.sy" -print -quit)"
|
||||||
|
if [[ -z "$sample_input" ]]; then
|
||||||
|
sample_input="$(find "$CASE_DIR" -type f -name "*.sy" | sort | head -n 1)"
|
||||||
|
fi
|
||||||
|
if [[ -z "$sample_input" ]]; then
|
||||||
|
echo "single sample: FAIL (no .sy case found under $CASE_DIR)" | tee -a "$LOG_FILE"
|
||||||
|
echo "stop here. see log: $LOG_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if "$VERIFY_SCRIPT" "$sample_input" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||||
|
echo "single sample: PASS" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "single sample: FAIL" | tee -a "$LOG_FILE"
|
||||||
|
echo "stop here. see log: $LOG_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[Step 2] full Lab2 regression" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
pass=0
|
||||||
|
fail=0
|
||||||
|
total=0
|
||||||
|
failed_list=()
|
||||||
|
|
||||||
|
while IFS= read -r -d '' sy; do
|
||||||
|
total=$((total + 1))
|
||||||
|
name="$(basename "$sy")"
|
||||||
|
echo "[$total] $name" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
if "$VERIFY_SCRIPT" "$sy" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||||
|
pass=$((pass + 1))
|
||||||
|
echo " PASS" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
fail=$((fail + 1))
|
||||||
|
failed_list+=("$sy")
|
||||||
|
echo " FAIL" | tee -a "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
done < <(find "$CASE_DIR" -type f -name "*.sy" -print0 | sort -z)
|
||||||
|
|
||||||
|
echo "" | tee -a "$LOG_FILE"
|
||||||
|
echo "[Summary]" | tee -a "$LOG_FILE"
|
||||||
|
echo "total: $total" | tee -a "$LOG_FILE"
|
||||||
|
echo "pass : $pass" | tee -a "$LOG_FILE"
|
||||||
|
echo "fail : $fail" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
if [[ $fail -gt 0 ]]; then
|
||||||
|
echo "failed cases:" | tee -a "$LOG_FILE"
|
||||||
|
for f in "${failed_list[@]}"; do
|
||||||
|
echo " - $f" | tee -a "$LOG_FILE"
|
||||||
|
done
|
||||||
|
echo "Lab2 target is not fully met yet." | tee -a "$LOG_FILE"
|
||||||
|
echo "see details in $LOG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "All Lab2 cases passed. Lab2 target regression is met." | tee -a "$LOG_FILE"
|
||||||
|
echo "see details in $LOG_FILE"
|
||||||
@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Lab3 full backend regression helper.
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/test_lab3.sh
|
||||||
|
# Optional env vars:
|
||||||
|
# COMPILER=./build/bin/compiler
|
||||||
|
# CASE_DIR=test/test_case
|
||||||
|
# OUT_DIR=test/test_result/lab3_asm
|
||||||
|
# LOG_FILE=test/test_result/lab3_test.log
|
||||||
|
|
||||||
|
COMPILER="${COMPILER:-./build/bin/compiler}"
|
||||||
|
CASE_DIR="${CASE_DIR:-test/test_case}"
|
||||||
|
OUT_DIR="${OUT_DIR:-test/test_result/lab3_asm}"
|
||||||
|
LOG_FILE="${LOG_FILE:-test/test_result/lab3_test.log}"
|
||||||
|
VERIFY_SCRIPT="./scripts/verify_asm.sh"
|
||||||
|
|
||||||
|
if [[ ! -x "$COMPILER" ]]; then
|
||||||
|
echo "compiler not found or not executable: $COMPILER" >&2
|
||||||
|
echo "build first:" >&2
|
||||||
|
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF" >&2
|
||||||
|
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -x "$VERIFY_SCRIPT" ]]; then
|
||||||
|
echo "verify script not found or not executable: $VERIFY_SCRIPT" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "$CASE_DIR" ]]; then
|
||||||
|
echo "case dir not found: $CASE_DIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$OUT_DIR"
|
||||||
|
|
||||||
|
probe_input="$CASE_DIR/functional/simple_add.sy"
|
||||||
|
probe_err="$OUT_DIR/.lab3_probe.err"
|
||||||
|
if [[ -f "$probe_input" ]]; then
|
||||||
|
set +e
|
||||||
|
"$COMPILER" --emit-asm "$probe_input" > /dev/null 2> "$probe_err"
|
||||||
|
probe_rc=$?
|
||||||
|
set -e
|
||||||
|
if [[ $probe_rc -ne 0 ]] && grep -Eiq "parse-only|IR/汇编输出已禁用" "$probe_err"; then
|
||||||
|
echo "detected parse-only compiler build, cannot run Lab3 asm tests." >&2
|
||||||
|
echo "rebuild with MIR/ASM enabled:" >&2
|
||||||
|
echo " cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCOMPILER_PARSE_ONLY=OFF" >&2
|
||||||
|
echo " cmake --build build -j \"\$(nproc)\"" >&2
|
||||||
|
rm -f "$probe_err"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
rm -f "$probe_err"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "$LOG_FILE")"
|
||||||
|
: > "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[Lab3] start test" | tee -a "$LOG_FILE"
|
||||||
|
echo "compiler : $COMPILER" | tee -a "$LOG_FILE"
|
||||||
|
echo "cases : $CASE_DIR" | tee -a "$LOG_FILE"
|
||||||
|
echo "out dir : $OUT_DIR" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
echo "[Step 1] single sample check: simple_add.sy" | tee -a "$LOG_FILE"
|
||||||
|
if [[ ! -f "$probe_input" ]]; then
|
||||||
|
echo "single sample: FAIL (missing $probe_input)" | tee -a "$LOG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if "$VERIFY_SCRIPT" "$probe_input" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||||
|
echo "single sample: PASS" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
echo "single sample: FAIL" | tee -a "$LOG_FILE"
|
||||||
|
echo "stop here. see log: $LOG_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[Step 2] full Lab3 asm regression" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
pass=0
|
||||||
|
fail=0
|
||||||
|
total=0
|
||||||
|
failed_list=()
|
||||||
|
|
||||||
|
while IFS= read -r -d '' sy; do
|
||||||
|
total=$((total + 1))
|
||||||
|
name="${sy#$CASE_DIR/}"
|
||||||
|
echo "[$total] $name" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
if "$VERIFY_SCRIPT" "$sy" "$OUT_DIR" --run >> "$LOG_FILE" 2>&1; then
|
||||||
|
pass=$((pass + 1))
|
||||||
|
echo " PASS" | tee -a "$LOG_FILE"
|
||||||
|
else
|
||||||
|
fail=$((fail + 1))
|
||||||
|
failed_list+=("$sy")
|
||||||
|
echo " FAIL" | tee -a "$LOG_FILE"
|
||||||
|
fi
|
||||||
|
done < <(find "$CASE_DIR" -type f -name "*.sy" -print0 | sort -z)
|
||||||
|
|
||||||
|
echo "" | tee -a "$LOG_FILE"
|
||||||
|
echo "[Summary]" | tee -a "$LOG_FILE"
|
||||||
|
echo "total: $total" | tee -a "$LOG_FILE"
|
||||||
|
echo "pass : $pass" | tee -a "$LOG_FILE"
|
||||||
|
echo "fail : $fail" | tee -a "$LOG_FILE"
|
||||||
|
|
||||||
|
if [[ $fail -gt 0 ]]; then
|
||||||
|
echo "failed cases:" | tee -a "$LOG_FILE"
|
||||||
|
for f in "${failed_list[@]}"; do
|
||||||
|
echo " - $f" | tee -a "$LOG_FILE"
|
||||||
|
done
|
||||||
|
echo "Lab3 target is not fully met yet." | tee -a "$LOG_FILE"
|
||||||
|
echo "see details in $LOG_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "All Lab3 cases passed. Lab3 target regression is met." | tee -a "$LOG_FILE"
|
||||||
|
echo "see details in $LOG_FILE"
|
||||||
@ -1,31 +1,148 @@
|
|||||||
// 当前仅支持 void、i32 和 i32*。
|
// 支持 void/i1/i32/float/ptr/array/function/label。
|
||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
|
|
||||||
namespace ir {
|
namespace ir {
|
||||||
|
|
||||||
Type::Type(Kind k) : kind_(k) {}
|
Type::Type(Kind k) : kind_(k) {}
|
||||||
|
|
||||||
|
Type::Type(Kind k, std::shared_ptr<Type> elem, size_t count)
|
||||||
|
: kind_(k), elem_type_(std::move(elem)), array_size_(count) {}
|
||||||
|
|
||||||
|
Type::Type(Kind k, std::shared_ptr<Type> ret,
|
||||||
|
std::vector<std::shared_ptr<Type>> params, bool is_vararg)
|
||||||
|
: kind_(k), ret_type_(std::move(ret)), param_types_(std::move(params)),
|
||||||
|
is_vararg_(is_vararg) {}
|
||||||
|
|
||||||
const std::shared_ptr<Type>& Type::GetVoidType() {
|
const std::shared_ptr<Type>& Type::GetVoidType() {
|
||||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Void);
|
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Void);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<Type>& Type::GetInt1Type() {
|
||||||
|
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Int1);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
const std::shared_ptr<Type>& Type::GetInt32Type() {
|
const std::shared_ptr<Type>& Type::GetInt32Type() {
|
||||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Int32);
|
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Int32);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::shared_ptr<Type>& Type::GetPtrInt32Type() {
|
const std::shared_ptr<Type>& Type::GetFloatType() {
|
||||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::PtrInt32);
|
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Float);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<Type>& Type::GetLabelType() {
|
||||||
|
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Label);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Type> Type::GetPointerType(std::shared_ptr<Type> elem) {
|
||||||
|
if (!elem) {
|
||||||
|
throw std::runtime_error("PointerType 缺少 element type");
|
||||||
|
}
|
||||||
|
return std::make_shared<Type>(Kind::Pointer, std::move(elem), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Type> Type::GetArrayType(std::shared_ptr<Type> elem,
|
||||||
|
size_t count) {
|
||||||
|
if (!elem) {
|
||||||
|
throw std::runtime_error("ArrayType 缺少 element type");
|
||||||
|
}
|
||||||
|
return std::make_shared<Type>(Kind::Array, std::move(elem), count);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Type> Type::GetFunctionType(
|
||||||
|
std::shared_ptr<Type> ret, std::vector<std::shared_ptr<Type>> params,
|
||||||
|
bool is_vararg) {
|
||||||
|
if (!ret) {
|
||||||
|
throw std::runtime_error("FunctionType 缺少 return type");
|
||||||
|
}
|
||||||
|
return std::make_shared<Type>(Kind::Function, std::move(ret),
|
||||||
|
std::move(params), is_vararg);
|
||||||
|
}
|
||||||
|
|
||||||
Type::Kind Type::GetKind() const { return kind_; }
|
Type::Kind Type::GetKind() const { return kind_; }
|
||||||
|
|
||||||
bool Type::IsVoid() const { return kind_ == Kind::Void; }
|
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::IsInt32() const { return kind_ == Kind::Int32; }
|
||||||
|
|
||||||
bool Type::IsPtrInt32() const { return kind_ == Kind::PtrInt32; }
|
bool Type::IsFloat() const { return kind_ == Kind::Float; }
|
||||||
|
|
||||||
|
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::IsLabel() const { return kind_ == Kind::Label; }
|
||||||
|
|
||||||
|
const std::shared_ptr<Type>& Type::GetElementType() const {
|
||||||
|
if (!elem_type_) {
|
||||||
|
throw std::runtime_error("Type 没有 element type");
|
||||||
|
}
|
||||||
|
return elem_type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Type::GetArraySize() const {
|
||||||
|
if (!IsArray()) {
|
||||||
|
throw std::runtime_error("Type 不是 array");
|
||||||
|
}
|
||||||
|
return array_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<Type>& Type::GetReturnType() const {
|
||||||
|
if (!IsFunction()) {
|
||||||
|
throw std::runtime_error("Type 不是 function");
|
||||||
|
}
|
||||||
|
return ret_type_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::shared_ptr<Type>>& Type::GetParamTypes() const {
|
||||||
|
if (!IsFunction()) {
|
||||||
|
throw std::runtime_error("Type 不是 function");
|
||||||
|
}
|
||||||
|
return param_types_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Type::IsVarArg() const {
|
||||||
|
if (!IsFunction()) {
|
||||||
|
throw std::runtime_error("Type 不是 function");
|
||||||
|
}
|
||||||
|
return is_vararg_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Type::Equals(const Type& other) const {
|
||||||
|
if (kind_ != other.kind_) return false;
|
||||||
|
switch (kind_) {
|
||||||
|
case Kind::Pointer:
|
||||||
|
return elem_type_ && other.elem_type_ &&
|
||||||
|
elem_type_->Equals(*other.elem_type_);
|
||||||
|
case Kind::Array:
|
||||||
|
return array_size_ == other.array_size_ && elem_type_ &&
|
||||||
|
other.elem_type_ && elem_type_->Equals(*other.elem_type_);
|
||||||
|
case Kind::Function: {
|
||||||
|
if (!ret_type_ || !other.ret_type_ ||
|
||||||
|
!ret_type_->Equals(*other.ret_type_) ||
|
||||||
|
is_vararg_ != other.is_vararg_ ||
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ir
|
} // namespace ir
|
||||||
|
|||||||
@ -1,4 +1,135 @@
|
|||||||
// CFG 简化:
|
#include "ir/IR.h"
|
||||||
// - 删除不可达块、合并空块、简化分支等
|
|
||||||
// - 改善 IR 结构,便于后续优化与后端生成
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
namespace ir {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Instruction* Terminator(BasicBlock& block) {
|
||||||
|
auto& insts = block.GetMutableInstructions();
|
||||||
|
if (insts.empty()) return nullptr;
|
||||||
|
auto* inst = insts.back().get();
|
||||||
|
return inst && inst->IsTerminator() ? inst : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddCFGEdge(BasicBlock* from, BasicBlock* to) {
|
||||||
|
if (!from || !to) return;
|
||||||
|
from->AddSuccessor(to);
|
||||||
|
to->AddPredecessor(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RebuildCFG(Function& func) {
|
||||||
|
for (const auto& block : func.GetBlocks()) {
|
||||||
|
if (!block) continue;
|
||||||
|
block->ClearPredecessors();
|
||||||
|
block->ClearSuccessors();
|
||||||
|
}
|
||||||
|
for (const auto& block : func.GetBlocks()) {
|
||||||
|
if (!block) continue;
|
||||||
|
auto* term = Terminator(*block);
|
||||||
|
if (auto* br = dynamic_cast<BranchInst*>(term)) {
|
||||||
|
AddCFGEdge(block.get(), br->GetDest());
|
||||||
|
} else if (auto* cbr = dynamic_cast<CondBrInst*>(term)) {
|
||||||
|
AddCFGEdge(block.get(), cbr->GetTrueDest());
|
||||||
|
AddCFGEdge(block.get(), cbr->GetFalseDest());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SimplifyBranches(Function& func) {
|
||||||
|
bool changed = false;
|
||||||
|
for (const auto& block : func.GetBlocks()) {
|
||||||
|
if (!block) continue;
|
||||||
|
auto* cbr = dynamic_cast<CondBrInst*>(Terminator(*block));
|
||||||
|
if (!cbr) continue;
|
||||||
|
BasicBlock* dest = nullptr;
|
||||||
|
if (cbr->GetTrueDest() == cbr->GetFalseDest()) {
|
||||||
|
dest = cbr->GetTrueDest();
|
||||||
|
} else if (auto* cond = dynamic_cast<ConstantInt*>(cbr->GetCond())) {
|
||||||
|
dest = cond->GetValue() != 0 ? cbr->GetTrueDest() : cbr->GetFalseDest();
|
||||||
|
}
|
||||||
|
if (dest) {
|
||||||
|
block->ReplaceTerminator(std::make_unique<BranchInst>(dest));
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_set<BasicBlock*> ReachableBlocks(Function& func) {
|
||||||
|
std::unordered_set<BasicBlock*> reachable;
|
||||||
|
std::queue<BasicBlock*> work;
|
||||||
|
if (auto* entry = func.GetEntry()) {
|
||||||
|
reachable.insert(entry);
|
||||||
|
work.push(entry);
|
||||||
|
}
|
||||||
|
while (!work.empty()) {
|
||||||
|
auto* block = work.front();
|
||||||
|
work.pop();
|
||||||
|
for (auto* succ : block->GetSuccessors()) {
|
||||||
|
if (succ && reachable.insert(succ).second) {
|
||||||
|
work.push(succ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reachable;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RemoveUnreachable(Function& func) {
|
||||||
|
auto reachable = ReachableBlocks(func);
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
for (const auto& block : func.GetBlocks()) {
|
||||||
|
if (!block || reachable.count(block.get()) == 0) continue;
|
||||||
|
for (const auto& inst : block->GetInstructions()) {
|
||||||
|
auto* phi = dynamic_cast<PhiInst*>(inst.get());
|
||||||
|
if (!phi) continue;
|
||||||
|
for (const auto& other : func.GetBlocks()) {
|
||||||
|
if (other && reachable.count(other.get()) == 0) {
|
||||||
|
phi->RemoveIncomingFrom(other.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& blocks = func.GetMutableBlocks();
|
||||||
|
for (auto it = blocks.begin(); it != blocks.end();) {
|
||||||
|
auto* block = it->get();
|
||||||
|
if (!block || reachable.count(block) != 0) {
|
||||||
|
++it;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& insts = block->GetMutableInstructions();
|
||||||
|
while (!insts.empty()) {
|
||||||
|
block->EraseInstruction(insts.back().get());
|
||||||
|
}
|
||||||
|
it = blocks.erase(it);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool RunCFGSimplify(Module& module) {
|
||||||
|
bool changed = false;
|
||||||
|
for (const auto& func : module.GetFunctions()) {
|
||||||
|
if (!func || func->IsDeclaration()) continue;
|
||||||
|
RebuildCFG(*func);
|
||||||
|
bool local_changed = SimplifyBranches(*func);
|
||||||
|
if (local_changed) {
|
||||||
|
RebuildCFG(*func);
|
||||||
|
}
|
||||||
|
local_changed |= RemoveUnreachable(*func);
|
||||||
|
if (local_changed) {
|
||||||
|
RebuildCFG(*func);
|
||||||
|
}
|
||||||
|
changed |= local_changed;
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ir
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,239 @@
|
|||||||
// IR 常量折叠:
|
#include "ir/IR.h"
|
||||||
// - 折叠可判定的常量表达式
|
|
||||||
// - 简化常量控制流分支(按实现范围裁剪)
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace ir {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
ConstantInt* AsConstInt(Value* value) {
|
||||||
|
return dynamic_cast<ConstantInt*>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstantFloat* AsConstFloat(Value* value) {
|
||||||
|
return dynamic_cast<ConstantFloat*>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsZero(Value* value) {
|
||||||
|
if (auto* i = AsConstInt(value)) return i->GetValue() == 0;
|
||||||
|
if (auto* f = AsConstFloat(value)) return f->GetValue() == 0.0f;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsOne(Value* value) {
|
||||||
|
if (auto* i = AsConstInt(value)) return i->GetValue() == 1;
|
||||||
|
if (auto* f = AsConstFloat(value)) return f->GetValue() == 1.0f;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstantValue* FoldBinary(BinaryInst& inst, Context& ctx) {
|
||||||
|
auto* li = AsConstInt(inst.GetLhs());
|
||||||
|
auto* ri = AsConstInt(inst.GetRhs());
|
||||||
|
if (li && ri) {
|
||||||
|
int lhs = li->GetValue();
|
||||||
|
int rhs = ri->GetValue();
|
||||||
|
switch (inst.GetOpcode()) {
|
||||||
|
case Opcode::Add:
|
||||||
|
return ctx.GetConstInt(lhs + rhs);
|
||||||
|
case Opcode::Sub:
|
||||||
|
return ctx.GetConstInt(lhs - rhs);
|
||||||
|
case Opcode::Mul:
|
||||||
|
return ctx.GetConstInt(lhs * rhs);
|
||||||
|
case Opcode::SDiv:
|
||||||
|
if (rhs != 0) return ctx.GetConstInt(lhs / rhs);
|
||||||
|
break;
|
||||||
|
case Opcode::SRem:
|
||||||
|
if (rhs != 0) return ctx.GetConstInt(lhs % rhs);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* lf = AsConstFloat(inst.GetLhs());
|
||||||
|
auto* rf = AsConstFloat(inst.GetRhs());
|
||||||
|
if (lf && rf) {
|
||||||
|
float lhs = lf->GetValue();
|
||||||
|
float rhs = rf->GetValue();
|
||||||
|
switch (inst.GetOpcode()) {
|
||||||
|
case Opcode::FAdd:
|
||||||
|
return ctx.GetConstFloat(lhs + rhs);
|
||||||
|
case Opcode::FSub:
|
||||||
|
return ctx.GetConstFloat(lhs - rhs);
|
||||||
|
case Opcode::FMul:
|
||||||
|
return ctx.GetConstFloat(lhs * rhs);
|
||||||
|
case Opcode::FDiv:
|
||||||
|
return ctx.GetConstFloat(lhs / rhs);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value* SimplifyBinary(BinaryInst& inst) {
|
||||||
|
auto op = inst.GetOpcode();
|
||||||
|
auto* lhs = inst.GetLhs();
|
||||||
|
auto* rhs = inst.GetRhs();
|
||||||
|
switch (op) {
|
||||||
|
case Opcode::Add:
|
||||||
|
case Opcode::FAdd:
|
||||||
|
if (IsZero(rhs)) return lhs;
|
||||||
|
if (IsZero(lhs)) return rhs;
|
||||||
|
break;
|
||||||
|
case Opcode::Sub:
|
||||||
|
case Opcode::FSub:
|
||||||
|
if (IsZero(rhs)) return lhs;
|
||||||
|
break;
|
||||||
|
case Opcode::Mul:
|
||||||
|
if (IsOne(rhs)) return lhs;
|
||||||
|
if (IsOne(lhs)) return rhs;
|
||||||
|
if (IsZero(rhs)) return rhs;
|
||||||
|
if (IsZero(lhs)) return lhs;
|
||||||
|
break;
|
||||||
|
case Opcode::FMul:
|
||||||
|
if (IsOne(rhs)) return lhs;
|
||||||
|
if (IsOne(lhs)) return rhs;
|
||||||
|
break;
|
||||||
|
case Opcode::SDiv:
|
||||||
|
case Opcode::FDiv:
|
||||||
|
if (IsOne(rhs)) return lhs;
|
||||||
|
break;
|
||||||
|
case Opcode::SRem:
|
||||||
|
if (IsOne(rhs)) return rhs;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstantInt* FoldICmp(ICmpInst& inst, Context& ctx) {
|
||||||
|
auto* lhs = AsConstInt(inst.GetLhs());
|
||||||
|
auto* rhs = AsConstInt(inst.GetRhs());
|
||||||
|
if (!lhs || !rhs) return nullptr;
|
||||||
|
int l = lhs->GetValue();
|
||||||
|
int r = rhs->GetValue();
|
||||||
|
bool result = false;
|
||||||
|
switch (inst.GetPredicate()) {
|
||||||
|
case ICmpPredicate::Eq:
|
||||||
|
result = l == r;
|
||||||
|
break;
|
||||||
|
case ICmpPredicate::Ne:
|
||||||
|
result = l != r;
|
||||||
|
break;
|
||||||
|
case ICmpPredicate::Slt:
|
||||||
|
result = l < r;
|
||||||
|
break;
|
||||||
|
case ICmpPredicate::Sle:
|
||||||
|
result = l <= r;
|
||||||
|
break;
|
||||||
|
case ICmpPredicate::Sgt:
|
||||||
|
result = l > r;
|
||||||
|
break;
|
||||||
|
case ICmpPredicate::Sge:
|
||||||
|
result = l >= r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ctx.GetConstBool(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstantInt* FoldFCmp(FCmpInst& inst, Context& ctx) {
|
||||||
|
auto* lhs = AsConstFloat(inst.GetLhs());
|
||||||
|
auto* rhs = AsConstFloat(inst.GetRhs());
|
||||||
|
if (!lhs || !rhs) return nullptr;
|
||||||
|
float l = lhs->GetValue();
|
||||||
|
float r = rhs->GetValue();
|
||||||
|
bool ordered = !std::isnan(l) && !std::isnan(r);
|
||||||
|
bool result = false;
|
||||||
|
switch (inst.GetPredicate()) {
|
||||||
|
case FCmpPredicate::Oeq:
|
||||||
|
result = ordered && l == r;
|
||||||
|
break;
|
||||||
|
case FCmpPredicate::One:
|
||||||
|
result = ordered && l != r;
|
||||||
|
break;
|
||||||
|
case FCmpPredicate::Olt:
|
||||||
|
result = ordered && l < r;
|
||||||
|
break;
|
||||||
|
case FCmpPredicate::Ole:
|
||||||
|
result = ordered && l <= r;
|
||||||
|
break;
|
||||||
|
case FCmpPredicate::Ogt:
|
||||||
|
result = ordered && l > r;
|
||||||
|
break;
|
||||||
|
case FCmpPredicate::Oge:
|
||||||
|
result = ordered && l >= r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ctx.GetConstBool(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
ConstantValue* FoldCast(CastInst& inst, Context& ctx) {
|
||||||
|
switch (inst.GetOpcode()) {
|
||||||
|
case Opcode::SIToFP:
|
||||||
|
if (auto* c = AsConstInt(inst.GetValue())) {
|
||||||
|
return ctx.GetConstFloat(static_cast<float>(c->GetValue()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Opcode::FPToSI:
|
||||||
|
if (auto* c = AsConstFloat(inst.GetValue())) {
|
||||||
|
return ctx.GetConstInt(static_cast<int>(c->GetValue()));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Opcode::ZExt:
|
||||||
|
if (auto* c = AsConstInt(inst.GetValue())) {
|
||||||
|
return ctx.GetConstInt(c->GetValue() != 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value* SimplifyPhi(PhiInst& phi) {
|
||||||
|
const auto& values = phi.GetIncomingValues();
|
||||||
|
if (values.empty()) return nullptr;
|
||||||
|
auto* first = values.front();
|
||||||
|
for (auto* value : values) {
|
||||||
|
if (value != first) return nullptr;
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
bool RunConstFold(Module& module) {
|
||||||
|
bool changed = false;
|
||||||
|
auto& ctx = module.GetContext();
|
||||||
|
for (const auto& func : module.GetFunctions()) {
|
||||||
|
if (!func || func->IsDeclaration()) continue;
|
||||||
|
for (const auto& block : func->GetBlocks()) {
|
||||||
|
if (!block) continue;
|
||||||
|
for (const auto& inst_ptr : block->GetInstructions()) {
|
||||||
|
auto* inst = inst_ptr.get();
|
||||||
|
Value* replacement = nullptr;
|
||||||
|
if (auto* bin = dynamic_cast<BinaryInst*>(inst)) {
|
||||||
|
replacement = FoldBinary(*bin, ctx);
|
||||||
|
if (!replacement) replacement = SimplifyBinary(*bin);
|
||||||
|
} else if (auto* icmp = dynamic_cast<ICmpInst*>(inst)) {
|
||||||
|
replacement = FoldICmp(*icmp, ctx);
|
||||||
|
} else if (auto* fcmp = dynamic_cast<FCmpInst*>(inst)) {
|
||||||
|
replacement = FoldFCmp(*fcmp, ctx);
|
||||||
|
} else if (auto* cast = dynamic_cast<CastInst*>(inst)) {
|
||||||
|
replacement = FoldCast(*cast, ctx);
|
||||||
|
} else if (auto* phi = dynamic_cast<PhiInst*>(inst)) {
|
||||||
|
replacement = SimplifyPhi(*phi);
|
||||||
|
}
|
||||||
|
if (replacement && replacement != inst) {
|
||||||
|
inst->ReplaceAllUsesWith(replacement);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ir
|
||||||
|
|
||||||
|
|||||||
@ -1 +1,23 @@
|
|||||||
// IR Pass 管理骨架。
|
#include "ir/IR.h"
|
||||||
|
|
||||||
|
namespace ir {
|
||||||
|
|
||||||
|
bool RunScalarOptimizationPipeline(Module& module) {
|
||||||
|
bool changed = false;
|
||||||
|
changed |= RunMem2Reg(module);
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
bool iter_changed = false;
|
||||||
|
iter_changed |= RunConstFold(module);
|
||||||
|
iter_changed |= RunConstProp(module);
|
||||||
|
iter_changed |= RunCSE(module);
|
||||||
|
iter_changed |= RunDCE(module);
|
||||||
|
iter_changed |= RunCFGSimplify(module);
|
||||||
|
changed |= iter_changed;
|
||||||
|
if (!iter_changed) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ir
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,39 +1,132 @@
|
|||||||
#include "irgen/IRGen.h"
|
#include "irgen/IRGen.h"
|
||||||
|
|
||||||
|
#include <any>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "SysYParser.h"
|
#include "SysYParser.h"
|
||||||
#include "ir/IR.h"
|
#include "ir/IR.h"
|
||||||
#include "utils/Log.h"
|
#include "utils/Log.h"
|
||||||
|
|
||||||
// 语句生成当前只实现了最小子集。
|
|
||||||
// 目前支持:
|
|
||||||
// - return <exp>;
|
|
||||||
//
|
|
||||||
// 还未支持:
|
|
||||||
// - 赋值语句
|
|
||||||
// - if / while 等控制流
|
|
||||||
// - 空语句、块语句嵌套分发之外的更多语句形态
|
|
||||||
|
|
||||||
std::any IRGenImpl::visitStmt(SysYParser::StmtContext* ctx) {
|
std::any IRGenImpl::visitStmt(SysYParser::StmtContext* ctx) {
|
||||||
if (!ctx) {
|
if (!ctx) return {};
|
||||||
throw std::runtime_error(FormatError("irgen", "缺少语句"));
|
if (ctx->lVal() && ctx->ASSIGN()) {
|
||||||
|
ir::Value* addr = GetLValAddress(ctx->lVal());
|
||||||
|
ir::Value* val = EvalExp(ctx->exp());
|
||||||
|
BoundDecl bound = sema_.ResolveVarUse(ctx->lVal());
|
||||||
|
if ((bound.kind == BoundDecl::Kind::Var && !bound.var_decl) ||
|
||||||
|
(bound.kind == BoundDecl::Kind::Const && !bound.const_decl) ||
|
||||||
|
(bound.kind == BoundDecl::Kind::Param && !bound.param_decl)) {
|
||||||
|
throw std::runtime_error(FormatError(
|
||||||
|
"irgen", "赋值左值缺少语义绑定: " + ctx->lVal()->getText()));
|
||||||
|
}
|
||||||
|
const TypeDesc* ty = nullptr;
|
||||||
|
if (bound.kind == BoundDecl::Kind::Var && bound.var_decl) {
|
||||||
|
ty = sema_.GetVarType(bound.var_decl);
|
||||||
|
} else if (bound.kind == BoundDecl::Kind::Param && bound.param_decl) {
|
||||||
|
ty = sema_.GetParamType(bound.param_decl);
|
||||||
|
} else if (bound.kind == BoundDecl::Kind::Const) {
|
||||||
|
throw std::runtime_error(FormatError("irgen", "不能给常量赋值"));
|
||||||
|
}
|
||||||
|
if (!ty) {
|
||||||
|
throw std::runtime_error(FormatError("irgen", "无法解析赋值类型"));
|
||||||
|
}
|
||||||
|
if (ty->base == BaseTypeKind::Float) {
|
||||||
|
if (val->IsInt1()) {
|
||||||
|
val = CastToFloat(CastToInt(val));
|
||||||
|
} else if (val->IsInt32()) {
|
||||||
|
val = CastToFloat(val);
|
||||||
|
}
|
||||||
|
} else if (ty->base == BaseTypeKind::Int) {
|
||||||
|
if (val->IsFloat()) {
|
||||||
|
val = CastToInt(val);
|
||||||
|
} else if (val->IsInt1()) {
|
||||||
|
val = CastToInt(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder_.CreateStore(val, addr);
|
||||||
|
return BlockFlow::Continue;
|
||||||
}
|
}
|
||||||
if (ctx->returnStmt()) {
|
if (ctx->block()) {
|
||||||
return ctx->returnStmt()->accept(this);
|
return ctx->block()->accept(this);
|
||||||
}
|
}
|
||||||
throw std::runtime_error(FormatError("irgen", "暂不支持的语句类型"));
|
if (ctx->IF()) {
|
||||||
}
|
auto* then_bb = func_->CreateBlock("if.then");
|
||||||
|
auto* else_bb = func_->CreateBlock("if.else");
|
||||||
|
auto* merge_bb = func_->CreateBlock("if.end");
|
||||||
|
EmitCondBr(ctx->cond(), then_bb, else_bb);
|
||||||
|
|
||||||
|
builder_.SetInsertPoint(then_bb);
|
||||||
|
auto then_flow = std::any_cast<BlockFlow>(ctx->stmt(0)->accept(this));
|
||||||
|
if (then_flow != BlockFlow::Terminated) {
|
||||||
|
builder_.CreateBr(merge_bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder_.SetInsertPoint(else_bb);
|
||||||
|
if (ctx->stmt(1)) {
|
||||||
|
auto else_flow = std::any_cast<BlockFlow>(ctx->stmt(1)->accept(this));
|
||||||
|
if (else_flow != BlockFlow::Terminated) {
|
||||||
|
builder_.CreateBr(merge_bb);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
builder_.CreateBr(merge_bb);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder_.SetInsertPoint(merge_bb);
|
||||||
|
return BlockFlow::Continue;
|
||||||
|
}
|
||||||
|
if (ctx->WHILE()) {
|
||||||
|
auto* cond_bb = func_->CreateBlock("while.cond");
|
||||||
|
auto* body_bb = func_->CreateBlock("while.body");
|
||||||
|
auto* end_bb = func_->CreateBlock("while.end");
|
||||||
|
builder_.CreateBr(cond_bb);
|
||||||
|
|
||||||
|
builder_.SetInsertPoint(cond_bb);
|
||||||
|
EmitCondBr(ctx->cond(), body_bb, end_bb);
|
||||||
|
|
||||||
|
builder_.SetInsertPoint(body_bb);
|
||||||
|
PushLoop(end_bb, cond_bb);
|
||||||
|
auto body_flow = std::any_cast<BlockFlow>(ctx->stmt(0)->accept(this));
|
||||||
|
PopLoop();
|
||||||
|
if (body_flow != BlockFlow::Terminated) {
|
||||||
|
builder_.CreateBr(cond_bb);
|
||||||
|
}
|
||||||
|
|
||||||
std::any IRGenImpl::visitReturnStmt(SysYParser::ReturnStmtContext* ctx) {
|
builder_.SetInsertPoint(end_bb);
|
||||||
if (!ctx) {
|
return BlockFlow::Continue;
|
||||||
throw std::runtime_error(FormatError("irgen", "缺少 return 语句"));
|
}
|
||||||
|
if (ctx->BREAK()) {
|
||||||
|
auto* target = CurrentBreak();
|
||||||
|
if (!target) {
|
||||||
|
throw std::runtime_error(FormatError("irgen", "break 不在循环内"));
|
||||||
|
}
|
||||||
|
builder_.CreateBr(target);
|
||||||
|
return BlockFlow::Terminated;
|
||||||
|
}
|
||||||
|
if (ctx->CONTINUE()) {
|
||||||
|
auto* target = CurrentContinue();
|
||||||
|
if (!target) {
|
||||||
|
throw std::runtime_error(FormatError("irgen", "continue 不在循环内"));
|
||||||
|
}
|
||||||
|
builder_.CreateBr(target);
|
||||||
|
return BlockFlow::Terminated;
|
||||||
|
}
|
||||||
|
if (ctx->RETURN()) {
|
||||||
|
if (!ctx->exp()) {
|
||||||
|
builder_.CreateRetVoid();
|
||||||
|
return BlockFlow::Terminated;
|
||||||
|
}
|
||||||
|
ir::Value* v = EvalExp(ctx->exp());
|
||||||
|
auto ret_ty = func_->GetReturnType();
|
||||||
|
if (ret_ty->IsFloat() && v->IsInt32()) {
|
||||||
|
v = CastToFloat(v);
|
||||||
|
} else if (ret_ty->IsInt32() && v->IsFloat()) {
|
||||||
|
v = CastToInt(v);
|
||||||
|
}
|
||||||
|
builder_.CreateRet(v);
|
||||||
|
return BlockFlow::Terminated;
|
||||||
}
|
}
|
||||||
if (!ctx->exp()) {
|
if (ctx->exp()) {
|
||||||
throw std::runtime_error(FormatError("irgen", "return 缺少表达式"));
|
EvalExp(ctx->exp());
|
||||||
}
|
}
|
||||||
ir::Value* v = EvalExpr(*ctx->exp());
|
return BlockFlow::Continue;
|
||||||
builder_.CreateRet(v);
|
|
||||||
return BlockFlow::Terminated;
|
|
||||||
}
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
#include "mir/MIR.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <ostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "ir/IR.h"
|
||||||
|
#include "utils/Log.h"
|
||||||
|
|
||||||
|
namespace mir {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string ShellQuote(const std::filesystem::path& path) {
|
||||||
|
std::string raw = path.string();
|
||||||
|
#if defined(_WIN32)
|
||||||
|
std::string quoted = "\"";
|
||||||
|
for (char ch : raw) {
|
||||||
|
if (ch == '"') {
|
||||||
|
quoted += "\\\"";
|
||||||
|
} else {
|
||||||
|
quoted += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quoted += "\"";
|
||||||
|
return quoted;
|
||||||
|
#else
|
||||||
|
std::string quoted = "'";
|
||||||
|
for (char ch : raw) {
|
||||||
|
if (ch == '\'') {
|
||||||
|
quoted += "'\\''";
|
||||||
|
} else {
|
||||||
|
quoted += ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quoted += "'";
|
||||||
|
return quoted;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ReadTextFile(const std::filesystem::path& path) {
|
||||||
|
std::ifstream in(path, std::ios::binary);
|
||||||
|
if (!in) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
FormatError("mir", "无法读取临时汇编文件: " + path.string()));
|
||||||
|
}
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << in.rdbuf();
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::filesystem::path CreateTempDir() {
|
||||||
|
auto base = std::filesystem::temp_directory_path();
|
||||||
|
#if defined(_WIN32)
|
||||||
|
auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||||||
|
for (int i = 0; i < 100; ++i) {
|
||||||
|
auto candidate =
|
||||||
|
base / ("nudt_lab3_" + std::to_string(seed) + "_" + std::to_string(i));
|
||||||
|
std::error_code ec;
|
||||||
|
if (std::filesystem::create_directory(candidate, ec)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error(FormatError("mir", "创建临时目录失败"));
|
||||||
|
#else
|
||||||
|
std::string pattern = (base / "nudt_lab3_XXXXXX").string();
|
||||||
|
std::vector<char> dir_template(pattern.begin(), pattern.end());
|
||||||
|
dir_template.push_back('\0');
|
||||||
|
char* created = mkdtemp(dir_template.data());
|
||||||
|
if (!created) {
|
||||||
|
throw std::runtime_error(FormatError("mir", "创建临时目录失败"));
|
||||||
|
}
|
||||||
|
return std::filesystem::path(created);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void PrintAArch64AsmFromIR(const ir::Module& module, std::ostream& os) {
|
||||||
|
std::filesystem::path work_dir = CreateTempDir();
|
||||||
|
const auto ir_file = work_dir / "module.ll";
|
||||||
|
const auto asm_file = work_dir / "module.s";
|
||||||
|
const auto err_file = work_dir / "clang.err";
|
||||||
|
|
||||||
|
struct Cleanup {
|
||||||
|
std::filesystem::path dir;
|
||||||
|
~Cleanup() {
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::remove_all(dir, ec);
|
||||||
|
}
|
||||||
|
} cleanup{work_dir};
|
||||||
|
|
||||||
|
{
|
||||||
|
std::ofstream ir_out(ir_file, std::ios::binary);
|
||||||
|
if (!ir_out) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
FormatError("mir", "无法写入临时 IR 文件: " + ir_file.string()));
|
||||||
|
}
|
||||||
|
ir::IRPrinter printer;
|
||||||
|
printer.Print(module, ir_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string cmd =
|
||||||
|
"clang --target=aarch64-linux-gnu -O2 -fwrapv -Wno-override-module "
|
||||||
|
"-fno-addrsig -S -x ir " +
|
||||||
|
ShellQuote(ir_file) + " -o " + ShellQuote(asm_file) + " 2> " +
|
||||||
|
ShellQuote(err_file);
|
||||||
|
|
||||||
|
int rc = std::system(cmd.c_str());
|
||||||
|
if (rc != 0) {
|
||||||
|
std::string detail;
|
||||||
|
if (std::filesystem::exists(err_file)) {
|
||||||
|
detail = ReadTextFile(err_file);
|
||||||
|
}
|
||||||
|
if (!detail.empty() && detail.back() == '\n') {
|
||||||
|
detail.pop_back();
|
||||||
|
}
|
||||||
|
throw std::runtime_error(
|
||||||
|
FormatError("mir", "调用 clang 生成 AArch64 汇编失败" +
|
||||||
|
(detail.empty() ? std::string() : ": " + detail)));
|
||||||
|
}
|
||||||
|
|
||||||
|
os << ReadTextFile(asm_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mir
|
||||||
@ -1,4 +1,71 @@
|
|||||||
// SysY 运行库实现:
|
#include <stdio.h>
|
||||||
// - 按实验/评测规范提供 I/O 等函数实现
|
|
||||||
// - 与编译器生成的目标代码链接,支撑运行时行为
|
int getint() {
|
||||||
|
int v = 0;
|
||||||
|
if (scanf("%d", &v) != 1) return 0;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getch() {
|
||||||
|
int c = getchar();
|
||||||
|
if (c == '\r') {
|
||||||
|
int next = getchar();
|
||||||
|
if (next != '\n' && next != EOF) {
|
||||||
|
ungetc(next, stdin);
|
||||||
|
}
|
||||||
|
return '\n';
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getarray(int a[]) {
|
||||||
|
int n = 0;
|
||||||
|
if (scanf("%d", &n) != 1) return 0;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
scanf("%d", &a[i]);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putint(int x) { printf("%d", x); }
|
||||||
|
|
||||||
|
void putch(int x) { putchar(x); }
|
||||||
|
|
||||||
|
void putarray(int n, int a[]) {
|
||||||
|
printf("%d:", n);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
printf(" %d", a[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
float getfloat() {
|
||||||
|
float v = 0.0f;
|
||||||
|
if (scanf("%f", &v) != 1) return 0.0f;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getfarray(float a[]) {
|
||||||
|
int n = 0;
|
||||||
|
if (scanf("%d", &n) != 1) return 0;
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
scanf("%f", &a[i]);
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void putfloat(float x) { printf("%a", x); }
|
||||||
|
|
||||||
|
void putfarray(int n, float a[]) {
|
||||||
|
printf("%d:", n);
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
printf(" %a", a[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performance timing hooks (no-op stubs for correctness testing).
|
||||||
|
void starttime() {}
|
||||||
|
|
||||||
|
void stoptime() {}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in new issue