Files
2026-05-30 07:59:28 +08:00

13 KiB
Raw Permalink Blame History

TinyCC 编译器改进计划

Feature Name: 2026-05-20-tinycc-improvements Updated: 2026-05-20

Description

本改进计划涵盖 TinyCC 编译器的 9 个核心改进方向,分为三个阶段:

  • 阶段一(基础完善):端到端测试、错误报告增强、仓库清理
  • 阶段二(功能完善):语义分析、预处理器集成、代码生成优化
  • 阶段三(高级特性)DWARF 调试信息、PE 格式支持、性能基准

Architecture

graph TD
    subgraph "阶段一:基础完善"
        A1[E2E 测试框架] --> A2[测试用例集合]
        B1[ErrorReporter 增强] --> B2[代码上下文格式化]
        C1[.gitignore 更新] --> C2[移除误提交文件]
    end
    
    subgraph "阶段二:功能完善"
        D1[SemanticAnalyzer 完善] --> D2[类型系统增强]
        D1 --> D3[作用域管理优化]
        E1[Preprocessor 集成] --> E2[宏展开引擎]
        E1 --> E3[头文件搜索机制]
        F1[优化 CodeGen] --> F2[寄存器分配器]
        F1 --> F3[指令选择优化]
    end
    
    subgraph "阶段三:高级特性"
        G1[DWARF 生成器] --> G2[调试信息编码]
        G1 --> G3[行号表生成]
        H1[PE Writer 完善] --> H2[PE 头生成]
        H1 --> H3[重定位表生成]
        I1[性能基准] --> I2[编译时间测量]
        I1 --> I3[执行时间测量]
    end
    
    A2 -. 验证 .-> D1
    B2 -. 集成 .-> D1
    D3 -. 输入 .-> F2
    E3 -. 输出 .-> A2

改进架构概览

改进计划遵循渐进式实现策略,每个阶段的输出为下一阶段提供基础:

  1. 阶段一建立测试基础设施和用户体验改进
  2. 阶段二完善编译器核心功能
  3. 阶段三添加高级特性和性能监控

Components and Interfaces

1. 端到端测试框架

职责

  • 编译测试 C 源文件并验证生成的可执行文件
  • 管理测试用例输入和预期输出
  • 报告测试通过/失败状态

接口

public interface IE2ETestRunner
{
    Task<TestResult> RunTestAsync(TestCase testCase);
    IEnumerable<TestCase> LoadTestsFromDirectory(string directory);
}

public record TestCase(
    string Name,
    string SourceCode,
    int ExpectedExitCode,
    string? ExpectedOutput = null
);

public record TestResult(
    string TestCaseName,
    bool Passed,
    int ActualExitCode,
    string? ActualOutput = null,
    string? ErrorMessage = null
);

实现策略

  • 使用 CompilerDriver 编译源代码到临时 ELF 文件
  • 使用 Process 类执行生成的可执行文件
  • 比较实际输出/退出码与预期值

2. 错误报告增强

职责

  • 格式化错误信息,包含代码上下文
  • 生成可视化错误位置标记
  • 支持多错误汇总输出

接口扩展

public record ErrorInfo(
    ErrorLevel Level,
    string Message,
    SourceLocation Location,
    string? SourceLine = null,        // 新增:出错的源代码行
    int? ColumnOffset = null,         // 新增:错误在行内的偏移
    string? Suggestion = null         // 新增:修复建议
);

public sealed class ErrorReporter : IErrorReporter
{
    private readonly List<ErrorInfo> _errors = new();
    private readonly Dictionary<string, string[]> _sourceCache = new(); // 新增:源代码缓存
    
    public void Report(ErrorInfo error);
    public void SetSourceLines(string fileName, string[] lines); // 新增:设置源代码行
    public string FormatErrors(); // 新增:格式化所有错误
}

格式化输出示例

