forked from NUDT-compiler/nudt-compiler-cpp
Compare commits
No commits in common. 'master' and 'master' have entirely different histories.
@ -1,7 +0,0 @@
|
|||||||
# 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"
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "nudt-compiler-cpp"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
@ -1,42 +1,17 @@
|
|||||||
// 符号表:记录局部变量/常量/参数定义。
|
// 极简符号表:记录局部变量定义点。
|
||||||
#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 EnterScope();
|
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
||||||
void ExitScope();
|
bool Contains(const std::string& name) const;
|
||||||
|
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::vector<std::unordered_map<std::string, SymbolEntry>> scopes_;
|
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
#!/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"
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
#!/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"
|
|
||||||
@ -1,119 +0,0 @@
|
|||||||
#!/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,148 +1,31 @@
|
|||||||
// 支持 void/i1/i32/float/ptr/array/function/label。
|
// 当前仅支持 void、i32 和 i32*。
|
||||||
#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::GetFloatType() {
|
const std::shared_ptr<Type>& Type::GetPtrInt32Type() {
|
||||||
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::Float);
|
static const std::shared_ptr<Type> type = std::make_shared<Type>(Kind::PtrInt32);
|
||||||
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::IsFloat() const { return kind_ == Kind::Float; }
|
bool Type::IsPtrInt32() const { return kind_ == Kind::PtrInt32; }
|
||||||
|
|
||||||
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,135 +1,4 @@
|
|||||||
#include "ir/IR.h"
|
// CFG 简化:
|
||||||
|
// - 删除不可达块、合并空块、简化分支等
|
||||||
#include <memory>
|
// - 改善 IR 结构,便于后续优化与后端生成
|
||||||
#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,239 +1,4 @@
|
|||||||
#include "ir/IR.h"
|
// IR 常量折叠:
|
||||||
|
// - 折叠可判定的常量表达式
|
||||||
#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,23 +1 @@
|
|||||||
#include "ir/IR.h"
|
// IR Pass 管理骨架。
|
||||||
|
|
||||||
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,132 +1,39 @@
|
|||||||
#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) return {};
|
if (!ctx) {
|
||||||
if (ctx->lVal() && ctx->ASSIGN()) {
|
throw std::runtime_error(FormatError("irgen", "缺少语句"));
|
||||||
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->block()) {
|
|
||||||
return ctx->block()->accept(this);
|
|
||||||
}
|
}
|
||||||
if (ctx->IF()) {
|
if (ctx->returnStmt()) {
|
||||||
auto* then_bb = func_->CreateBlock("if.then");
|
return ctx->returnStmt()->accept(this);
|
||||||
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()) {
|
throw std::runtime_error(FormatError("irgen", "暂不支持的语句类型"));
|
||||||
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);
|
std::any IRGenImpl::visitReturnStmt(SysYParser::ReturnStmtContext* ctx) {
|
||||||
PushLoop(end_bb, cond_bb);
|
if (!ctx) {
|
||||||
auto body_flow = std::any_cast<BlockFlow>(ctx->stmt(0)->accept(this));
|
throw std::runtime_error(FormatError("irgen", "缺少 return 语句"));
|
||||||
PopLoop();
|
|
||||||
if (body_flow != BlockFlow::Terminated) {
|
|
||||||
builder_.CreateBr(cond_bb);
|
|
||||||
}
|
|
||||||
|
|
||||||
builder_.SetInsertPoint(end_bb);
|
|
||||||
return BlockFlow::Continue;
|
|
||||||
}
|
|
||||||
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()) {
|
||||||
EvalExp(ctx->exp());
|
throw std::runtime_error(FormatError("irgen", "return 缺少表达式"));
|
||||||
}
|
}
|
||||||
return BlockFlow::Continue;
|
ir::Value* v = EvalExpr(*ctx->exp());
|
||||||
}
|
builder_.CreateRet(v);
|
||||||
|
return BlockFlow::Terminated;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
||||||
@ -1,132 +0,0 @@
|
|||||||
#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,2 +1,16 @@
|
|||||||
// 机器基本块:实现已并入头文件,本文件仅保留 TU 占位。
|
|
||||||
#include "mir/MIR.h"
|
#include "mir/MIR.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace mir {
|
||||||
|
|
||||||
|
MachineBasicBlock::MachineBasicBlock(std::string name)
|
||||||
|
: name_(std::move(name)) {}
|
||||||
|
|
||||||
|
MachineInstr& MachineBasicBlock::Append(Opcode opcode,
|
||||||
|
std::initializer_list<Operand> operands) {
|
||||||
|
instructions_.emplace_back(opcode, std::vector<Operand>(operands));
|
||||||
|
return instructions_.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mir
|
||||||
|
|||||||
@ -1,2 +1,10 @@
|
|||||||
// 机器上下文:实现已并入头文件,本文件仅保留 TU 占位。
|
|
||||||
#include "mir/MIR.h"
|
#include "mir/MIR.h"
|
||||||
|
|
||||||
|
namespace mir {
|
||||||
|
|
||||||
|
MIRContext& DefaultContext() {
|
||||||
|
static MIRContext ctx;
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mir
|
||||||
|
|||||||
@ -1,2 +1,33 @@
|
|||||||
// 机器函数:实现已并入头文件,本文件仅保留 TU 占位。
|
|
||||||
#include "mir/MIR.h"
|
#include "mir/MIR.h"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "utils/Log.h"
|
||||||
|
|
||||||
|
namespace mir {
|
||||||
|
|
||||||
|
MachineFunction::MachineFunction(std::string name)
|
||||||
|
: name_(std::move(name)), entry_("entry") {}
|
||||||
|
|
||||||
|
int MachineFunction::CreateFrameIndex(int size) {
|
||||||
|
int index = static_cast<int>(frame_slots_.size());
|
||||||
|
frame_slots_.push_back(FrameSlot{index, size, 0});
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
FrameSlot& MachineFunction::GetFrameSlot(int index) {
|
||||||
|
if (index < 0 || index >= static_cast<int>(frame_slots_.size())) {
|
||||||
|
throw std::runtime_error(FormatError("mir", "非法 FrameIndex"));
|
||||||
|
}
|
||||||
|
return frame_slots_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
const FrameSlot& MachineFunction::GetFrameSlot(int index) const {
|
||||||
|
if (index < 0 || index >= static_cast<int>(frame_slots_.size())) {
|
||||||
|
throw std::runtime_error(FormatError("mir", "非法 FrameIndex"));
|
||||||
|
}
|
||||||
|
return frame_slots_[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace mir
|
||||||
|
|||||||
@ -1,2 +1,23 @@
|
|||||||
// 机器指令:实现已并入头文件,本文件仅保留 TU 占位。
|
|
||||||
#include "mir/MIR.h"
|
#include "mir/MIR.h"
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace mir {
|
||||||
|
|
||||||
|
Operand::Operand(Kind kind, PhysReg reg, int imm)
|
||||||
|
: kind_(kind), reg_(reg), imm_(imm) {}
|
||||||
|
|
||||||
|
Operand Operand::Reg(PhysReg reg) { return Operand(Kind::Reg, reg, 0); }
|
||||||
|
|
||||||
|
Operand Operand::Imm(int value) {
|
||||||
|
return Operand(Kind::Imm, PhysReg::W0, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand Operand::FrameIndex(int index) {
|
||||||
|
return Operand(Kind::FrameIndex, PhysReg::W0, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
MachineInstr::MachineInstr(Opcode opcode, std::vector<Operand> operands)
|
||||||
|
: opcode_(opcode), operands_(std::move(operands)) {}
|
||||||
|
|
||||||
|
} // namespace mir
|
||||||
|
|||||||
@ -1,71 +1,4 @@
|
|||||||
#include <stdio.h>
|
// SysY 运行库实现:
|
||||||
|
// - 按实验/评测规范提供 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