Project Stage 1: Building and Exploring GCC on AArch64: A Deep Dive into the Build Process and Compilation Dumps

 

Introduction

The goal of this blog is to document my process of building the current development version of GCC on an AArch64 platform, with a particular emphasis on understanding the compilation stages via intermediate representation (IR) dumps. This journey has given me valuable insights into the GCC build system, compilation passes, and optimisation techniques.


Section 1: Preparing the Environment for GCC Build

Objective: Set up the necessary environment and dependencies to build GCC from source on an AArch64 platform.

Steps and Detailed Commands

  1. Install Essential Dependencies: To ensure a successful build, I installed essential libraries and tools required by GCC:

    sudo apt update sudo apt install build-essential libgmp-dev libmpfr-dev libmpc-dev git texinfo bison flex

    Each library supports a specific function:

    • GMP: For arbitrary precision arithmetic.
    • MPFR: For floating-point computations with precise control.
    • MPC: For complex number arithmetic.

    Having these dependencies installed prevents errors during the GCC configuration step.

  2. Cloning the GCC Repository: I chose to clone the official GCC Git repository to get the latest development version:

    git clone git://gcc.gnu.org/git/gcc.git

    This created a directory named gcc containing the GCC source code. Using Git allows me to keep the repository up-to-date with recent commits if needed.

Reflections

Setting up the environment was simple, though I double-checked each library to ensure that all dependencies were properly installed. The installation logs indicated that each package was successfully installed, avoiding potential configuration errors later.


Section 2: Configuring and Building GCC

Objective: Configure GCC to install locally and initiate the build process with optimizations for AArch64.

Configuration

  1. Create a Separate Build Directory: GCC’s documentation recommends using a separate build directory to avoid mixing source and build files:

    mkdir ~/gcc-build cd ~/gcc-build
  2. Run the Configure Script: The following configuration command sets up GCC for a personal, non-system installation:

    ../gcc/configure --prefix=$HOME/gcc-install --disable-multilib --enable-languages=c,c++
    • --prefix=$HOME/gcc-install: Installs GCC to a local directory, $HOME/gcc-install, isolating it from the system GCC.
    • --disable-multilib: Disables multilib support, focusing on single-architecture builds, which reduces build complexity.
    • --enable-languages=c,c++: Compiles only C and C++ compilers, reducing build time and resource usage.

    During configuration, the system checks for all dependencies and generates a Makefile with tailored build instructions.

  3. Potential Errors and Solutions:

    • Missing Dependencies: If any libraries were missing, the script halted with an error message specifying the missing package. Re-running apt install resolved these issues.
    • Disk Space: Ensure there’s enough disk space (at least 10GB), as the build process generates large intermediate files.

Build Process

  1. Run make to Start the Build: I used the time command to measure the build duration and -j to enable parallel jobs:

    time make -j$(nproc) |& tee build.log
    • -j$(nproc) dynamically sets the number of parallel jobs based on available CPU cores, which optimizes the build time.

    The build took approximately 38 minutes on my AArch64 system, as recorded in build.log.

    • Real time: 38m28s (actual elapsed time)
    • User time: 456m50s (CPU time spent in user mode across all cores)
    • System time: 11m54s (CPU time spent in kernel mode)
  2. Installation: After a successful build, I installed GCC to the specified directory:

    make install
  3. Verification: Adding the new GCC installation to PATH allowed me to verify the build:

    export PATH=$HOME/gcc-install/bin:$PATH gcc --version

    Output:

    gcc (GCC) [version number]

    This confirmed that GCC was installed and functional.

Reflections

The build was time-consuming, particularly on AArch64. Parallel jobs saved time, but memory usage needed to be carefully monitored. Running top on occasion helped to ensure that the system was not overloaded, which prevented build errors caused by resource constraints.


Section 3: Generating and Analyzing Dumps During Compilation Passes

Objective: Produce dumps at various compilation stages to examine GCC’s transformation of code from source to optimized machine instructions.

Creating a Test Program

To explore the IR at different compilation stages, I created a small test program, test.c:

// test.c int main() { int x = 5; int y = x * 2; return y; }

This simple program provides a clear view of transformations without excessive complexity.

Generating Dumps

Using GCC’s -fdump-tree-* and -fdump-rtl-* flags, I produced dumps to observe both tree-based and RTL (Register Transfer Language) representations.

  1. Tree Dumps:

    gcc -O2 -fdump-tree-all test.c -o test
    • -fdump-tree-all generates a dump file for each tree pass. Each file represents a stage in which the compiler transforms and optimizes high-level constructs.
    • Example files: test.c.004t.gimple, test.c.012t.optimized, etc.
  2. RTL Dumps:

    gcc -O2 -fdump-rtl-all test.c -o test
    • -fdump-rtl-all creates files for each RTL pass, showing low-level transformations close to the machine level.
    • Example files: test.c.153r.expand, test.c.231r.split1, etc.

Specific Pass Analysis

  1. Tree GIMPLE Pass: In the GIMPLE pass (-fdump-tree-gimple), the code is transformed into a simplified intermediate representation. Here, the program appears in three-address code, which makes optimizations easier to apply.

    Example GIMPLE Dump:

    int x = 5; int y = x * 2; return y;

    Observations: This pass represents code in a standardized form, stripping away high-level syntax in favor of basic operations, preparing it for further optimizations.

  2. RTL Expand Pass: The expand pass (-fdump-rtl-expand) translates GIMPLE into low-level RTL, introducing machine-specific details.

    Example RTL Expand Dump:

    (insn 10 9 11 2 (set (reg:SI 94) (const_int 5 [0x5])) [ ... ]) (insn 12 11 13 2 (set (reg:SI 95) (ashift:SI (reg:SI 94) (const_int 1 [0x1]))) [ ... ])

    Observations: This pass breaks down operations into register transfers and machine instructions, revealing how GCC manages low-level architecture details.

Reflections on Dumps

Analyzing tree and RTL dumps gave me insights into how GCC incrementally transforms code. The transition from GIMPLE to RTL, in particular, highlights how high-level constructs are eventually represented as machine-level instructions. However, understanding the RTL dumps required more research, and I found the GCC Internals Manual to be essential for interpreting specific RTL operations and registers.


Section 4: Reflections and Learning

Throughout this project, I encountered several challenges and learning experiences:

  • Challenges:

    • Build Time: The build process was lengthy, especially on AArch64. Monitoring memory usage with top and managing parallel jobs helped avoid interruptions.
    • Interpreting Dumps: Understanding RTL dumps was initially challenging. The manual provided insights into RTL codes and register usage, but some constructs required additional reading.
  • Learning Points:

    • Compiler Optimization: Observing the various passes and transformations in the dumps demonstrated GCC’s multi-step optimization process, from high-level syntax simplifications to low-level machine code generation.
    • Configuring GCC: This exercise gave me a practical understanding of GCC’s configuration options, which can greatly impact build complexity and performance.
  • Gaps in Knowledge: I still need to further explore specific RTL operations and learn more about how GCC optimizes larger programs. Future steps include experimenting with complex programs and more advanced optimization flags.


Conclusion

Building GCC on AArch64 and exploring its compilation passes has deepened my understanding of compilers. This project has been an engaging dive into GCC’s architecture, and I look forward to continuing my exploration.


Resources:


    Nhận xét

    Bài đăng phổ biến từ blog này

    Project Stage 2 (Part 2): Set Up GCC for My Clone-Pruning Pass

    Project Stage 2 (Part 3): Implementing the Clone-Pruning Logic

    SPO600 - Lab 3: 6502 Program Lab: Inches to Feet Converter