test.c:3:5: error: expected ';' before 'return'
  2 | int add(int a, int b) {
  3 |     int x = a + b
    |          ^^^^^^^^
  4 |     return x;
  |           ~~~~~
  help: add ';' at the end of the statement

3. 语义分析器完善

职责扩展

  • 实现完整的类型检查系统
  • 支持嵌套作用域管理
  • 检测函数签名不匹配

新增组件

public sealed class TypeChecker
{
    public CType? CheckBinaryOperation(TokenType op, CType left, CType right, SourceLocation loc);
    public CType? CheckUnaryOperation(TokenType op, CType operand, SourceLocation loc);
    public bool IsCompatible(CType source, CType target);
    public CType? PromoteType(CType type); // 类型提升
}

public sealed class ScopeManager
{
    private readonly Stack<Dictionary<string, Symbol>> _scopes = new();
    
    public void EnterScope();
    public void ExitScope();
    public void DeclareSymbol(string name, Symbol symbol);
    public Symbol? LookupSymbol(string name);
    public bool IsDeclared(string name);
}

类型检查规则

操作 左操作数 右操作数 结果类型
算术运算 整数/浮点 整数/浮点 提升后的类型
比较运算 数值类型 数值类型 int
赋值 类型 T 类型 S TS 必须可转换为 T

4. 预处理器集成

职责

  • 处理 #include#define、条件编译
  • 管理头文件搜索路径
  • 宏展开和参数替换

接口

public interface IPreprocessor
{
    string Preprocess(string sourceCode, string sourceFile);
    void AddIncludePath(string path);
    void DefineMacro(string name, string? value);
    void UndefineMacro(string name);
}

public sealed class Macro
{
    public string Name { get; }
    public string? Value { get; }
    public List<string>? Parameters { get; } // 函数宏参数
    public string? Body { get; }
}

集成到 CompilerDriver

// 在 CompilerDriver.Compile 中
var preprocessor = new Preprocessor(_errorReporter);
foreach (var includePath in options.IncludePaths)
{
    preprocessor.AddIncludePath(includePath);
}
var preprocessedSource = preprocessor.Preprocess(options.SourceFile);
var lexer = new Lexer(preprocessedSource, options.SourceFile, _errorReporter);

5. 代码生成优化

职责

  • 实现图着色寄存器分配
  • 指令选择和调度
  • 栈帧布局优化

寄存器分配器接口

public sealed class GraphColoringAllocator
{
    private readonly Dictionary<IrValue, HashSet<IrValue>> _interferenceGraph = new();
    private readonly Dictionary<IrValue, string> _allocation = new();
    private readonly HashSet<IrValue> _spilledVars = new();
    
    public void Allocate(IrFunction function, string[] availableRegs);
    public string? GetRegister(IrValue value);
    public bool IsSpilled(IrValue value);
}

优化验证策略

  • 比较优化前后生成的机器码长度
  • 验证优化后程序的执行结果正确性
  • 测量溢出变量数量

6. DWARF 调试信息生成器

职责

  • 生成 DWARF 调试信息节
  • 编码源文件路径和行号映射
  • 生成变量和类型调试信息

接口

public sealed class DwarfGenerator
{
    private readonly List<DwarfInfo> _debugInfo = new();
    private readonly List<DwarfLine> _lineTable = new();
    
    public void AddFile(string fileName);
    public void AddLineEntry(int fileIndex, int line, int address);
    public void AddVariable(string name, CType type, int scopeLevel, int offset);
    public byte[] GenerateDebugSection();
    public byte[] GenerateLineSection();
}

ELF 集成

  • ElfWriter 中添加 .debug_info.debug_line
  • 更新节头表和字符串表

7. PE 写出器完善

职责

  • 生成完整的 PE32+ 文件头
  • 创建 .text.data
  • 处理重定位和导入表

PE 文件结构

DOS Header (64 bytes)
PE Signature ("PE\0\0")
COFF File Header (20 bytes)
Optional Header (PE32+, 112 bytes)
Data Directories (16 entries)
Section Headers (40 bytes per section)
.text Section (代码)
.data Section (数据)

8. 性能基准测试框架

职责

  • 测量编译时间
  • 测量生成代码执行时间
  • 生成统计报告

接口

public sealed class BenchmarkRunner
{
    public BenchmarkResult RunCompilationBenchmark(string sourceFile, int iterations = 10);
    public BenchmarkResult RunExecutionBenchmark(string executable, int iterations = 100);
}

public record BenchmarkResult(
    string TestName,
    double MeanTimeMs,
    double MedianTimeMs,
    double StdDevMs,
    int Iterations
);

Data Models

错误信息模型(增强)

public enum ErrorLevel
{
    Warning,
    Error,
    Fatal
}

public readonly struct SourceLocation
{
    public string FileName { get; }
    public int Line { get; }
    public int Column { get; }
    public int Length { get; } // 新增:错误跨度
}

测试用例模型

public record TestCase(
    string Name,
    string SourceCode,
    int ExpectedExitCode,
    string? ExpectedOutput = null,
    string? ExpectedErrorPattern = null // 期望的错误模式
);

DWARF 调试信息模型

public record DwarfInfoEntry(
    uint Offset,
    uint AbbrevCode,
    Dictionary<uint, object> Attributes
);

public record DwarfLineEntry(
    int Address,
    int FileIndex,
    int Line,
    int Column,
    bool IsStatement,
    bool IsEndOfSequence
);

Correctness Properties

不变量

  1. 测试覆盖完整性: 每个 C 语言特性至少有一个 E2E 测试用例
  2. 错误信息准确性: 错误位置标记必须指向正确的源代码行和列
  3. 类型检查健全性: 类型检查必须拒绝所有类型错误的程序
  4. 寄存器分配正确性: 分配的寄存器不能干涉活跃变量
  5. 调试信息一致性: 调试信息中的行号必须与实际代码位置匹配

约束条件

  1. E2E 测试必须在 Linux x64 环境下运行
  2. 错误报告格式化器必须处理多字节字符
  3. 寄存器分配器必须遵循 System V AMD64 ABI
  4. DWARF 信息必须兼容 gdb 7.0+
  5. PE 文件必须兼容 Windows 10+ 加载器

Error Handling

错误场景与处理策略

错误场景 检测阶段 处理方式
头文件不存在 预处理 报告错误,提供搜索路径
宏重复定义 预处理 报告警告,使用新定义
未声明变量 语义分析 报告错误,标记位置
类型不匹配 语义分析 报告错误,显示期望和实际类型
寄存器溢出 代码生成 溢出到栈,更新栈帧布局
DWARF 编码失败 调试信息生成 报告错误,继续编译
PE 头生成失败 目标文件写入 报告错误,终止编译

错误恢复策略

  • 词法/语法错误: 尝试跳过错误 token继续解析
  • 语义错误: 收集所有错误,一次性输出
  • 代码生成错误: 立即终止,报告详细错误信息

Test Strategy

单元测试

  1. 错误报告测试: 验证错误信息格式化和代码上下文显示
  2. 类型检查测试: 验证各种类型场景的检查逻辑
  3. 寄存器分配测试: 验证图着色算法正确性
  4. DWARF 编码测试: 验证调试信息编码正确性

集成测试

  1. 端到端测试: 编译并运行测试 C 程序,验证输出
  2. 预处理器集成测试: 验证宏展开和头文件包含
  3. PE 格式测试: 在 Windows 环境验证生成的 PE 文件

E2E 测试用例集合

// test_arithmetic.c - 算术运算测试
int add(int a, int b) { return a + b; }
int main() { return add(3, 4) == 7 ? 0 : 1; }

// test_control_flow.c - 控制流测试
int main() {
    int sum = 0;
    for (int i = 1; i <= 10; i++) sum += i;
    return sum == 55 ? 0 : 1;
}

// test_functions.c - 函数调用测试
int factorial(int n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}
int main() { return factorial(5) == 120 ? 0 : 1; }

// test_pointers.c - 指针测试
int main() {
    int x = 42;
    int *p = &x;
    return *p == 42 ? 0 : 1;
}

// test_arrays.c - 数组测试
int main() {
    int arr[3] = {1, 2, 3};
    return arr[1] == 2 ? 0 : 1;
}

// test_macro.c - 宏测试
#define MAX(a, b) ((a) > (b) ? (a) : (b))
int main() { return MAX(3, 5) == 5 ? 0 : 1; }

性能基准测试

  • 编译时间基准: 测量编译标准 C 文件的时间(如 factorial.c, sort.c
  • 执行时间基准: 测量生成代码执行时间,与 gcc/clang 对比
  • 内存使用基准: 测量编译过程中的内存峰值

References