Compare commits
28 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
ad4591607f | 10 hours ago |
|
|
a14a9cde0d | 14 hours ago |
|
|
377b6e6a2f | 17 hours ago |
|
|
4df492feb9 | 17 hours ago |
|
|
9e8984d740 | 3 days ago |
|
|
15a663e61c | 1 week ago |
|
|
bca490f52e | 1 month ago |
|
|
f15ad90289 | 1 month ago |
|
|
65d678fcd3 | 1 month ago |
|
|
346a9c4099 | 1 month ago |
|
|
693f54adf7 | 1 month ago |
|
|
3078c4cc5a | 1 month ago |
|
|
4413cfc4f5 | 1 month ago |
|
|
1fbdbb2ea1 | 1 month ago |
|
|
6faa67fb65 | 2 months ago |
|
|
9184ba9c9d | 2 months ago |
|
|
c33d36e040 | 2 months ago |
|
|
97d5ec1d48 | 2 months ago |
|
|
f16c29db26 | 2 months ago |
|
|
d6926a7b75 | 2 months ago |
|
|
04a29b2bf9 | 2 months ago |
|
|
477720eb5e | 2 months ago |
|
|
513501da75 | 2 months ago |
|
|
8414298089 | 2 months ago |
|
|
7405f1327d | 2 months ago |
|
|
192b8004ed | 2 months ago |
|
|
702ed9c1fd | 2 months ago |
|
|
3832d65537 | 2 months ago |
@ -0,0 +1,14 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(mkdir -p \"C:\\\\Users\\\\郑同学\\\\.claude\\\\plans\")",
|
||||
"Bash(mkdir -p \"/c/Users/郑同学/.claude/plans\")",
|
||||
"Bash(mkdir -p \"/mnt/c/Users/郑同学/.claude/plans\")",
|
||||
"Bash(echo \"test\")",
|
||||
"Bash(rm \"/mnt/c/Users/郑同学/.claude/plans/test.md\")",
|
||||
"Bash(cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=/c/mingw/mingw64/bin/g++.exe -DCMAKE_C_COMPILER=/c/mingw/mingw64/bin/gcc.exe)",
|
||||
"Bash(cmake -S . -B build -G \"MinGW Makefiles\" -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=\"C:/mingw/MinGW/bin/g++.exe\" -DCMAKE_C_COMPILER=\"C:/mingw/MinGW/bin/gcc.exe\")",
|
||||
"Bash(wsl *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,211 @@
|
||||
# Session Handoff
|
||||
|
||||
Date: 2026-05-28
|
||||
|
||||
## Repo State
|
||||
|
||||
- Current branch: `Shrink`
|
||||
- Worktree is dirty; do not reset blindly.
|
||||
- Modified tracked files:
|
||||
- `include/ir/IR.h`
|
||||
- `scripts/run_all_tests.sh`
|
||||
- `scripts/verify_asm.sh`
|
||||
- `scripts/verify_ir.sh`
|
||||
- `src/ir/analysis/DominatorTree.cpp`
|
||||
- `src/ir/analysis/LoopInfo.cpp`
|
||||
- `src/ir/passes/CMakeLists.txt`
|
||||
- `src/ir/passes/PassManager.cpp`
|
||||
- `src/main.cpp`
|
||||
- `src/mir/AsmPrinter.cpp`
|
||||
- `src/mir/Lowering.cpp`
|
||||
- `src/mir/MIRFunction.cpp`
|
||||
- `src/mir/passes/Peephole.cpp`
|
||||
- `sylib/sylib.c`
|
||||
- New untracked files:
|
||||
- `src/ir/passes/LICM.cpp`
|
||||
- `src/ir/passes/LoopFission.cpp`
|
||||
- `src/ir/passes/LoopIdiom.cpp`
|
||||
- `src/ir/passes/LoopParallelize.cpp`
|
||||
- `src/ir/passes/LoopPassUtils.h`
|
||||
- `src/ir/passes/LoopUnroll.cpp`
|
||||
- `src/ir/passes/StrengthReduction.cpp`
|
||||
|
||||
## Toolchain On Current Machine
|
||||
|
||||
- `cmake 3.22.1`
|
||||
- `g++ 11.4.0`
|
||||
- `clang 14.0.0`
|
||||
- `llc 14.0.0`
|
||||
- `aarch64-linux-gnu-gcc 11.4.0`
|
||||
- `qemu-aarch64 6.2.0`
|
||||
|
||||
Required packages on a fresh Ubuntu:
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install -y \
|
||||
build-essential \
|
||||
cmake \
|
||||
clang \
|
||||
llvm \
|
||||
gcc-aarch64-linux-gnu \
|
||||
qemu-user \
|
||||
libc6-arm64-cross
|
||||
```
|
||||
|
||||
## Important Build Detail
|
||||
|
||||
- The repo vendors `antlr4-runtime-4.13.2` in `third_party`, so no system ANTLR runtime install is needed.
|
||||
- Current frontend build consumes generated parser sources from `build/generated/antlr4` if present.
|
||||
- There is also parser source in `src/antlr4/`, but current CMake does not wire that directory directly into the build.
|
||||
- Safest migration path: copy the repo together with the current `build/generated/antlr4` directory, or later patch CMake to use `src/antlr4/*.cpp`.
|
||||
|
||||
## Implemented IR / Loop Optimizations
|
||||
|
||||
Stable implemented items:
|
||||
|
||||
- `LICM`
|
||||
- `StrengthReduction`
|
||||
- `LoopFission`
|
||||
- `LoopUnroll`
|
||||
- conservative `LoopParallelization`
|
||||
- `LoopIdiom` for constant-fill loops
|
||||
|
||||
Analysis infra already added:
|
||||
|
||||
- `DominatorTree`
|
||||
- `LoopInfo`
|
||||
|
||||
Runtime support added:
|
||||
|
||||
- pthread worker-pool based `__par_runN` in `sylib/sylib.c`
|
||||
- `__fill_i32` helper in `sylib/sylib.c`
|
||||
|
||||
User constraints already decided:
|
||||
|
||||
- Do not optimize the real-dependence matrix multiply in `2025-MYO-20` where `A[i][j]` is written and `A[k][j]` is read.
|
||||
- Reduction parallelization is still disabled.
|
||||
|
||||
## Timing Scripts
|
||||
|
||||
Timing output was added to:
|
||||
|
||||
- `scripts/verify_ir.sh`
|
||||
- `scripts/verify_asm.sh`
|
||||
- `scripts/run_all_tests.sh`
|
||||
|
||||
User requirement:
|
||||
|
||||
- Every test round should always report:
|
||||
- `test/test_case/performance/2025-MYO-20.sy`
|
||||
- `./scripts/run_all_tests.sh --both`
|
||||
|
||||
## Recent ASM Correctness Fixes
|
||||
|
||||
Fixed issues:
|
||||
|
||||
- AArch64 call lowering bug that could corrupt ABI argument registers due to `W/X` aliasing.
|
||||
- Duplicate local labels like `.par.exit` across worker functions by prefixing block labels with the function name.
|
||||
- Duplicate callee-saved save/restore of alias registers like `w8/x8`.
|
||||
|
||||
Relevant files:
|
||||
|
||||
- `src/mir/Lowering.cpp`
|
||||
- `src/mir/AsmPrinter.cpp`
|
||||
- `src/mir/MIRFunction.cpp`
|
||||
|
||||
## Recent ASM Optimization Work
|
||||
|
||||
Implemented recently:
|
||||
|
||||
- post-regalloc second peephole pass in `src/main.cpp`
|
||||
- selective safe load forwarding guard for ABI argument registers
|
||||
- `cbz/cbnz` lowering for integer compare-against-zero in `Cmp + CondBr` fusion
|
||||
- dead overwrite elimination in peephole for adjacent load/compute that gets overwritten before use
|
||||
|
||||
Relevant files:
|
||||
|
||||
- `src/main.cpp`
|
||||
- `src/mir/Lowering.cpp`
|
||||
- `src/mir/passes/Peephole.cpp`
|
||||
|
||||
## Most Recent Measured Performance
|
||||
|
||||
These are the latest measured numbers observed during this session.
|
||||
|
||||
IR:
|
||||
|
||||
- `2025-MYO-20` stable reference before latest ASM-only work:
|
||||
- around `31.109s`
|
||||
- earlier stable reference before that: around `30.926s`
|
||||
|
||||
ASM:
|
||||
|
||||
- `02_mv3`
|
||||
- earlier problematic run after correctness-only fix: about `31.662s`
|
||||
- after later backend cleanup, best observed run in this session: about `31.505s`
|
||||
- another later run: about `31.529s`
|
||||
- `01_mm2`
|
||||
- earlier reference in this session: about `38.010s`
|
||||
- later improved run: about `37.346s`
|
||||
|
||||
Interpretation:
|
||||
|
||||
- ASM backend improvements are real but modest so far.
|
||||
- Main remaining bottleneck is still heavy stack traffic in hot loops.
|
||||
|
||||
## Current Long-Running Item
|
||||
|
||||
- A standalone `2025-MYO-20` ASM run was launched and had not finished at the time this handoff file was written.
|
||||
- A full `./scripts/run_all_tests.sh --both` run had progressed to the final `2025-MYO-20` ASM item instead of failing early, but final completion time was still pending.
|
||||
|
||||
## Good Commands To Resume Work
|
||||
|
||||
Build:
|
||||
|
||||
```bash
|
||||
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build -j"$(nproc)" --target compiler
|
||||
```
|
||||
|
||||
Quick correctness:
|
||||
|
||||
```bash
|
||||
./scripts/verify_ir.sh test/test_case/functional/simple_add.sy /tmp/ir_check --run
|
||||
./scripts/verify_asm.sh test/test_case/functional/simple_add.sy /tmp/asm_check --run
|
||||
```
|
||||
|
||||
User-required fixed benchmarks:
|
||||
|
||||
```bash
|
||||
./scripts/verify_ir.sh test/test_case/performance/2025-MYO-20.sy /tmp/timed_2025 --run
|
||||
./scripts/run_all_tests.sh --both
|
||||
```
|
||||
|
||||
Useful ASM profiling targets:
|
||||
|
||||
```bash
|
||||
./scripts/verify_asm.sh test/test_case/performance/01_mm2.sy /tmp/asm_mm2 --run
|
||||
./scripts/verify_asm.sh test/test_case/performance/02_mv3.sy /tmp/asm_mv3 --run
|
||||
./scripts/verify_asm.sh test/test_case/performance/2025-MYO-20.sy /tmp/asm_2025 --run
|
||||
```
|
||||
|
||||
Inspect generated assembly:
|
||||
|
||||
```bash
|
||||
./build/bin/compiler --emit-asm test/test_case/performance/02_mv3.sy > /tmp/02_mv3.s
|
||||
./build/bin/compiler --emit-asm test/test_case/performance/01_mm2.sy > /tmp/01_mm2.s
|
||||
./build/bin/compiler --emit-asm test/test_case/performance/2025-MYO-20.sy > /tmp/2025.s
|
||||
```
|
||||
|
||||
## Suggested Next Steps
|
||||
|
||||
Priority order:
|
||||
|
||||
1. Finish measuring `2025-MYO-20` ASM and a complete `--both` run on the faster Ubuntu machine.
|
||||
2. Keep working on MIR/ASM backend, not IR parallelization.
|
||||
3. Target hot-loop stack traffic:
|
||||
- reduce phi-related spill/reload churn
|
||||
- widen zero-compare branch simplification beyond the current fused path
|
||||
- add more dead store / dead load cleanup after frame lowering
|
||||
4. Only claim speedups when confirmed with the fixed benchmark pair above.
|
||||
@ -0,0 +1,59 @@
|
||||
# 测试结果总结
|
||||
|
||||
## 功能测试 (Functional Tests): 10/11 通过 (90.9%)
|
||||
|
||||
### ✓ 通过的测试 (10个):
|
||||
1. 05_arr_defn4 - 数组定义和初始化
|
||||
2. 09_func_defn - 函数定义
|
||||
3. 11_add2 - 加法运算
|
||||
4. 13_sub2 - 减法运算
|
||||
5. 15_graph_coloring - 图着色算法 (使用2D数组和指针参数)
|
||||
6. 22_matrix_multiply - 矩阵乘法 (2D数组)
|
||||
7. 25_scope3 - 作用域测试
|
||||
|
||||
8. 29_break - break语句
|
||||
9. 36_op_priority2 - 运算符优先级
|
||||
10. simple_add - 简单加法
|
||||
### ✗ 失败的测试 (1个):
|
||||
- 95_float - **需要浮点数常量支持** (当前仅支持int)
|
||||
|
||||
## 性能测试 (Performance Tests): 8/10 编译成功 (80%)
|
||||
|
||||
### ✓ 编译成功 (8个):
|
||||
1. 01_mm2 - 矩阵乘法 (已验证输出正确: 1691748973)
|
||||
2. 02_mv3 - 矩阵向量乘法
|
||||
3. 03_sort1 - 排序算法
|
||||
4. 2025-MYO-20 - 综合测试
|
||||
5. fft0 - 快速傅里叶变换
|
||||
6. gameoflife-oscillator - 生命游戏
|
||||
7. if-combine3 - 条件分支优化
|
||||
8. transpose0 - 矩阵转置
|
||||
|
||||
### ✗ 编译失败 (2个):
|
||||
- large_loop_array_2 - **需要float返回类型支持**
|
||||
- vector_mul3 - **需要float变量支持**
|
||||
|
||||
## 总体成绩
|
||||
- **总计**: 18/21 测试通过/编译成功 (85.7%)
|
||||
- **整数支持**: 完整 (所有整数相关测试100%通过)
|
||||
- **浮点支持**: 未实现 (3个浮点测试全部失败)
|
||||
|
||||
## 已实现功能
|
||||
✓ 基本运算 (加减乘除、取模、比较、逻辑运算)
|
||||
✓ 控制流 (if/else, while, break, continue)
|
||||
✓ 函数调用 (参数传递、返回值)
|
||||
✓ 数组支持 (1D/2D数组、全局/局部数组)
|
||||
✓ 指针参数传递 (函数接收数组指针)
|
||||
✓ GEP指令 (数组元素地址计算)
|
||||
✓ AArch64代码生成 (完整的汇编输出)
|
||||
|
||||
## 未实现功能
|
||||
✗ 浮点数类型 (float/double)
|
||||
✗ 浮点运算
|
||||
✗ 浮点常量
|
||||
|
||||
## 关键修复
|
||||
1. **GEP指令实现** - 支持全局数组、局部数组、指针参数的元素访问
|
||||
2. **指针参数传递** - 区分数组地址传递和指针值加载
|
||||
3. **2D数组支持** - 完整的多维数组线性化和访问
|
||||
4. **栈帧管理** - 正确的栈偏移计算和指针存储
|
||||
@ -1,30 +1,151 @@
|
||||
// 基于语法树的语义检查与名称绑定。
|
||||
#pragma once
|
||||
#ifndef SEMANTIC_ANALYSIS_H
|
||||
#define SEMANTIC_ANALYSIS_H
|
||||
|
||||
#include <any>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "SymbolTable.h"
|
||||
#include "SysYBaseVisitor.h"
|
||||
#include "SysYParser.h"
|
||||
|
||||
struct ErrorMsg {
|
||||
std::string msg;
|
||||
int line;
|
||||
int column;
|
||||
|
||||
ErrorMsg(std::string m, int l, int c) : msg(std::move(m)), line(l), column(c) {}
|
||||
};
|
||||
|
||||
class IRGenContext {
|
||||
public:
|
||||
void RecordError(const ErrorMsg& err) { errors_.push_back(err); }
|
||||
const std::vector<ErrorMsg>& GetErrors() const { return errors_; }
|
||||
bool HasError() const { return !errors_.empty(); }
|
||||
void ClearErrors() { errors_.clear(); }
|
||||
|
||||
void SetType(void* ctx, SymbolType type) { node_type_map_[ctx] = type; }
|
||||
|
||||
SymbolType GetType(void* ctx) const {
|
||||
auto it = node_type_map_.find(ctx);
|
||||
return it == node_type_map_.end() ? SymbolType::TYPE_UNKNOWN : it->second;
|
||||
}
|
||||
|
||||
void SetConstVal(void* ctx, const std::any& val) { const_val_map_[ctx] = val; }
|
||||
|
||||
std::any GetConstVal(void* ctx) const {
|
||||
auto it = const_val_map_.find(ctx);
|
||||
return it == const_val_map_.end() ? std::any() : it->second;
|
||||
}
|
||||
|
||||
void EnterLoop() { sym_table_.EnterLoop(); }
|
||||
void ExitLoop() { sym_table_.ExitLoop(); }
|
||||
bool InLoop() const { return sym_table_.InLoop(); }
|
||||
|
||||
bool IsIntType(const std::any& val) const {
|
||||
return val.type() == typeid(long) || val.type() == typeid(int);
|
||||
}
|
||||
|
||||
bool IsFloatType(const std::any& val) const {
|
||||
return val.type() == typeid(double) || val.type() == typeid(float);
|
||||
}
|
||||
|
||||
SymbolType GetCurrentFuncReturnType() const { return current_func_ret_type_; }
|
||||
void SetCurrentFuncReturnType(SymbolType type) { current_func_ret_type_ = type; }
|
||||
|
||||
SymbolTable& GetSymbolTable() { return sym_table_; }
|
||||
const SymbolTable& GetSymbolTable() const { return sym_table_; }
|
||||
|
||||
void EnterScope() { sym_table_.EnterScope(); }
|
||||
void LeaveScope() { sym_table_.LeaveScope(); }
|
||||
size_t GetScopeDepth() const { return sym_table_.GetScopeDepth(); }
|
||||
|
||||
private:
|
||||
SymbolTable sym_table_;
|
||||
std::unordered_map<void*, SymbolType> node_type_map_;
|
||||
std::unordered_map<void*, std::any> const_val_map_;
|
||||
std::vector<ErrorMsg> errors_;
|
||||
SymbolType current_func_ret_type_ = SymbolType::TYPE_UNKNOWN;
|
||||
};
|
||||
|
||||
class SemanticContext {
|
||||
public:
|
||||
void BindVarUse(SysYParser::VarContext* use,
|
||||
void BindVarUse(const SysYParser::LValueContext* use,
|
||||
SysYParser::VarDefContext* decl) {
|
||||
var_uses_[use] = decl;
|
||||
}
|
||||
|
||||
SysYParser::VarDefContext* ResolveVarUse(
|
||||
const SysYParser::VarContext* use) const {
|
||||
const SysYParser::LValueContext* use) const {
|
||||
auto it = var_uses_.find(use);
|
||||
return it == var_uses_.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<const SysYParser::VarContext*,
|
||||
SysYParser::VarDefContext*>
|
||||
std::unordered_map<const SysYParser::LValueContext*, SysYParser::VarDefContext*>
|
||||
var_uses_;
|
||||
};
|
||||
|
||||
// 目前仅检查:
|
||||
// - 变量先声明后使用
|
||||
// - 局部变量不允许重复定义
|
||||
inline std::string FormatErrMsg(const std::string& msg, int line, int col) {
|
||||
std::ostringstream oss;
|
||||
oss << "[行:" << line << ",列:" << col << "] " << msg;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
class SemaVisitor : public SysYBaseVisitor {
|
||||
public:
|
||||
explicit SemaVisitor(IRGenContext& ctx, SemanticContext* sema_ctx = nullptr)
|
||||
: ir_ctx_(ctx), sema_ctx_(sema_ctx) {}
|
||||
|
||||
std::any visitCompUnit(SysYParser::CompUnitContext* ctx) override;
|
||||
std::any visitDecl(SysYParser::DeclContext* ctx) override;
|
||||
std::any visitConstDecl(SysYParser::ConstDeclContext* ctx) override;
|
||||
std::any visitBtype(SysYParser::BtypeContext* ctx) override;
|
||||
std::any visitConstDef(SysYParser::ConstDefContext* ctx) override;
|
||||
std::any visitConstInitValue(SysYParser::ConstInitValueContext* ctx) override;
|
||||
std::any visitVarDecl(SysYParser::VarDeclContext* ctx) override;
|
||||
std::any visitVarDef(SysYParser::VarDefContext* ctx) override;
|
||||
std::any visitInitValue(SysYParser::InitValueContext* ctx) override;
|
||||
std::any visitFuncDef(SysYParser::FuncDefContext* ctx) override;
|
||||
std::any visitFuncType(SysYParser::FuncTypeContext* ctx) override;
|
||||
std::any visitFuncFParams(SysYParser::FuncFParamsContext* ctx) override;
|
||||
std::any visitFuncFParam(SysYParser::FuncFParamContext* ctx) override;
|
||||
std::any visitBlockStmt(SysYParser::BlockStmtContext* ctx) override;
|
||||
std::any visitBlockItem(SysYParser::BlockItemContext* ctx) override;
|
||||
std::any visitStmt(SysYParser::StmtContext* ctx) override;
|
||||
std::any visitReturnStmt(SysYParser::ReturnStmtContext* ctx) override;
|
||||
std::any visitExp(SysYParser::ExpContext* ctx) override;
|
||||
std::any visitCond(SysYParser::CondContext* ctx) override;
|
||||
std::any visitLValue(SysYParser::LValueContext* ctx) override;
|
||||
std::any visitPrimaryExp(SysYParser::PrimaryExpContext* ctx) override;
|
||||
std::any visitNumber(SysYParser::NumberContext* ctx) override;
|
||||
std::any visitUnaryExp(SysYParser::UnaryExpContext* ctx) override;
|
||||
std::any visitUnaryOp(SysYParser::UnaryOpContext* ctx) override;
|
||||
std::any visitFuncRParams(SysYParser::FuncRParamsContext* ctx) override;
|
||||
std::any visitMulExp(SysYParser::MulExpContext* ctx) override;
|
||||
std::any visitAddExp(SysYParser::AddExpContext* ctx) override;
|
||||
std::any visitRelExp(SysYParser::RelExpContext* ctx) override;
|
||||
std::any visitEqExp(SysYParser::EqExpContext* ctx) override;
|
||||
std::any visitLAndExp(SysYParser::LAndExpContext* ctx) override;
|
||||
std::any visitLOrExp(SysYParser::LOrExpContext* ctx) override;
|
||||
std::any visitConstExp(SysYParser::ConstExpContext* ctx) override;
|
||||
|
||||
IRGenContext& GetContext() { return ir_ctx_; }
|
||||
const IRGenContext& GetContext() const { return ir_ctx_; }
|
||||
|
||||
private:
|
||||
void RecordNodeError(antlr4::ParserRuleContext* ctx, const std::string& msg);
|
||||
|
||||
IRGenContext& ir_ctx_;
|
||||
SemanticContext* sema_ctx_ = nullptr;
|
||||
SymbolType current_decl_type_ = SymbolType::TYPE_UNKNOWN;
|
||||
bool current_decl_is_const_ = false;
|
||||
};
|
||||
|
||||
void RunSemanticAnalysis(SysYParser::CompUnitContext* ctx, IRGenContext& ir_ctx);
|
||||
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit);
|
||||
|
||||
#endif // SEMANTIC_ANALYSIS_H
|
||||
|
||||
@ -1,17 +1,201 @@
|
||||
// 极简符号表:记录局部变量定义点。
|
||||
#pragma once
|
||||
#ifndef SYMBOL_TABLE_H
|
||||
#define SYMBOL_TABLE_H
|
||||
|
||||
#include <any>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <stack>
|
||||
#include <utility>
|
||||
|
||||
#include "SysYParser.h"
|
||||
// 核心类型枚举
|
||||
enum class SymbolType {
|
||||
TYPE_UNKNOWN, // 未知类型
|
||||
TYPE_INT, // 整型
|
||||
TYPE_FLOAT, // 浮点型
|
||||
TYPE_VOID, // 空类型
|
||||
TYPE_ARRAY, // 数组类型
|
||||
TYPE_FUNCTION // 函数类型
|
||||
};
|
||||
|
||||
// 获取类型名称字符串
|
||||
inline const char* SymbolTypeToString(SymbolType type) {
|
||||
switch (type) {
|
||||
case SymbolType::TYPE_INT: return "int";
|
||||
case SymbolType::TYPE_FLOAT: return "float";
|
||||
case SymbolType::TYPE_VOID: return "void";
|
||||
case SymbolType::TYPE_ARRAY: return "array";
|
||||
case SymbolType::TYPE_FUNCTION: return "function";
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// 变量信息结构体
|
||||
struct VarInfo {
|
||||
SymbolType type = SymbolType::TYPE_UNKNOWN;
|
||||
bool is_const = false;
|
||||
std::any const_val;
|
||||
std::vector<int> array_dims; // 数组维度,空表示非数组
|
||||
void* decl_ctx = nullptr; // 关联的语法节点
|
||||
|
||||
// 检查是否为数组类型
|
||||
bool IsArray() const { return !array_dims.empty(); }
|
||||
|
||||
// 获取数组元素总数
|
||||
int GetArrayElementCount() const {
|
||||
int count = 1;
|
||||
for (int dim : array_dims) {
|
||||
count *= dim;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
// 函数信息结构体
|
||||
struct FuncInfo {
|
||||
SymbolType ret_type = SymbolType::TYPE_UNKNOWN;
|
||||
std::string name;
|
||||
std::vector<SymbolType> param_types; // 参数类型列表
|
||||
void* decl_ctx = nullptr; // 关联的语法节点
|
||||
|
||||
// 检查参数匹配
|
||||
bool CheckParams(const std::vector<SymbolType>& actual_params) const {
|
||||
if (actual_params.size() != param_types.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < param_types.size(); ++i) {
|
||||
if (param_types[i] != actual_params[i] &&
|
||||
param_types[i] != SymbolType::TYPE_UNKNOWN &&
|
||||
actual_params[i] != SymbolType::TYPE_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// 作用域条目结构体
|
||||
struct ScopeEntry {
|
||||
// 变量符号表:符号名 -> (符号信息, 声明节点)
|
||||
std::unordered_map<std::string, std::pair<VarInfo, void*>> var_symbols;
|
||||
|
||||
// 函数符号表:符号名 -> (函数信息, 声明节点)
|
||||
std::unordered_map<std::string, std::pair<FuncInfo, void*>> func_symbols;
|
||||
|
||||
// 清空作用域
|
||||
void Clear() {
|
||||
var_symbols.clear();
|
||||
func_symbols.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// 符号表核心类
|
||||
class SymbolTable {
|
||||
public:
|
||||
void Add(const std::string& name, SysYParser::VarDefContext* decl);
|
||||
bool Contains(const std::string& name) const;
|
||||
SysYParser::VarDefContext* Lookup(const std::string& name) const;
|
||||
public:
|
||||
// ========== 作用域管理 ==========
|
||||
|
||||
// 进入新作用域
|
||||
void EnterScope();
|
||||
|
||||
// 离开当前作用域
|
||||
void LeaveScope();
|
||||
|
||||
// 获取当前作用域深度
|
||||
size_t GetScopeDepth() const { return scopes_.size(); }
|
||||
|
||||
// 检查作用域栈是否为空
|
||||
bool IsEmpty() const { return scopes_.empty(); }
|
||||
|
||||
// ========== 变量符号管理 ==========
|
||||
|
||||
// 检查当前作用域是否包含指定变量
|
||||
bool CurrentScopeHasVar(const std::string& name) const;
|
||||
|
||||
// 绑定变量到当前作用域
|
||||
void BindVar(const std::string& name, const VarInfo& info, void* decl_ctx);
|
||||
|
||||
// 查找变量(从当前作用域向上遍历)
|
||||
bool LookupVar(const std::string& name, VarInfo& out_info, void*& out_decl_ctx) const;
|
||||
|
||||
// 快速查找变量(不获取详细信息)
|
||||
bool HasVar(const std::string& name) const {
|
||||
VarInfo info;
|
||||
void* ctx;
|
||||
return LookupVar(name, info, ctx);
|
||||
}
|
||||
|
||||
// ========== 函数符号管理 ==========
|
||||
|
||||
// 检查当前作用域是否包含指定函数
|
||||
bool CurrentScopeHasFunc(const std::string& name) const;
|
||||
|
||||
// 绑定函数到当前作用域
|
||||
void BindFunc(const std::string& name, const FuncInfo& info, void* decl_ctx);
|
||||
|
||||
// 查找函数(从当前作用域向上遍历)
|
||||
bool LookupFunc(const std::string& name, FuncInfo& out_info, void*& out_decl_ctx) const;
|
||||
|
||||
// 快速查找函数(不获取详细信息)
|
||||
bool HasFunc(const std::string& name) const {
|
||||
FuncInfo info;
|
||||
void* ctx;
|
||||
return LookupFunc(name, info, ctx);
|
||||
}
|
||||
|
||||
// ========== 循环状态管理 ==========
|
||||
|
||||
// 进入循环
|
||||
void EnterLoop();
|
||||
|
||||
// 离开循环
|
||||
void ExitLoop();
|
||||
|
||||
// 检查是否在循环内
|
||||
bool InLoop() const;
|
||||
|
||||
// 获取循环嵌套深度
|
||||
int GetLoopDepth() const { return loop_depth_; }
|
||||
|
||||
// ========== 辅助功能 ==========
|
||||
|
||||
// 清空所有作用域和状态
|
||||
void Clear();
|
||||
|
||||
// 获取当前作用域中所有变量名
|
||||
std::vector<std::string> GetCurrentScopeVarNames() const;
|
||||
|
||||
// 获取当前作用域中所有函数名
|
||||
std::vector<std::string> GetCurrentScopeFuncNames() const;
|
||||
|
||||
// 调试:打印符号表内容
|
||||
void Dump() const;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, SysYParser::VarDefContext*> table_;
|
||||
private:
|
||||
// 作用域栈
|
||||
std::stack<ScopeEntry> scopes_;
|
||||
|
||||
// 循环嵌套深度
|
||||
int loop_depth_ = 0;
|
||||
};
|
||||
|
||||
// 类型兼容性检查函数
|
||||
inline bool IsTypeCompatible(SymbolType expected, SymbolType actual) {
|
||||
if (expected == SymbolType::TYPE_UNKNOWN || actual == SymbolType::TYPE_UNKNOWN) {
|
||||
return true; // 未知类型视为兼容
|
||||
}
|
||||
|
||||
// 基本类型兼容规则
|
||||
if (expected == actual) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// int 可以隐式转换为 float
|
||||
if (expected == SymbolType::TYPE_FLOAT && actual == SymbolType::TYPE_INT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // SYMBOL_TABLE_H
|
||||
@ -0,0 +1,76 @@
|
||||
---
|
||||
📊 Lab3 完成情况总结
|
||||
|
||||
✅ 最终测试结果
|
||||
|
||||
- 通过率: 21/21 测试全部通过 ✓ (100%)
|
||||
- Functional 测试: 11/11 通过
|
||||
- Performance 测试: 10/10 通过
|
||||
- 测试时间: 2026年04月24日
|
||||
- 状态: Lab3 要求完全满足 ✓
|
||||
|
||||
---
|
||||
🎯 核心技术实现
|
||||
|
||||
1. 完整数组支持 (主要提交: 1fbdbb2)
|
||||
|
||||
- ✅ 实现 GEP 指令支持全局数组、局部数组、指针参数
|
||||
- ✅ 2D 数组线性化及正确地址计算
|
||||
- ✅ 指针参数传递机制(区分数组地址传递和指针值加载)
|
||||
- ✅ 新增 MIR 指令: LoadIndirect, StoreIndirect, LoadStackAddr
|
||||
- ✅ 支持多维数组访问 array[i][j]
|
||||
|
||||
2. 浮点数支持 (提交: 1fbdbb2 + 346a9c4)
|
||||
|
||||
- ✅ IR 类型系统扩展: Float32 和 PtrFloat32
|
||||
- ✅ 浮点常量 ConstantFloat 及 Context 管理
|
||||
- ✅ IRGen 支持浮点变量、字面量、函数参数/返回值
|
||||
- ✅ MIR 浮点寄存器: S0-S10
|
||||
- ✅ MIR 浮点指令: FAddRR, FSubRR, FMulRR, FDivRR, FCmpRR
|
||||
- ✅ IEEE 754 合规: 修复 NaN 比较的正确处理
|
||||
|
||||
3. 关键 Bug 修复
|
||||
|
||||
- ✅ 大偏移量栈访问 (提交: 3078c4c): 修复寄存器冲突问题
|
||||
- ✅ 控制流指令 (提交: 693f54a): 消除 Br 和 CondBr 编译警告
|
||||
- ✅ 浮点比较 (提交: 346a9c4): IEEE 754 标准 NaN 处理
|
||||
- ✅ GEP 结果存储: 使用 8 字节指针槽
|
||||
- ✅ 函数调用: 修复数组参数传递机制
|
||||
|
||||
---
|
||||
🛠️ 测试效率优化
|
||||
|
||||
创建批量测试脚本 scripts/batch_test.sh
|
||||
|
||||
功能特性:
|
||||
- 📁 自动测试 functional 和 performance 两个目录
|
||||
- ⏱️ 智能超时控制 (functional: 60s, performance: 600s)
|
||||
- 📊 自动对比输出和退出码
|
||||
- 📝 生成详细测试报告 (test_results.txt)
|
||||
- 📈 实时统计: 总计/通过/失败/跳过/通过率
|
||||
|
||||
使用方式:
|
||||
./scripts/batch_test.sh
|
||||
|
||||
---
|
||||
📈 代码变更统计
|
||||
|
||||
涉及 30 个文件, 主要修改:
|
||||
- src/mir/Lowering.cpp: +994 行 (核心指令选择逻辑)
|
||||
- src/mir/AsmPrinter.cpp: +421 行 (汇编生成)
|
||||
- src/irgen/IRGenDecl.cpp: +330 行 (数组/浮点声明)
|
||||
- src/mir/passes/Peephole.cpp: +292 行 (窥孔优化)
|
||||
- include/mir/MIR.h: +91 行 (MIR 指令扩展)
|
||||
- 总计: +2700 行, -257 行
|
||||
|
||||
---
|
||||
🎓 技术亮点
|
||||
|
||||
1. 完整的编译链路: SysY源码 → IR → MIR → AArch64汇编 → 可执行程序
|
||||
2. 严格的语义支持: 完全覆盖 Lab3 要求的 SysY 语义
|
||||
3. 健壮的测试: 包含矩阵乘法、图算法、FFT、康威生命游戏等复杂测试
|
||||
4. 自动化工具: 显著提升测试效率和开发体验
|
||||
|
||||
---
|
||||
结论: Lab3 不仅完成了基本要求,还在数组、浮点、测试自动化方面做了深度优化,实现了 100%
|
||||
测试通过率 🎉
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "nudt-compiler-cpp",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@ -0,0 +1,3 @@
|
||||
This project is dual-licensed under the Unlicense and MIT licenses.
|
||||
|
||||
You may use this code under the terms of either license.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,46 @@
|
||||
# `@img/sharp-libvips-linux-x64`
|
||||
|
||||
Prebuilt libvips and dependencies for use with sharp on Linux (glibc) x64.
|
||||
|
||||
## Licensing
|
||||
|
||||
This software contains third-party libraries
|
||||
used under the terms of the following licences:
|
||||
|
||||
| Library | Used under the terms of |
|
||||
|---------------|-----------------------------------------------------------------------------------------------------------|
|
||||
| aom | BSD 2-Clause + [Alliance for Open Media Patent License 1.0](https://aomedia.org/license/patent-license/) |
|
||||
| cairo | Mozilla Public License 2.0 |
|
||||
| cgif | MIT Licence |
|
||||
| expat | MIT Licence |
|
||||
| fontconfig | [fontconfig Licence](https://gitlab.freedesktop.org/fontconfig/fontconfig/blob/main/COPYING) (BSD-like) |
|
||||
| freetype | [freetype Licence](https://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT) (BSD-like) |
|
||||
| fribidi | LGPLv3 |
|
||||
| glib | LGPLv3 |
|
||||
| harfbuzz | MIT Licence |
|
||||
| highway | Apache-2.0 License, BSD 3-Clause |
|
||||
| lcms | MIT Licence |
|
||||
| libarchive | BSD 2-Clause |
|
||||
| libexif | LGPLv3 |
|
||||
| libffi | MIT Licence |
|
||||
| libheif | LGPLv3 |
|
||||
| libimagequant | [BSD 2-Clause](https://github.com/lovell/libimagequant/blob/main/COPYRIGHT) |
|
||||
| libnsgif | MIT Licence |
|
||||
| libpng | [libpng License](https://github.com/pnggroup/libpng/blob/master/LICENSE) |
|
||||
| librsvg | LGPLv3 |
|
||||
| libspng | [BSD 2-Clause, libpng License](https://github.com/randy408/libspng/blob/master/LICENSE) |
|
||||
| libtiff | [libtiff License](https://gitlab.com/libtiff/libtiff/blob/master/LICENSE.md) (BSD-like) |
|
||||
| libvips | LGPLv3 |
|
||||
| libwebp | New BSD License |
|
||||
| libxml2 | MIT Licence |
|
||||
| mozjpeg | [zlib License, IJG License, BSD-3-Clause](https://github.com/mozilla/mozjpeg/blob/master/LICENSE.md) |
|
||||
| pango | LGPLv3 |
|
||||
| pixman | MIT Licence |
|
||||
| proxy-libintl | LGPLv3 |
|
||||
| zlib-ng | [zlib Licence](https://github.com/zlib-ng/zlib-ng/blob/develop/LICENSE.md) |
|
||||
|
||||
Use of libraries under the terms of the LGPLv3 is via the
|
||||
"any later version" clause of the LGPLv2 or LGPLv2.1.
|
||||
|
||||
Please report any errors or omissions via
|
||||
https://github.com/lovell/sharp-libvips/issues/new
|
||||
@ -0,0 +1,221 @@
|
||||
/* glibconfig.h
|
||||
*
|
||||
* This is a generated file. Please modify 'glibconfig.h.in'
|
||||
*/
|
||||
|
||||
#ifndef __GLIBCONFIG_H__
|
||||
#define __GLIBCONFIG_H__
|
||||
|
||||
#include <glib/gmacros.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <float.h>
|
||||
#define GLIB_HAVE_ALLOCA_H
|
||||
|
||||
#define GLIB_STATIC_COMPILATION 1
|
||||
#define GOBJECT_STATIC_COMPILATION 1
|
||||
#define GIO_STATIC_COMPILATION 1
|
||||
#define GMODULE_STATIC_COMPILATION 1
|
||||
#define GI_STATIC_COMPILATION 1
|
||||
#define G_INTL_STATIC_COMPILATION 1
|
||||
#define FFI_STATIC_BUILD 1
|
||||
|
||||
/* Specifies that GLib's g_print*() functions wrap the
|
||||
* system printf functions. This is useful to know, for example,
|
||||
* when using glibc's register_printf_function().
|
||||
*/
|
||||
#define GLIB_USING_SYSTEM_PRINTF
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_MINFLOAT FLT_MIN
|
||||
#define G_MAXFLOAT FLT_MAX
|
||||
#define G_MINDOUBLE DBL_MIN
|
||||
#define G_MAXDOUBLE DBL_MAX
|
||||
#define G_MINSHORT SHRT_MIN
|
||||
#define G_MAXSHORT SHRT_MAX
|
||||
#define G_MAXUSHORT USHRT_MAX
|
||||
#define G_MININT INT_MIN
|
||||
#define G_MAXINT INT_MAX
|
||||
#define G_MAXUINT UINT_MAX
|
||||
#define G_MINLONG LONG_MIN
|
||||
#define G_MAXLONG LONG_MAX
|
||||
#define G_MAXULONG ULONG_MAX
|
||||
|
||||
typedef signed char gint8;
|
||||
typedef unsigned char guint8;
|
||||
|
||||
typedef signed short gint16;
|
||||
typedef unsigned short guint16;
|
||||
|
||||
#define G_GINT16_MODIFIER "h"
|
||||
#define G_GINT16_FORMAT "hi"
|
||||
#define G_GUINT16_FORMAT "hu"
|
||||
|
||||
|
||||
typedef signed int gint32;
|
||||
typedef unsigned int guint32;
|
||||
|
||||
#define G_GINT32_MODIFIER ""
|
||||
#define G_GINT32_FORMAT "i"
|
||||
#define G_GUINT32_FORMAT "u"
|
||||
|
||||
|
||||
#define G_HAVE_GINT64 1 /* deprecated, always true */
|
||||
|
||||
typedef signed long gint64;
|
||||
typedef unsigned long guint64;
|
||||
|
||||
#define G_GINT64_CONSTANT(val) (val##L)
|
||||
#define G_GUINT64_CONSTANT(val) (val##UL)
|
||||
|
||||
#define G_GINT64_MODIFIER "l"
|
||||
#define G_GINT64_FORMAT "li"
|
||||
#define G_GUINT64_FORMAT "lu"
|
||||
|
||||
|
||||
#define GLIB_SIZEOF_VOID_P 8
|
||||
#define GLIB_SIZEOF_LONG 8
|
||||
#define GLIB_SIZEOF_SIZE_T 8
|
||||
#define GLIB_SIZEOF_SSIZE_T 8
|
||||
|
||||
typedef signed long gssize;
|
||||
typedef unsigned long gsize;
|
||||
#define G_GSIZE_MODIFIER "l"
|
||||
#define G_GSSIZE_MODIFIER "l"
|
||||
#define G_GSIZE_FORMAT "lu"
|
||||
#define G_GSSIZE_FORMAT "li"
|
||||
|
||||
#define G_MAXSIZE G_MAXULONG
|
||||
#define G_MINSSIZE G_MINLONG
|
||||
#define G_MAXSSIZE G_MAXLONG
|
||||
|
||||
typedef gint64 goffset;
|
||||
#define G_MINOFFSET G_MININT64
|
||||
#define G_MAXOFFSET G_MAXINT64
|
||||
|
||||
#define G_GOFFSET_MODIFIER G_GINT64_MODIFIER
|
||||
#define G_GOFFSET_FORMAT G_GINT64_FORMAT
|
||||
#define G_GOFFSET_CONSTANT(val) G_GINT64_CONSTANT(val)
|
||||
|
||||
#define G_POLLFD_FORMAT "%d"
|
||||
|
||||
#define GPOINTER_TO_INT(p) ((gint) (glong) (p))
|
||||
#define GPOINTER_TO_UINT(p) ((guint) (gulong) (p))
|
||||
|
||||
#define GINT_TO_POINTER(i) ((gpointer) (glong) (i))
|
||||
#define GUINT_TO_POINTER(u) ((gpointer) (gulong) (u))
|
||||
|
||||
typedef signed long gintptr;
|
||||
typedef unsigned long guintptr;
|
||||
|
||||
#define G_GINTPTR_MODIFIER "l"
|
||||
#define G_GINTPTR_FORMAT "li"
|
||||
#define G_GUINTPTR_FORMAT "lu"
|
||||
|
||||
#define GLIB_MAJOR_VERSION 2
|
||||
#define GLIB_MINOR_VERSION 86
|
||||
#define GLIB_MICRO_VERSION 1
|
||||
|
||||
#define G_OS_UNIX
|
||||
|
||||
#define G_VA_COPY va_copy
|
||||
|
||||
#define G_VA_COPY_AS_ARRAY 1
|
||||
|
||||
#define G_HAVE_ISO_VARARGS 1
|
||||
|
||||
/* gcc-2.95.x supports both gnu style and ISO varargs, but if -ansi
|
||||
* is passed ISO vararg support is turned off, and there is no work
|
||||
* around to turn it on, so we unconditionally turn it off.
|
||||
*/
|
||||
#if __GNUC__ == 2 && __GNUC_MINOR__ == 95
|
||||
# undef G_HAVE_ISO_VARARGS
|
||||
#endif
|
||||
|
||||
#define G_HAVE_GROWING_STACK 0
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define G_HAVE_GNUC_VARARGS 1
|
||||
#endif
|
||||
|
||||
#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
|
||||
#define G_GNUC_INTERNAL __attribute__((visibility("hidden")))
|
||||
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
|
||||
#define G_GNUC_INTERNAL __hidden
|
||||
#elif defined (__GNUC__) && defined (G_HAVE_GNUC_VISIBILITY)
|
||||
#define G_GNUC_INTERNAL __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define G_GNUC_INTERNAL
|
||||
#endif
|
||||
|
||||
#define G_THREADS_ENABLED
|
||||
#define G_THREADS_IMPL_POSIX
|
||||
|
||||
#define G_ATOMIC_LOCK_FREE
|
||||
|
||||
#define GINT16_TO_LE(val) ((gint16) (val))
|
||||
#define GUINT16_TO_LE(val) ((guint16) (val))
|
||||
#define GINT16_TO_BE(val) ((gint16) GUINT16_SWAP_LE_BE (val))
|
||||
#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val))
|
||||
|
||||
#define GINT32_TO_LE(val) ((gint32) (val))
|
||||
#define GUINT32_TO_LE(val) ((guint32) (val))
|
||||
#define GINT32_TO_BE(val) ((gint32) GUINT32_SWAP_LE_BE (val))
|
||||
#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val))
|
||||
|
||||
#define GINT64_TO_LE(val) ((gint64) (val))
|
||||
#define GUINT64_TO_LE(val) ((guint64) (val))
|
||||
#define GINT64_TO_BE(val) ((gint64) GUINT64_SWAP_LE_BE (val))
|
||||
#define GUINT64_TO_BE(val) (GUINT64_SWAP_LE_BE (val))
|
||||
|
||||
#define GLONG_TO_LE(val) ((glong) GINT64_TO_LE (val))
|
||||
#define GULONG_TO_LE(val) ((gulong) GUINT64_TO_LE (val))
|
||||
#define GLONG_TO_BE(val) ((glong) GINT64_TO_BE (val))
|
||||
#define GULONG_TO_BE(val) ((gulong) GUINT64_TO_BE (val))
|
||||
#define GINT_TO_LE(val) ((gint) GINT32_TO_LE (val))
|
||||
#define GUINT_TO_LE(val) ((guint) GUINT32_TO_LE (val))
|
||||
#define GINT_TO_BE(val) ((gint) GINT32_TO_BE (val))
|
||||
#define GUINT_TO_BE(val) ((guint) GUINT32_TO_BE (val))
|
||||
#define GSIZE_TO_LE(val) ((gsize) GUINT64_TO_LE (val))
|
||||
#define GSSIZE_TO_LE(val) ((gssize) GINT64_TO_LE (val))
|
||||
#define GSIZE_TO_BE(val) ((gsize) GUINT64_TO_BE (val))
|
||||
#define GSSIZE_TO_BE(val) ((gssize) GINT64_TO_BE (val))
|
||||
#define G_BYTE_ORDER G_LITTLE_ENDIAN
|
||||
|
||||
#define GLIB_SYSDEF_POLLIN =1
|
||||
#define GLIB_SYSDEF_POLLOUT =4
|
||||
#define GLIB_SYSDEF_POLLPRI =2
|
||||
#define GLIB_SYSDEF_POLLHUP =16
|
||||
#define GLIB_SYSDEF_POLLERR =8
|
||||
#define GLIB_SYSDEF_POLLNVAL =32
|
||||
|
||||
/* No way to disable deprecation warnings for macros, so only emit deprecation
|
||||
* warnings on platforms where usage of this macro is broken */
|
||||
#if defined(__APPLE__) || defined(_MSC_VER) || defined(__CYGWIN__)
|
||||
#define G_MODULE_SUFFIX "so" GLIB_DEPRECATED_MACRO_IN_2_76
|
||||
#else
|
||||
#define G_MODULE_SUFFIX "so"
|
||||
#endif
|
||||
|
||||
typedef int GPid;
|
||||
#define G_PID_FORMAT "i"
|
||||
|
||||
#define GLIB_SYSDEF_AF_UNIX 1
|
||||
#define GLIB_SYSDEF_AF_INET 2
|
||||
#define GLIB_SYSDEF_AF_INET6 10
|
||||
|
||||
#define GLIB_SYSDEF_MSG_OOB 1
|
||||
#define GLIB_SYSDEF_MSG_PEEK 2
|
||||
#define GLIB_SYSDEF_MSG_DONTROUTE 4
|
||||
|
||||
#define G_DIR_SEPARATOR '/'
|
||||
#define G_DIR_SEPARATOR_S "/"
|
||||
#define G_SEARCHPATH_SEPARATOR ':'
|
||||
#define G_SEARCHPATH_SEPARATOR_S ":"
|
||||
|
||||
#undef G_HAVE_FREE_SIZED
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GLIBCONFIG_H__ */
|
||||
@ -0,0 +1 @@
|
||||
module.exports = __dirname;
|
||||
Binary file not shown.
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@img/sharp-libvips-linux-x64",
|
||||
"version": "1.2.4",
|
||||
"description": "Prebuilt libvips and dependencies for use with sharp on Linux (glibc) x64",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lovell/sharp-libvips.git",
|
||||
"directory": "npm/linux-x64"
|
||||
},
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./lib": "./lib/index.js",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"config": {
|
||||
"glibc": ">=2.26"
|
||||
},
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"libc": [
|
||||
"glibc"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"aom": "3.13.1",
|
||||
"archive": "3.8.2",
|
||||
"cairo": "1.18.4",
|
||||
"cgif": "0.5.0",
|
||||
"exif": "0.6.25",
|
||||
"expat": "2.7.3",
|
||||
"ffi": "3.5.2",
|
||||
"fontconfig": "2.17.1",
|
||||
"freetype": "2.14.1",
|
||||
"fribidi": "1.0.16",
|
||||
"glib": "2.86.1",
|
||||
"harfbuzz": "12.1.0",
|
||||
"heif": "1.20.2",
|
||||
"highway": "1.3.0",
|
||||
"imagequant": "2.4.1",
|
||||
"lcms": "2.17",
|
||||
"mozjpeg": "0826579",
|
||||
"pango": "1.57.0",
|
||||
"pixman": "0.46.4",
|
||||
"png": "1.6.50",
|
||||
"proxy-libintl": "0.5",
|
||||
"rsvg": "2.61.2",
|
||||
"spng": "0.7.4",
|
||||
"tiff": "4.7.1",
|
||||
"vips": "8.17.3",
|
||||
"webp": "1.6.0",
|
||||
"xml2": "2.15.1",
|
||||
"zlib-ng": "2.2.5"
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
# `@img/sharp-libvips-linuxmusl-x64`
|
||||
|
||||
Prebuilt libvips and dependencies for use with sharp on Linux (musl) x64.
|
||||
|
||||
## Licensing
|
||||
|
||||
This software contains third-party libraries
|
||||
used under the terms of the following licences:
|
||||
|
||||
| Library | Used under the terms of |
|
||||
|---------------|-----------------------------------------------------------------------------------------------------------|
|
||||
| aom | BSD 2-Clause + [Alliance for Open Media Patent License 1.0](https://aomedia.org/license/patent-license/) |
|
||||
| cairo | Mozilla Public License 2.0 |
|
||||
| cgif | MIT Licence |
|
||||
| expat | MIT Licence |
|
||||
| fontconfig | [fontconfig Licence](https://gitlab.freedesktop.org/fontconfig/fontconfig/blob/main/COPYING) (BSD-like) |
|
||||
| freetype | [freetype Licence](https://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT) (BSD-like) |
|
||||
| fribidi | LGPLv3 |
|
||||
| glib | LGPLv3 |
|
||||
| harfbuzz | MIT Licence |
|
||||
| highway | Apache-2.0 License, BSD 3-Clause |
|
||||
| lcms | MIT Licence |
|
||||
| libarchive | BSD 2-Clause |
|
||||
| libexif | LGPLv3 |
|
||||
| libffi | MIT Licence |
|
||||
| libheif | LGPLv3 |
|
||||
| libimagequant | [BSD 2-Clause](https://github.com/lovell/libimagequant/blob/main/COPYRIGHT) |
|
||||
| libnsgif | MIT Licence |
|
||||
| libpng | [libpng License](https://github.com/pnggroup/libpng/blob/master/LICENSE) |
|
||||
| librsvg | LGPLv3 |
|
||||
| libspng | [BSD 2-Clause, libpng License](https://github.com/randy408/libspng/blob/master/LICENSE) |
|
||||
| libtiff | [libtiff License](https://gitlab.com/libtiff/libtiff/blob/master/LICENSE.md) (BSD-like) |
|
||||
| libvips | LGPLv3 |
|
||||
| libwebp | New BSD License |
|
||||
| libxml2 | MIT Licence |
|
||||
| mozjpeg | [zlib License, IJG License, BSD-3-Clause](https://github.com/mozilla/mozjpeg/blob/master/LICENSE.md) |
|
||||
| pango | LGPLv3 |
|
||||
| pixman | MIT Licence |
|
||||
| proxy-libintl | LGPLv3 |
|
||||
| zlib-ng | [zlib Licence](https://github.com/zlib-ng/zlib-ng/blob/develop/LICENSE.md) |
|
||||
|
||||
Use of libraries under the terms of the LGPLv3 is via the
|
||||
"any later version" clause of the LGPLv2 or LGPLv2.1.
|
||||
|
||||
Please report any errors or omissions via
|
||||
https://github.com/lovell/sharp-libvips/issues/new
|
||||
221
node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h
generated
vendored
221
node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h
generated
vendored
@ -0,0 +1,221 @@
|
||||
/* glibconfig.h
|
||||
*
|
||||
* This is a generated file. Please modify 'glibconfig.h.in'
|
||||
*/
|
||||
|
||||
#ifndef __GLIBCONFIG_H__
|
||||
#define __GLIBCONFIG_H__
|
||||
|
||||
#include <glib/gmacros.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <float.h>
|
||||
#define GLIB_HAVE_ALLOCA_H
|
||||
|
||||
#define GLIB_STATIC_COMPILATION 1
|
||||
#define GOBJECT_STATIC_COMPILATION 1
|
||||
#define GIO_STATIC_COMPILATION 1
|
||||
#define GMODULE_STATIC_COMPILATION 1
|
||||
#define GI_STATIC_COMPILATION 1
|
||||
#define G_INTL_STATIC_COMPILATION 1
|
||||
#define FFI_STATIC_BUILD 1
|
||||
|
||||
/* Specifies that GLib's g_print*() functions wrap the
|
||||
* system printf functions. This is useful to know, for example,
|
||||
* when using glibc's register_printf_function().
|
||||
*/
|
||||
#define GLIB_USING_SYSTEM_PRINTF
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_MINFLOAT FLT_MIN
|
||||
#define G_MAXFLOAT FLT_MAX
|
||||
#define G_MINDOUBLE DBL_MIN
|
||||
#define G_MAXDOUBLE DBL_MAX
|
||||
#define G_MINSHORT SHRT_MIN
|
||||
#define G_MAXSHORT SHRT_MAX
|
||||
#define G_MAXUSHORT USHRT_MAX
|
||||
#define G_MININT INT_MIN
|
||||
#define G_MAXINT INT_MAX
|
||||
#define G_MAXUINT UINT_MAX
|
||||
#define G_MINLONG LONG_MIN
|
||||
#define G_MAXLONG LONG_MAX
|
||||
#define G_MAXULONG ULONG_MAX
|
||||
|
||||
typedef signed char gint8;
|
||||
typedef unsigned char guint8;
|
||||
|
||||
typedef signed short gint16;
|
||||
typedef unsigned short guint16;
|
||||
|
||||
#define G_GINT16_MODIFIER "h"
|
||||
#define G_GINT16_FORMAT "hi"
|
||||
#define G_GUINT16_FORMAT "hu"
|
||||
|
||||
|
||||
typedef signed int gint32;
|
||||
typedef unsigned int guint32;
|
||||
|
||||
#define G_GINT32_MODIFIER ""
|
||||
#define G_GINT32_FORMAT "i"
|
||||
#define G_GUINT32_FORMAT "u"
|
||||
|
||||
|
||||
#define G_HAVE_GINT64 1 /* deprecated, always true */
|
||||
|
||||
typedef signed long gint64;
|
||||
typedef unsigned long guint64;
|
||||
|
||||
#define G_GINT64_CONSTANT(val) (val##L)
|
||||
#define G_GUINT64_CONSTANT(val) (val##UL)
|
||||
|
||||
#define G_GINT64_MODIFIER "l"
|
||||
#define G_GINT64_FORMAT "li"
|
||||
#define G_GUINT64_FORMAT "lu"
|
||||
|
||||
|
||||
#define GLIB_SIZEOF_VOID_P 8
|
||||
#define GLIB_SIZEOF_LONG 8
|
||||
#define GLIB_SIZEOF_SIZE_T 8
|
||||
#define GLIB_SIZEOF_SSIZE_T 8
|
||||
|
||||
typedef signed long gssize;
|
||||
typedef unsigned long gsize;
|
||||
#define G_GSIZE_MODIFIER "l"
|
||||
#define G_GSSIZE_MODIFIER "l"
|
||||
#define G_GSIZE_FORMAT "lu"
|
||||
#define G_GSSIZE_FORMAT "li"
|
||||
|
||||
#define G_MAXSIZE G_MAXULONG
|
||||
#define G_MINSSIZE G_MINLONG
|
||||
#define G_MAXSSIZE G_MAXLONG
|
||||
|
||||
typedef gint64 goffset;
|
||||
#define G_MINOFFSET G_MININT64
|
||||
#define G_MAXOFFSET G_MAXINT64
|
||||
|
||||
#define G_GOFFSET_MODIFIER G_GINT64_MODIFIER
|
||||
#define G_GOFFSET_FORMAT G_GINT64_FORMAT
|
||||
#define G_GOFFSET_CONSTANT(val) G_GINT64_CONSTANT(val)
|
||||
|
||||
#define G_POLLFD_FORMAT "%d"
|
||||
|
||||
#define GPOINTER_TO_INT(p) ((gint) (glong) (p))
|
||||
#define GPOINTER_TO_UINT(p) ((guint) (gulong) (p))
|
||||
|
||||
#define GINT_TO_POINTER(i) ((gpointer) (glong) (i))
|
||||
#define GUINT_TO_POINTER(u) ((gpointer) (gulong) (u))
|
||||
|
||||
typedef signed long gintptr;
|
||||
typedef unsigned long guintptr;
|
||||
|
||||
#define G_GINTPTR_MODIFIER "l"
|
||||
#define G_GINTPTR_FORMAT "li"
|
||||
#define G_GUINTPTR_FORMAT "lu"
|
||||
|
||||
#define GLIB_MAJOR_VERSION 2
|
||||
#define GLIB_MINOR_VERSION 86
|
||||
#define GLIB_MICRO_VERSION 1
|
||||
|
||||
#define G_OS_UNIX
|
||||
|
||||
#define G_VA_COPY va_copy
|
||||
|
||||
#define G_VA_COPY_AS_ARRAY 1
|
||||
|
||||
#define G_HAVE_ISO_VARARGS 1
|
||||
|
||||
/* gcc-2.95.x supports both gnu style and ISO varargs, but if -ansi
|
||||
* is passed ISO vararg support is turned off, and there is no work
|
||||
* around to turn it on, so we unconditionally turn it off.
|
||||
*/
|
||||
#if __GNUC__ == 2 && __GNUC_MINOR__ == 95
|
||||
# undef G_HAVE_ISO_VARARGS
|
||||
#endif
|
||||
|
||||
#define G_HAVE_GROWING_STACK 0
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define G_HAVE_GNUC_VARARGS 1
|
||||
#endif
|
||||
|
||||
#if defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590)
|
||||
#define G_GNUC_INTERNAL __attribute__((visibility("hidden")))
|
||||
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
|
||||
#define G_GNUC_INTERNAL __hidden
|
||||
#elif defined (__GNUC__) && defined (G_HAVE_GNUC_VISIBILITY)
|
||||
#define G_GNUC_INTERNAL __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define G_GNUC_INTERNAL
|
||||
#endif
|
||||
|
||||
#define G_THREADS_ENABLED
|
||||
#define G_THREADS_IMPL_POSIX
|
||||
|
||||
#define G_ATOMIC_LOCK_FREE
|
||||
|
||||
#define GINT16_TO_LE(val) ((gint16) (val))
|
||||
#define GUINT16_TO_LE(val) ((guint16) (val))
|
||||
#define GINT16_TO_BE(val) ((gint16) GUINT16_SWAP_LE_BE (val))
|
||||
#define GUINT16_TO_BE(val) (GUINT16_SWAP_LE_BE (val))
|
||||
|
||||
#define GINT32_TO_LE(val) ((gint32) (val))
|
||||
#define GUINT32_TO_LE(val) ((guint32) (val))
|
||||
#define GINT32_TO_BE(val) ((gint32) GUINT32_SWAP_LE_BE (val))
|
||||
#define GUINT32_TO_BE(val) (GUINT32_SWAP_LE_BE (val))
|
||||
|
||||
#define GINT64_TO_LE(val) ((gint64) (val))
|
||||
#define GUINT64_TO_LE(val) ((guint64) (val))
|
||||
#define GINT64_TO_BE(val) ((gint64) GUINT64_SWAP_LE_BE (val))
|
||||
#define GUINT64_TO_BE(val) (GUINT64_SWAP_LE_BE (val))
|
||||
|
||||
#define GLONG_TO_LE(val) ((glong) GINT64_TO_LE (val))
|
||||
#define GULONG_TO_LE(val) ((gulong) GUINT64_TO_LE (val))
|
||||
#define GLONG_TO_BE(val) ((glong) GINT64_TO_BE (val))
|
||||
#define GULONG_TO_BE(val) ((gulong) GUINT64_TO_BE (val))
|
||||
#define GINT_TO_LE(val) ((gint) GINT32_TO_LE (val))
|
||||
#define GUINT_TO_LE(val) ((guint) GUINT32_TO_LE (val))
|
||||
#define GINT_TO_BE(val) ((gint) GINT32_TO_BE (val))
|
||||
#define GUINT_TO_BE(val) ((guint) GUINT32_TO_BE (val))
|
||||
#define GSIZE_TO_LE(val) ((gsize) GUINT64_TO_LE (val))
|
||||
#define GSSIZE_TO_LE(val) ((gssize) GINT64_TO_LE (val))
|
||||
#define GSIZE_TO_BE(val) ((gsize) GUINT64_TO_BE (val))
|
||||
#define GSSIZE_TO_BE(val) ((gssize) GINT64_TO_BE (val))
|
||||
#define G_BYTE_ORDER G_LITTLE_ENDIAN
|
||||
|
||||
#define GLIB_SYSDEF_POLLIN =1
|
||||
#define GLIB_SYSDEF_POLLOUT =4
|
||||
#define GLIB_SYSDEF_POLLPRI =2
|
||||
#define GLIB_SYSDEF_POLLHUP =16
|
||||
#define GLIB_SYSDEF_POLLERR =8
|
||||
#define GLIB_SYSDEF_POLLNVAL =32
|
||||
|
||||
/* No way to disable deprecation warnings for macros, so only emit deprecation
|
||||
* warnings on platforms where usage of this macro is broken */
|
||||
#if defined(__APPLE__) || defined(_MSC_VER) || defined(__CYGWIN__)
|
||||
#define G_MODULE_SUFFIX "so" GLIB_DEPRECATED_MACRO_IN_2_76
|
||||
#else
|
||||
#define G_MODULE_SUFFIX "so"
|
||||
#endif
|
||||
|
||||
typedef int GPid;
|
||||
#define G_PID_FORMAT "i"
|
||||
|
||||
#define GLIB_SYSDEF_AF_UNIX 1
|
||||
#define GLIB_SYSDEF_AF_INET 2
|
||||
#define GLIB_SYSDEF_AF_INET6 10
|
||||
|
||||
#define GLIB_SYSDEF_MSG_OOB 1
|
||||
#define GLIB_SYSDEF_MSG_PEEK 2
|
||||
#define GLIB_SYSDEF_MSG_DONTROUTE 4
|
||||
|
||||
#define G_DIR_SEPARATOR '/'
|
||||
#define G_DIR_SEPARATOR_S "/"
|
||||
#define G_SEARCHPATH_SEPARATOR ':'
|
||||
#define G_SEARCHPATH_SEPARATOR_S ":"
|
||||
|
||||
#undef G_HAVE_FREE_SIZED
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GLIBCONFIG_H__ */
|
||||
@ -0,0 +1 @@
|
||||
module.exports = __dirname;
|
||||
Binary file not shown.
@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@img/sharp-libvips-linuxmusl-x64",
|
||||
"version": "1.2.4",
|
||||
"description": "Prebuilt libvips and dependencies for use with sharp on Linux (musl) x64",
|
||||
"author": "Lovell Fuller <npm@lovell.info>",
|
||||
"homepage": "https://sharp.pixelplumbing.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/lovell/sharp-libvips.git",
|
||||
"directory": "npm/linuxmusl-x64"
|
||||
},
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"preferUnplugged": true,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"versions.json"
|
||||
],
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./lib": "./lib/index.js",
|
||||
"./package": "./package.json",
|
||||
"./versions": "./versions.json"
|
||||
},
|
||||
"config": {
|
||||
"musl": ">=1.2.2"
|
||||
},
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
{
|
||||
"aom": "3.13.1",
|
||||
"archive": "3.8.2",
|
||||
"cairo": "1.18.4",
|
||||
"cgif": "0.5.0",
|
||||
"exif": "0.6.25",
|
||||
"expat": "2.7.3",
|
||||
"ffi": "3.5.2",
|
||||
"fontconfig": "2.17.1",
|
||||
"freetype": "2.14.1",
|
||||
"fribidi": "1.0.16",
|
||||
"glib": "2.86.1",
|
||||
"harfbuzz": "12.1.0",
|
||||
"heif": "1.20.2",
|
||||
"highway": "1.3.0",
|
||||
"imagequant": "2.4.1",
|
||||
"lcms": "2.17",
|
||||
"mozjpeg": "0826579",
|
||||
"pango": "1.57.0",
|
||||
"pixman": "0.46.4",
|
||||
"png": "1.6.50",
|
||||
"proxy-libintl": "0.5",
|
||||
"rsvg": "2.61.2",
|
||||
"spng": "0.7.4",
|
||||
"tiff": "4.7.1",
|
||||
"vips": "8.17.3",
|
||||
"webp": "1.6.0",
|
||||
"xml2": "2.15.1",
|
||||
"zlib-ng": "2.2.5"
|
||||
}
|
||||
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "nudt-compiler-cpp",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
{}
|
||||
@ -0,0 +1,39 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
COMPILER = "./build/bin/compiler"
|
||||
TEST_DIR = "./test/test_case/functional"
|
||||
|
||||
pass_cnt = 0
|
||||
fail_cnt = 0
|
||||
|
||||
print("===== SysY Batch Test Start =====")
|
||||
|
||||
for file in os.listdir(TEST_DIR):
|
||||
if not file.endswith(".sy"):
|
||||
continue
|
||||
|
||||
path = os.path.join(TEST_DIR, file)
|
||||
print(f"[TEST] {file} ... ", end="")
|
||||
|
||||
result = subprocess.run(
|
||||
[COMPILER, "--emit-parse-tree", path],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
print("PASS")
|
||||
pass_cnt += 1
|
||||
else:
|
||||
print("FAIL")
|
||||
fail_cnt += 1
|
||||
print("---- Error ----")
|
||||
print(result.stderr.decode())
|
||||
print("---------------")
|
||||
|
||||
print("===============================")
|
||||
print(f"Total: {pass_cnt + fail_cnt}")
|
||||
print(f"PASS : {pass_cnt}")
|
||||
print(f"FAIL : {fail_cnt}")
|
||||
print("===============================")
|
||||
@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
output_file="test_results.txt"
|
||||
test_dirs=("test/test_case/functional" "test/test_case/performance")
|
||||
|
||||
# 初始化输出文件
|
||||
{
|
||||
echo "开始批量测试..."
|
||||
echo "测试时间: $(date)"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
} > "$output_file"
|
||||
|
||||
total=0
|
||||
passed=0
|
||||
failed=0
|
||||
skipped=0
|
||||
|
||||
for test_dir in "${test_dirs[@]}"; do
|
||||
if [[ ! -d "$test_dir" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "测试目录: $test_dir" | tee -a "$output_file"
|
||||
echo "----------------------------------------" >> "$output_file"
|
||||
|
||||
# 使用简单的 for 循环
|
||||
for test_file in "$test_dir"/*.sy; do
|
||||
if [[ ! -f "$test_file" ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
((total++))
|
||||
name=$(basename "$test_file" .sy)
|
||||
|
||||
# 显示当前测试
|
||||
echo -n "测试 $name ... "
|
||||
echo ""
|
||||
|
||||
# 根据目录设置输出路径和超时时间
|
||||
if [[ "$test_dir" == *"functional"* ]]; then
|
||||
out_dir="test/test_result/function/asm"
|
||||
timeout_sec=60
|
||||
else
|
||||
out_dir="test/test_result/performance/asm"
|
||||
timeout_sec=600 # 性能测试增加到 3 分钟
|
||||
fi
|
||||
|
||||
# 运行测试
|
||||
temp_output=$(mktemp)
|
||||
if timeout ${timeout_sec}s ./scripts/verify_asm.sh "$test_file" "$out_dir" --run > "$temp_output" 2>&1; then
|
||||
# 提取并保存关键信息到文件
|
||||
{
|
||||
grep "运行 " "$temp_output" || echo "运行 $out_dir/$name ..."
|
||||
grep "退出码:" "$temp_output" || echo "退出码: 失败"
|
||||
|
||||
if grep -q "输出匹配:" "$temp_output"; then
|
||||
grep "输出匹配:" "$temp_output"
|
||||
echo ""
|
||||
((passed++))
|
||||
echo "✓"
|
||||
echo ""
|
||||
elif grep -q "输出不匹配:" "$temp_output"; then
|
||||
grep "输出不匹配:" "$temp_output"
|
||||
echo ""
|
||||
((failed++))
|
||||
echo "✗ (输出不匹配)"
|
||||
elif grep -q "未找到预期输出文件" "$temp_output"; then
|
||||
echo "未找到预期输出文件,跳过比对"
|
||||
echo ""
|
||||
((skipped++))
|
||||
echo "⊘ (无期望输出)"
|
||||
else
|
||||
echo "测试完成"
|
||||
echo ""
|
||||
((passed++))
|
||||
echo "✓"
|
||||
echo ""
|
||||
fi
|
||||
} >> "$output_file"
|
||||
else
|
||||
# 测试失败或超时
|
||||
{
|
||||
echo "运行 $out_dir/$name ..."
|
||||
echo "退出码: 超时或失败"
|
||||
echo "测试失败"
|
||||
echo ""
|
||||
} >> "$output_file"
|
||||
((failed++))
|
||||
echo "✗ (失败/超时)"
|
||||
fi
|
||||
|
||||
rm -f "$temp_output"
|
||||
done
|
||||
|
||||
echo "" >> "$output_file"
|
||||
done
|
||||
|
||||
# 输出统计
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "测试统计:"
|
||||
echo " 总计: $total"
|
||||
echo " 通过: $passed"
|
||||
echo " 失败: $failed"
|
||||
echo " 跳过: $skipped"
|
||||
if [[ $total -gt 0 ]]; then
|
||||
pass_rate=$(awk "BEGIN {printf \"%.1f\", ($passed/$total)*100}")
|
||||
echo " 通过率: ${pass_rate}%"
|
||||
fi
|
||||
echo ""
|
||||
echo "详细结果已保存到: $output_file"
|
||||
|
||||
# 保存统计到文件
|
||||
{
|
||||
echo "========================================"
|
||||
echo "测试统计:"
|
||||
echo " 总计: $total"
|
||||
echo " 通过: $passed"
|
||||
echo " 失败: $failed"
|
||||
echo " 跳过: $skipped"
|
||||
if [[ $total -gt 0 ]]; then
|
||||
pass_rate=$(awk "BEGIN {printf \"%.1f\", ($passed/$total)*100}")
|
||||
echo " 通过率: ${pass_rate}%"
|
||||
fi
|
||||
} >> "$output_file"
|
||||
|
||||
# 返回状态码
|
||||
if [[ $failed -eq 0 ]]; then
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
@ -0,0 +1,7 @@
|
||||
|
||||
// Generated from SysY.g4 by ANTLR 4.7.2
|
||||
|
||||
|
||||
#include "SysYBaseVisitor.h"
|
||||
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
|
||||
// Generated from SysY.g4 by ANTLR 4.7.2
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
#include "SysYVisitor.h"
|
||||
|
||||
|
||||
/**
|
||||
* This class provides an empty implementation of SysYVisitor, which can be
|
||||
* extended to create a visitor which only needs to handle a subset of the available methods.
|
||||
*/
|
||||
class SysYBaseVisitor : public SysYVisitor {
|
||||
public:
|
||||
|
||||
virtual antlrcpp::Any visitCompUnit(SysYParser::CompUnitContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitDecl(SysYParser::DeclContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitConstDecl(SysYParser::ConstDeclContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitBType(SysYParser::BTypeContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitConstDef(SysYParser::ConstDefContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitConstInitVal(SysYParser::ConstInitValContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitVarDecl(SysYParser::VarDeclContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitVarDef(SysYParser::VarDefContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitInitVal(SysYParser::InitValContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitFuncDef(SysYParser::FuncDefContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitFuncType(SysYParser::FuncTypeContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitFuncFParams(SysYParser::FuncFParamsContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitFuncFParam(SysYParser::FuncFParamContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitBlock(SysYParser::BlockContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitBlockItem(SysYParser::BlockItemContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitStmt(SysYParser::StmtContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitExp(SysYParser::ExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitCond(SysYParser::CondContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitLVal(SysYParser::LValContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitPrimaryExp(SysYParser::PrimaryExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitNumber(SysYParser::NumberContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitUnaryExp(SysYParser::UnaryExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitUnaryOp(SysYParser::UnaryOpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitFuncRParams(SysYParser::FuncRParamsContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitMulExp(SysYParser::MulExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitAddExp(SysYParser::AddExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitRelExp(SysYParser::RelExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitEqExp(SysYParser::EqExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitLAndExp(SysYParser::LAndExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitLOrExp(SysYParser::LOrExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
virtual antlrcpp::Any visitConstExp(SysYParser::ConstExpContext *ctx) override {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -0,0 +1,377 @@
|
||||
|
||||
// Generated from SysY.g4 by ANTLR 4.7.2
|
||||
|
||||
|
||||
#include "SysYLexer.h"
|
||||
|
||||
|
||||
using namespace antlr4;
|
||||
|
||||
|
||||
SysYLexer::SysYLexer(CharStream *input) : Lexer(input) {
|
||||
_interpreter = new atn::LexerATNSimulator(this, _atn, _decisionToDFA, _sharedContextCache);
|
||||
}
|
||||
|
||||
SysYLexer::~SysYLexer() {
|
||||
delete _interpreter;
|
||||
}
|
||||
|
||||
std::string SysYLexer::getGrammarFileName() const {
|
||||
return "SysY.g4";
|
||||
}
|
||||
|
||||
const std::vector<std::string>& SysYLexer::getRuleNames() const {
|
||||
return _ruleNames;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& SysYLexer::getChannelNames() const {
|
||||
return _channelNames;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& SysYLexer::getModeNames() const {
|
||||
return _modeNames;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& SysYLexer::getTokenNames() const {
|
||||
return _tokenNames;
|
||||
}
|
||||
|
||||
dfa::Vocabulary& SysYLexer::getVocabulary() const {
|
||||
return _vocabulary;
|
||||
}
|
||||
|
||||
const std::vector<uint16_t> SysYLexer::getSerializedATN() const {
|
||||
return _serializedATN;
|
||||
}
|
||||
|
||||
const atn::ATN& SysYLexer::getATN() const {
|
||||
return _atn;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Static vars and initialization.
|
||||
std::vector<dfa::DFA> SysYLexer::_decisionToDFA;
|
||||
atn::PredictionContextCache SysYLexer::_sharedContextCache;
|
||||
|
||||
// We own the ATN which in turn owns the ATN states.
|
||||
atn::ATN SysYLexer::_atn;
|
||||
std::vector<uint16_t> SysYLexer::_serializedATN;
|
||||
|
||||
std::vector<std::string> SysYLexer::_ruleNames = {
|
||||
u8"T__0", u8"T__1", u8"T__2", u8"T__3", u8"T__4", u8"T__5", u8"T__6",
|
||||
u8"T__7", u8"T__8", u8"T__9", u8"T__10", u8"T__11", u8"T__12", u8"T__13",
|
||||
u8"T__14", u8"T__15", u8"T__16", u8"T__17", u8"T__18", u8"T__19", u8"T__20",
|
||||
u8"T__21", u8"T__22", u8"T__23", u8"T__24", u8"T__25", u8"T__26", u8"T__27",
|
||||
u8"T__28", u8"T__29", u8"T__30", u8"T__31", u8"T__32", u8"DIGIT", u8"HEXDIGIT",
|
||||
u8"EXP", u8"PEXP", u8"FloatConst", u8"IntConst", u8"Ident", u8"WS", u8"LINE_COMMENT",
|
||||
u8"BLOCK_COMMENT"
|
||||
};
|
||||
|
||||
std::vector<std::string> SysYLexer::_channelNames = {
|
||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
|
||||
};
|
||||
|
||||
std::vector<std::string> SysYLexer::_modeNames = {
|
||||
u8"DEFAULT_MODE"
|
||||
};
|
||||
|
||||
std::vector<std::string> SysYLexer::_literalNames = {
|
||||
"", u8"'const'", u8"','", u8"';'", u8"'int'", u8"'float'", u8"'['", u8"']'",
|
||||
u8"'='", u8"'{'", u8"'}'", u8"'('", u8"')'", u8"'void'", u8"'if'", u8"'else'",
|
||||
u8"'while'", u8"'break'", u8"'continue'", u8"'return'", u8"'+'", u8"'-'",
|
||||
u8"'!'", u8"'*'", u8"'/'", u8"'%'", u8"'<'", u8"'>'", u8"'<='", u8"'>='",
|
||||
u8"'=='", u8"'!='", u8"'&&'", u8"'||'"
|
||||
};
|
||||
|
||||
std::vector<std::string> SysYLexer::_symbolicNames = {
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", u8"FloatConst",
|
||||
u8"IntConst", u8"Ident", u8"WS", u8"LINE_COMMENT", u8"BLOCK_COMMENT"
|
||||
};
|
||||
|
||||
dfa::Vocabulary SysYLexer::_vocabulary(_literalNames, _symbolicNames);
|
||||
|
||||
std::vector<std::string> SysYLexer::_tokenNames;
|
||||
|
||||
SysYLexer::Initializer::Initializer() {
|
||||
// This code could be in a static initializer lambda, but VS doesn't allow access to private class members from there.
|
||||
for (size_t i = 0; i < _symbolicNames.size(); ++i) {
|
||||
std::string name = _vocabulary.getLiteralName(i);
|
||||
if (name.empty()) {
|
||||
name = _vocabulary.getSymbolicName(i);
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
_tokenNames.push_back("<INVALID>");
|
||||
} else {
|
||||
_tokenNames.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
_serializedATN = {
|
||||
0x3, 0x608b, 0xa72a, 0x8133, 0xb9ed, 0x417c, 0x3be7, 0x7786, 0x5964,
|
||||
0x2, 0x29, 0x160, 0x8, 0x1, 0x4, 0x2, 0x9, 0x2, 0x4, 0x3, 0x9, 0x3,
|
||||
0x4, 0x4, 0x9, 0x4, 0x4, 0x5, 0x9, 0x5, 0x4, 0x6, 0x9, 0x6, 0x4, 0x7,
|
||||
0x9, 0x7, 0x4, 0x8, 0x9, 0x8, 0x4, 0x9, 0x9, 0x9, 0x4, 0xa, 0x9, 0xa,
|
||||
0x4, 0xb, 0x9, 0xb, 0x4, 0xc, 0x9, 0xc, 0x4, 0xd, 0x9, 0xd, 0x4, 0xe,
|
||||
0x9, 0xe, 0x4, 0xf, 0x9, 0xf, 0x4, 0x10, 0x9, 0x10, 0x4, 0x11, 0x9,
|
||||
0x11, 0x4, 0x12, 0x9, 0x12, 0x4, 0x13, 0x9, 0x13, 0x4, 0x14, 0x9, 0x14,
|
||||
0x4, 0x15, 0x9, 0x15, 0x4, 0x16, 0x9, 0x16, 0x4, 0x17, 0x9, 0x17, 0x4,
|
||||
0x18, 0x9, 0x18, 0x4, 0x19, 0x9, 0x19, 0x4, 0x1a, 0x9, 0x1a, 0x4, 0x1b,
|
||||
0x9, 0x1b, 0x4, 0x1c, 0x9, 0x1c, 0x4, 0x1d, 0x9, 0x1d, 0x4, 0x1e, 0x9,
|
||||
0x1e, 0x4, 0x1f, 0x9, 0x1f, 0x4, 0x20, 0x9, 0x20, 0x4, 0x21, 0x9, 0x21,
|
||||
0x4, 0x22, 0x9, 0x22, 0x4, 0x23, 0x9, 0x23, 0x4, 0x24, 0x9, 0x24, 0x4,
|
||||
0x25, 0x9, 0x25, 0x4, 0x26, 0x9, 0x26, 0x4, 0x27, 0x9, 0x27, 0x4, 0x28,
|
||||
0x9, 0x28, 0x4, 0x29, 0x9, 0x29, 0x4, 0x2a, 0x9, 0x2a, 0x4, 0x2b, 0x9,
|
||||
0x2b, 0x4, 0x2c, 0x9, 0x2c, 0x3, 0x2, 0x3, 0x2, 0x3, 0x2, 0x3, 0x2,
|
||||
0x3, 0x2, 0x3, 0x2, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x4, 0x3, 0x5,
|
||||
0x3, 0x5, 0x3, 0x5, 0x3, 0x5, 0x3, 0x6, 0x3, 0x6, 0x3, 0x6, 0x3, 0x6,
|
||||
0x3, 0x6, 0x3, 0x6, 0x3, 0x7, 0x3, 0x7, 0x3, 0x8, 0x3, 0x8, 0x3, 0x9,
|
||||
0x3, 0x9, 0x3, 0xa, 0x3, 0xa, 0x3, 0xb, 0x3, 0xb, 0x3, 0xc, 0x3, 0xc,
|
||||
0x3, 0xd, 0x3, 0xd, 0x3, 0xe, 0x3, 0xe, 0x3, 0xe, 0x3, 0xe, 0x3, 0xe,
|
||||
0x3, 0xf, 0x3, 0xf, 0x3, 0xf, 0x3, 0x10, 0x3, 0x10, 0x3, 0x10, 0x3,
|
||||
0x10, 0x3, 0x10, 0x3, 0x11, 0x3, 0x11, 0x3, 0x11, 0x3, 0x11, 0x3, 0x11,
|
||||
0x3, 0x11, 0x3, 0x12, 0x3, 0x12, 0x3, 0x12, 0x3, 0x12, 0x3, 0x12, 0x3,
|
||||
0x12, 0x3, 0x13, 0x3, 0x13, 0x3, 0x13, 0x3, 0x13, 0x3, 0x13, 0x3, 0x13,
|
||||
0x3, 0x13, 0x3, 0x13, 0x3, 0x13, 0x3, 0x14, 0x3, 0x14, 0x3, 0x14, 0x3,
|
||||
0x14, 0x3, 0x14, 0x3, 0x14, 0x3, 0x14, 0x3, 0x15, 0x3, 0x15, 0x3, 0x16,
|
||||
0x3, 0x16, 0x3, 0x17, 0x3, 0x17, 0x3, 0x18, 0x3, 0x18, 0x3, 0x19, 0x3,
|
||||
0x19, 0x3, 0x1a, 0x3, 0x1a, 0x3, 0x1b, 0x3, 0x1b, 0x3, 0x1c, 0x3, 0x1c,
|
||||
0x3, 0x1d, 0x3, 0x1d, 0x3, 0x1d, 0x3, 0x1e, 0x3, 0x1e, 0x3, 0x1e, 0x3,
|
||||
0x1f, 0x3, 0x1f, 0x3, 0x1f, 0x3, 0x20, 0x3, 0x20, 0x3, 0x20, 0x3, 0x21,
|
||||
0x3, 0x21, 0x3, 0x21, 0x3, 0x22, 0x3, 0x22, 0x3, 0x22, 0x3, 0x23, 0x3,
|
||||
0x23, 0x3, 0x24, 0x3, 0x24, 0x3, 0x25, 0x3, 0x25, 0x5, 0x25, 0xcd, 0xa,
|
||||
0x25, 0x3, 0x25, 0x6, 0x25, 0xd0, 0xa, 0x25, 0xd, 0x25, 0xe, 0x25, 0xd1,
|
||||
0x3, 0x26, 0x3, 0x26, 0x5, 0x26, 0xd6, 0xa, 0x26, 0x3, 0x26, 0x6, 0x26,
|
||||
0xd9, 0xa, 0x26, 0xd, 0x26, 0xe, 0x26, 0xda, 0x3, 0x27, 0x3, 0x27, 0x3,
|
||||
0x27, 0x3, 0x27, 0x5, 0x27, 0xe1, 0xa, 0x27, 0x3, 0x27, 0x6, 0x27, 0xe4,
|
||||
0xa, 0x27, 0xd, 0x27, 0xe, 0x27, 0xe5, 0x3, 0x27, 0x3, 0x27, 0x7, 0x27,
|
||||
0xea, 0xa, 0x27, 0xc, 0x27, 0xe, 0x27, 0xed, 0xb, 0x27, 0x3, 0x27, 0x3,
|
||||
0x27, 0x6, 0x27, 0xf1, 0xa, 0x27, 0xd, 0x27, 0xe, 0x27, 0xf2, 0x3, 0x27,
|
||||
0x6, 0x27, 0xf6, 0xa, 0x27, 0xd, 0x27, 0xe, 0x27, 0xf7, 0x5, 0x27, 0xfa,
|
||||
0xa, 0x27, 0x3, 0x27, 0x3, 0x27, 0x3, 0x27, 0x3, 0x27, 0x6, 0x27, 0x100,
|
||||
0xa, 0x27, 0xd, 0x27, 0xe, 0x27, 0x101, 0x3, 0x27, 0x5, 0x27, 0x105,
|
||||
0xa, 0x27, 0x3, 0x27, 0x6, 0x27, 0x108, 0xa, 0x27, 0xd, 0x27, 0xe, 0x27,
|
||||
0x109, 0x3, 0x27, 0x3, 0x27, 0x7, 0x27, 0x10e, 0xa, 0x27, 0xc, 0x27,
|
||||
0xe, 0x27, 0x111, 0xb, 0x27, 0x3, 0x27, 0x5, 0x27, 0x114, 0xa, 0x27,
|
||||
0x3, 0x27, 0x6, 0x27, 0x117, 0xa, 0x27, 0xd, 0x27, 0xe, 0x27, 0x118,
|
||||
0x3, 0x27, 0x3, 0x27, 0x5, 0x27, 0x11d, 0xa, 0x27, 0x3, 0x28, 0x3, 0x28,
|
||||
0x3, 0x28, 0x7, 0x28, 0x122, 0xa, 0x28, 0xc, 0x28, 0xe, 0x28, 0x125,
|
||||
0xb, 0x28, 0x3, 0x28, 0x3, 0x28, 0x6, 0x28, 0x129, 0xa, 0x28, 0xd, 0x28,
|
||||
0xe, 0x28, 0x12a, 0x3, 0x28, 0x3, 0x28, 0x3, 0x28, 0x3, 0x28, 0x5, 0x28,
|
||||
0x131, 0xa, 0x28, 0x3, 0x28, 0x6, 0x28, 0x134, 0xa, 0x28, 0xd, 0x28,
|
||||
0xe, 0x28, 0x135, 0x5, 0x28, 0x138, 0xa, 0x28, 0x3, 0x29, 0x3, 0x29,
|
||||
0x7, 0x29, 0x13c, 0xa, 0x29, 0xc, 0x29, 0xe, 0x29, 0x13f, 0xb, 0x29,
|
||||
0x3, 0x2a, 0x6, 0x2a, 0x142, 0xa, 0x2a, 0xd, 0x2a, 0xe, 0x2a, 0x143,
|
||||
0x3, 0x2a, 0x3, 0x2a, 0x3, 0x2b, 0x3, 0x2b, 0x3, 0x2b, 0x3, 0x2b, 0x7,
|
||||
0x2b, 0x14c, 0xa, 0x2b, 0xc, 0x2b, 0xe, 0x2b, 0x14f, 0xb, 0x2b, 0x3,
|
||||
0x2b, 0x3, 0x2b, 0x3, 0x2c, 0x3, 0x2c, 0x3, 0x2c, 0x3, 0x2c, 0x7, 0x2c,
|
||||
0x157, 0xa, 0x2c, 0xc, 0x2c, 0xe, 0x2c, 0x15a, 0xb, 0x2c, 0x3, 0x2c,
|
||||
0x3, 0x2c, 0x3, 0x2c, 0x3, 0x2c, 0x3, 0x2c, 0x3, 0x158, 0x2, 0x2d, 0x3,
|
||||
0x3, 0x5, 0x4, 0x7, 0x5, 0x9, 0x6, 0xb, 0x7, 0xd, 0x8, 0xf, 0x9, 0x11,
|
||||
0xa, 0x13, 0xb, 0x15, 0xc, 0x17, 0xd, 0x19, 0xe, 0x1b, 0xf, 0x1d, 0x10,
|
||||
0x1f, 0x11, 0x21, 0x12, 0x23, 0x13, 0x25, 0x14, 0x27, 0x15, 0x29, 0x16,
|
||||
0x2b, 0x17, 0x2d, 0x18, 0x2f, 0x19, 0x31, 0x1a, 0x33, 0x1b, 0x35, 0x1c,
|
||||
0x37, 0x1d, 0x39, 0x1e, 0x3b, 0x1f, 0x3d, 0x20, 0x3f, 0x21, 0x41, 0x22,
|
||||
0x43, 0x23, 0x45, 0x2, 0x47, 0x2, 0x49, 0x2, 0x4b, 0x2, 0x4d, 0x24,
|
||||
0x4f, 0x25, 0x51, 0x26, 0x53, 0x27, 0x55, 0x28, 0x57, 0x29, 0x3, 0x2,
|
||||
0xd, 0x3, 0x2, 0x32, 0x3b, 0x5, 0x2, 0x32, 0x3b, 0x43, 0x48, 0x63, 0x68,
|
||||
0x4, 0x2, 0x47, 0x47, 0x67, 0x67, 0x4, 0x2, 0x2d, 0x2d, 0x2f, 0x2f,
|
||||
0x4, 0x2, 0x52, 0x52, 0x72, 0x72, 0x3, 0x2, 0x33, 0x3b, 0x3, 0x2, 0x32,
|
||||
0x39, 0x5, 0x2, 0x43, 0x5c, 0x61, 0x61, 0x63, 0x7c, 0x6, 0x2, 0x32,
|
||||
0x3b, 0x43, 0x5c, 0x61, 0x61, 0x63, 0x7c, 0x5, 0x2, 0xb, 0xc, 0xf, 0xf,
|
||||
0x22, 0x22, 0x4, 0x2, 0xc, 0xc, 0xf, 0xf, 0x2, 0x17a, 0x2, 0x3, 0x3,
|
||||
0x2, 0x2, 0x2, 0x2, 0x5, 0x3, 0x2, 0x2, 0x2, 0x2, 0x7, 0x3, 0x2, 0x2,
|
||||
0x2, 0x2, 0x9, 0x3, 0x2, 0x2, 0x2, 0x2, 0xb, 0x3, 0x2, 0x2, 0x2, 0x2,
|
||||
0xd, 0x3, 0x2, 0x2, 0x2, 0x2, 0xf, 0x3, 0x2, 0x2, 0x2, 0x2, 0x11, 0x3,
|
||||
0x2, 0x2, 0x2, 0x2, 0x13, 0x3, 0x2, 0x2, 0x2, 0x2, 0x15, 0x3, 0x2, 0x2,
|
||||
0x2, 0x2, 0x17, 0x3, 0x2, 0x2, 0x2, 0x2, 0x19, 0x3, 0x2, 0x2, 0x2, 0x2,
|
||||
0x1b, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1d, 0x3, 0x2, 0x2, 0x2, 0x2, 0x1f,
|
||||
0x3, 0x2, 0x2, 0x2, 0x2, 0x21, 0x3, 0x2, 0x2, 0x2, 0x2, 0x23, 0x3, 0x2,
|
||||
0x2, 0x2, 0x2, 0x25, 0x3, 0x2, 0x2, 0x2, 0x2, 0x27, 0x3, 0x2, 0x2, 0x2,
|
||||
0x2, 0x29, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2b, 0x3, 0x2, 0x2, 0x2, 0x2, 0x2d,
|
||||
0x3, 0x2, 0x2, 0x2, 0x2, 0x2f, 0x3, 0x2, 0x2, 0x2, 0x2, 0x31, 0x3, 0x2,
|
||||
0x2, 0x2, 0x2, 0x33, 0x3, 0x2, 0x2, 0x2, 0x2, 0x35, 0x3, 0x2, 0x2, 0x2,
|
||||
0x2, 0x37, 0x3, 0x2, 0x2, 0x2, 0x2, 0x39, 0x3, 0x2, 0x2, 0x2, 0x2, 0x3b,
|
||||
0x3, 0x2, 0x2, 0x2, 0x2, 0x3d, 0x3, 0x2, 0x2, 0x2, 0x2, 0x3f, 0x3, 0x2,
|
||||
0x2, 0x2, 0x2, 0x41, 0x3, 0x2, 0x2, 0x2, 0x2, 0x43, 0x3, 0x2, 0x2, 0x2,
|
||||
0x2, 0x4d, 0x3, 0x2, 0x2, 0x2, 0x2, 0x4f, 0x3, 0x2, 0x2, 0x2, 0x2, 0x51,
|
||||
0x3, 0x2, 0x2, 0x2, 0x2, 0x53, 0x3, 0x2, 0x2, 0x2, 0x2, 0x55, 0x3, 0x2,
|
||||
0x2, 0x2, 0x2, 0x57, 0x3, 0x2, 0x2, 0x2, 0x3, 0x59, 0x3, 0x2, 0x2, 0x2,
|
||||
0x5, 0x5f, 0x3, 0x2, 0x2, 0x2, 0x7, 0x61, 0x3, 0x2, 0x2, 0x2, 0x9, 0x63,
|
||||
0x3, 0x2, 0x2, 0x2, 0xb, 0x67, 0x3, 0x2, 0x2, 0x2, 0xd, 0x6d, 0x3, 0x2,
|
||||
0x2, 0x2, 0xf, 0x6f, 0x3, 0x2, 0x2, 0x2, 0x11, 0x71, 0x3, 0x2, 0x2,
|
||||
0x2, 0x13, 0x73, 0x3, 0x2, 0x2, 0x2, 0x15, 0x75, 0x3, 0x2, 0x2, 0x2,
|
||||
0x17, 0x77, 0x3, 0x2, 0x2, 0x2, 0x19, 0x79, 0x3, 0x2, 0x2, 0x2, 0x1b,
|
||||
0x7b, 0x3, 0x2, 0x2, 0x2, 0x1d, 0x80, 0x3, 0x2, 0x2, 0x2, 0x1f, 0x83,
|
||||
0x3, 0x2, 0x2, 0x2, 0x21, 0x88, 0x3, 0x2, 0x2, 0x2, 0x23, 0x8e, 0x3,
|
||||
0x2, 0x2, 0x2, 0x25, 0x94, 0x3, 0x2, 0x2, 0x2, 0x27, 0x9d, 0x3, 0x2,
|
||||
0x2, 0x2, 0x29, 0xa4, 0x3, 0x2, 0x2, 0x2, 0x2b, 0xa6, 0x3, 0x2, 0x2,
|
||||
0x2, 0x2d, 0xa8, 0x3, 0x2, 0x2, 0x2, 0x2f, 0xaa, 0x3, 0x2, 0x2, 0x2,
|
||||
0x31, 0xac, 0x3, 0x2, 0x2, 0x2, 0x33, 0xae, 0x3, 0x2, 0x2, 0x2, 0x35,
|
||||
0xb0, 0x3, 0x2, 0x2, 0x2, 0x37, 0xb2, 0x3, 0x2, 0x2, 0x2, 0x39, 0xb4,
|
||||
0x3, 0x2, 0x2, 0x2, 0x3b, 0xb7, 0x3, 0x2, 0x2, 0x2, 0x3d, 0xba, 0x3,
|
||||
0x2, 0x2, 0x2, 0x3f, 0xbd, 0x3, 0x2, 0x2, 0x2, 0x41, 0xc0, 0x3, 0x2,
|
||||
0x2, 0x2, 0x43, 0xc3, 0x3, 0x2, 0x2, 0x2, 0x45, 0xc6, 0x3, 0x2, 0x2,
|
||||
0x2, 0x47, 0xc8, 0x3, 0x2, 0x2, 0x2, 0x49, 0xca, 0x3, 0x2, 0x2, 0x2,
|
||||
0x4b, 0xd3, 0x3, 0x2, 0x2, 0x2, 0x4d, 0x11c, 0x3, 0x2, 0x2, 0x2, 0x4f,
|
||||
0x137, 0x3, 0x2, 0x2, 0x2, 0x51, 0x139, 0x3, 0x2, 0x2, 0x2, 0x53, 0x141,
|
||||
0x3, 0x2, 0x2, 0x2, 0x55, 0x147, 0x3, 0x2, 0x2, 0x2, 0x57, 0x152, 0x3,
|
||||
0x2, 0x2, 0x2, 0x59, 0x5a, 0x7, 0x65, 0x2, 0x2, 0x5a, 0x5b, 0x7, 0x71,
|
||||
0x2, 0x2, 0x5b, 0x5c, 0x7, 0x70, 0x2, 0x2, 0x5c, 0x5d, 0x7, 0x75, 0x2,
|
||||
0x2, 0x5d, 0x5e, 0x7, 0x76, 0x2, 0x2, 0x5e, 0x4, 0x3, 0x2, 0x2, 0x2,
|
||||
0x5f, 0x60, 0x7, 0x2e, 0x2, 0x2, 0x60, 0x6, 0x3, 0x2, 0x2, 0x2, 0x61,
|
||||
0x62, 0x7, 0x3d, 0x2, 0x2, 0x62, 0x8, 0x3, 0x2, 0x2, 0x2, 0x63, 0x64,
|
||||
0x7, 0x6b, 0x2, 0x2, 0x64, 0x65, 0x7, 0x70, 0x2, 0x2, 0x65, 0x66, 0x7,
|
||||
0x76, 0x2, 0x2, 0x66, 0xa, 0x3, 0x2, 0x2, 0x2, 0x67, 0x68, 0x7, 0x68,
|
||||
0x2, 0x2, 0x68, 0x69, 0x7, 0x6e, 0x2, 0x2, 0x69, 0x6a, 0x7, 0x71, 0x2,
|
||||
0x2, 0x6a, 0x6b, 0x7, 0x63, 0x2, 0x2, 0x6b, 0x6c, 0x7, 0x76, 0x2, 0x2,
|
||||
0x6c, 0xc, 0x3, 0x2, 0x2, 0x2, 0x6d, 0x6e, 0x7, 0x5d, 0x2, 0x2, 0x6e,
|
||||
0xe, 0x3, 0x2, 0x2, 0x2, 0x6f, 0x70, 0x7, 0x5f, 0x2, 0x2, 0x70, 0x10,
|
||||
0x3, 0x2, 0x2, 0x2, 0x71, 0x72, 0x7, 0x3f, 0x2, 0x2, 0x72, 0x12, 0x3,
|
||||
0x2, 0x2, 0x2, 0x73, 0x74, 0x7, 0x7d, 0x2, 0x2, 0x74, 0x14, 0x3, 0x2,
|
||||
0x2, 0x2, 0x75, 0x76, 0x7, 0x7f, 0x2, 0x2, 0x76, 0x16, 0x3, 0x2, 0x2,
|
||||
0x2, 0x77, 0x78, 0x7, 0x2a, 0x2, 0x2, 0x78, 0x18, 0x3, 0x2, 0x2, 0x2,
|
||||
0x79, 0x7a, 0x7, 0x2b, 0x2, 0x2, 0x7a, 0x1a, 0x3, 0x2, 0x2, 0x2, 0x7b,
|
||||
0x7c, 0x7, 0x78, 0x2, 0x2, 0x7c, 0x7d, 0x7, 0x71, 0x2, 0x2, 0x7d, 0x7e,
|
||||
0x7, 0x6b, 0x2, 0x2, 0x7e, 0x7f, 0x7, 0x66, 0x2, 0x2, 0x7f, 0x1c, 0x3,
|
||||
0x2, 0x2, 0x2, 0x80, 0x81, 0x7, 0x6b, 0x2, 0x2, 0x81, 0x82, 0x7, 0x68,
|
||||
0x2, 0x2, 0x82, 0x1e, 0x3, 0x2, 0x2, 0x2, 0x83, 0x84, 0x7, 0x67, 0x2,
|
||||
0x2, 0x84, 0x85, 0x7, 0x6e, 0x2, 0x2, 0x85, 0x86, 0x7, 0x75, 0x2, 0x2,
|
||||
0x86, 0x87, 0x7, 0x67, 0x2, 0x2, 0x87, 0x20, 0x3, 0x2, 0x2, 0x2, 0x88,
|
||||
0x89, 0x7, 0x79, 0x2, 0x2, 0x89, 0x8a, 0x7, 0x6a, 0x2, 0x2, 0x8a, 0x8b,
|
||||
0x7, 0x6b, 0x2, 0x2, 0x8b, 0x8c, 0x7, 0x6e, 0x2, 0x2, 0x8c, 0x8d, 0x7,
|
||||
0x67, 0x2, 0x2, 0x8d, 0x22, 0x3, 0x2, 0x2, 0x2, 0x8e, 0x8f, 0x7, 0x64,
|
||||
0x2, 0x2, 0x8f, 0x90, 0x7, 0x74, 0x2, 0x2, 0x90, 0x91, 0x7, 0x67, 0x2,
|
||||
0x2, 0x91, 0x92, 0x7, 0x63, 0x2, 0x2, 0x92, 0x93, 0x7, 0x6d, 0x2, 0x2,
|
||||
0x93, 0x24, 0x3, 0x2, 0x2, 0x2, 0x94, 0x95, 0x7, 0x65, 0x2, 0x2, 0x95,
|
||||
0x96, 0x7, 0x71, 0x2, 0x2, 0x96, 0x97, 0x7, 0x70, 0x2, 0x2, 0x97, 0x98,
|
||||
0x7, 0x76, 0x2, 0x2, 0x98, 0x99, 0x7, 0x6b, 0x2, 0x2, 0x99, 0x9a, 0x7,
|
||||
0x70, 0x2, 0x2, 0x9a, 0x9b, 0x7, 0x77, 0x2, 0x2, 0x9b, 0x9c, 0x7, 0x67,
|
||||
0x2, 0x2, 0x9c, 0x26, 0x3, 0x2, 0x2, 0x2, 0x9d, 0x9e, 0x7, 0x74, 0x2,
|
||||
0x2, 0x9e, 0x9f, 0x7, 0x67, 0x2, 0x2, 0x9f, 0xa0, 0x7, 0x76, 0x2, 0x2,
|
||||
0xa0, 0xa1, 0x7, 0x77, 0x2, 0x2, 0xa1, 0xa2, 0x7, 0x74, 0x2, 0x2, 0xa2,
|
||||
0xa3, 0x7, 0x70, 0x2, 0x2, 0xa3, 0x28, 0x3, 0x2, 0x2, 0x2, 0xa4, 0xa5,
|
||||
0x7, 0x2d, 0x2, 0x2, 0xa5, 0x2a, 0x3, 0x2, 0x2, 0x2, 0xa6, 0xa7, 0x7,
|
||||
0x2f, 0x2, 0x2, 0xa7, 0x2c, 0x3, 0x2, 0x2, 0x2, 0xa8, 0xa9, 0x7, 0x23,
|
||||
0x2, 0x2, 0xa9, 0x2e, 0x3, 0x2, 0x2, 0x2, 0xaa, 0xab, 0x7, 0x2c, 0x2,
|
||||
0x2, 0xab, 0x30, 0x3, 0x2, 0x2, 0x2, 0xac, 0xad, 0x7, 0x31, 0x2, 0x2,
|
||||
0xad, 0x32, 0x3, 0x2, 0x2, 0x2, 0xae, 0xaf, 0x7, 0x27, 0x2, 0x2, 0xaf,
|
||||
0x34, 0x3, 0x2, 0x2, 0x2, 0xb0, 0xb1, 0x7, 0x3e, 0x2, 0x2, 0xb1, 0x36,
|
||||
0x3, 0x2, 0x2, 0x2, 0xb2, 0xb3, 0x7, 0x40, 0x2, 0x2, 0xb3, 0x38, 0x3,
|
||||
0x2, 0x2, 0x2, 0xb4, 0xb5, 0x7, 0x3e, 0x2, 0x2, 0xb5, 0xb6, 0x7, 0x3f,
|
||||
0x2, 0x2, 0xb6, 0x3a, 0x3, 0x2, 0x2, 0x2, 0xb7, 0xb8, 0x7, 0x40, 0x2,
|
||||
0x2, 0xb8, 0xb9, 0x7, 0x3f, 0x2, 0x2, 0xb9, 0x3c, 0x3, 0x2, 0x2, 0x2,
|
||||
0xba, 0xbb, 0x7, 0x3f, 0x2, 0x2, 0xbb, 0xbc, 0x7, 0x3f, 0x2, 0x2, 0xbc,
|
||||
0x3e, 0x3, 0x2, 0x2, 0x2, 0xbd, 0xbe, 0x7, 0x23, 0x2, 0x2, 0xbe, 0xbf,
|
||||
0x7, 0x3f, 0x2, 0x2, 0xbf, 0x40, 0x3, 0x2, 0x2, 0x2, 0xc0, 0xc1, 0x7,
|
||||
0x28, 0x2, 0x2, 0xc1, 0xc2, 0x7, 0x28, 0x2, 0x2, 0xc2, 0x42, 0x3, 0x2,
|
||||
0x2, 0x2, 0xc3, 0xc4, 0x7, 0x7e, 0x2, 0x2, 0xc4, 0xc5, 0x7, 0x7e, 0x2,
|
||||
0x2, 0xc5, 0x44, 0x3, 0x2, 0x2, 0x2, 0xc6, 0xc7, 0x9, 0x2, 0x2, 0x2,
|
||||
0xc7, 0x46, 0x3, 0x2, 0x2, 0x2, 0xc8, 0xc9, 0x9, 0x3, 0x2, 0x2, 0xc9,
|
||||
0x48, 0x3, 0x2, 0x2, 0x2, 0xca, 0xcc, 0x9, 0x4, 0x2, 0x2, 0xcb, 0xcd,
|
||||
0x9, 0x5, 0x2, 0x2, 0xcc, 0xcb, 0x3, 0x2, 0x2, 0x2, 0xcc, 0xcd, 0x3,
|
||||
0x2, 0x2, 0x2, 0xcd, 0xcf, 0x3, 0x2, 0x2, 0x2, 0xce, 0xd0, 0x5, 0x45,
|
||||
0x23, 0x2, 0xcf, 0xce, 0x3, 0x2, 0x2, 0x2, 0xd0, 0xd1, 0x3, 0x2, 0x2,
|
||||
0x2, 0xd1, 0xcf, 0x3, 0x2, 0x2, 0x2, 0xd1, 0xd2, 0x3, 0x2, 0x2, 0x2,
|
||||
0xd2, 0x4a, 0x3, 0x2, 0x2, 0x2, 0xd3, 0xd5, 0x9, 0x6, 0x2, 0x2, 0xd4,
|
||||
0xd6, 0x9, 0x5, 0x2, 0x2, 0xd5, 0xd4, 0x3, 0x2, 0x2, 0x2, 0xd5, 0xd6,
|
||||
0x3, 0x2, 0x2, 0x2, 0xd6, 0xd8, 0x3, 0x2, 0x2, 0x2, 0xd7, 0xd9, 0x5,
|
||||
0x45, 0x23, 0x2, 0xd8, 0xd7, 0x3, 0x2, 0x2, 0x2, 0xd9, 0xda, 0x3, 0x2,
|
||||
0x2, 0x2, 0xda, 0xd8, 0x3, 0x2, 0x2, 0x2, 0xda, 0xdb, 0x3, 0x2, 0x2,
|
||||
0x2, 0xdb, 0x4c, 0x3, 0x2, 0x2, 0x2, 0xdc, 0xdd, 0x7, 0x32, 0x2, 0x2,
|
||||
0xdd, 0xe1, 0x7, 0x7a, 0x2, 0x2, 0xde, 0xdf, 0x7, 0x32, 0x2, 0x2, 0xdf,
|
||||
0xe1, 0x7, 0x5a, 0x2, 0x2, 0xe0, 0xdc, 0x3, 0x2, 0x2, 0x2, 0xe0, 0xde,
|
||||
0x3, 0x2, 0x2, 0x2, 0xe1, 0xf9, 0x3, 0x2, 0x2, 0x2, 0xe2, 0xe4, 0x5,
|
||||
0x47, 0x24, 0x2, 0xe3, 0xe2, 0x3, 0x2, 0x2, 0x2, 0xe4, 0xe5, 0x3, 0x2,
|
||||
0x2, 0x2, 0xe5, 0xe3, 0x3, 0x2, 0x2, 0x2, 0xe5, 0xe6, 0x3, 0x2, 0x2,
|
||||
0x2, 0xe6, 0xe7, 0x3, 0x2, 0x2, 0x2, 0xe7, 0xeb, 0x7, 0x30, 0x2, 0x2,
|
||||
0xe8, 0xea, 0x5, 0x47, 0x24, 0x2, 0xe9, 0xe8, 0x3, 0x2, 0x2, 0x2, 0xea,
|
||||
0xed, 0x3, 0x2, 0x2, 0x2, 0xeb, 0xe9, 0x3, 0x2, 0x2, 0x2, 0xeb, 0xec,
|
||||
0x3, 0x2, 0x2, 0x2, 0xec, 0xfa, 0x3, 0x2, 0x2, 0x2, 0xed, 0xeb, 0x3,
|
||||
0x2, 0x2, 0x2, 0xee, 0xf0, 0x7, 0x30, 0x2, 0x2, 0xef, 0xf1, 0x5, 0x47,
|
||||
0x24, 0x2, 0xf0, 0xef, 0x3, 0x2, 0x2, 0x2, 0xf1, 0xf2, 0x3, 0x2, 0x2,
|
||||
0x2, 0xf2, 0xf0, 0x3, 0x2, 0x2, 0x2, 0xf2, 0xf3, 0x3, 0x2, 0x2, 0x2,
|
||||
0xf3, 0xfa, 0x3, 0x2, 0x2, 0x2, 0xf4, 0xf6, 0x5, 0x47, 0x24, 0x2, 0xf5,
|
||||
0xf4, 0x3, 0x2, 0x2, 0x2, 0xf6, 0xf7, 0x3, 0x2, 0x2, 0x2, 0xf7, 0xf5,
|
||||
0x3, 0x2, 0x2, 0x2, 0xf7, 0xf8, 0x3, 0x2, 0x2, 0x2, 0xf8, 0xfa, 0x3,
|
||||
0x2, 0x2, 0x2, 0xf9, 0xe3, 0x3, 0x2, 0x2, 0x2, 0xf9, 0xee, 0x3, 0x2,
|
||||
0x2, 0x2, 0xf9, 0xf5, 0x3, 0x2, 0x2, 0x2, 0xfa, 0xfb, 0x3, 0x2, 0x2,
|
||||
0x2, 0xfb, 0xfc, 0x5, 0x4b, 0x26, 0x2, 0xfc, 0x11d, 0x3, 0x2, 0x2, 0x2,
|
||||
0xfd, 0xff, 0x7, 0x30, 0x2, 0x2, 0xfe, 0x100, 0x5, 0x45, 0x23, 0x2,
|
||||
0xff, 0xfe, 0x3, 0x2, 0x2, 0x2, 0x100, 0x101, 0x3, 0x2, 0x2, 0x2, 0x101,
|
||||
0xff, 0x3, 0x2, 0x2, 0x2, 0x101, 0x102, 0x3, 0x2, 0x2, 0x2, 0x102, 0x104,
|
||||
0x3, 0x2, 0x2, 0x2, 0x103, 0x105, 0x5, 0x49, 0x25, 0x2, 0x104, 0x103,
|
||||
0x3, 0x2, 0x2, 0x2, 0x104, 0x105, 0x3, 0x2, 0x2, 0x2, 0x105, 0x11d,
|
||||
0x3, 0x2, 0x2, 0x2, 0x106, 0x108, 0x5, 0x45, 0x23, 0x2, 0x107, 0x106,
|
||||
0x3, 0x2, 0x2, 0x2, 0x108, 0x109, 0x3, 0x2, 0x2, 0x2, 0x109, 0x107,
|
||||
0x3, 0x2, 0x2, 0x2, 0x109, 0x10a, 0x3, 0x2, 0x2, 0x2, 0x10a, 0x10b,
|
||||
0x3, 0x2, 0x2, 0x2, 0x10b, 0x10f, 0x7, 0x30, 0x2, 0x2, 0x10c, 0x10e,
|
||||
0x5, 0x45, 0x23, 0x2, 0x10d, 0x10c, 0x3, 0x2, 0x2, 0x2, 0x10e, 0x111,
|
||||
0x3, 0x2, 0x2, 0x2, 0x10f, 0x10d, 0x3, 0x2, 0x2, 0x2, 0x10f, 0x110,
|
||||
0x3, 0x2, 0x2, 0x2, 0x110, 0x113, 0x3, 0x2, 0x2, 0x2, 0x111, 0x10f,
|
||||
0x3, 0x2, 0x2, 0x2, 0x112, 0x114, 0x5, 0x49, 0x25, 0x2, 0x113, 0x112,
|
||||
0x3, 0x2, 0x2, 0x2, 0x113, 0x114, 0x3, 0x2, 0x2, 0x2, 0x114, 0x11d,
|
||||
0x3, 0x2, 0x2, 0x2, 0x115, 0x117, 0x5, 0x45, 0x23, 0x2, 0x116, 0x115,
|
||||
0x3, 0x2, 0x2, 0x2, 0x117, 0x118, 0x3, 0x2, 0x2, 0x2, 0x118, 0x116,
|
||||
0x3, 0x2, 0x2, 0x2, 0x118, 0x119, 0x3, 0x2, 0x2, 0x2, 0x119, 0x11a,
|
||||
0x3, 0x2, 0x2, 0x2, 0x11a, 0x11b, 0x5, 0x49, 0x25, 0x2, 0x11b, 0x11d,
|
||||
0x3, 0x2, 0x2, 0x2, 0x11c, 0xe0, 0x3, 0x2, 0x2, 0x2, 0x11c, 0xfd, 0x3,
|
||||
0x2, 0x2, 0x2, 0x11c, 0x107, 0x3, 0x2, 0x2, 0x2, 0x11c, 0x116, 0x3,
|
||||
0x2, 0x2, 0x2, 0x11d, 0x4e, 0x3, 0x2, 0x2, 0x2, 0x11e, 0x138, 0x7, 0x32,
|
||||
0x2, 0x2, 0x11f, 0x123, 0x9, 0x7, 0x2, 0x2, 0x120, 0x122, 0x9, 0x2,
|
||||
0x2, 0x2, 0x121, 0x120, 0x3, 0x2, 0x2, 0x2, 0x122, 0x125, 0x3, 0x2,
|
||||
0x2, 0x2, 0x123, 0x121, 0x3, 0x2, 0x2, 0x2, 0x123, 0x124, 0x3, 0x2,
|
||||
0x2, 0x2, 0x124, 0x138, 0x3, 0x2, 0x2, 0x2, 0x125, 0x123, 0x3, 0x2,
|
||||
0x2, 0x2, 0x126, 0x128, 0x7, 0x32, 0x2, 0x2, 0x127, 0x129, 0x9, 0x8,
|
||||
0x2, 0x2, 0x128, 0x127, 0x3, 0x2, 0x2, 0x2, 0x129, 0x12a, 0x3, 0x2,
|
||||
0x2, 0x2, 0x12a, 0x128, 0x3, 0x2, 0x2, 0x2, 0x12a, 0x12b, 0x3, 0x2,
|
||||
0x2, 0x2, 0x12b, 0x138, 0x3, 0x2, 0x2, 0x2, 0x12c, 0x12d, 0x7, 0x32,
|
||||
0x2, 0x2, 0x12d, 0x131, 0x7, 0x7a, 0x2, 0x2, 0x12e, 0x12f, 0x7, 0x32,
|
||||
0x2, 0x2, 0x12f, 0x131, 0x7, 0x5a, 0x2, 0x2, 0x130, 0x12c, 0x3, 0x2,
|
||||
0x2, 0x2, 0x130, 0x12e, 0x3, 0x2, 0x2, 0x2, 0x131, 0x133, 0x3, 0x2,
|
||||
0x2, 0x2, 0x132, 0x134, 0x9, 0x3, 0x2, 0x2, 0x133, 0x132, 0x3, 0x2,
|
||||
0x2, 0x2, 0x134, 0x135, 0x3, 0x2, 0x2, 0x2, 0x135, 0x133, 0x3, 0x2,
|
||||
0x2, 0x2, 0x135, 0x136, 0x3, 0x2, 0x2, 0x2, 0x136, 0x138, 0x3, 0x2,
|
||||
0x2, 0x2, 0x137, 0x11e, 0x3, 0x2, 0x2, 0x2, 0x137, 0x11f, 0x3, 0x2,
|
||||
0x2, 0x2, 0x137, 0x126, 0x3, 0x2, 0x2, 0x2, 0x137, 0x130, 0x3, 0x2,
|
||||
0x2, 0x2, 0x138, 0x50, 0x3, 0x2, 0x2, 0x2, 0x139, 0x13d, 0x9, 0x9, 0x2,
|
||||
0x2, 0x13a, 0x13c, 0x9, 0xa, 0x2, 0x2, 0x13b, 0x13a, 0x3, 0x2, 0x2,
|
||||
0x2, 0x13c, 0x13f, 0x3, 0x2, 0x2, 0x2, 0x13d, 0x13b, 0x3, 0x2, 0x2,
|
||||
0x2, 0x13d, 0x13e, 0x3, 0x2, 0x2, 0x2, 0x13e, 0x52, 0x3, 0x2, 0x2, 0x2,
|
||||
0x13f, 0x13d, 0x3, 0x2, 0x2, 0x2, 0x140, 0x142, 0x9, 0xb, 0x2, 0x2,
|
||||
0x141, 0x140, 0x3, 0x2, 0x2, 0x2, 0x142, 0x143, 0x3, 0x2, 0x2, 0x2,
|
||||
0x143, 0x141, 0x3, 0x2, 0x2, 0x2, 0x143, 0x144, 0x3, 0x2, 0x2, 0x2,
|
||||
0x144, 0x145, 0x3, 0x2, 0x2, 0x2, 0x145, 0x146, 0x8, 0x2a, 0x2, 0x2,
|
||||
0x146, 0x54, 0x3, 0x2, 0x2, 0x2, 0x147, 0x148, 0x7, 0x31, 0x2, 0x2,
|
||||
0x148, 0x149, 0x7, 0x31, 0x2, 0x2, 0x149, 0x14d, 0x3, 0x2, 0x2, 0x2,
|
||||
0x14a, 0x14c, 0xa, 0xc, 0x2, 0x2, 0x14b, 0x14a, 0x3, 0x2, 0x2, 0x2,
|
||||
0x14c, 0x14f, 0x3, 0x2, 0x2, 0x2, 0x14d, 0x14b, 0x3, 0x2, 0x2, 0x2,
|
||||
0x14d, 0x14e, 0x3, 0x2, 0x2, 0x2, 0x14e, 0x150, 0x3, 0x2, 0x2, 0x2,
|
||||
0x14f, 0x14d, 0x3, 0x2, 0x2, 0x2, 0x150, 0x151, 0x8, 0x2b, 0x2, 0x2,
|
||||
0x151, 0x56, 0x3, 0x2, 0x2, 0x2, 0x152, 0x153, 0x7, 0x31, 0x2, 0x2,
|
||||
0x153, 0x154, 0x7, 0x2c, 0x2, 0x2, 0x154, 0x158, 0x3, 0x2, 0x2, 0x2,
|
||||
0x155, 0x157, 0xb, 0x2, 0x2, 0x2, 0x156, 0x155, 0x3, 0x2, 0x2, 0x2,
|
||||
0x157, 0x15a, 0x3, 0x2, 0x2, 0x2, 0x158, 0x159, 0x3, 0x2, 0x2, 0x2,
|
||||
0x158, 0x156, 0x3, 0x2, 0x2, 0x2, 0x159, 0x15b, 0x3, 0x2, 0x2, 0x2,
|
||||
0x15a, 0x158, 0x3, 0x2, 0x2, 0x2, 0x15b, 0x15c, 0x7, 0x2c, 0x2, 0x2,
|
||||
0x15c, 0x15d, 0x7, 0x31, 0x2, 0x2, 0x15d, 0x15e, 0x3, 0x2, 0x2, 0x2,
|
||||
0x15e, 0x15f, 0x8, 0x2c, 0x2, 0x2, 0x15f, 0x58, 0x3, 0x2, 0x2, 0x2,
|
||||
0x1d, 0x2, 0xcc, 0xd1, 0xd5, 0xda, 0xe0, 0xe5, 0xeb, 0xf2, 0xf7, 0xf9,
|
||||
0x101, 0x104, 0x109, 0x10f, 0x113, 0x118, 0x11c, 0x123, 0x12a, 0x130,
|
||||
0x135, 0x137, 0x13d, 0x143, 0x14d, 0x158, 0x3, 0x8, 0x2, 0x2,
|
||||
};
|
||||
|
||||
atn::ATNDeserializer deserializer;
|
||||
_atn = deserializer.deserialize(_serializedATN);
|
||||
|
||||
size_t count = _atn.getNumberOfDecisions();
|
||||
_decisionToDFA.reserve(count);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
_decisionToDFA.emplace_back(_atn.getDecisionState(i), i);
|
||||
}
|
||||
}
|
||||
|
||||
SysYLexer::Initializer SysYLexer::_init;
|
||||
@ -0,0 +1,62 @@
|
||||
|
||||
// Generated from SysY.g4 by ANTLR 4.7.2
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
|
||||
|
||||
|
||||
|
||||
class SysYLexer : public antlr4::Lexer {
|
||||
public:
|
||||
enum {
|
||||
T__0 = 1, T__1 = 2, T__2 = 3, T__3 = 4, T__4 = 5, T__5 = 6, T__6 = 7,
|
||||
T__7 = 8, T__8 = 9, T__9 = 10, T__10 = 11, T__11 = 12, T__12 = 13, T__13 = 14,
|
||||
T__14 = 15, T__15 = 16, T__16 = 17, T__17 = 18, T__18 = 19, T__19 = 20,
|
||||
T__20 = 21, T__21 = 22, T__22 = 23, T__23 = 24, T__24 = 25, T__25 = 26,
|
||||
T__26 = 27, T__27 = 28, T__28 = 29, T__29 = 30, T__30 = 31, T__31 = 32,
|
||||
T__32 = 33, FloatConst = 34, IntConst = 35, Ident = 36, WS = 37, LINE_COMMENT = 38,
|
||||
BLOCK_COMMENT = 39
|
||||
};
|
||||
|
||||
SysYLexer(antlr4::CharStream *input);
|
||||
~SysYLexer();
|
||||
|
||||
virtual std::string getGrammarFileName() const override;
|
||||
virtual const std::vector<std::string>& getRuleNames() const override;
|
||||
|
||||
virtual const std::vector<std::string>& getChannelNames() const override;
|
||||
virtual const std::vector<std::string>& getModeNames() const override;
|
||||
virtual const std::vector<std::string>& getTokenNames() const override; // deprecated, use vocabulary instead
|
||||
virtual antlr4::dfa::Vocabulary& getVocabulary() const override;
|
||||
|
||||
virtual const std::vector<uint16_t> getSerializedATN() const override;
|
||||
virtual const antlr4::atn::ATN& getATN() const override;
|
||||
|
||||
private:
|
||||
static std::vector<antlr4::dfa::DFA> _decisionToDFA;
|
||||
static antlr4::atn::PredictionContextCache _sharedContextCache;
|
||||
static std::vector<std::string> _ruleNames;
|
||||
static std::vector<std::string> _tokenNames;
|
||||
static std::vector<std::string> _channelNames;
|
||||
static std::vector<std::string> _modeNames;
|
||||
|
||||
static std::vector<std::string> _literalNames;
|
||||
static std::vector<std::string> _symbolicNames;
|
||||
static antlr4::dfa::Vocabulary _vocabulary;
|
||||
static antlr4::atn::ATN _atn;
|
||||
static std::vector<uint16_t> _serializedATN;
|
||||
|
||||
|
||||
// Individual action functions triggered by action() above.
|
||||
|
||||
// Individual semantic predicate functions triggered by sempred() above.
|
||||
|
||||
struct Initializer {
|
||||
Initializer();
|
||||
};
|
||||
static Initializer _init;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,513 @@
|
||||
|
||||
// Generated from SysY.g4 by ANTLR 4.7.2
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
|
||||
|
||||
|
||||
|
||||
class SysYParser : public antlr4::Parser {
|
||||
public:
|
||||
enum {
|
||||
T__0 = 1, T__1 = 2, T__2 = 3, T__3 = 4, T__4 = 5, T__5 = 6, T__6 = 7,
|
||||
T__7 = 8, T__8 = 9, T__9 = 10, T__10 = 11, T__11 = 12, T__12 = 13, T__13 = 14,
|
||||
T__14 = 15, T__15 = 16, T__16 = 17, T__17 = 18, T__18 = 19, T__19 = 20,
|
||||
T__20 = 21, T__21 = 22, T__22 = 23, T__23 = 24, T__24 = 25, T__25 = 26,
|
||||
T__26 = 27, T__27 = 28, T__28 = 29, T__29 = 30, T__30 = 31, T__31 = 32,
|
||||
T__32 = 33, FloatConst = 34, IntConst = 35, Ident = 36, WS = 37, LINE_COMMENT = 38,
|
||||
BLOCK_COMMENT = 39
|
||||
};
|
||||
|
||||
enum {
|
||||
RuleCompUnit = 0, RuleDecl = 1, RuleConstDecl = 2, RuleBType = 3, RuleConstDef = 4,
|
||||
RuleConstInitVal = 5, RuleVarDecl = 6, RuleVarDef = 7, RuleInitVal = 8,
|
||||
RuleFuncDef = 9, RuleFuncType = 10, RuleFuncFParams = 11, RuleFuncFParam = 12,
|
||||
RuleBlock = 13, RuleBlockItem = 14, RuleStmt = 15, RuleExp = 16, RuleCond = 17,
|
||||
RuleLVal = 18, RulePrimaryExp = 19, RuleNumber = 20, RuleUnaryExp = 21,
|
||||
RuleUnaryOp = 22, RuleFuncRParams = 23, RuleMulExp = 24, RuleAddExp = 25,
|
||||
RuleRelExp = 26, RuleEqExp = 27, RuleLAndExp = 28, RuleLOrExp = 29,
|
||||
RuleConstExp = 30
|
||||
};
|
||||
|
||||
SysYParser(antlr4::TokenStream *input);
|
||||
~SysYParser();
|
||||
|
||||
virtual std::string getGrammarFileName() const override;
|
||||
virtual const antlr4::atn::ATN& getATN() const override { return _atn; };
|
||||
virtual const std::vector<std::string>& getTokenNames() const override { return _tokenNames; }; // deprecated: use vocabulary instead.
|
||||
virtual const std::vector<std::string>& getRuleNames() const override;
|
||||
virtual antlr4::dfa::Vocabulary& getVocabulary() const override;
|
||||
|
||||
|
||||
class CompUnitContext;
|
||||
class DeclContext;
|
||||
class ConstDeclContext;
|
||||
class BTypeContext;
|
||||
class ConstDefContext;
|
||||
class ConstInitValContext;
|
||||
class VarDeclContext;
|
||||
class VarDefContext;
|
||||
class InitValContext;
|
||||
class FuncDefContext;
|
||||
class FuncTypeContext;
|
||||
class FuncFParamsContext;
|
||||
class FuncFParamContext;
|
||||
class BlockContext;
|
||||
class BlockItemContext;
|
||||
class StmtContext;
|
||||
class ExpContext;
|
||||
class CondContext;
|
||||
class LValContext;
|
||||
class PrimaryExpContext;
|
||||
class NumberContext;
|
||||
class UnaryExpContext;
|
||||
class UnaryOpContext;
|
||||
class FuncRParamsContext;
|
||||
class MulExpContext;
|
||||
class AddExpContext;
|
||||
class RelExpContext;
|
||||
class EqExpContext;
|
||||
class LAndExpContext;
|
||||
class LOrExpContext;
|
||||
class ConstExpContext;
|
||||
|
||||
class CompUnitContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
CompUnitContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<DeclContext *> decl();
|
||||
DeclContext* decl(size_t i);
|
||||
std::vector<FuncDefContext *> funcDef();
|
||||
FuncDefContext* funcDef(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
CompUnitContext* compUnit();
|
||||
|
||||
class DeclContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
DeclContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
ConstDeclContext *constDecl();
|
||||
VarDeclContext *varDecl();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
DeclContext* decl();
|
||||
|
||||
class ConstDeclContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
ConstDeclContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
BTypeContext *bType();
|
||||
std::vector<ConstDefContext *> constDef();
|
||||
ConstDefContext* constDef(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
ConstDeclContext* constDecl();
|
||||
|
||||
class BTypeContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
BTypeContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
BTypeContext* bType();
|
||||
|
||||
class ConstDefContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
ConstDefContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
antlr4::tree::TerminalNode *Ident();
|
||||
ConstInitValContext *constInitVal();
|
||||
std::vector<ConstExpContext *> constExp();
|
||||
ConstExpContext* constExp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
ConstDefContext* constDef();
|
||||
|
||||
class ConstInitValContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
ConstInitValContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
ConstExpContext *constExp();
|
||||
std::vector<ConstInitValContext *> constInitVal();
|
||||
ConstInitValContext* constInitVal(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
ConstInitValContext* constInitVal();
|
||||
|
||||
class VarDeclContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
VarDeclContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
BTypeContext *bType();
|
||||
std::vector<VarDefContext *> varDef();
|
||||
VarDefContext* varDef(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
VarDeclContext* varDecl();
|
||||
|
||||
class VarDefContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
VarDefContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
antlr4::tree::TerminalNode *Ident();
|
||||
std::vector<ConstExpContext *> constExp();
|
||||
ConstExpContext* constExp(size_t i);
|
||||
InitValContext *initVal();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
VarDefContext* varDef();
|
||||
|
||||
class InitValContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
InitValContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
ExpContext *exp();
|
||||
std::vector<InitValContext *> initVal();
|
||||
InitValContext* initVal(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
InitValContext* initVal();
|
||||
|
||||
class FuncDefContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
FuncDefContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
FuncTypeContext *funcType();
|
||||
antlr4::tree::TerminalNode *Ident();
|
||||
BlockContext *block();
|
||||
FuncFParamsContext *funcFParams();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
FuncDefContext* funcDef();
|
||||
|
||||
class FuncTypeContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
FuncTypeContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
FuncTypeContext* funcType();
|
||||
|
||||
class FuncFParamsContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
FuncFParamsContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<FuncFParamContext *> funcFParam();
|
||||
FuncFParamContext* funcFParam(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
FuncFParamsContext* funcFParams();
|
||||
|
||||
class FuncFParamContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
FuncFParamContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
BTypeContext *bType();
|
||||
antlr4::tree::TerminalNode *Ident();
|
||||
std::vector<ExpContext *> exp();
|
||||
ExpContext* exp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
FuncFParamContext* funcFParam();
|
||||
|
||||
class BlockContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
BlockContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<BlockItemContext *> blockItem();
|
||||
BlockItemContext* blockItem(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
BlockContext* block();
|
||||
|
||||
class BlockItemContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
BlockItemContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
DeclContext *decl();
|
||||
StmtContext *stmt();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
BlockItemContext* blockItem();
|
||||
|
||||
class StmtContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
StmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
LValContext *lVal();
|
||||
ExpContext *exp();
|
||||
BlockContext *block();
|
||||
CondContext *cond();
|
||||
std::vector<StmtContext *> stmt();
|
||||
StmtContext* stmt(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
StmtContext* stmt();
|
||||
|
||||
class ExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
ExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
AddExpContext *addExp();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
ExpContext* exp();
|
||||
|
||||
class CondContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
CondContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
LOrExpContext *lOrExp();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
CondContext* cond();
|
||||
|
||||
class LValContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
LValContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
antlr4::tree::TerminalNode *Ident();
|
||||
std::vector<ExpContext *> exp();
|
||||
ExpContext* exp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
LValContext* lVal();
|
||||
|
||||
class PrimaryExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
PrimaryExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
ExpContext *exp();
|
||||
LValContext *lVal();
|
||||
NumberContext *number();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
PrimaryExpContext* primaryExp();
|
||||
|
||||
class NumberContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
NumberContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
antlr4::tree::TerminalNode *FloatConst();
|
||||
antlr4::tree::TerminalNode *IntConst();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
NumberContext* number();
|
||||
|
||||
class UnaryExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
UnaryExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
PrimaryExpContext *primaryExp();
|
||||
antlr4::tree::TerminalNode *Ident();
|
||||
FuncRParamsContext *funcRParams();
|
||||
UnaryOpContext *unaryOp();
|
||||
UnaryExpContext *unaryExp();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
UnaryExpContext* unaryExp();
|
||||
|
||||
class UnaryOpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
UnaryOpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
UnaryOpContext* unaryOp();
|
||||
|
||||
class FuncRParamsContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
FuncRParamsContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<ExpContext *> exp();
|
||||
ExpContext* exp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
FuncRParamsContext* funcRParams();
|
||||
|
||||
class MulExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
MulExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<UnaryExpContext *> unaryExp();
|
||||
UnaryExpContext* unaryExp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
MulExpContext* mulExp();
|
||||
|
||||
class AddExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
AddExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<MulExpContext *> mulExp();
|
||||
MulExpContext* mulExp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
AddExpContext* addExp();
|
||||
|
||||
class RelExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
RelExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<AddExpContext *> addExp();
|
||||
AddExpContext* addExp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
RelExpContext* relExp();
|
||||
|
||||
class EqExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
EqExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<RelExpContext *> relExp();
|
||||
RelExpContext* relExp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
EqExpContext* eqExp();
|
||||
|
||||
class LAndExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
LAndExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<EqExpContext *> eqExp();
|
||||
EqExpContext* eqExp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
LAndExpContext* lAndExp();
|
||||
|
||||
class LOrExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
LOrExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
std::vector<LAndExpContext *> lAndExp();
|
||||
LAndExpContext* lAndExp(size_t i);
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
LOrExpContext* lOrExp();
|
||||
|
||||
class ConstExpContext : public antlr4::ParserRuleContext {
|
||||
public:
|
||||
ConstExpContext(antlr4::ParserRuleContext *parent, size_t invokingState);
|
||||
virtual size_t getRuleIndex() const override;
|
||||
AddExpContext *addExp();
|
||||
|
||||
virtual antlrcpp::Any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
|
||||
|
||||
};
|
||||
|
||||
ConstExpContext* constExp();
|
||||
|
||||
|
||||
private:
|
||||
static std::vector<antlr4::dfa::DFA> _decisionToDFA;
|
||||
static antlr4::atn::PredictionContextCache _sharedContextCache;
|
||||
static std::vector<std::string> _ruleNames;
|
||||
static std::vector<std::string> _tokenNames;
|
||||
|
||||
static std::vector<std::string> _literalNames;
|
||||
static std::vector<std::string> _symbolicNames;
|
||||
static antlr4::dfa::Vocabulary _vocabulary;
|
||||
static antlr4::atn::ATN _atn;
|
||||
static std::vector<uint16_t> _serializedATN;
|
||||
|
||||
|
||||
struct Initializer {
|
||||
Initializer();
|
||||
};
|
||||
static Initializer _init;
|
||||
};
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
|
||||
// Generated from SysY.g4 by ANTLR 4.7.2
|
||||
|
||||
|
||||
#include "SysYVisitor.h"
|
||||
|
||||
|
||||
@ -0,0 +1,86 @@
|
||||
|
||||
// Generated from SysY.g4 by ANTLR 4.7.2
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "antlr4-runtime.h"
|
||||
#include "SysYParser.h"
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This class defines an abstract visitor for a parse tree
|
||||
* produced by SysYParser.
|
||||
*/
|
||||
class SysYVisitor : public antlr4::tree::AbstractParseTreeVisitor {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Visit parse trees produced by SysYParser.
|
||||
*/
|
||||
virtual antlrcpp::Any visitCompUnit(SysYParser::CompUnitContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitDecl(SysYParser::DeclContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitConstDecl(SysYParser::ConstDeclContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitBType(SysYParser::BTypeContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitConstDef(SysYParser::ConstDefContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitConstInitVal(SysYParser::ConstInitValContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitVarDecl(SysYParser::VarDeclContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitVarDef(SysYParser::VarDefContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitInitVal(SysYParser::InitValContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitFuncDef(SysYParser::FuncDefContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitFuncType(SysYParser::FuncTypeContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitFuncFParams(SysYParser::FuncFParamsContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitFuncFParam(SysYParser::FuncFParamContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitBlock(SysYParser::BlockContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitBlockItem(SysYParser::BlockItemContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitStmt(SysYParser::StmtContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitExp(SysYParser::ExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitCond(SysYParser::CondContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitLVal(SysYParser::LValContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitPrimaryExp(SysYParser::PrimaryExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitNumber(SysYParser::NumberContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitUnaryExp(SysYParser::UnaryExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitUnaryOp(SysYParser::UnaryOpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitFuncRParams(SysYParser::FuncRParamsContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitMulExp(SysYParser::MulExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitAddExp(SysYParser::AddExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitRelExp(SysYParser::RelExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitEqExp(SysYParser::EqExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitLAndExp(SysYParser::LAndExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitLOrExp(SysYParser::LOrExpContext *context) = 0;
|
||||
|
||||
virtual antlrcpp::Any visitConstExp(SysYParser::ConstExpContext *context) = 0;
|
||||
|
||||
|
||||
};
|
||||
|
||||
@ -0,0 +1,202 @@
|
||||
// 循环分裂:
|
||||
// - 针对单块循环中两段彼此独立的 store 语句组做保守分裂
|
||||
// - 仅处理单归纳变量、无其他 loop-carried phi 的情形
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "LoopPassUtils.h"
|
||||
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
namespace {
|
||||
|
||||
Value* StripPointerBase(Value* value) {
|
||||
while (auto* gep = dynamic_cast<GepInst*>(value)) {
|
||||
value = gep->GetBase();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool IsFissionCandidate(const CanonicalLoopMatch& match) {
|
||||
if (match.loop->GetChildren().size() != 0) return false;
|
||||
if (match.loop->GetBlocks().size() != 2) return false;
|
||||
if (match.body != match.latch) return false;
|
||||
if (match.header_phis.size() != 1) return false;
|
||||
if (match.header_phis.front() != match.induction.phi) return false;
|
||||
if (match.induction.step <= 0) return false;
|
||||
auto* body_term =
|
||||
dynamic_cast<BranchInst*>(match.body->MutableInstructions().back().get());
|
||||
return body_term && body_term->GetTarget() == match.header;
|
||||
}
|
||||
|
||||
bool DependsOnAny(Instruction* inst, const std::unordered_set<Instruction*>& defs) {
|
||||
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
||||
auto* def = dynamic_cast<Instruction*>(inst->GetOperand(i));
|
||||
if (def && defs.count(def) != 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CollectMemoryBases(const std::vector<Instruction*>& group,
|
||||
std::unordered_set<Value*>* loads,
|
||||
std::unordered_set<Value*>* stores) {
|
||||
for (auto* inst : group) {
|
||||
if (auto* load = dynamic_cast<LoadInst*>(inst)) {
|
||||
loads->insert(StripPointerBase(load->GetPtr()));
|
||||
} else if (auto* store = dynamic_cast<StoreInst*>(inst)) {
|
||||
stores->insert(StripPointerBase(store->GetPtr()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HasCrossMemoryDependence(const std::vector<Instruction*>& group1,
|
||||
const std::vector<Instruction*>& group2) {
|
||||
std::unordered_set<Value*> loads1;
|
||||
std::unordered_set<Value*> stores1;
|
||||
std::unordered_set<Value*> loads2;
|
||||
std::unordered_set<Value*> stores2;
|
||||
CollectMemoryBases(group1, &loads1, &stores1);
|
||||
CollectMemoryBases(group2, &loads2, &stores2);
|
||||
|
||||
for (auto* base : stores1) {
|
||||
if (stores2.count(base) != 0 || loads2.count(base) != 0) return true;
|
||||
}
|
||||
for (auto* base : stores2) {
|
||||
if (loads1.count(base) != 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RunFissionOnLoop(Function& func, const CanonicalLoopMatch& match,
|
||||
Context& ctx) {
|
||||
if (!IsFissionCandidate(match)) return false;
|
||||
|
||||
std::vector<Instruction*> body_insts;
|
||||
for (const auto& inst_ptr : match.body->GetInstructions()) {
|
||||
if (!inst_ptr.get()->IsTerminator()) {
|
||||
body_insts.push_back(inst_ptr.get());
|
||||
}
|
||||
}
|
||||
if (body_insts.size() < 3) return false;
|
||||
|
||||
auto* iv_next = dynamic_cast<Instruction*>(match.induction.next);
|
||||
if (!iv_next || iv_next->GetParent() != match.body) return false;
|
||||
|
||||
std::vector<size_t> store_positions;
|
||||
for (size_t i = 0; i < body_insts.size(); ++i) {
|
||||
if (dynamic_cast<StoreInst*>(body_insts[i]) != nullptr) {
|
||||
store_positions.push_back(i);
|
||||
}
|
||||
}
|
||||
if (store_positions.size() != 2) return false;
|
||||
|
||||
const size_t first_store_idx = store_positions[0];
|
||||
const size_t second_store_idx = store_positions[1];
|
||||
if (body_insts.back() != iv_next) return false;
|
||||
if (second_store_idx + 1 != body_insts.size() - 1) return false;
|
||||
|
||||
auto* first_store = static_cast<StoreInst*>(body_insts[first_store_idx]);
|
||||
auto* second_store = static_cast<StoreInst*>(body_insts[second_store_idx]);
|
||||
if (StripPointerBase(first_store->GetPtr()) == StripPointerBase(second_store->GetPtr())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Instruction*> group1(body_insts.begin(),
|
||||
body_insts.begin() + first_store_idx + 1);
|
||||
std::vector<Instruction*> group2(body_insts.begin() + first_store_idx + 1,
|
||||
body_insts.begin() + second_store_idx + 1);
|
||||
|
||||
std::unordered_set<Instruction*> group1_defs(group1.begin(), group1.end());
|
||||
std::unordered_set<Instruction*> group2_defs(group2.begin(), group2.end());
|
||||
group1_defs.erase(iv_next);
|
||||
group2_defs.erase(iv_next);
|
||||
|
||||
for (auto* inst : group2) {
|
||||
if (DependsOnAny(inst, group1_defs)) return false;
|
||||
}
|
||||
for (auto* inst : group1) {
|
||||
if (DependsOnAny(inst, group2_defs)) return false;
|
||||
}
|
||||
if (HasCrossMemoryDependence(group1, group2)) return false;
|
||||
|
||||
auto* original_exit = match.exit;
|
||||
std::string block_suffix = ctx.NextTemp();
|
||||
if (!block_suffix.empty() && block_suffix.front() == '%') {
|
||||
block_suffix.erase(0, 1);
|
||||
}
|
||||
auto* preheader2 =
|
||||
func.CreateBlock(match.header->GetName() + ".fission.pre." + block_suffix);
|
||||
auto* header2 =
|
||||
func.CreateBlock(match.header->GetName() + ".fission.hdr." + block_suffix);
|
||||
auto* body2 =
|
||||
func.CreateBlock(match.body->GetName() + ".fission.body." + block_suffix);
|
||||
|
||||
preheader2->Append<BranchInst>(Type::GetVoidType(), header2);
|
||||
|
||||
auto* iv2 = header2->PrependPhi(Type::GetInt32Type(), ctx.NextTemp());
|
||||
iv2->AddIncoming(match.induction.init, preheader2);
|
||||
|
||||
auto* cmp2 = header2->Append<CmpInst>(
|
||||
match.header_cmp->GetCmpOp(), Type::GetInt32Type(), iv2, match.bound,
|
||||
ctx.NextTemp());
|
||||
header2->Append<CondBranchInst>(Type::GetVoidType(), cmp2, body2, original_exit);
|
||||
|
||||
ValueMap remap;
|
||||
remap.emplace(match.induction.phi, iv2);
|
||||
for (auto* inst : group2) {
|
||||
auto cloned = CloneInstruction(inst, remap, ".f2");
|
||||
if (!cloned) return false;
|
||||
auto* raw = cloned.get();
|
||||
body2->MutableInstructions().push_back(std::move(cloned));
|
||||
raw->SetParent(body2);
|
||||
remap[inst] = raw;
|
||||
}
|
||||
|
||||
auto next2_cloned = CloneInstruction(iv_next, remap, ".f2");
|
||||
if (!next2_cloned) return false;
|
||||
auto* next2 = next2_cloned.get();
|
||||
body2->MutableInstructions().push_back(std::move(next2_cloned));
|
||||
next2->SetParent(body2);
|
||||
body2->Append<BranchInst>(Type::GetVoidType(), header2);
|
||||
iv2->AddIncoming(next2, body2);
|
||||
|
||||
const bool exit_is_true = (match.header_branch->GetTrueBlock() == original_exit);
|
||||
match.header_branch->SetOperand(exit_is_true ? 1 : 2, preheader2);
|
||||
match.header->RemoveSuccessor(original_exit);
|
||||
match.header->AddSuccessor(preheader2);
|
||||
preheader2->AddPredecessor(match.header);
|
||||
original_exit->RemovePredecessor(match.header);
|
||||
|
||||
for (auto* inst : group2) {
|
||||
match.body->RemoveInstruction(inst);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool RunLoopFission(Function& func, Context& ctx) {
|
||||
if (func.IsExternal()) return false;
|
||||
|
||||
analysis::DominatorTree dom_tree(func);
|
||||
analysis::LoopInfo loop_info(func, dom_tree);
|
||||
|
||||
for (const auto& loop_ptr : loop_info.GetLoops()) {
|
||||
auto match = MatchCanonicalLoop(loop_ptr.get());
|
||||
if (!match.has_value()) continue;
|
||||
if (RunFissionOnLoop(func, *match, ctx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace ir
|
||||
@ -0,0 +1,465 @@
|
||||
// 循环习语优化:
|
||||
// - 将连续常量填充的规范循环替换为运行时批量填充调用
|
||||
// - 当前仅处理 step=1、init=0、单 store 的 innermost 循环
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "LoopPassUtils.h"
|
||||
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
namespace {
|
||||
|
||||
struct FillLoopCandidate {
|
||||
analysis::Loop* loop = nullptr;
|
||||
BasicBlock* preheader = nullptr;
|
||||
BasicBlock* header = nullptr;
|
||||
BasicBlock* exit = nullptr;
|
||||
PhiInst* induction = nullptr;
|
||||
Value* bound = nullptr;
|
||||
Value* base_ptr = nullptr;
|
||||
Value* offset = nullptr;
|
||||
int fill_value = 0;
|
||||
};
|
||||
|
||||
struct GuardedRowFillCandidate {
|
||||
analysis::Loop* loop = nullptr;
|
||||
BasicBlock* preheader = nullptr;
|
||||
BasicBlock* header = nullptr;
|
||||
BasicBlock* body = nullptr;
|
||||
BasicBlock* action = nullptr;
|
||||
BasicBlock* latch = nullptr;
|
||||
BasicBlock* exit = nullptr;
|
||||
PhiInst* induction = nullptr;
|
||||
Value* bound = nullptr;
|
||||
PhiInst* linear = nullptr;
|
||||
Value* linear_init = nullptr;
|
||||
int linear_step = 0;
|
||||
Value* base_ptr = nullptr;
|
||||
Value* threshold = nullptr;
|
||||
bool prefix = false;
|
||||
int fill_value = 0;
|
||||
};
|
||||
|
||||
bool ExprDependsOn(Value* value, Value* needle,
|
||||
std::unordered_set<Value*>& visiting) {
|
||||
if (value == needle) return true;
|
||||
auto* inst = dynamic_cast<Instruction*>(value);
|
||||
if (!inst) return false;
|
||||
if (!visiting.insert(value).second) return false;
|
||||
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
||||
if (ExprDependsOn(inst->GetOperand(i), needle, visiting)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExprDependsOn(Value* value, Value* needle) {
|
||||
std::unordered_set<Value*> visiting;
|
||||
return ExprDependsOn(value, needle, visiting);
|
||||
}
|
||||
|
||||
Value* GetIncomingForBlock(PhiInst* phi, BasicBlock* block) {
|
||||
if (!phi || !block) return nullptr;
|
||||
for (size_t i = 0; i < phi->GetNumIncoming(); ++i) {
|
||||
if (phi->GetIncomingBlock(i) == block) {
|
||||
return phi->GetIncomingValue(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Value* MaterializeInvariantExpr(Value* value, analysis::Loop* loop, IRBuilder& builder,
|
||||
ValueMap& remap) {
|
||||
auto it = remap.find(value);
|
||||
if (it != remap.end()) return it->second;
|
||||
if (dynamic_cast<ConstantValue*>(value) || dynamic_cast<Argument*>(value) ||
|
||||
dynamic_cast<GlobalVariable*>(value) || dynamic_cast<Function*>(value)) {
|
||||
return value;
|
||||
}
|
||||
auto* inst = dynamic_cast<Instruction*>(value);
|
||||
if (!inst || !loop->Contains(inst->GetParent())) return value;
|
||||
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
||||
auto* operand = inst->GetOperand(i);
|
||||
remap[operand] = MaterializeInvariantExpr(operand, loop, builder, remap);
|
||||
}
|
||||
auto cloned = CloneInstruction(inst, remap, ".idiom");
|
||||
if (!cloned) return nullptr;
|
||||
auto* raw = cloned.get();
|
||||
InsertInstruction(builder.GetInsertBlock(), std::move(cloned));
|
||||
remap[inst] = raw;
|
||||
return raw;
|
||||
}
|
||||
|
||||
bool HasOutsideUse(Instruction* inst, analysis::Loop* loop) {
|
||||
for (const auto& use : inst->GetUses()) {
|
||||
auto* user = dynamic_cast<Instruction*>(use.GetUser());
|
||||
if (!user) return true;
|
||||
if (!user->GetParent() || !loop->Contains(user->GetParent())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Value* MatchContiguousOffset(Value* index, PhiInst* iv, analysis::Loop* loop) {
|
||||
if (index == iv) return nullptr;
|
||||
auto* bin = dynamic_cast<BinaryInst*>(index);
|
||||
if (!bin || bin->GetOpcode() != Opcode::Add ||
|
||||
!bin->GetType() || !bin->GetType()->IsInt32()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (bin->GetLhs() == iv && IsLoopInvariantValue(bin->GetRhs(), loop)) {
|
||||
return bin->GetRhs();
|
||||
}
|
||||
if (bin->GetRhs() == iv && IsLoopInvariantValue(bin->GetLhs(), loop)) {
|
||||
return bin->GetLhs();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool BuildFillLoopCandidate(Function& func, analysis::Loop* loop,
|
||||
FillLoopCandidate* out) {
|
||||
(void)func;
|
||||
auto match = MatchCanonicalLoop(loop);
|
||||
if (!match.has_value()) return false;
|
||||
if (match->loop->GetChildren().size() != 0) return false;
|
||||
if (match->body != match->latch || loop->GetBlocks().size() != 2) return false;
|
||||
if (match->header_phis.size() != 1 ||
|
||||
match->header_phis.front() != match->induction.phi) {
|
||||
return false;
|
||||
}
|
||||
if (match->induction.step != 1) return false;
|
||||
if (match->header_cmp->GetCmpOp() != CmpOp::Lt) return false;
|
||||
auto* init_ci = dynamic_cast<ConstantInt*>(match->induction.init);
|
||||
if (!init_ci || init_ci->GetValue() != 0) return false;
|
||||
if (!match->exit->GetInstructions().empty() &&
|
||||
dynamic_cast<PhiInst*>(match->exit->GetInstructions().front().get()) != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StoreInst* store = nullptr;
|
||||
std::vector<Instruction*> body_insts;
|
||||
for (const auto& inst_ptr : match->body->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (dynamic_cast<PhiInst*>(inst) != nullptr || inst->IsTerminator()) continue;
|
||||
body_insts.push_back(inst);
|
||||
switch (inst->GetOpcode()) {
|
||||
case Opcode::Add:
|
||||
case Opcode::Gep:
|
||||
case Opcode::Store:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
if (inst != match->induction.next && HasOutsideUse(inst, loop)) {
|
||||
return false;
|
||||
}
|
||||
if (auto* maybe_store = dynamic_cast<StoreInst*>(inst)) {
|
||||
if (store) return false;
|
||||
store = maybe_store;
|
||||
}
|
||||
}
|
||||
if (!store) return false;
|
||||
|
||||
auto* fill_ci = dynamic_cast<ConstantInt*>(store->GetValue());
|
||||
if (!fill_ci) return false;
|
||||
auto* gep = dynamic_cast<GepInst*>(store->GetPtr());
|
||||
if (!gep || !gep->GetBase() || !gep->GetBase()->GetType() ||
|
||||
!gep->GetBase()->GetType()->IsPtrInt32()) {
|
||||
return false;
|
||||
}
|
||||
Value* offset = MatchContiguousOffset(gep->GetIndex(), match->induction.phi, loop);
|
||||
if (gep->GetIndex() != match->induction.phi && offset == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
out->loop = loop;
|
||||
out->preheader = match->preheader;
|
||||
out->header = match->header;
|
||||
out->exit = match->exit;
|
||||
out->induction = match->induction.phi;
|
||||
out->bound = match->bound;
|
||||
out->base_ptr = gep->GetBase();
|
||||
out->offset = offset;
|
||||
out->fill_value = fill_ci->GetValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
Function* GetOrCreateFillI32(Module& module) {
|
||||
if (auto* fn = module.FindFunction("__fill_i32")) return fn;
|
||||
auto* fn = module.CreateFunction("__fill_i32", Type::GetVoidType(),
|
||||
{Type::GetPtrInt32Type(), Type::GetInt32Type(),
|
||||
Type::GetInt32Type()});
|
||||
fn->SetExternal(true);
|
||||
return fn;
|
||||
}
|
||||
|
||||
Function* GetOrCreateFillRowsI32(Module& module) {
|
||||
if (auto* fn = module.FindFunction("__fill_rows_i32")) return fn;
|
||||
auto* fn = module.CreateFunction(
|
||||
"__fill_rows_i32", Type::GetVoidType(),
|
||||
{Type::GetPtrInt32Type(), Type::GetInt32Type(), Type::GetInt32Type(),
|
||||
Type::GetInt32Type(), Type::GetInt32Type(), Type::GetInt32Type()});
|
||||
fn->SetExternal(true);
|
||||
return fn;
|
||||
}
|
||||
|
||||
bool BuildGuardedRowFillCandidate(Function& func, analysis::Loop* loop,
|
||||
GuardedRowFillCandidate* out) {
|
||||
(void)func;
|
||||
if (!loop) return false;
|
||||
if (loop->GetChildren().size() != 0) return false;
|
||||
if (loop->GetBlocks().size() != 4) return false;
|
||||
|
||||
auto* header = loop->GetHeader();
|
||||
auto* preheader = loop->GetPreheader();
|
||||
if (!header || !preheader) return false;
|
||||
if (loop->GetLatches().size() != 1) return false;
|
||||
auto* latch = loop->GetLatches().front();
|
||||
if (!latch) return false;
|
||||
|
||||
auto* header_term = header->HasTerminator()
|
||||
? dynamic_cast<CondBranchInst*>(
|
||||
header->MutableInstructions().back().get())
|
||||
: nullptr;
|
||||
if (!header_term) return false;
|
||||
auto* header_cmp = dynamic_cast<CmpInst*>(header_term->GetCond());
|
||||
if (!header_cmp || header_cmp->GetCmpOp() != CmpOp::Lt) return false;
|
||||
|
||||
auto induction = MatchCanonicalInduction(header, preheader, latch);
|
||||
if (!induction.has_value() || induction->step != 1) return false;
|
||||
Value* bound = nullptr;
|
||||
BasicBlock* body = nullptr;
|
||||
BasicBlock* exit = nullptr;
|
||||
if (loop->Contains(header_term->GetTrueBlock()) &&
|
||||
!loop->Contains(header_term->GetFalseBlock())) {
|
||||
body = header_term->GetTrueBlock();
|
||||
exit = header_term->GetFalseBlock();
|
||||
} else if (loop->Contains(header_term->GetFalseBlock()) &&
|
||||
!loop->Contains(header_term->GetTrueBlock())) {
|
||||
body = header_term->GetFalseBlock();
|
||||
exit = header_term->GetTrueBlock();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (header_cmp->GetLhs() == induction->phi &&
|
||||
IsLoopInvariantValue(header_cmp->GetRhs(), loop)) {
|
||||
bound = header_cmp->GetRhs();
|
||||
} else if (header_cmp->GetRhs() == induction->phi &&
|
||||
IsLoopInvariantValue(header_cmp->GetLhs(), loop)) {
|
||||
bound = header_cmp->GetLhs();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto header_phis = CollectHeaderPhis(header);
|
||||
if (header_phis.size() != 2) return false;
|
||||
PhiInst* linear_phi = nullptr;
|
||||
for (auto* phi : header_phis) {
|
||||
if (phi != induction->phi) {
|
||||
linear_phi = phi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!linear_phi || !linear_phi->GetType() || !linear_phi->GetType()->IsInt32()) {
|
||||
return false;
|
||||
}
|
||||
auto* linear_init = GetIncomingForBlock(linear_phi, preheader);
|
||||
auto* linear_next = GetIncomingForBlock(linear_phi, latch);
|
||||
auto* linear_next_bin = dynamic_cast<BinaryInst*>(linear_next);
|
||||
if (!linear_init || !linear_next_bin ||
|
||||
linear_next_bin->GetOpcode() != Opcode::Add ||
|
||||
linear_next_bin->GetLhs() != linear_phi) {
|
||||
return false;
|
||||
}
|
||||
auto* linear_step_ci = dynamic_cast<ConstantInt*>(linear_next_bin->GetRhs());
|
||||
if (!linear_step_ci || linear_step_ci->GetValue() <= 0) return false;
|
||||
|
||||
auto* guard = body->HasTerminator()
|
||||
? dynamic_cast<CondBranchInst*>(body->MutableInstructions().back().get())
|
||||
: nullptr;
|
||||
if (!guard) return false;
|
||||
BasicBlock* action = nullptr;
|
||||
if (guard->GetTrueBlock() == latch && loop->Contains(guard->GetFalseBlock())) {
|
||||
action = guard->GetFalseBlock();
|
||||
} else if (guard->GetFalseBlock() == latch &&
|
||||
loop->Contains(guard->GetTrueBlock())) {
|
||||
action = guard->GetTrueBlock();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
auto* action_term =
|
||||
dynamic_cast<BranchInst*>(action->MutableInstructions().back().get());
|
||||
if (!action_term || action_term->GetTarget() != latch) return false;
|
||||
|
||||
CallInst* fill_call = nullptr;
|
||||
GepInst* fill_gep = nullptr;
|
||||
for (const auto& inst_ptr : action->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (inst->IsTerminator()) continue;
|
||||
if (auto* gep = dynamic_cast<GepInst*>(inst)) {
|
||||
fill_gep = gep;
|
||||
continue;
|
||||
}
|
||||
fill_call = dynamic_cast<CallInst*>(inst);
|
||||
}
|
||||
if (!fill_call || !fill_gep || fill_call->GetNumArgs() != 3) return false;
|
||||
auto* callee = fill_call->GetCallee();
|
||||
if (!callee || callee->GetName() != "__fill_i32") return false;
|
||||
auto* fill_value = dynamic_cast<ConstantInt*>(fill_call->GetArg(2));
|
||||
if (!fill_value) return false;
|
||||
if (fill_call->GetArg(0) != fill_gep || fill_call->GetArg(1) != bound) {
|
||||
return false;
|
||||
}
|
||||
if (fill_gep->GetIndex() != linear_phi) return false;
|
||||
if (!fill_gep->GetBase() || !fill_gep->GetBase()->GetType() ||
|
||||
!fill_gep->GetBase()->GetType()->IsPtrInt32()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* guard_cmp = dynamic_cast<CmpInst*>(guard->GetCond());
|
||||
if (!guard_cmp || !guard_cmp->GetType() || !guard_cmp->GetType()->IsInt32()) {
|
||||
return false;
|
||||
}
|
||||
Value* threshold = nullptr;
|
||||
bool prefix = false;
|
||||
bool suffix = false;
|
||||
if (guard_cmp->GetLhs() == induction->phi &&
|
||||
!ExprDependsOn(guard_cmp->GetRhs(), induction->phi) &&
|
||||
!ExprDependsOn(guard_cmp->GetRhs(), linear_phi)) {
|
||||
threshold = guard_cmp->GetRhs();
|
||||
if (guard_cmp->GetCmpOp() == CmpOp::Lt && action == guard->GetTrueBlock()) {
|
||||
prefix = true;
|
||||
} else if (guard_cmp->GetCmpOp() == CmpOp::Ge &&
|
||||
action == guard->GetTrueBlock()) {
|
||||
suffix = true;
|
||||
} else if (guard_cmp->GetCmpOp() == CmpOp::Lt &&
|
||||
action == guard->GetFalseBlock()) {
|
||||
suffix = true;
|
||||
} else if (guard_cmp->GetCmpOp() == CmpOp::Ge &&
|
||||
action == guard->GetFalseBlock()) {
|
||||
prefix = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
out->loop = loop;
|
||||
out->preheader = preheader;
|
||||
out->header = header;
|
||||
out->body = body;
|
||||
out->action = action;
|
||||
out->latch = latch;
|
||||
out->exit = exit;
|
||||
out->induction = induction->phi;
|
||||
out->bound = bound;
|
||||
out->linear = linear_phi;
|
||||
out->linear_init = linear_init;
|
||||
out->linear_step = linear_step_ci->GetValue();
|
||||
out->base_ptr = fill_gep->GetBase();
|
||||
out->fill_value = fill_value->GetValue();
|
||||
out->threshold = threshold;
|
||||
out->prefix = prefix;
|
||||
return prefix || suffix;
|
||||
}
|
||||
|
||||
bool RunFillLoop(Function& func, const FillLoopCandidate& cand,
|
||||
Module& module, Context& ctx) {
|
||||
(void)func;
|
||||
auto* fill_fn = GetOrCreateFillI32(module);
|
||||
auto* preheader = cand.preheader;
|
||||
if (preheader->HasTerminator()) {
|
||||
preheader->RemoveInstruction(preheader->MutableInstructions().back().get());
|
||||
}
|
||||
|
||||
IRBuilder builder(ctx, preheader);
|
||||
Value* start_ptr = cand.base_ptr;
|
||||
if (cand.offset) {
|
||||
start_ptr = builder.CreateGep(cand.base_ptr, cand.offset, ctx.NextTemp());
|
||||
}
|
||||
builder.CreateCall(fill_fn, {start_ptr, cand.bound, ctx.GetConstInt(cand.fill_value)},
|
||||
"");
|
||||
preheader->Append<BranchInst>(Type::GetVoidType(), cand.exit);
|
||||
|
||||
cand.induction->RemoveIncomingBlock(preheader);
|
||||
preheader->RemoveSuccessor(cand.header);
|
||||
cand.header->RemovePredecessor(preheader);
|
||||
preheader->AddSuccessor(cand.exit);
|
||||
cand.exit->AddPredecessor(preheader);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RunGuardedRowFillLoop(Function& func, const GuardedRowFillCandidate& cand,
|
||||
Module& module, Context& ctx) {
|
||||
(void)func;
|
||||
auto* fill_rows_fn = GetOrCreateFillRowsI32(module);
|
||||
auto* preheader = cand.preheader;
|
||||
if (preheader->HasTerminator()) {
|
||||
preheader->RemoveInstruction(preheader->MutableInstructions().back().get());
|
||||
}
|
||||
|
||||
IRBuilder builder(ctx, preheader);
|
||||
ValueMap remap;
|
||||
auto* threshold =
|
||||
MaterializeInvariantExpr(cand.threshold, cand.loop, builder, remap);
|
||||
if (!threshold) return false;
|
||||
Value* start_index = cand.prefix ? ctx.GetConstInt(0) : threshold;
|
||||
Value* rows = cand.prefix ? threshold : nullptr;
|
||||
if (!cand.prefix) {
|
||||
rows = builder.CreateSub(cand.bound, start_index, ctx.NextTemp());
|
||||
}
|
||||
auto* start_offset_mul =
|
||||
builder.CreateMul(start_index, ctx.GetConstInt(cand.linear_step), ctx.NextTemp());
|
||||
auto* start_offset =
|
||||
builder.CreateAdd(cand.linear_init, start_offset_mul, ctx.NextTemp());
|
||||
builder.CreateCall(fill_rows_fn,
|
||||
{cand.base_ptr, start_offset, rows,
|
||||
ctx.GetConstInt(cand.linear_step), cand.bound,
|
||||
ctx.GetConstInt(cand.fill_value)},
|
||||
"");
|
||||
preheader->Append<BranchInst>(Type::GetVoidType(), cand.exit);
|
||||
|
||||
cand.induction->RemoveIncomingBlock(preheader);
|
||||
cand.linear->RemoveIncomingBlock(preheader);
|
||||
preheader->RemoveSuccessor(cand.header);
|
||||
cand.header->RemovePredecessor(preheader);
|
||||
preheader->AddSuccessor(cand.exit);
|
||||
cand.exit->AddPredecessor(preheader);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool RunLoopIdiom(Function& func, Module& module, Context& ctx) {
|
||||
if (func.IsExternal()) return false;
|
||||
|
||||
analysis::DominatorTree dom_tree(func);
|
||||
analysis::LoopInfo loop_info(func, dom_tree);
|
||||
|
||||
for (const auto& loop_ptr : loop_info.GetLoops()) {
|
||||
GuardedRowFillCandidate row_fill;
|
||||
if (BuildGuardedRowFillCandidate(func, loop_ptr.get(), &row_fill)) {
|
||||
if (RunGuardedRowFillLoop(func, row_fill, module, ctx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FillLoopCandidate cand;
|
||||
if (!BuildFillLoopCandidate(func, loop_ptr.get(), &cand)) continue;
|
||||
if (RunFillLoop(func, cand, module, ctx)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace ir
|
||||
@ -0,0 +1,846 @@
|
||||
// 循环并行化:
|
||||
// - 将一部分安全的规范循环抽取成 worker 函数
|
||||
// - 通过运行时 __par_runN 启动固定线程数并行执行
|
||||
//
|
||||
// 当前限制:
|
||||
// - 仅并行化不存在 SSA live-out 的循环
|
||||
// - 循环访问对象必须是全局数组/全局变量
|
||||
// - 不支持循环中的普通函数调用
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "LoopPassUtils.h"
|
||||
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
bool RunSimpleDCE(Function& func);
|
||||
bool RunCFGSimplify(Function& func, Context& ctx);
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int kParallelLoopSlots = 8;
|
||||
constexpr int kParallelThreads = 4;
|
||||
|
||||
enum class ParallelLoopKind {
|
||||
Pointwise,
|
||||
ReductionAddI32,
|
||||
GuardedFillI32,
|
||||
};
|
||||
|
||||
struct LoopContextValue {
|
||||
Value* original = nullptr;
|
||||
GlobalVariable* slot = nullptr;
|
||||
};
|
||||
|
||||
bool ExprDependsOn(Value* value, Value* needle,
|
||||
std::unordered_set<Value*>& visiting) {
|
||||
if (value == needle) return true;
|
||||
auto* inst = dynamic_cast<Instruction*>(value);
|
||||
if (!inst) return false;
|
||||
if (!visiting.insert(value).second) return false;
|
||||
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
||||
if (ExprDependsOn(inst->GetOperand(i), needle, visiting)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExprDependsOn(Value* value, Value* needle) {
|
||||
std::unordered_set<Value*> visiting;
|
||||
return ExprDependsOn(value, needle, visiting);
|
||||
}
|
||||
|
||||
Value* StripPointerBase(Value* value) {
|
||||
while (auto* gep = dynamic_cast<GepInst*>(value)) {
|
||||
value = gep->GetBase();
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool IsSupportedParallelInst(Instruction* inst) {
|
||||
switch (inst->GetOpcode()) {
|
||||
case Opcode::Add:
|
||||
case Opcode::Sub:
|
||||
case Opcode::Mul:
|
||||
case Opcode::Div:
|
||||
case Opcode::Mod:
|
||||
case Opcode::Cmp:
|
||||
case Opcode::Cast:
|
||||
case Opcode::Load:
|
||||
case Opcode::Store:
|
||||
case Opcode::Br:
|
||||
case Opcode::CondBr:
|
||||
case Opcode::Gep:
|
||||
case Opcode::Phi:
|
||||
case Opcode::Ret:
|
||||
return true;
|
||||
case Opcode::Call:
|
||||
case Opcode::Alloca:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsScalarContextCandidate(Value* value) {
|
||||
auto* arg = dynamic_cast<Argument*>(value);
|
||||
if (arg && (arg->GetType()->IsInt32() || arg->GetType()->IsFloat32())) {
|
||||
return true;
|
||||
}
|
||||
auto* inst = dynamic_cast<Instruction*>(value);
|
||||
if (inst && inst->GetType() &&
|
||||
(inst->GetType()->IsInt32() || inst->GetType()->IsFloat32())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HasOutsideUse(Instruction* inst, analysis::Loop* loop) {
|
||||
for (const auto& use : inst->GetUses()) {
|
||||
auto* user = dynamic_cast<Instruction*>(use.GetUser());
|
||||
if (!user) return true;
|
||||
if (!user->GetParent() || !loop->Contains(user->GetParent())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReplaceUsesOutsideLoop(Value* value, Value* replacement,
|
||||
analysis::Loop* loop) {
|
||||
if (!value || !replacement || !loop) return;
|
||||
auto uses = value->GetUses();
|
||||
for (const auto& use : uses) {
|
||||
auto* user = dynamic_cast<Instruction*>(use.GetUser());
|
||||
if (!user) continue;
|
||||
auto* parent = user->GetParent();
|
||||
if (parent && loop->Contains(parent)) continue;
|
||||
user->SetOperand(use.GetOperandIndex(), replacement);
|
||||
}
|
||||
}
|
||||
|
||||
Value* GetIncomingForBlock(PhiInst* phi, BasicBlock* block) {
|
||||
if (!phi || !block) return nullptr;
|
||||
for (size_t i = 0; i < phi->GetNumIncoming(); ++i) {
|
||||
if (phi->GetIncomingBlock(i) == block) {
|
||||
return phi->GetIncomingValue(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct ParallelLoopCandidate {
|
||||
Function* parent = nullptr;
|
||||
analysis::Loop* loop = nullptr;
|
||||
ParallelLoopKind kind = ParallelLoopKind::Pointwise;
|
||||
BasicBlock* header = nullptr;
|
||||
BasicBlock* body = nullptr;
|
||||
BasicBlock* guard = nullptr;
|
||||
BasicBlock* action = nullptr;
|
||||
BasicBlock* preheader = nullptr;
|
||||
BasicBlock* exit = nullptr;
|
||||
BasicBlock* latch = nullptr;
|
||||
CmpInst* header_cmp = nullptr;
|
||||
PhiInst* induction = nullptr;
|
||||
Value* induction_next = nullptr;
|
||||
Value* bound = nullptr;
|
||||
PhiInst* linear = nullptr;
|
||||
Value* linear_init = nullptr;
|
||||
Value* linear_next = nullptr;
|
||||
int linear_step = 0;
|
||||
PhiInst* reduction = nullptr;
|
||||
Value* reduction_init = nullptr;
|
||||
Value* reduction_next = nullptr;
|
||||
bool has_loads = false;
|
||||
std::vector<LoopContextValue> contexts;
|
||||
};
|
||||
|
||||
bool BuildGuardedFillCandidate(Function& func, analysis::Loop* loop,
|
||||
ParallelLoopCandidate* out) {
|
||||
if (!loop) return false;
|
||||
auto* header = loop->GetHeader();
|
||||
auto* preheader = loop->GetPreheader();
|
||||
if (!header || !preheader) return false;
|
||||
if (loop->GetChildren().size() != 0) return false;
|
||||
if (loop->GetBlocks().size() != 4) return false;
|
||||
if (loop->GetLatches().size() != 1) return false;
|
||||
auto* latch = loop->GetLatches().front();
|
||||
if (!latch) return false;
|
||||
|
||||
auto* header_term = header->HasTerminator()
|
||||
? dynamic_cast<CondBranchInst*>(
|
||||
header->MutableInstructions().back().get())
|
||||
: nullptr;
|
||||
if (!header_term) return false;
|
||||
auto* header_cmp = dynamic_cast<CmpInst*>(header_term->GetCond());
|
||||
if (!header_cmp || header_cmp->GetCmpOp() != CmpOp::Lt) return false;
|
||||
|
||||
BasicBlock* body = nullptr;
|
||||
BasicBlock* exit = nullptr;
|
||||
if (loop->Contains(header_term->GetTrueBlock()) &&
|
||||
!loop->Contains(header_term->GetFalseBlock())) {
|
||||
body = header_term->GetTrueBlock();
|
||||
exit = header_term->GetFalseBlock();
|
||||
} else if (loop->Contains(header_term->GetFalseBlock()) &&
|
||||
!loop->Contains(header_term->GetTrueBlock())) {
|
||||
body = header_term->GetFalseBlock();
|
||||
exit = header_term->GetTrueBlock();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto induction = MatchCanonicalInduction(header, preheader, latch);
|
||||
if (!induction.has_value() || induction->step != 1) return false;
|
||||
Value* bound = nullptr;
|
||||
if (header_cmp->GetLhs() == induction->phi &&
|
||||
IsLoopInvariantValue(header_cmp->GetRhs(), loop)) {
|
||||
bound = header_cmp->GetRhs();
|
||||
} else if (header_cmp->GetRhs() == induction->phi &&
|
||||
IsLoopInvariantValue(header_cmp->GetLhs(), loop)) {
|
||||
bound = header_cmp->GetLhs();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto header_phis = CollectHeaderPhis(header);
|
||||
if (header_phis.size() != 2) return false;
|
||||
|
||||
PhiInst* linear_phi = nullptr;
|
||||
for (auto* phi : header_phis) {
|
||||
if (phi != induction->phi) {
|
||||
linear_phi = phi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!linear_phi || !linear_phi->GetType() || !linear_phi->GetType()->IsInt32()) {
|
||||
return false;
|
||||
}
|
||||
auto* linear_init = GetIncomingForBlock(linear_phi, preheader);
|
||||
auto* linear_next = GetIncomingForBlock(linear_phi, latch);
|
||||
auto* linear_next_bin = dynamic_cast<BinaryInst*>(linear_next);
|
||||
if (!linear_init || !linear_next_bin ||
|
||||
linear_next_bin->GetOpcode() != Opcode::Add ||
|
||||
linear_next_bin->GetLhs() != linear_phi) {
|
||||
return false;
|
||||
}
|
||||
auto* linear_step_ci = dynamic_cast<ConstantInt*>(linear_next_bin->GetRhs());
|
||||
if (!linear_step_ci || linear_step_ci->GetValue() <= 0) return false;
|
||||
|
||||
auto* guard = dynamic_cast<CondBranchInst*>(body->MutableInstructions().back().get());
|
||||
if (!guard) return false;
|
||||
auto* true_bb = guard->GetTrueBlock();
|
||||
auto* false_bb = guard->GetFalseBlock();
|
||||
if (!loop->Contains(true_bb) && !loop->Contains(false_bb)) return false;
|
||||
if (loop->Contains(true_bb) && loop->Contains(false_bb)) {
|
||||
if (true_bb == latch || false_bb == latch) {
|
||||
// fine
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
BasicBlock* action = nullptr;
|
||||
if (true_bb == latch && false_bb != latch && loop->Contains(false_bb)) {
|
||||
action = false_bb;
|
||||
} else if (false_bb == latch && true_bb != latch &&
|
||||
loop->Contains(true_bb)) {
|
||||
action = true_bb;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (action == header || action == body) return false;
|
||||
auto* action_term =
|
||||
dynamic_cast<BranchInst*>(action->MutableInstructions().back().get());
|
||||
if (!action_term || action_term->GetTarget() != latch) return false;
|
||||
|
||||
CallInst* fill_call = nullptr;
|
||||
for (const auto& inst_ptr : action->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (inst->IsTerminator()) continue;
|
||||
if (auto* gep = dynamic_cast<GepInst*>(inst)) {
|
||||
if (gep->GetBase() == nullptr || gep->GetIndex() == nullptr) return false;
|
||||
continue;
|
||||
}
|
||||
fill_call = dynamic_cast<CallInst*>(inst);
|
||||
if (!fill_call) return false;
|
||||
}
|
||||
if (!fill_call || fill_call->GetNumArgs() != 3) return false;
|
||||
auto* callee = fill_call->GetCallee();
|
||||
if (!callee || callee->GetName() != "__fill_i32") return false;
|
||||
auto* fill_ptr = dynamic_cast<GepInst*>(fill_call->GetArg(0));
|
||||
auto* fill_count = fill_call->GetArg(1);
|
||||
auto* fill_value = dynamic_cast<ConstantInt*>(fill_call->GetArg(2));
|
||||
if (!fill_ptr || !fill_value) return false;
|
||||
if (fill_ptr->GetBase() == nullptr || !fill_ptr->GetBase()->GetType() ||
|
||||
!fill_ptr->GetBase()->GetType()->IsPtrInt32()) {
|
||||
return false;
|
||||
}
|
||||
if (fill_ptr->GetIndex() != linear_phi) return false;
|
||||
if (fill_count != bound) return false;
|
||||
|
||||
std::vector<Value*> context_values;
|
||||
std::unordered_set<Value*> seen_contexts;
|
||||
auto collect_contexts = [&](BasicBlock* block) -> bool {
|
||||
for (const auto& inst_ptr : block->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (dynamic_cast<PhiInst*>(inst) != nullptr || inst->IsTerminator()) continue;
|
||||
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
||||
Value* operand = inst->GetOperand(i);
|
||||
if (dynamic_cast<ConstantValue*>(operand) || dynamic_cast<Function*>(operand) ||
|
||||
dynamic_cast<BasicBlock*>(operand) || dynamic_cast<GlobalVariable*>(operand)) {
|
||||
continue;
|
||||
}
|
||||
auto* operand_inst = dynamic_cast<Instruction*>(operand);
|
||||
if ((operand_inst && loop->Contains(operand_inst->GetParent())) ||
|
||||
operand == induction->phi || operand == induction->next ||
|
||||
operand == linear_phi || operand == linear_next) {
|
||||
continue;
|
||||
}
|
||||
if (!IsScalarContextCandidate(operand)) return false;
|
||||
if (seen_contexts.insert(operand).second) {
|
||||
context_values.push_back(operand);
|
||||
}
|
||||
}
|
||||
if (inst != linear_next && inst != induction->next &&
|
||||
HasOutsideUse(inst, loop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!collect_contexts(body) || !collect_contexts(action) ||
|
||||
!collect_contexts(latch)) {
|
||||
return false;
|
||||
}
|
||||
if (context_values.size() > 6) return false;
|
||||
|
||||
out->parent = &func;
|
||||
out->loop = loop;
|
||||
out->kind = ParallelLoopKind::GuardedFillI32;
|
||||
out->header = header;
|
||||
out->body = body;
|
||||
out->guard = body;
|
||||
out->action = action;
|
||||
out->preheader = preheader;
|
||||
out->exit = exit;
|
||||
out->latch = latch;
|
||||
out->header_cmp = header_cmp;
|
||||
out->induction = induction->phi;
|
||||
out->induction_next = induction->next;
|
||||
out->bound = bound;
|
||||
out->linear = linear_phi;
|
||||
out->linear_init = linear_init;
|
||||
out->linear_next = linear_next;
|
||||
out->linear_step = linear_step_ci->GetValue();
|
||||
out->has_loads = true;
|
||||
for (Value* value : context_values) {
|
||||
out->contexts.push_back({value, nullptr});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildParallelCandidate(Function& func, analysis::Loop* loop,
|
||||
ParallelLoopCandidate* out) {
|
||||
if (BuildGuardedFillCandidate(func, loop, out)) return true;
|
||||
if (!loop || !loop->IsParallelCandidate()) return false;
|
||||
|
||||
auto match = MatchCanonicalLoop(loop);
|
||||
if (!match.has_value()) return false;
|
||||
|
||||
if (match->body != match->latch || loop->GetBlocks().size() != 2) return false;
|
||||
if (match->exit->GetInstructions().size() > 0 &&
|
||||
dynamic_cast<PhiInst*>(match->exit->GetInstructions().front().get()) != nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PhiInst* reduction_phi = nullptr;
|
||||
Value* reduction_init = nullptr;
|
||||
Value* reduction_next = nullptr;
|
||||
ParallelLoopKind kind = ParallelLoopKind::Pointwise;
|
||||
if (match->header_phis.size() == 1 &&
|
||||
match->header_phis.front() == match->induction.phi) {
|
||||
kind = ParallelLoopKind::Pointwise;
|
||||
} else if (false && match->header_phis.size() == 2) {
|
||||
for (auto* phi : match->header_phis) {
|
||||
if (phi != match->induction.phi) {
|
||||
reduction_phi = phi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!reduction_phi || !reduction_phi->GetType() ||
|
||||
!reduction_phi->GetType()->IsInt32()) {
|
||||
return false;
|
||||
}
|
||||
reduction_init = GetIncomingForBlock(reduction_phi, match->preheader);
|
||||
reduction_next = GetIncomingForBlock(reduction_phi, match->latch);
|
||||
if (!reduction_init || !reduction_next) return false;
|
||||
auto* reduction_next_inst = dynamic_cast<Instruction*>(reduction_next);
|
||||
if (!reduction_next_inst || reduction_next_inst->GetParent() != match->body) {
|
||||
return false;
|
||||
}
|
||||
auto* init_ci = dynamic_cast<ConstantInt*>(reduction_init);
|
||||
if (!init_ci || init_ci->GetValue() != 0) return false;
|
||||
auto* red_next_bin = dynamic_cast<BinaryInst*>(reduction_next);
|
||||
if (!red_next_bin || red_next_bin->GetOpcode() != Opcode::Add ||
|
||||
!red_next_bin->GetType() || !red_next_bin->GetType()->IsInt32()) {
|
||||
return false;
|
||||
}
|
||||
Value* other = nullptr;
|
||||
if (red_next_bin->GetLhs() == reduction_phi) {
|
||||
other = red_next_bin->GetRhs();
|
||||
} else if (red_next_bin->GetRhs() == reduction_phi) {
|
||||
other = red_next_bin->GetLhs();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
if (ExprDependsOn(other, reduction_phi)) return false;
|
||||
kind = ParallelLoopKind::ReductionAddI32;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_set<Value*> store_bases;
|
||||
std::unordered_set<Value*> load_bases;
|
||||
std::vector<Value*> context_values;
|
||||
std::unordered_set<Value*> seen_contexts;
|
||||
|
||||
for (const auto& bb_ptr : func.GetBlocks()) {
|
||||
auto* block = bb_ptr.get();
|
||||
if (!loop->Contains(block)) continue;
|
||||
for (const auto& inst_ptr : block->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (!IsSupportedParallelInst(inst)) return false;
|
||||
if (inst->GetOpcode() == Opcode::Call) return false;
|
||||
if (kind == ParallelLoopKind::ReductionAddI32 &&
|
||||
inst->GetOpcode() == Opcode::Store) {
|
||||
return false;
|
||||
}
|
||||
if (inst->GetOpcode() != Opcode::Store && inst->GetOpcode() != Opcode::Br &&
|
||||
inst->GetOpcode() != Opcode::CondBr && inst->GetOpcode() != Opcode::Phi &&
|
||||
inst != reduction_phi &&
|
||||
HasOutsideUse(inst, loop)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < inst->GetNumOperands(); ++i) {
|
||||
Value* operand = inst->GetOperand(i);
|
||||
if (dynamic_cast<ConstantValue*>(operand) || dynamic_cast<Function*>(operand) ||
|
||||
dynamic_cast<BasicBlock*>(operand) || dynamic_cast<GlobalVariable*>(operand)) {
|
||||
continue;
|
||||
}
|
||||
auto* operand_inst = dynamic_cast<Instruction*>(operand);
|
||||
if ((operand_inst && loop->Contains(operand_inst->GetParent())) ||
|
||||
operand == match->induction.phi ||
|
||||
operand == match->induction.next ||
|
||||
operand == reduction_phi ||
|
||||
operand == reduction_next) {
|
||||
continue;
|
||||
}
|
||||
if (!IsScalarContextCandidate(operand)) return false;
|
||||
if (seen_contexts.insert(operand).second) {
|
||||
context_values.push_back(operand);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* load = dynamic_cast<LoadInst*>(inst)) {
|
||||
Value* base = StripPointerBase(load->GetPtr());
|
||||
if (dynamic_cast<GlobalVariable*>(base) == nullptr) return false;
|
||||
load_bases.insert(base);
|
||||
if (auto* gep = dynamic_cast<GepInst*>(load->GetPtr())) {
|
||||
if (base == StripPointerBase(load->GetPtr()) &&
|
||||
!ExprDependsOn(gep->GetIndex(), match->induction.phi)) {
|
||||
// allowed for pure reads; dependence checked later with stores
|
||||
}
|
||||
}
|
||||
} else if (auto* store = dynamic_cast<StoreInst*>(inst)) {
|
||||
Value* base = StripPointerBase(store->GetPtr());
|
||||
auto* gv = dynamic_cast<GlobalVariable*>(base);
|
||||
if (!gv || gv->GetCount() <= 1) return false;
|
||||
store_bases.insert(base);
|
||||
auto* gep = dynamic_cast<GepInst*>(store->GetPtr());
|
||||
if (!gep || !ExprDependsOn(gep->GetIndex(), match->induction.phi)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Value* base : store_bases) {
|
||||
if (load_bases.count(base) == 0) continue;
|
||||
for (const auto& bb_ptr : func.GetBlocks()) {
|
||||
auto* block = bb_ptr.get();
|
||||
if (!loop->Contains(block)) continue;
|
||||
for (const auto& inst_ptr : block->GetInstructions()) {
|
||||
auto* load = dynamic_cast<LoadInst*>(inst_ptr.get());
|
||||
if (!load) continue;
|
||||
if (StripPointerBase(load->GetPtr()) != base) continue;
|
||||
auto* gep = dynamic_cast<GepInst*>(load->GetPtr());
|
||||
if (!gep || !ExprDependsOn(gep->GetIndex(), match->induction.phi)) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context_values.size() > 6) return false;
|
||||
|
||||
out->parent = &func;
|
||||
out->loop = loop;
|
||||
out->kind = kind;
|
||||
out->header = match->header;
|
||||
out->body = match->body;
|
||||
out->preheader = match->preheader;
|
||||
out->exit = match->exit;
|
||||
out->latch = match->latch;
|
||||
out->header_cmp = match->header_cmp;
|
||||
out->induction = match->induction.phi;
|
||||
out->induction_next = match->induction.next;
|
||||
out->bound = match->bound;
|
||||
out->reduction = reduction_phi;
|
||||
out->reduction_init = reduction_init;
|
||||
out->reduction_next = reduction_next;
|
||||
out->has_loads = !load_bases.empty();
|
||||
for (Value* value : context_values) {
|
||||
out->contexts.push_back({value, nullptr});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsGeneratedParallelWorker(const Function& func) {
|
||||
return func.GetName().rfind("__par_worker", 0) == 0;
|
||||
}
|
||||
|
||||
Function* GetOrCreateRuntimeLauncher(Module& module, int slot) {
|
||||
const std::string name = "__par_run" + std::to_string(slot);
|
||||
if (auto* fn = module.FindFunction(name)) return fn;
|
||||
auto* fn = module.CreateFunction(name, Type::GetVoidType(), {});
|
||||
fn->SetExternal(true);
|
||||
return fn;
|
||||
}
|
||||
|
||||
std::string NextWorkerName(int slot) {
|
||||
return "__par_worker" + std::to_string(slot);
|
||||
}
|
||||
|
||||
void CloneWorkerBlocks(const ParallelLoopCandidate& cand, Function* worker,
|
||||
GlobalVariable* bound_slot,
|
||||
const std::vector<LoopContextValue>& ctx_slots,
|
||||
GlobalVariable* reduction_slot, Context& ctx) {
|
||||
if (cand.kind == ParallelLoopKind::GuardedFillI32) {
|
||||
auto* entry = worker->GetEntry();
|
||||
auto* tid = worker->GetArgument(0);
|
||||
auto* header = worker->CreateBlock(cand.header->GetName());
|
||||
auto* guard = worker->CreateBlock(cand.guard->GetName());
|
||||
auto* action = worker->CreateBlock(cand.action->GetName());
|
||||
auto* latch = worker->CreateBlock(cand.latch->GetName());
|
||||
auto* worker_exit = worker->CreateBlock("par.exit");
|
||||
|
||||
IRBuilder builder(ctx, entry);
|
||||
auto* bound_val = builder.CreateLoad(bound_slot, ctx.NextTemp());
|
||||
Value* threads_val = ctx.GetConstInt(kParallelThreads);
|
||||
auto* start_mul = builder.CreateMul(tid, bound_val, ctx.NextTemp());
|
||||
auto* start = builder.CreateDiv(start_mul, threads_val, ctx.NextTemp());
|
||||
auto* next_tid = builder.CreateAdd(tid, ctx.GetConstInt(1), ctx.NextTemp());
|
||||
auto* end_mul = builder.CreateMul(next_tid, bound_val, ctx.NextTemp());
|
||||
auto* end = builder.CreateDiv(end_mul, threads_val, ctx.NextTemp());
|
||||
|
||||
ValueMap remap;
|
||||
remap[cand.bound] = bound_val;
|
||||
for (const auto& ctx_value : ctx_slots) {
|
||||
builder.SetInsertPoint(entry);
|
||||
auto* loaded = builder.CreateLoad(ctx_value.slot, ctx.NextTemp());
|
||||
remap[ctx_value.original] = loaded;
|
||||
}
|
||||
builder.SetInsertPoint(entry);
|
||||
auto* start_linear_mul =
|
||||
builder.CreateMul(start, ctx.GetConstInt(cand.linear_step), ctx.NextTemp());
|
||||
Value* linear_init = cand.linear_init;
|
||||
auto it = remap.find(cand.linear_init);
|
||||
if (it != remap.end()) linear_init = it->second;
|
||||
auto* start_linear = builder.CreateAdd(linear_init, start_linear_mul, ctx.NextTemp());
|
||||
builder.CreateBr(header);
|
||||
|
||||
auto* new_iv = header->PrependPhi(cand.induction->GetType(), ctx.NextTemp());
|
||||
auto* new_linear = header->PrependPhi(cand.linear->GetType(), ctx.NextTemp());
|
||||
remap[cand.induction] = new_iv;
|
||||
remap[cand.linear] = new_linear;
|
||||
|
||||
if (auto cloned_cmp = CloneInstruction(cand.header_cmp, remap, ".par")) {
|
||||
auto* raw_cmp = static_cast<CmpInst*>(cloned_cmp.get());
|
||||
header->MutableInstructions().push_back(std::move(cloned_cmp));
|
||||
raw_cmp->SetParent(header);
|
||||
remap[cand.header_cmp] = raw_cmp;
|
||||
if (cand.header_cmp->GetLhs() == cand.induction &&
|
||||
cand.header_cmp->GetRhs() == cand.bound) {
|
||||
raw_cmp->SetOperand(0, new_iv);
|
||||
raw_cmp->SetOperand(1, end);
|
||||
} else if (cand.header_cmp->GetRhs() == cand.induction &&
|
||||
cand.header_cmp->GetLhs() == cand.bound) {
|
||||
raw_cmp->SetOperand(0, end);
|
||||
raw_cmp->SetOperand(1, new_iv);
|
||||
}
|
||||
header->Append<CondBranchInst>(Type::GetVoidType(), raw_cmp, guard, worker_exit);
|
||||
}
|
||||
|
||||
for (const auto& inst_ptr : cand.guard->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (dynamic_cast<PhiInst*>(inst) != nullptr || inst->IsTerminator()) continue;
|
||||
if (auto cloned = CloneInstruction(inst, remap, ".par")) {
|
||||
auto* raw = cloned.get();
|
||||
guard->MutableInstructions().push_back(std::move(cloned));
|
||||
raw->SetParent(guard);
|
||||
remap[inst] = raw;
|
||||
}
|
||||
}
|
||||
auto* guard_term =
|
||||
static_cast<CondBranchInst*>(cand.guard->MutableInstructions().back().get());
|
||||
auto* guard_cond = RemapValue(guard_term->GetCond(), remap);
|
||||
BasicBlock* true_target =
|
||||
(guard_term->GetTrueBlock() == cand.action) ? action : latch;
|
||||
BasicBlock* false_target =
|
||||
(guard_term->GetFalseBlock() == cand.action) ? action : latch;
|
||||
guard->Append<CondBranchInst>(Type::GetVoidType(), guard_cond, true_target,
|
||||
false_target);
|
||||
|
||||
for (const auto& inst_ptr : cand.action->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (dynamic_cast<PhiInst*>(inst) != nullptr || inst->IsTerminator()) continue;
|
||||
if (auto cloned = CloneInstruction(inst, remap, ".par")) {
|
||||
auto* raw = cloned.get();
|
||||
action->MutableInstructions().push_back(std::move(cloned));
|
||||
raw->SetParent(action);
|
||||
remap[inst] = raw;
|
||||
}
|
||||
}
|
||||
action->Append<BranchInst>(Type::GetVoidType(), latch);
|
||||
|
||||
for (const auto& inst_ptr : cand.latch->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (dynamic_cast<PhiInst*>(inst) != nullptr || inst->IsTerminator()) continue;
|
||||
if (auto cloned = CloneInstruction(inst, remap, ".par")) {
|
||||
auto* raw = cloned.get();
|
||||
latch->MutableInstructions().push_back(std::move(cloned));
|
||||
raw->SetParent(latch);
|
||||
remap[inst] = raw;
|
||||
}
|
||||
}
|
||||
latch->Append<BranchInst>(Type::GetVoidType(), header);
|
||||
|
||||
new_iv->AddIncoming(start, entry);
|
||||
new_iv->AddIncoming(RemapValue(cand.induction_next, remap), latch);
|
||||
new_linear->AddIncoming(start_linear, entry);
|
||||
new_linear->AddIncoming(RemapValue(cand.linear_next, remap), latch);
|
||||
worker_exit->Append<ReturnInst>(Type::GetVoidType(), nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto* entry = worker->GetEntry();
|
||||
auto* tid = worker->GetArgument(0);
|
||||
auto* header = worker->CreateBlock(cand.header->GetName());
|
||||
auto* body = worker->CreateBlock(cand.body->GetName());
|
||||
auto* worker_exit = worker->CreateBlock("par.exit");
|
||||
|
||||
IRBuilder builder(ctx, entry);
|
||||
auto* bound_val = builder.CreateLoad(bound_slot, ctx.NextTemp());
|
||||
Value* threads_val = ctx.GetConstInt(kParallelThreads);
|
||||
auto* start_mul = builder.CreateMul(tid, bound_val, ctx.NextTemp());
|
||||
auto* start = builder.CreateDiv(start_mul, threads_val, ctx.NextTemp());
|
||||
auto* next_tid = builder.CreateAdd(tid, ctx.GetConstInt(1), ctx.NextTemp());
|
||||
auto* end_mul = builder.CreateMul(next_tid, bound_val, ctx.NextTemp());
|
||||
auto* end = builder.CreateDiv(end_mul, threads_val, ctx.NextTemp());
|
||||
|
||||
ValueMap remap;
|
||||
remap[cand.induction] = start;
|
||||
remap[cand.bound] = bound_val;
|
||||
for (const auto& ctx_value : ctx_slots) {
|
||||
builder.SetInsertPoint(entry);
|
||||
auto* loaded = builder.CreateLoad(ctx_value.slot, ctx.NextTemp());
|
||||
remap[ctx_value.original] = loaded;
|
||||
}
|
||||
builder.SetInsertPoint(entry);
|
||||
builder.CreateBr(header);
|
||||
auto* new_phi = header->PrependPhi(cand.induction->GetType(), ctx.NextTemp());
|
||||
remap[cand.induction] = new_phi;
|
||||
PhiInst* new_reduction_phi = nullptr;
|
||||
if (cand.kind == ParallelLoopKind::ReductionAddI32) {
|
||||
new_reduction_phi = header->PrependPhi(Type::GetInt32Type(), ctx.NextTemp());
|
||||
remap[cand.reduction] = new_reduction_phi;
|
||||
}
|
||||
|
||||
if (auto cloned_cmp = CloneInstruction(cand.header_cmp, remap, ".par")) {
|
||||
auto* raw_cmp = static_cast<CmpInst*>(cloned_cmp.get());
|
||||
header->MutableInstructions().push_back(std::move(cloned_cmp));
|
||||
raw_cmp->SetParent(header);
|
||||
remap[cand.header_cmp] = raw_cmp;
|
||||
if (cand.header_cmp->GetLhs() == cand.induction &&
|
||||
cand.header_cmp->GetRhs() == cand.bound) {
|
||||
raw_cmp->SetOperand(0, new_phi);
|
||||
raw_cmp->SetOperand(1, end);
|
||||
} else if (cand.header_cmp->GetRhs() == cand.induction &&
|
||||
cand.header_cmp->GetLhs() == cand.bound) {
|
||||
raw_cmp->SetOperand(0, end);
|
||||
raw_cmp->SetOperand(1, new_phi);
|
||||
}
|
||||
header->Append<CondBranchInst>(Type::GetVoidType(), raw_cmp, body, worker_exit);
|
||||
}
|
||||
|
||||
for (const auto& inst_ptr : cand.body->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (dynamic_cast<PhiInst*>(inst) != nullptr) continue;
|
||||
if (inst->GetOpcode() == Opcode::Br) continue;
|
||||
if (auto cloned = CloneInstruction(inst, remap, ".par")) {
|
||||
auto* raw = cloned.get();
|
||||
body->MutableInstructions().push_back(std::move(cloned));
|
||||
raw->SetParent(body);
|
||||
remap[inst] = raw;
|
||||
}
|
||||
}
|
||||
new_phi->AddIncoming(start, entry);
|
||||
new_phi->AddIncoming(RemapValue(cand.induction_next, remap), body);
|
||||
if (new_reduction_phi) {
|
||||
new_reduction_phi->AddIncoming(ctx.GetConstInt(0), entry);
|
||||
new_reduction_phi->AddIncoming(RemapValue(cand.reduction_next, remap), body);
|
||||
}
|
||||
body->Append<BranchInst>(Type::GetVoidType(), header);
|
||||
|
||||
if (new_reduction_phi) {
|
||||
IRBuilder exit_builder(ctx, worker_exit);
|
||||
auto* partial_ptr = exit_builder.CreateGep(reduction_slot, tid, ctx.NextTemp());
|
||||
exit_builder.CreateStore(new_reduction_phi, partial_ptr);
|
||||
}
|
||||
worker_exit->Append<ReturnInst>(Type::GetVoidType(), nullptr);
|
||||
}
|
||||
|
||||
bool ParallelizeCandidate(Module& module, ParallelLoopCandidate& cand, int slot) {
|
||||
auto& ctx = module.GetContext();
|
||||
auto* bound_slot =
|
||||
module.CreateGlobalVar("__par_bound" + std::to_string(slot), 0, 1,
|
||||
Type::GetPtrInt32Type());
|
||||
GlobalVariable* reduction_slot = nullptr;
|
||||
if (cand.kind == ParallelLoopKind::ReductionAddI32) {
|
||||
reduction_slot = module.CreateGlobalVar(
|
||||
"__par_red" + std::to_string(slot), 0, kParallelThreads,
|
||||
Type::GetPtrInt32Type());
|
||||
}
|
||||
for (size_t i = 0; i < cand.contexts.size(); ++i) {
|
||||
auto& entry = cand.contexts[i];
|
||||
bool is_float = entry.original->GetType() && entry.original->GetType()->IsFloat32();
|
||||
entry.slot = module.CreateGlobalVar(
|
||||
"__par_ctx" + std::to_string(slot) + "_" + std::to_string(i), 0, 1,
|
||||
is_float ? Type::GetPtrFloat32Type() : Type::GetPtrInt32Type());
|
||||
}
|
||||
|
||||
auto* worker =
|
||||
module.CreateFunction(NextWorkerName(slot), Type::GetVoidType(),
|
||||
{Type::GetInt32Type()});
|
||||
CloneWorkerBlocks(cand, worker, bound_slot, cand.contexts, reduction_slot, ctx);
|
||||
|
||||
auto* launcher = GetOrCreateRuntimeLauncher(module, slot);
|
||||
auto* preheader = cand.preheader;
|
||||
if (preheader->HasTerminator()) {
|
||||
preheader->RemoveInstruction(preheader->MutableInstructions().back().get());
|
||||
}
|
||||
for (const auto& ctx_value : cand.contexts) {
|
||||
InsertInstruction(preheader, std::make_unique<StoreInst>(
|
||||
Type::GetVoidType(), ctx_value.original,
|
||||
ctx_value.slot));
|
||||
}
|
||||
InsertInstruction(preheader, std::make_unique<StoreInst>(Type::GetVoidType(),
|
||||
cand.bound, bound_slot));
|
||||
InsertCallBeforeTerminator(preheader, launcher, {}, "");
|
||||
Value* reduced_value = nullptr;
|
||||
if (cand.kind == ParallelLoopKind::ReductionAddI32) {
|
||||
IRBuilder builder(ctx, preheader);
|
||||
reduced_value = cand.reduction_init;
|
||||
for (int tid = 0; tid < kParallelThreads; ++tid) {
|
||||
auto* partial_ptr =
|
||||
builder.CreateGep(reduction_slot, ctx.GetConstInt(tid), ctx.NextTemp());
|
||||
auto* partial_val = builder.CreateLoad(partial_ptr, ctx.NextTemp());
|
||||
reduced_value = builder.CreateAdd(reduced_value, partial_val, ctx.NextTemp());
|
||||
}
|
||||
}
|
||||
cand.induction->RemoveIncomingBlock(preheader);
|
||||
if (cand.linear) {
|
||||
cand.linear->RemoveIncomingBlock(preheader);
|
||||
}
|
||||
if (cand.reduction) {
|
||||
cand.reduction->RemoveIncomingBlock(preheader);
|
||||
}
|
||||
preheader->RemoveSuccessor(cand.header);
|
||||
cand.header->RemovePredecessor(preheader);
|
||||
preheader->AddSuccessor(cand.exit);
|
||||
cand.exit->AddPredecessor(preheader);
|
||||
if (cand.kind == ParallelLoopKind::ReductionAddI32 && reduced_value) {
|
||||
ReplaceUsesOutsideLoop(cand.reduction, reduced_value, cand.loop);
|
||||
}
|
||||
preheader->Append<BranchInst>(Type::GetVoidType(), cand.exit);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool RunLoopParallelization(Module& module) {
|
||||
bool changed = false;
|
||||
int store_only_slots = 0;
|
||||
for (int slot = 0; slot < kParallelLoopSlots; ++slot) {
|
||||
ParallelLoopCandidate cand;
|
||||
bool found = false;
|
||||
|
||||
for (const auto& func_ptr : module.GetFunctions()) {
|
||||
auto* func = func_ptr.get();
|
||||
if (!func || func->IsExternal() || IsGeneratedParallelWorker(*func)) continue;
|
||||
|
||||
analysis::DominatorTree dom_tree(*func);
|
||||
analysis::LoopInfo loop_info(*func, dom_tree);
|
||||
std::vector<analysis::Loop*> loops;
|
||||
for (const auto& loop_ptr : loop_info.GetLoops()) {
|
||||
loops.push_back(loop_ptr.get());
|
||||
}
|
||||
std::sort(loops.begin(), loops.end(),
|
||||
[](analysis::Loop* lhs, analysis::Loop* rhs) {
|
||||
if (lhs->GetDepth() != rhs->GetDepth()) {
|
||||
return lhs->GetDepth() < rhs->GetDepth();
|
||||
}
|
||||
return lhs->GetHeader()->GetName() < rhs->GetHeader()->GetName();
|
||||
});
|
||||
|
||||
ParallelLoopCandidate fallback_store_only;
|
||||
bool have_fallback_store_only = false;
|
||||
for (auto* loop : loops) {
|
||||
if (BuildParallelCandidate(*func, loop, &cand)) {
|
||||
if (cand.has_loads) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (store_only_slots < 1 && !have_fallback_store_only) {
|
||||
fallback_store_only = cand;
|
||||
have_fallback_store_only = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found && have_fallback_store_only) {
|
||||
cand = fallback_store_only;
|
||||
found = true;
|
||||
}
|
||||
if (found) break;
|
||||
}
|
||||
|
||||
if (!found) break;
|
||||
bool local_changed = ParallelizeCandidate(module, cand, slot);
|
||||
changed |= local_changed;
|
||||
if (local_changed && cand.parent) {
|
||||
RunSimpleDCE(*cand.parent);
|
||||
RunCFGSimplify(*cand.parent, module.GetContext());
|
||||
}
|
||||
if (!cand.has_loads) {
|
||||
++store_only_slots;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace ir
|
||||
@ -0,0 +1,309 @@
|
||||
#pragma once
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
struct CanonicalInductionInfo {
|
||||
PhiInst* phi = nullptr;
|
||||
Value* init = nullptr;
|
||||
Value* next = nullptr;
|
||||
int step = 0;
|
||||
};
|
||||
|
||||
struct CanonicalLoopMatch {
|
||||
analysis::Loop* loop = nullptr;
|
||||
BasicBlock* preheader = nullptr;
|
||||
BasicBlock* header = nullptr;
|
||||
BasicBlock* exit = nullptr;
|
||||
BasicBlock* body = nullptr;
|
||||
BasicBlock* latch = nullptr;
|
||||
CondBranchInst* header_branch = nullptr;
|
||||
CmpInst* header_cmp = nullptr;
|
||||
Value* bound = nullptr;
|
||||
CanonicalInductionInfo induction;
|
||||
std::vector<PhiInst*> header_phis;
|
||||
};
|
||||
|
||||
using ValueMap = std::unordered_map<Value*, Value*>;
|
||||
|
||||
inline Value* RemapValue(Value* value, const ValueMap& remap) {
|
||||
auto it = remap.find(value);
|
||||
return it != remap.end() ? it->second : value;
|
||||
}
|
||||
|
||||
inline std::vector<PhiInst*> CollectHeaderPhis(BasicBlock* header) {
|
||||
std::vector<PhiInst*> phis;
|
||||
if (!header) return phis;
|
||||
for (const auto& inst_ptr : header->GetInstructions()) {
|
||||
auto* phi = dynamic_cast<PhiInst*>(inst_ptr.get());
|
||||
if (!phi) break;
|
||||
phis.push_back(phi);
|
||||
}
|
||||
return phis;
|
||||
}
|
||||
|
||||
inline void InsertInstruction(BasicBlock* block,
|
||||
std::unique_ptr<Instruction> inst) {
|
||||
auto& insts = block->MutableInstructions();
|
||||
auto insert_it = insts.end();
|
||||
if (block->HasTerminator()) {
|
||||
insert_it = insts.end() - 1;
|
||||
}
|
||||
inst->SetParent(block);
|
||||
insts.insert(insert_it, std::move(inst));
|
||||
}
|
||||
|
||||
inline Instruction* AppendOwnedInstruction(BasicBlock* block,
|
||||
std::unique_ptr<Instruction> inst) {
|
||||
auto* raw = inst.get();
|
||||
InsertInstruction(block, std::move(inst));
|
||||
return raw;
|
||||
}
|
||||
|
||||
inline BinaryInst* InsertBinaryBeforeTerminator(BasicBlock* block, Opcode opcode,
|
||||
Value* lhs, Value* rhs,
|
||||
const std::string& name) {
|
||||
auto inst = std::make_unique<BinaryInst>(opcode, lhs->GetType(), lhs, rhs, name);
|
||||
auto* raw = inst.get();
|
||||
InsertInstruction(block, std::move(inst));
|
||||
return raw;
|
||||
}
|
||||
|
||||
inline CmpInst* InsertCmpBeforeTerminator(BasicBlock* block, CmpOp cmp_op,
|
||||
Value* lhs, Value* rhs,
|
||||
const std::string& name) {
|
||||
auto inst =
|
||||
std::make_unique<CmpInst>(cmp_op, Type::GetInt32Type(), lhs, rhs, name);
|
||||
auto* raw = inst.get();
|
||||
InsertInstruction(block, std::move(inst));
|
||||
return raw;
|
||||
}
|
||||
|
||||
inline BranchInst* InsertBranchBeforeTerminator(BasicBlock* block,
|
||||
BasicBlock* target) {
|
||||
auto inst = std::make_unique<BranchInst>(Type::GetVoidType(), target);
|
||||
auto* raw = inst.get();
|
||||
InsertInstruction(block, std::move(inst));
|
||||
return raw;
|
||||
}
|
||||
|
||||
inline CondBranchInst* InsertCondBrBeforeTerminator(BasicBlock* block, Value* cond,
|
||||
BasicBlock* true_bb,
|
||||
BasicBlock* false_bb) {
|
||||
auto inst = std::make_unique<CondBranchInst>(Type::GetVoidType(), cond,
|
||||
true_bb, false_bb);
|
||||
auto* raw = inst.get();
|
||||
InsertInstruction(block, std::move(inst));
|
||||
return raw;
|
||||
}
|
||||
|
||||
inline CallInst* InsertCallBeforeTerminator(BasicBlock* block, Function* callee,
|
||||
const std::vector<Value*>& args,
|
||||
const std::string& name) {
|
||||
auto inst = std::make_unique<CallInst>(callee->GetType(), callee, args, name);
|
||||
auto* raw = inst.get();
|
||||
InsertInstruction(block, std::move(inst));
|
||||
return raw;
|
||||
}
|
||||
|
||||
inline std::string CloneName(const std::string& base, const std::string& suffix) {
|
||||
if (base.empty()) return base;
|
||||
return base + suffix;
|
||||
}
|
||||
|
||||
inline std::unique_ptr<Instruction> CloneInstruction(Instruction* inst,
|
||||
const ValueMap& remap,
|
||||
const std::string& suffix) {
|
||||
switch (inst->GetOpcode()) {
|
||||
case Opcode::Add:
|
||||
case Opcode::Sub:
|
||||
case Opcode::Mul:
|
||||
case Opcode::Div:
|
||||
case Opcode::Mod: {
|
||||
auto* bin = static_cast<BinaryInst*>(inst);
|
||||
return std::make_unique<BinaryInst>(
|
||||
inst->GetOpcode(), inst->GetType(), RemapValue(bin->GetLhs(), remap),
|
||||
RemapValue(bin->GetRhs(), remap), CloneName(inst->GetName(), suffix));
|
||||
}
|
||||
case Opcode::Cmp: {
|
||||
auto* cmp = static_cast<CmpInst*>(inst);
|
||||
return std::make_unique<CmpInst>(
|
||||
cmp->GetCmpOp(), inst->GetType(), RemapValue(cmp->GetLhs(), remap),
|
||||
RemapValue(cmp->GetRhs(), remap), CloneName(inst->GetName(), suffix));
|
||||
}
|
||||
case Opcode::Cast: {
|
||||
auto* cast = static_cast<CastInst*>(inst);
|
||||
return std::make_unique<CastInst>(cast->GetCastOp(), inst->GetType(),
|
||||
RemapValue(cast->GetValue(), remap),
|
||||
CloneName(inst->GetName(), suffix));
|
||||
}
|
||||
case Opcode::Load: {
|
||||
auto* load = static_cast<LoadInst*>(inst);
|
||||
return std::make_unique<LoadInst>(inst->GetType(),
|
||||
RemapValue(load->GetPtr(), remap),
|
||||
CloneName(inst->GetName(), suffix));
|
||||
}
|
||||
case Opcode::Store: {
|
||||
auto* store = static_cast<StoreInst*>(inst);
|
||||
return std::make_unique<StoreInst>(
|
||||
Type::GetVoidType(), RemapValue(store->GetValue(), remap),
|
||||
RemapValue(store->GetPtr(), remap));
|
||||
}
|
||||
case Opcode::Call: {
|
||||
auto* call = static_cast<CallInst*>(inst);
|
||||
std::vector<Value*> args;
|
||||
args.reserve(call->GetNumArgs());
|
||||
for (size_t i = 0; i < call->GetNumArgs(); ++i) {
|
||||
args.push_back(RemapValue(call->GetArg(i), remap));
|
||||
}
|
||||
return std::make_unique<CallInst>(inst->GetType(), call->GetCallee(), args,
|
||||
CloneName(inst->GetName(), suffix));
|
||||
}
|
||||
case Opcode::Gep: {
|
||||
auto* gep = static_cast<GepInst*>(inst);
|
||||
return std::make_unique<GepInst>(inst->GetType(),
|
||||
RemapValue(gep->GetBase(), remap),
|
||||
RemapValue(gep->GetIndex(), remap),
|
||||
CloneName(inst->GetName(), suffix));
|
||||
}
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool IsLoopInvariantValue(Value* value, analysis::Loop* loop) {
|
||||
if (!value) return false;
|
||||
if (dynamic_cast<ConstantValue*>(value) != nullptr) return true;
|
||||
if (dynamic_cast<Argument*>(value) != nullptr) return true;
|
||||
if (dynamic_cast<GlobalVariable*>(value) != nullptr) return true;
|
||||
if (dynamic_cast<Function*>(value) != nullptr) return true;
|
||||
auto* inst = dynamic_cast<Instruction*>(value);
|
||||
return !inst || !inst->GetParent() || !loop->Contains(inst->GetParent());
|
||||
}
|
||||
|
||||
inline std::optional<CanonicalInductionInfo> MatchCanonicalInduction(
|
||||
BasicBlock* header, BasicBlock* preheader, BasicBlock* latch) {
|
||||
if (!header || !preheader || !latch) return std::nullopt;
|
||||
|
||||
for (auto* phi : CollectHeaderPhis(header)) {
|
||||
if (!phi || phi->GetType() == nullptr || !phi->GetType()->IsInt32()) continue;
|
||||
if (phi->GetNumIncoming() != 2) continue;
|
||||
|
||||
Value* init = nullptr;
|
||||
Value* next = nullptr;
|
||||
for (size_t i = 0; i < phi->GetNumIncoming(); ++i) {
|
||||
auto* incoming_bb = phi->GetIncomingBlock(i);
|
||||
if (incoming_bb == preheader) {
|
||||
init = phi->GetIncomingValue(i);
|
||||
} else if (incoming_bb == latch) {
|
||||
next = phi->GetIncomingValue(i);
|
||||
}
|
||||
}
|
||||
if (!init || !next) continue;
|
||||
|
||||
auto* next_inst = dynamic_cast<BinaryInst*>(next);
|
||||
if (!next_inst) continue;
|
||||
if (next_inst->GetOpcode() != Opcode::Add &&
|
||||
next_inst->GetOpcode() != Opcode::Sub) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Value* other = nullptr;
|
||||
bool phi_on_lhs = false;
|
||||
if (next_inst->GetLhs() == phi) {
|
||||
other = next_inst->GetRhs();
|
||||
phi_on_lhs = true;
|
||||
} else if (next_inst->GetRhs() == phi) {
|
||||
other = next_inst->GetLhs();
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* step_ci = dynamic_cast<ConstantInt*>(other);
|
||||
if (!step_ci) continue;
|
||||
|
||||
int step = step_ci->GetValue();
|
||||
if (next_inst->GetOpcode() == Opcode::Sub) {
|
||||
if (!phi_on_lhs) continue;
|
||||
step = -step;
|
||||
}
|
||||
if (step == 0) continue;
|
||||
|
||||
return CanonicalInductionInfo{phi, init, next, step};
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
inline std::optional<CanonicalLoopMatch> MatchCanonicalLoop(analysis::Loop* loop) {
|
||||
if (!loop) return std::nullopt;
|
||||
auto* header = loop->GetHeader();
|
||||
auto* preheader = loop->GetPreheader();
|
||||
if (!header || !preheader) return std::nullopt;
|
||||
if (loop->GetLatches().size() != 1) return std::nullopt;
|
||||
auto* latch = loop->GetLatches().front();
|
||||
if (!latch) return std::nullopt;
|
||||
|
||||
auto* header_term = header->HasTerminator()
|
||||
? dynamic_cast<CondBranchInst*>(
|
||||
header->MutableInstructions().back().get())
|
||||
: nullptr;
|
||||
if (!header_term) return std::nullopt;
|
||||
|
||||
auto* cmp = dynamic_cast<CmpInst*>(header_term->GetCond());
|
||||
if (!cmp) return std::nullopt;
|
||||
|
||||
BasicBlock* body = nullptr;
|
||||
BasicBlock* exit = nullptr;
|
||||
if (loop->Contains(header_term->GetTrueBlock()) &&
|
||||
!loop->Contains(header_term->GetFalseBlock())) {
|
||||
body = header_term->GetTrueBlock();
|
||||
exit = header_term->GetFalseBlock();
|
||||
} else if (loop->Contains(header_term->GetFalseBlock()) &&
|
||||
!loop->Contains(header_term->GetTrueBlock())) {
|
||||
body = header_term->GetFalseBlock();
|
||||
exit = header_term->GetTrueBlock();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto induction = MatchCanonicalInduction(header, preheader, latch);
|
||||
if (!induction.has_value()) return std::nullopt;
|
||||
|
||||
Value* bound = nullptr;
|
||||
if (cmp->GetLhs() == induction->phi &&
|
||||
IsLoopInvariantValue(cmp->GetRhs(), loop)) {
|
||||
bound = cmp->GetRhs();
|
||||
} else if (cmp->GetRhs() == induction->phi &&
|
||||
IsLoopInvariantValue(cmp->GetLhs(), loop)) {
|
||||
bound = cmp->GetLhs();
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
CanonicalLoopMatch match;
|
||||
match.loop = loop;
|
||||
match.preheader = preheader;
|
||||
match.header = header;
|
||||
match.exit = exit;
|
||||
match.body = body;
|
||||
match.latch = latch;
|
||||
match.header_branch = header_term;
|
||||
match.header_cmp = cmp;
|
||||
match.bound = bound;
|
||||
match.induction = *induction;
|
||||
match.header_phis = CollectHeaderPhis(header);
|
||||
return match;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace ir
|
||||
@ -0,0 +1,143 @@
|
||||
// 循环展开:
|
||||
// - 针对单块 innermost 规范循环做因子 2 的保守展开
|
||||
// - 使用一次额外比较保护余数路径,避免要求静态 trip count
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "LoopPassUtils.h"
|
||||
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsUnrollableLoop(const CanonicalLoopMatch& match) {
|
||||
if (match.induction.step <= 0) return false;
|
||||
if (match.loop->GetChildren().size() != 0) return false;
|
||||
if (match.loop->GetBlocks().size() != 2) return false;
|
||||
if (match.body != match.latch) return false;
|
||||
if (!match.body || !match.body->HasTerminator()) return false;
|
||||
|
||||
auto* body_term =
|
||||
dynamic_cast<BranchInst*>(match.body->MutableInstructions().back().get());
|
||||
if (!body_term || body_term->GetTarget() != match.header) return false;
|
||||
|
||||
if (match.header_cmp->GetLhs() != match.induction.phi) return false;
|
||||
if (match.header_cmp->GetCmpOp() != CmpOp::Lt &&
|
||||
match.header_cmp->GetCmpOp() != CmpOp::Le) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t body_inst_count = match.body->GetInstructions().size();
|
||||
if (body_inst_count <= 1 || body_inst_count > 18) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Value* GetLatchIncomingForPhi(PhiInst* phi, BasicBlock* latch) {
|
||||
for (size_t i = 0; i < phi->GetNumIncoming(); ++i) {
|
||||
if (phi->GetIncomingBlock(i) == latch) {
|
||||
return phi->GetIncomingValue(i);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool RunUnrollOnLoop(Function& func, const CanonicalLoopMatch& match,
|
||||
Context& ctx, int unroll_index) {
|
||||
(void)unroll_index;
|
||||
if (!IsUnrollableLoop(match)) return false;
|
||||
|
||||
auto* body = match.body;
|
||||
auto* header = match.header;
|
||||
auto* body_term =
|
||||
static_cast<BranchInst*>(body->MutableInstructions().back().get());
|
||||
(void)body_term;
|
||||
|
||||
std::string block_suffix = ctx.NextTemp();
|
||||
if (!block_suffix.empty() && block_suffix.front() == '%') {
|
||||
block_suffix.erase(0, 1);
|
||||
}
|
||||
auto* body2 = func.CreateBlock(body->GetName() + ".unroll." + block_suffix);
|
||||
|
||||
std::unordered_map<Value*, Value*> seed_map;
|
||||
for (auto* phi : match.header_phis) {
|
||||
auto* incoming = GetLatchIncomingForPhi(phi, match.latch);
|
||||
if (!incoming) return false;
|
||||
seed_map.emplace(phi, incoming);
|
||||
}
|
||||
|
||||
ValueMap clone_map = seed_map;
|
||||
std::vector<Instruction*> originals;
|
||||
for (const auto& inst_ptr : body->GetInstructions()) {
|
||||
if (inst_ptr.get()->IsTerminator()) continue;
|
||||
originals.push_back(inst_ptr.get());
|
||||
}
|
||||
|
||||
for (auto* inst : originals) {
|
||||
auto cloned = CloneInstruction(inst, clone_map, ".u2");
|
||||
if (!cloned) return false;
|
||||
auto* raw = cloned.get();
|
||||
body2->MutableInstructions().push_back(std::move(cloned));
|
||||
raw->SetParent(body2);
|
||||
clone_map[inst] = raw;
|
||||
}
|
||||
|
||||
body2->Append<BranchInst>(Type::GetVoidType(), header);
|
||||
|
||||
Value* iv_after_one = GetLatchIncomingForPhi(match.induction.phi, match.latch);
|
||||
if (!iv_after_one) return false;
|
||||
|
||||
auto* first_cmp = InsertCmpBeforeTerminator(
|
||||
body, match.header_cmp->GetCmpOp(), iv_after_one, match.bound,
|
||||
ctx.NextTemp());
|
||||
|
||||
body->RemoveInstruction(body->MutableInstructions().back().get());
|
||||
body->Append<CondBranchInst>(Type::GetVoidType(), first_cmp, body2, header);
|
||||
|
||||
body->AddSuccessor(body2);
|
||||
body2->AddPredecessor(body);
|
||||
body2->AddSuccessor(header);
|
||||
header->AddPredecessor(body2);
|
||||
|
||||
for (auto* phi : match.header_phis) {
|
||||
auto* incoming = GetLatchIncomingForPhi(phi, match.latch);
|
||||
Value* second_value = incoming;
|
||||
auto it = clone_map.find(incoming);
|
||||
if (it != clone_map.end()) {
|
||||
second_value = it->second;
|
||||
} else {
|
||||
second_value = RemapValue(incoming, clone_map);
|
||||
}
|
||||
phi->AddIncoming(second_value, body2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool RunLoopUnroll(Function& func, Context& ctx) {
|
||||
if (func.IsExternal()) return false;
|
||||
|
||||
analysis::DominatorTree dom_tree(func);
|
||||
analysis::LoopInfo loop_info(func, dom_tree);
|
||||
|
||||
bool changed = false;
|
||||
int unroll_index = 0;
|
||||
for (const auto& loop_ptr : loop_info.GetLoops()) {
|
||||
auto match = MatchCanonicalLoop(loop_ptr.get());
|
||||
if (!match.has_value()) continue;
|
||||
if (RunUnrollOnLoop(func, *match, ctx, unroll_index++)) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace ir
|
||||
@ -0,0 +1,130 @@
|
||||
// 强度削弱:
|
||||
// - 识别规范归纳变量 iv
|
||||
// - 将循环内的 iv * C 改写成辅助 phi + 常量增量递推
|
||||
|
||||
#include "ir/IR.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "LoopPassUtils.h"
|
||||
|
||||
namespace ir {
|
||||
namespace passes {
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsStrengthReductionCandidate(Instruction* inst, PhiInst* iv) {
|
||||
auto* bin = dynamic_cast<BinaryInst*>(inst);
|
||||
if (!bin || bin->GetOpcode() != Opcode::Mul) return false;
|
||||
if (!bin->GetType() || !bin->GetType()->IsInt32()) return false;
|
||||
return bin->GetLhs() == iv || bin->GetRhs() == iv;
|
||||
}
|
||||
|
||||
int ExtractScale(BinaryInst* mul, PhiInst* iv) {
|
||||
auto* lhs_ci = dynamic_cast<ConstantInt*>(mul->GetLhs());
|
||||
auto* rhs_ci = dynamic_cast<ConstantInt*>(mul->GetRhs());
|
||||
if (mul->GetLhs() == iv && rhs_ci) return rhs_ci->GetValue();
|
||||
if (mul->GetRhs() == iv && lhs_ci) return lhs_ci->GetValue();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool HasConstantScale(BinaryInst* mul, PhiInst* iv) {
|
||||
if (!mul || !iv) return false;
|
||||
return (mul->GetLhs() == iv &&
|
||||
dynamic_cast<ConstantInt*>(mul->GetRhs()) != nullptr) ||
|
||||
(mul->GetRhs() == iv &&
|
||||
dynamic_cast<ConstantInt*>(mul->GetLhs()) != nullptr);
|
||||
}
|
||||
|
||||
Value* GetOrCreateScaledRecurrence(
|
||||
const CanonicalLoopMatch& match, int scale, Context& ctx,
|
||||
std::unordered_map<int, Value*>& recurrence_by_scale) {
|
||||
if (scale == 0) return ctx.GetConstInt(0);
|
||||
if (scale == 1) return match.induction.phi;
|
||||
auto it = recurrence_by_scale.find(scale);
|
||||
if (it != recurrence_by_scale.end()) return it->second;
|
||||
|
||||
auto* init_scale =
|
||||
InsertBinaryBeforeTerminator(match.preheader, Opcode::Mul,
|
||||
match.induction.init, ctx.GetConstInt(scale),
|
||||
ctx.NextTemp());
|
||||
|
||||
auto* sr_phi =
|
||||
match.header->PrependPhi(Type::GetInt32Type(), ctx.NextTemp());
|
||||
|
||||
auto* sr_next =
|
||||
InsertBinaryBeforeTerminator(match.latch, Opcode::Add, sr_phi,
|
||||
ctx.GetConstInt(match.induction.step * scale),
|
||||
ctx.NextTemp());
|
||||
|
||||
sr_phi->AddIncoming(init_scale, match.preheader);
|
||||
sr_phi->AddIncoming(sr_next, match.latch);
|
||||
recurrence_by_scale.emplace(scale, sr_phi);
|
||||
return sr_phi;
|
||||
}
|
||||
|
||||
bool ReplaceMulWithRecurrence(
|
||||
const CanonicalLoopMatch& match, BinaryInst* mul, Context& ctx,
|
||||
std::unordered_map<int, Value*>& recurrence_by_scale) {
|
||||
const int scale = ExtractScale(mul, match.induction.phi);
|
||||
auto* replacement =
|
||||
GetOrCreateScaledRecurrence(match, scale, ctx, recurrence_by_scale);
|
||||
if (!replacement) return false;
|
||||
|
||||
mul->ReplaceAllUsesWith(replacement);
|
||||
mul->GetParent()->RemoveInstruction(mul);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool RunStrengthReduction(Function& func, Context& ctx) {
|
||||
if (func.IsExternal()) return false;
|
||||
|
||||
analysis::DominatorTree dom_tree(func);
|
||||
analysis::LoopInfo loop_info(func, dom_tree);
|
||||
|
||||
std::vector<analysis::Loop*> loops;
|
||||
for (const auto& loop_ptr : loop_info.GetLoops()) {
|
||||
loops.push_back(loop_ptr.get());
|
||||
}
|
||||
std::sort(loops.begin(), loops.end(),
|
||||
[](analysis::Loop* lhs, analysis::Loop* rhs) {
|
||||
if (lhs->GetDepth() != rhs->GetDepth()) {
|
||||
return lhs->GetDepth() > rhs->GetDepth();
|
||||
}
|
||||
return lhs->GetHeader()->GetName() < rhs->GetHeader()->GetName();
|
||||
});
|
||||
|
||||
bool changed = false;
|
||||
for (auto* loop : loops) {
|
||||
auto match = MatchCanonicalLoop(loop);
|
||||
if (!match.has_value()) continue;
|
||||
|
||||
std::vector<BinaryInst*> candidates;
|
||||
for (auto* block : loop->GetBlocks()) {
|
||||
for (const auto& inst_ptr : block->GetInstructions()) {
|
||||
auto* inst = inst_ptr.get();
|
||||
if (IsStrengthReductionCandidate(inst, match->induction.phi)) {
|
||||
auto* mul = static_cast<BinaryInst*>(inst);
|
||||
if (HasConstantScale(mul, match->induction.phi)) {
|
||||
candidates.push_back(mul);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<int, Value*> recurrence_by_scale;
|
||||
for (auto* mul : candidates) {
|
||||
changed |= ReplaceMulWithRecurrence(*match, mul, ctx, recurrence_by_scale);
|
||||
}
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
} // namespace passes
|
||||
} // namespace ir
|
||||
@ -0,0 +1,137 @@
|
||||
#include "irgen/IRGen.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "SysYParser.h"
|
||||
#include "utils/Log.h"
|
||||
|
||||
// 内部辅助:不依赖类成员,只需 ConstEnv。
|
||||
namespace {
|
||||
|
||||
double EvalAddExp(SysYParser::AddExpContext* ctx,
|
||||
const IRGenImpl::ConstEnv& int_env,
|
||||
const IRGenImpl::ConstFloatEnv& float_env);
|
||||
double EvalMulExp(SysYParser::MulExpContext* ctx,
|
||||
const IRGenImpl::ConstEnv& int_env,
|
||||
const IRGenImpl::ConstFloatEnv& float_env);
|
||||
double EvalUnaryExp(SysYParser::UnaryExpContext* ctx,
|
||||
const IRGenImpl::ConstEnv& int_env,
|
||||
const IRGenImpl::ConstFloatEnv& float_env);
|
||||
|
||||
int ParseIntLiteral(const std::string& text) {
|
||||
if (text.size() >= 2 && text[0] == '0' &&
|
||||
(text[1] == 'x' || text[1] == 'X')) {
|
||||
return std::stoi(text, nullptr, 16);
|
||||
}
|
||||
if (text.size() > 1 && text[0] == '0') {
|
||||
return std::stoi(text, nullptr, 8);
|
||||
}
|
||||
return std::stoi(text);
|
||||
}
|
||||
|
||||
double EvalPrimary(SysYParser::PrimaryExpContext* ctx,
|
||||
const IRGenImpl::ConstEnv& int_env,
|
||||
const IRGenImpl::ConstFloatEnv& float_env) {
|
||||
if (!ctx) throw std::runtime_error(FormatError("consteval", "空主表达式"));
|
||||
if (ctx->number()) {
|
||||
if (ctx->number()->ILITERAL()) {
|
||||
return static_cast<double>(ParseIntLiteral(ctx->number()->getText()));
|
||||
}
|
||||
if (ctx->number()->FLITERAL()) {
|
||||
return static_cast<double>(std::strtof(ctx->number()->getText().c_str(), nullptr));
|
||||
}
|
||||
throw std::runtime_error(FormatError("consteval", "非法数字字面量"));
|
||||
}
|
||||
if (ctx->exp()) return EvalAddExp(ctx->exp()->addExp(), int_env, float_env);
|
||||
if (ctx->lValue()) {
|
||||
if (!ctx->lValue()->ID())
|
||||
throw std::runtime_error(FormatError("consteval", "非法 lValue"));
|
||||
const std::string name = ctx->lValue()->ID()->getText();
|
||||
auto it_int = int_env.find(name);
|
||||
if (it_int != int_env.end()) return static_cast<double>(it_int->second);
|
||||
auto it_float = float_env.find(name);
|
||||
if (it_float != float_env.end()) return static_cast<double>(it_float->second);
|
||||
throw std::runtime_error(
|
||||
FormatError("consteval", "constExp 引用非 const 变量: " + name));
|
||||
}
|
||||
throw std::runtime_error(FormatError("consteval", "不支持的主表达式形式"));
|
||||
}
|
||||
|
||||
double EvalUnaryExp(SysYParser::UnaryExpContext* ctx,
|
||||
const IRGenImpl::ConstEnv& int_env,
|
||||
const IRGenImpl::ConstFloatEnv& float_env) {
|
||||
if (!ctx) throw std::runtime_error(FormatError("consteval", "空一元表达式"));
|
||||
if (ctx->primaryExp()) return EvalPrimary(ctx->primaryExp(), int_env, float_env);
|
||||
if (ctx->unaryOp() && ctx->unaryExp()) {
|
||||
double v = EvalUnaryExp(ctx->unaryExp(), int_env, float_env);
|
||||
if (ctx->unaryOp()->SUB()) return -v;
|
||||
if (ctx->unaryOp()->ADD()) return v;
|
||||
if (ctx->unaryOp()->NOT()) return (v == 0.0) ? 1.0 : 0.0;
|
||||
}
|
||||
throw std::runtime_error(
|
||||
FormatError("consteval", "函数调用不能出现在 constExp 中"));
|
||||
}
|
||||
|
||||
double EvalMulExp(SysYParser::MulExpContext* ctx,
|
||||
const IRGenImpl::ConstEnv& int_env,
|
||||
const IRGenImpl::ConstFloatEnv& float_env) {
|
||||
if (!ctx) throw std::runtime_error(FormatError("consteval", "空乘法表达式"));
|
||||
if (ctx->mulExp()) {
|
||||
double lhs = EvalMulExp(ctx->mulExp(), int_env, float_env);
|
||||
double rhs = EvalUnaryExp(ctx->unaryExp(), int_env, float_env);
|
||||
if (ctx->MUL()) return lhs * rhs;
|
||||
if (ctx->DIV()) {
|
||||
if (rhs == 0.0) throw std::runtime_error("除以零");
|
||||
return lhs / rhs;
|
||||
}
|
||||
if (ctx->MOD()) {
|
||||
if (rhs == 0.0) throw std::runtime_error("模零");
|
||||
return std::fmod(lhs, rhs);
|
||||
}
|
||||
throw std::runtime_error(FormatError("consteval", "未知乘法运算符"));
|
||||
}
|
||||
return EvalUnaryExp(ctx->unaryExp(), int_env, float_env);
|
||||
}
|
||||
|
||||
double EvalAddExp(SysYParser::AddExpContext* ctx,
|
||||
const IRGenImpl::ConstEnv& int_env,
|
||||
const IRGenImpl::ConstFloatEnv& float_env) {
|
||||
if (!ctx) throw std::runtime_error(FormatError("consteval", "空加法表达式"));
|
||||
if (ctx->addExp()) {
|
||||
double lhs = EvalAddExp(ctx->addExp(), int_env, float_env);
|
||||
double rhs = EvalMulExp(ctx->mulExp(), int_env, float_env);
|
||||
if (ctx->ADD()) return lhs + rhs;
|
||||
if (ctx->SUB()) return lhs - rhs;
|
||||
throw std::runtime_error(FormatError("consteval", "未知加法运算符"));
|
||||
}
|
||||
return EvalMulExp(ctx->mulExp(), int_env, float_env);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int IRGenImpl::EvalConstExpr(SysYParser::ConstExpContext* ctx) const {
|
||||
if (!ctx || !ctx->addExp())
|
||||
throw std::runtime_error(FormatError("consteval", "空 constExp"));
|
||||
return static_cast<int>(EvalAddExp(ctx->addExp(), const_env_, const_float_env_));
|
||||
}
|
||||
|
||||
float IRGenImpl::EvalConstExprAsFloat(SysYParser::ConstExpContext* ctx) const {
|
||||
if (!ctx || !ctx->addExp())
|
||||
throw std::runtime_error(FormatError("consteval", "空 constExp"));
|
||||
return static_cast<float>(EvalAddExp(ctx->addExp(), const_env_, const_float_env_));
|
||||
}
|
||||
|
||||
int IRGenImpl::EvalExpAsConst(SysYParser::ExpContext* ctx) const {
|
||||
if (!ctx || !ctx->addExp())
|
||||
throw std::runtime_error(FormatError("consteval", "空 exp"));
|
||||
return static_cast<int>(EvalAddExp(ctx->addExp(), const_env_, const_float_env_));
|
||||
}
|
||||
|
||||
float IRGenImpl::EvalExpAsConstFloat(SysYParser::ExpContext* ctx) const {
|
||||
if (!ctx || !ctx->addExp())
|
||||
throw std::runtime_error(FormatError("consteval", "空 exp"));
|
||||
return static_cast<float>(EvalAddExp(ctx->addExp(), const_env_, const_float_env_));
|
||||
}
|
||||
@ -0,0 +1,623 @@
|
||||
#include "mir/MIR.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace mir {
|
||||
namespace {
|
||||
|
||||
bool IsControlTransfer(const MachineInstr& inst) {
|
||||
switch (inst.GetOpcode()) {
|
||||
case Opcode::B:
|
||||
case Opcode::Bcond:
|
||||
case Opcode::FBcond:
|
||||
case Opcode::Cbnz:
|
||||
case Opcode::Cbz:
|
||||
case Opcode::Ret:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> GetLoadSlot(const MachineInstr& inst) {
|
||||
const auto& ops = inst.GetOperands();
|
||||
if (inst.GetOpcode() != Opcode::LoadStack || ops.size() < 2 ||
|
||||
!ops[1].IsFrameIndex()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return ops[1].GetFrameIndex();
|
||||
}
|
||||
|
||||
std::optional<int> GetStoreSlot(const MachineInstr& inst) {
|
||||
const auto& ops = inst.GetOperands();
|
||||
if (inst.GetOpcode() != Opcode::StoreStack || ops.size() < 2 ||
|
||||
!ops[1].IsFrameIndex()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return ops[1].GetFrameIndex();
|
||||
}
|
||||
|
||||
bool IsOpaqueSlotUse(const MachineInstr& inst, int* slot) {
|
||||
const auto& ops = inst.GetOperands();
|
||||
switch (inst.GetOpcode()) {
|
||||
case Opcode::LoadStackOffset:
|
||||
case Opcode::StoreStackOffset:
|
||||
case Opcode::LoadStackAddr:
|
||||
if (ops.size() >= 2 && ops[1].IsFrameIndex()) {
|
||||
*slot = ops[1].GetFrameIndex();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SameReg(PhysReg lhs, PhysReg rhs) {
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
bool IsPromotableWReg(PhysReg reg) {
|
||||
if (reg >= PhysReg::W0 && reg <= PhysReg::W11) return true;
|
||||
return reg == PhysReg::W19 || reg == PhysReg::W20 || reg == PhysReg::W21 ||
|
||||
reg == PhysReg::W22 || reg == PhysReg::W23 || reg == PhysReg::W24;
|
||||
}
|
||||
|
||||
bool IsPromotableXReg(PhysReg reg) {
|
||||
if (reg >= PhysReg::X0 && reg <= PhysReg::X11) return true;
|
||||
return reg == PhysReg::X19 || reg == PhysReg::X20 || reg == PhysReg::X21 ||
|
||||
reg == PhysReg::X22 || reg == PhysReg::X23 || reg == PhysReg::X24;
|
||||
}
|
||||
|
||||
bool IsPromotableSReg(PhysReg reg) {
|
||||
return reg >= PhysReg::S0 && reg <= PhysReg::S10;
|
||||
}
|
||||
|
||||
size_t FirstTerminatorIndex(const std::vector<MachineInstr>& insts) {
|
||||
for (size_t i = 0; i < insts.size(); ++i) {
|
||||
if (IsControlTransfer(insts[i])) return i;
|
||||
}
|
||||
return insts.size();
|
||||
}
|
||||
|
||||
void InsertBeforeTerminators(std::vector<MachineInstr>& insts,
|
||||
const std::vector<MachineInstr>& inserted) {
|
||||
const size_t pos = FirstTerminatorIndex(insts);
|
||||
insts.insert(insts.begin() + static_cast<long>(pos), inserted.begin(),
|
||||
inserted.end());
|
||||
}
|
||||
|
||||
struct SlotUseInfo {
|
||||
enum class RegKind { Unknown, W, X, S, Invalid };
|
||||
|
||||
int slot = -1;
|
||||
int loads = 0;
|
||||
int stores = 0;
|
||||
int body_loads = 0;
|
||||
int body_stores = 0;
|
||||
int after_call_uses = 0;
|
||||
RegKind reg_kind = RegKind::Unknown;
|
||||
std::unordered_set<size_t> use_blocks;
|
||||
};
|
||||
|
||||
struct SlotPick {
|
||||
int slot = -1;
|
||||
SlotUseInfo::RegKind reg_kind = SlotUseInfo::RegKind::Unknown;
|
||||
bool write_back = true;
|
||||
};
|
||||
|
||||
struct LoopCandidate {
|
||||
size_t header = 0;
|
||||
size_t latch = 0;
|
||||
int score = 0;
|
||||
std::vector<SlotPick> slots;
|
||||
std::unordered_set<size_t> blocks;
|
||||
};
|
||||
|
||||
struct Promotion {
|
||||
int slot = -1;
|
||||
PhysReg reg = PhysReg::W19;
|
||||
SlotUseInfo::RegKind reg_kind = SlotUseInfo::RegKind::Unknown;
|
||||
bool write_back = true;
|
||||
};
|
||||
|
||||
SlotUseInfo::RegKind ClassifyPromotableReg(PhysReg reg) {
|
||||
if (IsPromotableWReg(reg)) return SlotUseInfo::RegKind::W;
|
||||
if (IsPromotableXReg(reg)) return SlotUseInfo::RegKind::X;
|
||||
if (IsPromotableSReg(reg)) return SlotUseInfo::RegKind::S;
|
||||
return SlotUseInfo::RegKind::Invalid;
|
||||
}
|
||||
|
||||
void NoteSlotRegUse(SlotUseInfo& info, PhysReg reg) {
|
||||
SlotUseInfo::RegKind use_kind = ClassifyPromotableReg(reg);
|
||||
if (use_kind == SlotUseInfo::RegKind::Invalid ||
|
||||
(info.reg_kind != SlotUseInfo::RegKind::Unknown &&
|
||||
info.reg_kind != use_kind)) {
|
||||
info.reg_kind = SlotUseInfo::RegKind::Invalid;
|
||||
return;
|
||||
}
|
||||
info.reg_kind = use_kind;
|
||||
}
|
||||
|
||||
int SlotScore(const SlotUseInfo& info) {
|
||||
int score = (info.body_loads + info.body_stores) * 4 + info.loads +
|
||||
info.stores;
|
||||
if (info.stores == 0) {
|
||||
score += 80 + info.body_loads * 6;
|
||||
}
|
||||
if (info.body_loads > 0 && info.body_stores > 0) {
|
||||
score += info.use_blocks.size() > 1 ? 140 : 20;
|
||||
}
|
||||
if (info.use_blocks.size() > 1) {
|
||||
score += static_cast<int>(info.use_blocks.size() - 1) * 24;
|
||||
}
|
||||
if (info.reg_kind == SlotUseInfo::RegKind::S && info.after_call_uses > 0) {
|
||||
score += 180 + info.after_call_uses * 8;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
PhysReg GprForIndex(SlotUseInfo::RegKind kind, size_t index) {
|
||||
static const std::vector<PhysReg> w_regs = {PhysReg::W19, PhysReg::W20,
|
||||
PhysReg::W21, PhysReg::W22,
|
||||
PhysReg::W23, PhysReg::W24};
|
||||
static const std::vector<PhysReg> x_regs = {PhysReg::X19, PhysReg::X20,
|
||||
PhysReg::X21, PhysReg::X22,
|
||||
PhysReg::X23, PhysReg::X24};
|
||||
if (kind == SlotUseInfo::RegKind::X) return x_regs[index];
|
||||
return w_regs[index];
|
||||
}
|
||||
|
||||
std::vector<size_t> GetSuccessors(
|
||||
const MachineFunction& function, size_t block_index,
|
||||
const std::unordered_map<std::string, size_t>& block_index_by_name) {
|
||||
const auto& blocks = function.GetBlocks();
|
||||
const auto& insts = blocks[block_index]->GetInstructions();
|
||||
std::vector<size_t> succs;
|
||||
for (const auto& inst : insts) {
|
||||
const auto& ops = inst.GetOperands();
|
||||
switch (inst.GetOpcode()) {
|
||||
case Opcode::B:
|
||||
case Opcode::Bcond:
|
||||
case Opcode::FBcond:
|
||||
if (!ops.empty() && ops[0].IsSymbol()) {
|
||||
auto it = block_index_by_name.find(ops[0].GetSymbol());
|
||||
if (it != block_index_by_name.end()) succs.push_back(it->second);
|
||||
}
|
||||
break;
|
||||
case Opcode::Cbnz:
|
||||
case Opcode::Cbz:
|
||||
if (ops.size() > 1 && ops[1].IsSymbol()) {
|
||||
auto it = block_index_by_name.find(ops[1].GetSymbol());
|
||||
if (it != block_index_by_name.end()) succs.push_back(it->second);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!insts.empty()) {
|
||||
Opcode last = insts.back().GetOpcode();
|
||||
if (last != Opcode::B && last != Opcode::Ret &&
|
||||
block_index + 1 < blocks.size()) {
|
||||
succs.push_back(block_index + 1);
|
||||
}
|
||||
}
|
||||
std::sort(succs.begin(), succs.end());
|
||||
succs.erase(std::unique(succs.begin(), succs.end()), succs.end());
|
||||
return succs;
|
||||
}
|
||||
|
||||
bool InLoop(const LoopCandidate& loop, size_t index) {
|
||||
return loop.blocks.count(index) != 0;
|
||||
}
|
||||
|
||||
std::vector<size_t> SortedLoopBlocks(const LoopCandidate& loop) {
|
||||
std::vector<size_t> blocks(loop.blocks.begin(), loop.blocks.end());
|
||||
std::sort(blocks.begin(), blocks.end());
|
||||
return blocks;
|
||||
}
|
||||
|
||||
std::vector<std::vector<size_t>> BuildSuccessors(
|
||||
const MachineFunction& function,
|
||||
const std::unordered_map<std::string, size_t>& block_index_by_name) {
|
||||
std::vector<std::vector<size_t>> succs(function.GetBlocks().size());
|
||||
for (size_t i = 0; i < succs.size(); ++i) {
|
||||
succs[i] = GetSuccessors(function, i, block_index_by_name);
|
||||
}
|
||||
return succs;
|
||||
}
|
||||
|
||||
std::vector<std::vector<size_t>> BuildPredecessors(
|
||||
const std::vector<std::vector<size_t>>& succs) {
|
||||
std::vector<std::vector<size_t>> preds(succs.size());
|
||||
for (size_t i = 0; i < succs.size(); ++i) {
|
||||
for (size_t succ : succs[i]) {
|
||||
preds[succ].push_back(i);
|
||||
}
|
||||
}
|
||||
for (auto& pred_list : preds) {
|
||||
std::sort(pred_list.begin(), pred_list.end());
|
||||
pred_list.erase(std::unique(pred_list.begin(), pred_list.end()),
|
||||
pred_list.end());
|
||||
}
|
||||
return preds;
|
||||
}
|
||||
|
||||
std::vector<std::set<size_t>> ComputeDominators(
|
||||
size_t block_count, const std::vector<std::vector<size_t>>& preds) {
|
||||
std::vector<std::set<size_t>> doms(block_count);
|
||||
if (block_count == 0) return doms;
|
||||
|
||||
doms[0].insert(0);
|
||||
for (size_t i = 1; i < block_count; ++i) {
|
||||
for (size_t j = 0; j < block_count; ++j) doms[i].insert(j);
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for (size_t block = 1; block < block_count; ++block) {
|
||||
std::set<size_t> next;
|
||||
bool first_pred = true;
|
||||
for (size_t pred : preds[block]) {
|
||||
if (first_pred) {
|
||||
next = doms[pred];
|
||||
first_pred = false;
|
||||
continue;
|
||||
}
|
||||
std::set<size_t> intersection;
|
||||
std::set_intersection(next.begin(), next.end(), doms[pred].begin(),
|
||||
doms[pred].end(),
|
||||
std::inserter(intersection,
|
||||
intersection.begin()));
|
||||
next = std::move(intersection);
|
||||
}
|
||||
next.insert(block);
|
||||
if (next != doms[block]) {
|
||||
doms[block] = std::move(next);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return doms;
|
||||
}
|
||||
|
||||
std::unordered_set<size_t> BuildNaturalLoop(
|
||||
size_t header, size_t latch,
|
||||
const std::vector<std::vector<size_t>>& preds) {
|
||||
std::unordered_set<size_t> loop_blocks;
|
||||
std::vector<size_t> worklist;
|
||||
loop_blocks.insert(header);
|
||||
loop_blocks.insert(latch);
|
||||
worklist.push_back(latch);
|
||||
|
||||
while (!worklist.empty()) {
|
||||
size_t block = worklist.back();
|
||||
worklist.pop_back();
|
||||
for (size_t pred : preds[block]) {
|
||||
if (loop_blocks.insert(pred).second && pred != header) {
|
||||
worklist.push_back(pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
return loop_blocks;
|
||||
}
|
||||
|
||||
bool HasSingleEntry(size_t header, const std::unordered_set<size_t>& loop_blocks,
|
||||
const std::vector<std::vector<size_t>>& preds) {
|
||||
for (size_t block : loop_blocks) {
|
||||
if (block == header) continue;
|
||||
for (size_t pred : preds[block]) {
|
||||
if (loop_blocks.count(pred) == 0) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<LoopCandidate> FindLoopCandidates(MachineFunction& function) {
|
||||
const auto& blocks = function.GetBlocks();
|
||||
std::unordered_map<std::string, size_t> block_index_by_name;
|
||||
for (size_t i = 0; i < blocks.size(); ++i) {
|
||||
block_index_by_name[blocks[i]->GetName()] = i;
|
||||
}
|
||||
|
||||
std::unordered_set<int> opaque_slots;
|
||||
for (const auto& bb : blocks) {
|
||||
for (const auto& inst : bb->GetInstructions()) {
|
||||
int slot = -1;
|
||||
if (IsOpaqueSlotUse(inst, &slot)) opaque_slots.insert(slot);
|
||||
}
|
||||
}
|
||||
|
||||
auto succs = BuildSuccessors(function, block_index_by_name);
|
||||
auto preds = BuildPredecessors(succs);
|
||||
auto doms = ComputeDominators(blocks.size(), preds);
|
||||
|
||||
std::vector<LoopCandidate> candidates;
|
||||
for (size_t latch = 0; latch < blocks.size(); ++latch) {
|
||||
for (size_t header : succs[latch]) {
|
||||
if (header == latch) continue;
|
||||
if (header >= doms.size() || doms[latch].count(header) == 0) continue;
|
||||
|
||||
auto loop_blocks = BuildNaturalLoop(header, latch, preds);
|
||||
if (loop_blocks.size() > 24) continue;
|
||||
if (!HasSingleEntry(header, loop_blocks, preds)) continue;
|
||||
|
||||
std::unordered_map<int, SlotUseInfo> slot_info;
|
||||
for (size_t bi : loop_blocks) {
|
||||
bool seen_call = false;
|
||||
for (const auto& cur : blocks[bi]->GetInstructions()) {
|
||||
if (cur.GetOpcode() == Opcode::Bl) {
|
||||
seen_call = true;
|
||||
}
|
||||
if (auto slot = GetLoadSlot(cur);
|
||||
slot.has_value() && !opaque_slots.count(*slot)) {
|
||||
auto& info = slot_info[*slot];
|
||||
info.slot = *slot;
|
||||
const auto& ops = cur.GetOperands();
|
||||
if (ops.empty() || !ops[0].IsReg()) {
|
||||
info.reg_kind = SlotUseInfo::RegKind::Invalid;
|
||||
} else {
|
||||
NoteSlotRegUse(info, ops[0].GetReg());
|
||||
}
|
||||
++info.loads;
|
||||
info.use_blocks.insert(bi);
|
||||
if (seen_call) ++info.after_call_uses;
|
||||
if (bi != header) ++info.body_loads;
|
||||
}
|
||||
if (auto slot = GetStoreSlot(cur);
|
||||
slot.has_value() && !opaque_slots.count(*slot)) {
|
||||
auto& info = slot_info[*slot];
|
||||
info.slot = *slot;
|
||||
const auto& ops = cur.GetOperands();
|
||||
if (ops.empty() || !ops[0].IsReg()) {
|
||||
info.reg_kind = SlotUseInfo::RegKind::Invalid;
|
||||
} else {
|
||||
NoteSlotRegUse(info, ops[0].GetReg());
|
||||
}
|
||||
++info.stores;
|
||||
info.use_blocks.insert(bi);
|
||||
if (seen_call) ++info.after_call_uses;
|
||||
if (bi != header) ++info.body_stores;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<SlotUseInfo> ranked;
|
||||
for (const auto& [slot, info] : slot_info) {
|
||||
if (info.reg_kind == SlotUseInfo::RegKind::Invalid ||
|
||||
info.reg_kind == SlotUseInfo::RegKind::Unknown) {
|
||||
continue;
|
||||
}
|
||||
const int slot_size = function.GetFrameSlot(slot).size;
|
||||
if (info.reg_kind == SlotUseInfo::RegKind::X) {
|
||||
if (slot_size != 8) continue;
|
||||
} else if (slot_size != 4) {
|
||||
continue;
|
||||
}
|
||||
if (info.loads == 0) continue;
|
||||
if (info.stores == 0 && info.loads < 2) continue;
|
||||
if (info.stores > 0 && info.loads + info.stores < 2) continue;
|
||||
ranked.push_back(info);
|
||||
}
|
||||
std::sort(ranked.begin(), ranked.end(),
|
||||
[](const SlotUseInfo& lhs, const SlotUseInfo& rhs) {
|
||||
int lhs_score = SlotScore(lhs);
|
||||
int rhs_score = SlotScore(rhs);
|
||||
if (lhs_score != rhs_score) return lhs_score > rhs_score;
|
||||
return lhs.slot < rhs.slot;
|
||||
});
|
||||
if (ranked.empty()) continue;
|
||||
|
||||
LoopCandidate cand;
|
||||
cand.header = header;
|
||||
cand.latch = latch;
|
||||
cand.blocks = std::move(loop_blocks);
|
||||
int gpr_slots = 0;
|
||||
int s_slots = 0;
|
||||
constexpr int kMaxGprSlots = 6;
|
||||
constexpr int kMaxSSlots = 3;
|
||||
for (const auto& info : ranked) {
|
||||
if (info.reg_kind == SlotUseInfo::RegKind::W ||
|
||||
info.reg_kind == SlotUseInfo::RegKind::X) {
|
||||
if (gpr_slots >= kMaxGprSlots) continue;
|
||||
++gpr_slots;
|
||||
} else if (info.reg_kind == SlotUseInfo::RegKind::S) {
|
||||
if (s_slots >= kMaxSSlots) continue;
|
||||
++s_slots;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
cand.slots.push_back(
|
||||
SlotPick{info.slot, info.reg_kind, info.stores > 0});
|
||||
cand.score += SlotScore(info);
|
||||
}
|
||||
if (cand.slots.empty()) continue;
|
||||
candidates.push_back(std::move(cand));
|
||||
}
|
||||
}
|
||||
std::sort(candidates.begin(), candidates.end(),
|
||||
[](const LoopCandidate& lhs, const LoopCandidate& rhs) {
|
||||
if (lhs.score != rhs.score) return lhs.score > rhs.score;
|
||||
if (lhs.blocks.size() != rhs.blocks.size()) {
|
||||
return lhs.blocks.size() > rhs.blocks.size();
|
||||
}
|
||||
return lhs.header < rhs.header;
|
||||
});
|
||||
return candidates;
|
||||
}
|
||||
|
||||
void PromoteLoopSlots(MachineFunction& function, const LoopCandidate& loop) {
|
||||
const std::vector<PhysReg> s_regs = {PhysReg::S8, PhysReg::S9,
|
||||
PhysReg::S10};
|
||||
std::unordered_map<int, Promotion> slot_to_promotion;
|
||||
std::vector<Promotion> promotions;
|
||||
size_t next_gpr_reg = 0;
|
||||
size_t next_s_reg = 0;
|
||||
for (const auto& slot : loop.slots) {
|
||||
PhysReg reg = PhysReg::W19;
|
||||
if (slot.reg_kind == SlotUseInfo::RegKind::W ||
|
||||
slot.reg_kind == SlotUseInfo::RegKind::X) {
|
||||
if (next_gpr_reg >= 6) continue;
|
||||
reg = GprForIndex(slot.reg_kind, next_gpr_reg++);
|
||||
} else if (slot.reg_kind == SlotUseInfo::RegKind::S) {
|
||||
if (next_s_reg >= s_regs.size()) continue;
|
||||
reg = s_regs[next_s_reg++];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
Promotion promotion{slot.slot, reg, slot.reg_kind, slot.write_back};
|
||||
slot_to_promotion[slot.slot] = promotion;
|
||||
promotions.push_back(promotion);
|
||||
function.AddUsedCalleeSaved(reg);
|
||||
}
|
||||
|
||||
const auto& blocks = function.GetBlocks();
|
||||
std::unordered_map<std::string, size_t> block_index_by_name;
|
||||
for (size_t i = 0; i < blocks.size(); ++i) {
|
||||
block_index_by_name[blocks[i]->GetName()] = i;
|
||||
}
|
||||
auto succs = BuildSuccessors(function, block_index_by_name);
|
||||
auto preds = BuildPredecessors(succs);
|
||||
|
||||
for (size_t bi : SortedLoopBlocks(loop)) {
|
||||
auto& insts = blocks[bi]->GetInstructions();
|
||||
std::vector<MachineInstr> rewritten;
|
||||
rewritten.reserve(insts.size());
|
||||
for (const auto& inst : insts) {
|
||||
if (auto slot = GetLoadSlot(inst); slot.has_value()) {
|
||||
auto it = slot_to_promotion.find(*slot);
|
||||
if (it != slot_to_promotion.end()) {
|
||||
const auto& ops = inst.GetOperands();
|
||||
PhysReg dst = ops[0].GetReg();
|
||||
if (!SameReg(dst, it->second.reg)) {
|
||||
Opcode mov_opcode =
|
||||
it->second.reg_kind == SlotUseInfo::RegKind::S
|
||||
? Opcode::FMovReg
|
||||
: Opcode::MovReg;
|
||||
rewritten.emplace_back(
|
||||
mov_opcode,
|
||||
std::vector<Operand>{Operand::Reg(dst),
|
||||
Operand::Reg(it->second.reg)});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (auto slot = GetStoreSlot(inst); slot.has_value()) {
|
||||
auto it = slot_to_promotion.find(*slot);
|
||||
if (it != slot_to_promotion.end()) {
|
||||
const auto& ops = inst.GetOperands();
|
||||
PhysReg src = ops[0].GetReg();
|
||||
if (!SameReg(src, it->second.reg)) {
|
||||
Opcode mov_opcode =
|
||||
it->second.reg_kind == SlotUseInfo::RegKind::S
|
||||
? Opcode::FMovReg
|
||||
: Opcode::MovReg;
|
||||
rewritten.emplace_back(
|
||||
mov_opcode,
|
||||
std::vector<Operand>{Operand::Reg(it->second.reg),
|
||||
Operand::Reg(src)});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
rewritten.push_back(inst);
|
||||
}
|
||||
insts = std::move(rewritten);
|
||||
}
|
||||
|
||||
for (size_t pred = 0; pred < blocks.size(); ++pred) {
|
||||
if (std::find(succs[pred].begin(), succs[pred].end(), loop.header) ==
|
||||
succs[pred].end()) {
|
||||
continue;
|
||||
}
|
||||
if (InLoop(loop, pred)) continue;
|
||||
std::vector<MachineInstr> loads;
|
||||
for (const auto& promotion : promotions) {
|
||||
loads.emplace_back(Opcode::LoadStack,
|
||||
std::vector<Operand>{
|
||||
Operand::Reg(promotion.reg),
|
||||
Operand::FrameIndex(promotion.slot)});
|
||||
}
|
||||
InsertBeforeTerminators(blocks[pred]->GetInstructions(), loads);
|
||||
}
|
||||
|
||||
std::unordered_set<size_t> exit_blocks_with_stores;
|
||||
for (size_t bi : SortedLoopBlocks(loop)) {
|
||||
bool needs_local_exit_store = false;
|
||||
for (size_t succ : succs[bi]) {
|
||||
if (InLoop(loop, succ)) continue;
|
||||
|
||||
bool exit_has_only_loop_preds = true;
|
||||
for (size_t pred : preds[succ]) {
|
||||
if (!InLoop(loop, pred)) {
|
||||
exit_has_only_loop_preds = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (exit_has_only_loop_preds) {
|
||||
if (exit_blocks_with_stores.insert(succ).second) {
|
||||
std::vector<MachineInstr> stores;
|
||||
for (const auto& promotion : promotions) {
|
||||
if (!promotion.write_back) continue;
|
||||
stores.emplace_back(
|
||||
Opcode::StoreStack,
|
||||
std::vector<Operand>{
|
||||
Operand::Reg(promotion.reg),
|
||||
Operand::FrameIndex(promotion.slot)});
|
||||
}
|
||||
auto& exit_insts = blocks[succ]->GetInstructions();
|
||||
exit_insts.insert(exit_insts.begin(), stores.begin(), stores.end());
|
||||
}
|
||||
} else {
|
||||
needs_local_exit_store = true;
|
||||
}
|
||||
}
|
||||
if (!needs_local_exit_store) continue;
|
||||
std::vector<MachineInstr> stores;
|
||||
for (const auto& promotion : promotions) {
|
||||
if (!promotion.write_back) continue;
|
||||
stores.emplace_back(Opcode::StoreStack,
|
||||
std::vector<Operand>{
|
||||
Operand::Reg(promotion.reg),
|
||||
Operand::FrameIndex(promotion.slot)});
|
||||
}
|
||||
InsertBeforeTerminators(blocks[bi]->GetInstructions(), stores);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void RunLoopSlotPromotion(MachineFunction& function) {
|
||||
auto candidates = FindLoopCandidates(function);
|
||||
std::unordered_set<size_t> promoted_blocks;
|
||||
int promoted_loop_count = 0;
|
||||
constexpr int kMaxPromotedLoops = 4;
|
||||
constexpr int kMinLoopScore = 32;
|
||||
|
||||
for (const auto& loop : candidates) {
|
||||
if (loop.score < kMinLoopScore) break;
|
||||
|
||||
bool overlaps_existing_loop = false;
|
||||
for (size_t block : loop.blocks) {
|
||||
if (promoted_blocks.count(block) != 0) {
|
||||
overlaps_existing_loop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (overlaps_existing_loop) continue;
|
||||
|
||||
PromoteLoopSlots(function, loop);
|
||||
promoted_blocks.insert(loop.blocks.begin(), loop.blocks.end());
|
||||
++promoted_loop_count;
|
||||
if (promoted_loop_count >= kMaxPromotedLoops) break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mir
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,200 +1,490 @@
|
||||
#include "sem/Sema.h"
|
||||
|
||||
#include <any>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "SysYBaseVisitor.h"
|
||||
#include "sem/SymbolTable.h"
|
||||
#include "utils/Log.h"
|
||||
|
||||
namespace {
|
||||
|
||||
std::string GetLValueName(SysYParser::LValueContext& lvalue) {
|
||||
if (!lvalue.ID()) {
|
||||
throw std::runtime_error(FormatError("sema", "非法左值"));
|
||||
SymbolType ParseType(const std::string& text) {
|
||||
if (text == "int") {
|
||||
return SymbolType::TYPE_INT;
|
||||
}
|
||||
if (text == "float") {
|
||||
return SymbolType::TYPE_FLOAT;
|
||||
}
|
||||
if (text == "void") {
|
||||
return SymbolType::TYPE_VOID;
|
||||
}
|
||||
return lvalue.ID()->getText();
|
||||
return SymbolType::TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
class SemaVisitor final : public SysYBaseVisitor {
|
||||
public:
|
||||
std::any visitCompUnit(SysYParser::CompUnitContext* ctx) override {
|
||||
if (!ctx) {
|
||||
throw std::runtime_error(FormatError("sema", "缺少编译单元"));
|
||||
}
|
||||
auto* func = ctx->funcDef();
|
||||
if (!func || !func->blockStmt()) {
|
||||
throw std::runtime_error(FormatError("sema", "缺少 main 函数定义"));
|
||||
}
|
||||
if (!func->ID() || func->ID()->getText() != "main") {
|
||||
throw std::runtime_error(FormatError("sema", "缺少 main 函数定义"));
|
||||
}
|
||||
func->accept(this);
|
||||
if (!seen_return_) {
|
||||
throw std::runtime_error(
|
||||
FormatError("sema", "main 函数必须包含 return 语句"));
|
||||
}
|
||||
SymbolType MergeNumericType(SymbolType lhs, SymbolType rhs) {
|
||||
if (lhs == SymbolType::TYPE_FLOAT || rhs == SymbolType::TYPE_FLOAT) {
|
||||
return SymbolType::TYPE_FLOAT;
|
||||
}
|
||||
if (lhs == SymbolType::TYPE_INT && rhs == SymbolType::TYPE_INT) {
|
||||
return SymbolType::TYPE_INT;
|
||||
}
|
||||
if (lhs != SymbolType::TYPE_UNKNOWN) {
|
||||
return lhs;
|
||||
}
|
||||
return rhs;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SemaVisitor::RecordNodeError(antlr4::ParserRuleContext* ctx,
|
||||
const std::string& msg) {
|
||||
if (!ctx || !ctx->getStart()) {
|
||||
ir_ctx_.RecordError(ErrorMsg(msg, 0, 0));
|
||||
return;
|
||||
}
|
||||
ir_ctx_.RecordError(ErrorMsg(msg, ctx->getStart()->getLine(),
|
||||
ctx->getStart()->getCharPositionInLine() + 1));
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitCompUnit(SysYParser::CompUnitContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitDecl(SysYParser::DeclContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitConstDecl(SysYParser::ConstDeclContext* ctx) {
|
||||
current_decl_is_const_ = true;
|
||||
current_decl_type_ = SymbolType::TYPE_UNKNOWN;
|
||||
if (ctx && ctx->btype()) {
|
||||
current_decl_type_ = ParseType(ctx->btype()->getText());
|
||||
}
|
||||
std::any result = visitChildren(ctx);
|
||||
current_decl_is_const_ = false;
|
||||
current_decl_type_ = SymbolType::TYPE_UNKNOWN;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitBtype(SysYParser::BtypeContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitConstDef(SysYParser::ConstDefContext* ctx) {
|
||||
if (!ctx || !ctx->ID()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitFuncDef(SysYParser::FuncDefContext* ctx) override {
|
||||
if (!ctx || !ctx->blockStmt()) {
|
||||
throw std::runtime_error(FormatError("sema", "缺少 main 函数定义"));
|
||||
}
|
||||
if (!ctx->funcType() || !ctx->funcType()->INT()) {
|
||||
throw std::runtime_error(FormatError("sema", "当前仅支持 int main"));
|
||||
}
|
||||
const auto& items = ctx->blockStmt()->blockItem();
|
||||
if (items.empty()) {
|
||||
throw std::runtime_error(
|
||||
FormatError("sema", "main 函数不能为空,且必须以 return 结束"));
|
||||
}
|
||||
ctx->blockStmt()->accept(this);
|
||||
const std::string name = ctx->ID()->getText();
|
||||
auto& table = ir_ctx_.GetSymbolTable();
|
||||
if (table.CurrentScopeHasVar(name)) {
|
||||
RecordNodeError(ctx, "重复定义变量: " + name);
|
||||
} else {
|
||||
VarInfo info;
|
||||
info.type = current_decl_type_;
|
||||
info.is_const = true;
|
||||
info.decl_ctx = ctx;
|
||||
table.BindVar(name, info, ctx);
|
||||
}
|
||||
|
||||
ir_ctx_.SetType(ctx, current_decl_type_);
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitConstInitValue(SysYParser::ConstInitValueContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitVarDecl(SysYParser::VarDeclContext* ctx) {
|
||||
current_decl_is_const_ = false;
|
||||
current_decl_type_ = SymbolType::TYPE_UNKNOWN;
|
||||
if (ctx && ctx->btype()) {
|
||||
current_decl_type_ = ParseType(ctx->btype()->getText());
|
||||
}
|
||||
std::any result = visitChildren(ctx);
|
||||
current_decl_type_ = SymbolType::TYPE_UNKNOWN;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitVarDef(SysYParser::VarDefContext* ctx) {
|
||||
if (!ctx || !ctx->ID()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitBlockStmt(SysYParser::BlockStmtContext* ctx) override {
|
||||
if (!ctx) {
|
||||
throw std::runtime_error(FormatError("sema", "缺少语句块"));
|
||||
}
|
||||
const auto& items = ctx->blockItem();
|
||||
for (size_t i = 0; i < items.size(); ++i) {
|
||||
auto* item = items[i];
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
if (seen_return_) {
|
||||
throw std::runtime_error(
|
||||
FormatError("sema", "return 必须是 main 函数中的最后一条语句"));
|
||||
const std::string name = ctx->ID()->getText();
|
||||
auto& table = ir_ctx_.GetSymbolTable();
|
||||
if (table.CurrentScopeHasVar(name)) {
|
||||
RecordNodeError(ctx, "重复定义变量: " + name);
|
||||
} else {
|
||||
VarInfo info;
|
||||
info.type = current_decl_type_;
|
||||
info.is_const = current_decl_is_const_;
|
||||
info.decl_ctx = ctx;
|
||||
table.BindVar(name, info, ctx);
|
||||
}
|
||||
|
||||
ir_ctx_.SetType(ctx, current_decl_type_);
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitInitValue(SysYParser::InitValueContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitFuncDef(SysYParser::FuncDefContext* ctx) {
|
||||
if (!ctx || !ctx->ID() || !ctx->funcType()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::string func_name = ctx->ID()->getText();
|
||||
SymbolType ret_type = ParseType(ctx->funcType()->getText());
|
||||
ir_ctx_.SetCurrentFuncReturnType(ret_type);
|
||||
|
||||
auto& table = ir_ctx_.GetSymbolTable();
|
||||
if (table.CurrentScopeHasFunc(func_name)) {
|
||||
RecordNodeError(ctx, "重复定义函数: " + func_name);
|
||||
} else {
|
||||
FuncInfo info;
|
||||
info.name = func_name;
|
||||
info.ret_type = ret_type;
|
||||
info.decl_ctx = ctx;
|
||||
if (ctx->funcFParams()) {
|
||||
for (auto* param : ctx->funcFParams()->funcFParam()) {
|
||||
if (!param || !param->btype()) {
|
||||
info.param_types.push_back(SymbolType::TYPE_UNKNOWN);
|
||||
} else {
|
||||
info.param_types.push_back(ParseType(param->btype()->getText()));
|
||||
}
|
||||
}
|
||||
current_item_index_ = i;
|
||||
total_items_ = items.size();
|
||||
item->accept(this);
|
||||
}
|
||||
table.BindFunc(func_name, info, ctx);
|
||||
}
|
||||
|
||||
ir_ctx_.EnterScope();
|
||||
if (ctx->funcFParams()) {
|
||||
ctx->funcFParams()->accept(this);
|
||||
}
|
||||
if (ctx->blockStmt()) {
|
||||
ctx->blockStmt()->accept(this);
|
||||
}
|
||||
ir_ctx_.LeaveScope();
|
||||
|
||||
ir_ctx_.SetCurrentFuncReturnType(SymbolType::TYPE_UNKNOWN);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitFuncType(SysYParser::FuncTypeContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitFuncFParams(SysYParser::FuncFParamsContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitFuncFParam(SysYParser::FuncFParamContext* ctx) {
|
||||
if (!ctx || !ctx->ID() || !ctx->btype()) {
|
||||
return {};
|
||||
}
|
||||
const std::string name = ctx->ID()->getText();
|
||||
auto& table = ir_ctx_.GetSymbolTable();
|
||||
if (table.CurrentScopeHasVar(name)) {
|
||||
RecordNodeError(ctx, "重复定义形参: " + name);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitBlockItem(SysYParser::BlockItemContext* ctx) override {
|
||||
if (!ctx) {
|
||||
throw std::runtime_error(FormatError("sema", "暂不支持的语句或声明"));
|
||||
}
|
||||
if (ctx->decl()) {
|
||||
ctx->decl()->accept(this);
|
||||
return {};
|
||||
}
|
||||
if (ctx->stmt()) {
|
||||
ctx->stmt()->accept(this);
|
||||
return {};
|
||||
}
|
||||
throw std::runtime_error(FormatError("sema", "暂不支持的语句或声明"));
|
||||
VarInfo info;
|
||||
info.type = ParseType(ctx->btype()->getText());
|
||||
info.is_const = false;
|
||||
info.decl_ctx = ctx;
|
||||
table.BindVar(name, info, ctx);
|
||||
ir_ctx_.SetType(ctx, info.type);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitBlockStmt(SysYParser::BlockStmtContext* ctx) {
|
||||
ir_ctx_.EnterScope();
|
||||
std::any result = visitChildren(ctx);
|
||||
ir_ctx_.LeaveScope();
|
||||
return result;
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitBlockItem(SysYParser::BlockItemContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitStmt(SysYParser::StmtContext* ctx) {
|
||||
if (!ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitDecl(SysYParser::DeclContext* ctx) override {
|
||||
if (!ctx) {
|
||||
throw std::runtime_error(FormatError("sema", "非法变量声明"));
|
||||
}
|
||||
if (!ctx->btype() || !ctx->btype()->INT()) {
|
||||
throw std::runtime_error(FormatError("sema", "当前仅支持局部 int 变量声明"));
|
||||
}
|
||||
auto* var_def = ctx->varDef();
|
||||
if (!var_def || !var_def->lValue()) {
|
||||
throw std::runtime_error(FormatError("sema", "非法变量声明"));
|
||||
}
|
||||
const std::string name = GetLValueName(*var_def->lValue());
|
||||
if (table_.Contains(name)) {
|
||||
throw std::runtime_error(FormatError("sema", "重复定义变量: " + name));
|
||||
}
|
||||
if (auto* init = var_def->initValue()) {
|
||||
if (!init->exp()) {
|
||||
throw std::runtime_error(FormatError("sema", "当前不支持聚合初始化"));
|
||||
}
|
||||
init->exp()->accept(this);
|
||||
if (ctx->WHILE()) {
|
||||
ir_ctx_.EnterLoop();
|
||||
std::any result = visitChildren(ctx);
|
||||
ir_ctx_.ExitLoop();
|
||||
return result;
|
||||
}
|
||||
|
||||
if (ctx->BREAK() && !ir_ctx_.InLoop()) {
|
||||
RecordNodeError(ctx, "break 只能出现在循环语句中");
|
||||
}
|
||||
|
||||
if (ctx->CONTINUE() && !ir_ctx_.InLoop()) {
|
||||
RecordNodeError(ctx, "continue 只能出现在循环语句中");
|
||||
}
|
||||
|
||||
if (ctx->lValue() && ctx->exp()) {
|
||||
ctx->lValue()->accept(this);
|
||||
ctx->exp()->accept(this);
|
||||
SymbolType lhs = ir_ctx_.GetType(ctx->lValue());
|
||||
SymbolType rhs = ir_ctx_.GetType(ctx->exp());
|
||||
if (!IsTypeCompatible(lhs, rhs)) {
|
||||
RecordNodeError(ctx, "赋值两侧类型不兼容");
|
||||
}
|
||||
table_.Add(name, var_def);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitStmt(SysYParser::StmtContext* ctx) override {
|
||||
if (!ctx || !ctx->returnStmt()) {
|
||||
throw std::runtime_error(FormatError("sema", "暂不支持的语句或声明"));
|
||||
}
|
||||
ctx->returnStmt()->accept(this);
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitReturnStmt(SysYParser::ReturnStmtContext* ctx) {
|
||||
if (!ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitReturnStmt(SysYParser::ReturnStmtContext* ctx) override {
|
||||
if (!ctx || !ctx->exp()) {
|
||||
throw std::runtime_error(FormatError("sema", "return 缺少表达式"));
|
||||
}
|
||||
SymbolType ret_type = ir_ctx_.GetCurrentFuncReturnType();
|
||||
if (ctx->exp()) {
|
||||
ctx->exp()->accept(this);
|
||||
seen_return_ = true;
|
||||
if (current_item_index_ + 1 != total_items_) {
|
||||
throw std::runtime_error(
|
||||
FormatError("sema", "return 必须是 main 函数中的最后一条语句"));
|
||||
SymbolType expr_type = ir_ctx_.GetType(ctx->exp());
|
||||
if (ret_type == SymbolType::TYPE_VOID) {
|
||||
RecordNodeError(ctx, "void 函数不应返回表达式");
|
||||
} else if (!IsTypeCompatible(ret_type, expr_type)) {
|
||||
RecordNodeError(ctx, "return 表达式类型与函数返回类型不匹配");
|
||||
}
|
||||
} else if (ret_type != SymbolType::TYPE_VOID &&
|
||||
ret_type != SymbolType::TYPE_UNKNOWN) {
|
||||
RecordNodeError(ctx, "非 void 函数 return 必须带表达式");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitExp(SysYParser::ExpContext* ctx) {
|
||||
if (!ctx || !ctx->addExp()) {
|
||||
return {};
|
||||
}
|
||||
ctx->addExp()->accept(this);
|
||||
ir_ctx_.SetType(ctx, ir_ctx_.GetType(ctx->addExp()));
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitCond(SysYParser::CondContext* ctx) {
|
||||
if (!ctx || !ctx->lOrExp()) {
|
||||
return {};
|
||||
}
|
||||
ctx->lOrExp()->accept(this);
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_INT);
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitLValue(SysYParser::LValueContext* ctx) {
|
||||
if (!ctx || !ctx->ID()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitParenExp(SysYParser::ParenExpContext* ctx) override {
|
||||
if (!ctx || !ctx->exp()) {
|
||||
throw std::runtime_error(FormatError("sema", "非法括号表达式"));
|
||||
VarInfo var;
|
||||
void* decl_ctx = nullptr;
|
||||
auto& table = ir_ctx_.GetSymbolTable();
|
||||
const std::string name = ctx->ID()->getText();
|
||||
if (!table.LookupVar(name, var, decl_ctx)) {
|
||||
RecordNodeError(ctx, "未定义变量: " + name);
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_UNKNOWN);
|
||||
return {};
|
||||
}
|
||||
|
||||
ir_ctx_.SetType(ctx, var.type);
|
||||
|
||||
if (sema_ctx_ && decl_ctx) {
|
||||
auto* rule = static_cast<antlr4::ParserRuleContext*>(decl_ctx);
|
||||
if (auto* var_def = dynamic_cast<SysYParser::VarDefContext*>(rule)) {
|
||||
sema_ctx_->BindVarUse(ctx, var_def);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitPrimaryExp(SysYParser::PrimaryExpContext* ctx) {
|
||||
if (!ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ctx->exp()) {
|
||||
ctx->exp()->accept(this);
|
||||
ir_ctx_.SetType(ctx, ir_ctx_.GetType(ctx->exp()));
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitVarExp(SysYParser::VarExpContext* ctx) override {
|
||||
if (!ctx || !ctx->var()) {
|
||||
throw std::runtime_error(FormatError("sema", "非法变量表达式"));
|
||||
}
|
||||
ctx->var()->accept(this);
|
||||
if (ctx->lValue()) {
|
||||
ctx->lValue()->accept(this);
|
||||
ir_ctx_.SetType(ctx, ir_ctx_.GetType(ctx->lValue()));
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitNumberExp(SysYParser::NumberExpContext* ctx) override {
|
||||
if (!ctx || !ctx->number() || !ctx->number()->ILITERAL()) {
|
||||
throw std::runtime_error(FormatError("sema", "当前仅支持整数字面量"));
|
||||
}
|
||||
if (ctx->number()) {
|
||||
ctx->number()->accept(this);
|
||||
ir_ctx_.SetType(ctx, ir_ctx_.GetType(ctx->number()));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitNumber(SysYParser::NumberContext* ctx) {
|
||||
if (!ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitAdditiveExp(SysYParser::AdditiveExpContext* ctx) override {
|
||||
if (!ctx || !ctx->exp(0) || !ctx->exp(1)) {
|
||||
throw std::runtime_error(FormatError("sema", "暂不支持的表达式形式"));
|
||||
}
|
||||
ctx->exp(0)->accept(this);
|
||||
ctx->exp(1)->accept(this);
|
||||
if (ctx->ILITERAL()) {
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_INT);
|
||||
ir_ctx_.SetConstVal(ctx, std::any(0L));
|
||||
} else if (ctx->FLITERAL()) {
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_FLOAT);
|
||||
ir_ctx_.SetConstVal(ctx, std::any(0.0));
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitUnaryExp(SysYParser::UnaryExpContext* ctx) {
|
||||
if (!ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any visitVar(SysYParser::VarContext* ctx) override {
|
||||
if (!ctx || !ctx->ID()) {
|
||||
throw std::runtime_error(FormatError("sema", "非法变量引用"));
|
||||
}
|
||||
const std::string name = ctx->ID()->getText();
|
||||
auto* decl = table_.Lookup(name);
|
||||
if (!decl) {
|
||||
throw std::runtime_error(FormatError("sema", "使用了未定义的变量: " + name));
|
||||
if (ctx->primaryExp()) {
|
||||
ctx->primaryExp()->accept(this);
|
||||
ir_ctx_.SetType(ctx, ir_ctx_.GetType(ctx->primaryExp()));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ctx->unaryOp() && ctx->unaryExp()) {
|
||||
ctx->unaryExp()->accept(this);
|
||||
ir_ctx_.SetType(ctx, ir_ctx_.GetType(ctx->unaryExp()));
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ctx->ID()) {
|
||||
FuncInfo fn;
|
||||
void* decl_ctx = nullptr;
|
||||
if (!ir_ctx_.GetSymbolTable().LookupFunc(ctx->ID()->getText(), fn, decl_ctx)) {
|
||||
RecordNodeError(ctx, "未定义函数: " + ctx->ID()->getText());
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_UNKNOWN);
|
||||
} else {
|
||||
ir_ctx_.SetType(ctx, fn.ret_type);
|
||||
}
|
||||
sema_.BindVarUse(ctx, decl);
|
||||
}
|
||||
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitUnaryOp(SysYParser::UnaryOpContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitFuncRParams(SysYParser::FuncRParamsContext* ctx) {
|
||||
return visitChildren(ctx);
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitMulExp(SysYParser::MulExpContext* ctx) {
|
||||
if (!ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
SemanticContext TakeSemanticContext() { return std::move(sema_); }
|
||||
if (ctx->mulExp()) {
|
||||
ctx->mulExp()->accept(this);
|
||||
}
|
||||
if (ctx->unaryExp()) {
|
||||
ctx->unaryExp()->accept(this);
|
||||
}
|
||||
|
||||
private:
|
||||
SymbolTable table_;
|
||||
SemanticContext sema_;
|
||||
bool seen_return_ = false;
|
||||
size_t current_item_index_ = 0;
|
||||
size_t total_items_ = 0;
|
||||
};
|
||||
SymbolType lhs = ctx->mulExp() ? ir_ctx_.GetType(ctx->mulExp())
|
||||
: ir_ctx_.GetType(ctx->unaryExp());
|
||||
SymbolType rhs = ir_ctx_.GetType(ctx->unaryExp());
|
||||
ir_ctx_.SetType(ctx, MergeNumericType(lhs, rhs));
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
std::any SemaVisitor::visitAddExp(SysYParser::AddExpContext* ctx) {
|
||||
if (!ctx) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (ctx->addExp()) {
|
||||
ctx->addExp()->accept(this);
|
||||
}
|
||||
if (ctx->mulExp()) {
|
||||
ctx->mulExp()->accept(this);
|
||||
}
|
||||
|
||||
SymbolType lhs = ctx->addExp() ? ir_ctx_.GetType(ctx->addExp())
|
||||
: ir_ctx_.GetType(ctx->mulExp());
|
||||
SymbolType rhs = ir_ctx_.GetType(ctx->mulExp());
|
||||
ir_ctx_.SetType(ctx, MergeNumericType(lhs, rhs));
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitRelExp(SysYParser::RelExpContext* ctx) {
|
||||
if (ctx) {
|
||||
visitChildren(ctx);
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_INT);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitEqExp(SysYParser::EqExpContext* ctx) {
|
||||
if (ctx) {
|
||||
visitChildren(ctx);
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_INT);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitLAndExp(SysYParser::LAndExpContext* ctx) {
|
||||
if (ctx) {
|
||||
visitChildren(ctx);
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_INT);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitLOrExp(SysYParser::LOrExpContext* ctx) {
|
||||
if (ctx) {
|
||||
visitChildren(ctx);
|
||||
ir_ctx_.SetType(ctx, SymbolType::TYPE_INT);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::any SemaVisitor::visitConstExp(SysYParser::ConstExpContext* ctx) {
|
||||
if (!ctx || !ctx->addExp()) {
|
||||
return {};
|
||||
}
|
||||
ctx->addExp()->accept(this);
|
||||
ir_ctx_.SetType(ctx, ir_ctx_.GetType(ctx->addExp()));
|
||||
return {};
|
||||
}
|
||||
|
||||
void RunSemanticAnalysis(SysYParser::CompUnitContext* ctx, IRGenContext& ir_ctx) {
|
||||
if (!ctx) {
|
||||
throw std::invalid_argument("CompUnitContext is null");
|
||||
}
|
||||
|
||||
ir_ctx.EnterScope();
|
||||
SemaVisitor visitor(ir_ctx, nullptr);
|
||||
visitor.visit(ctx);
|
||||
ir_ctx.LeaveScope();
|
||||
}
|
||||
|
||||
SemanticContext RunSema(SysYParser::CompUnitContext& comp_unit) {
|
||||
SemaVisitor visitor;
|
||||
comp_unit.accept(&visitor);
|
||||
return visitor.TakeSemanticContext();
|
||||
IRGenContext ctx;
|
||||
SemanticContext sema_ctx;
|
||||
ctx.EnterScope();
|
||||
SemaVisitor visitor(ctx, &sema_ctx);
|
||||
visitor.visit(&comp_unit);
|
||||
ctx.LeaveScope();
|
||||
return sema_ctx;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue