0
0
Lập trình
Admin Team
Admin Teamtechmely

Biên dịch LLVM và Chạy Pass Đầu Tiên của Bạn

Đăng vào 1 tháng trước

• 5 phút đọc

Mục Lục

  1. Giới thiệu
  2. Yêu cầu và Thiết lập Môi trường
  3. Bước 1: Biên dịch LLVM từ Mã nguồn
  4. Bước 2: Viết Pass Hello World cho LLVM
  5. Bước 3: Biên dịch Pass
  6. Bước 4: Chạy Pass
  7. Kết luận
  8. Câu hỏi thường gặp

Giới thiệu

Nếu bạn từng tò mò về cách hoạt động của các trình biên dịch, LLVM là một trong những sân chơi tốt nhất mà bạn có thể tìm thấy. Nó mạnh mẽ, linh hoạt và được sử dụng rộng rãi từ các ngôn ngữ lập trình đến các trình điều khiển GPU. Tuy nhiên, LLVM khá lớn và việc bắt đầu có thể khiến bạn cảm thấy choáng ngợp.

Một điểm khởi đầu tuyệt vời là với các pass của LLVM. Chúng là các khối xây dựng của tối ưu hóa trình biên dịch và phân tích chương trình. Bằng cách tạo ra các pass của riêng bạn và thực nghiệm với IR, bạn sẽ nhanh chóng nắm bắt được những khái niệm cốt lõi làm cho LLVM hoạt động.

Bài viết này sẽ hướng dẫn bạn từ việc xây dựng LLVM đến việc chạy một pass "hello world" đơn giản — bước đầu tiên của bạn vào thế giới nội bộ của trình biên dịch.

Yêu cầu và Thiết lập Môi trường

Trước khi bắt đầu, hãy đảm bảo rằng bạn đã cài đặt các công cụ cần thiết. Trên Ubuntu/Debian, bạn có thể cài đặt chúng bằng lệnh sau:

bash Copy
sudo apt -y install gcc g++ git cmake ccache ninja-build zlib1g-dev

Chúng ta cũng cần Git để clone mã nguồn của LLVM, và Ccache để giảm thời gian biên dịch của LLVM.

Bước 1: Biên dịch LLVM từ Mã nguồn

Thực hiện theo các bước sau để clone, cấu hình và biên dịch LLVM (dựa trên hướng dẫn chính thức của LLVM và kinh nghiệm của chúng tôi):

bash Copy
git clone https://github.com/llvm/llvm-project.git
cd llvm-project/
git checkout -b llvm-17 llvmorg-17.0.1

mkdir build
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \
      -DCMAKE_EXPORT_COMPILE_COMMANDS=TRUE \
      -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
      -DLLVM_ENABLE_PROJECTS=clang \
      -DCMAKE_INSTALL_PREFIX=/opt/llvm \
      -B build -S llvm

cmake --build build -j$(nproc)
cmake --build build --target check-all -j$(nproc)

👉 check-all chạy bộ kiểm tra của LLVM. Nếu tất cả các bài kiểm tra đều vượt qua, bạn đã sẵn sàng.
👉 -DBUILD_SHARED_LIBS giúp tránh các vấn đề khi chạy pass của bạn với opt.

Để cài đặt LLVM vào /opt/llvm, hãy chạy:

bash Copy
cmake --install build

Tại thời điểm này, bạn đã có một phiên bản LLVM + Clang hoạt động.

Bước 2: Viết Pass Hello World cho LLVM

Bây giờ, hãy viết một dummy pass đơn giản. Pass này sẽ không thay đổi chương trình — nó chỉ in ra một thông điệp mỗi khi nó chạy.

Tạo một tệp mới hellopass.cpp với nội dung sau:

cpp Copy
#include "llvm/IR/Function.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/raw_ostream.h"

using namespace llvm;

namespace {
struct HelloWorldPass : PassInfoMixin<HelloWorldPass> {
  PreservedAnalyses run(Function &F, FunctionAnalysisManager &) {
    errs() << "Hello from: " << F.getName() << "\n";
    return PreservedAnalyses::all();
  }
};
} // namespace

// Đăng ký pass
extern "C" ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() {
  return {
    LLVM_PLUGIN_API_VERSION, "HelloWorldPass", LLVM_VERSION_STRING,
    [](PassBuilder &PB) {
      PB.registerPipelineParsingCallback(
        [](StringRef Name, FunctionPassManager &FPM,
           ArrayRef<PassBuilder::PipelineElement>) {
          if (Name == "hello-world") {
            FPM.addPass(HelloWorldPass());
            return true;
          }
          return false;
        });
    }
  };
}

Các bài viết sau sẽ đi sâu vào giải thích về các pass. Hãy nhớ rằng, về cơ bản, chúng ta có hai "phần/trách nhiệm" trong đoạn mã trên: Logic của pass và việc đăng ký nó. Phương thức run là nơi logic của bạn xảy ra. Ở đây, chúng ta chỉ in ra tên hàm.

Bước 3: Biên dịch Pass

Sử dụng CMake để biên dịch pass thành một thư viện chia sẻ. Tạo một tệp CMakeLists.txt:

cmake Copy
cmake_minimum_required(VERSION 3.13)

project(HelloPass LANGUAGES CXX)

find_package(LLVM REQUIRED CONFIG)

include_directories(${LLVM_INCLUDE_DIRS})
add_definitions(${LLVM_DEFINITIONS})
add_library(HelloPass MODULE hellopass.cpp)
# Đảm bảo sử dụng C++17
set_target_properties(HelloPass PROPERTIES CXX_STANDARD 17 
                      CXX_STANDARD_REQUIRED YES)

Sau đó biên dịch:

bash Copy
mkdir build && cd build
cmake -DLLVM_DIR=/opt/llvm/lib/cmake/llvm -DCMAKE_CXX_COMPILER='clang++' ..
cmake --build .

Kết quả sẽ tạo ra libHelloPass.so.

Bước 4: Chạy Pass

Bây giờ, hãy biên dịch một tệp C++ đơn giản thành LLVM IR:

cpp Copy
// test.cc
int add(int a, int b) {
   return a + b;
}

Biên dịch thành LLVM IR:

bash Copy
/opt/llvm/bin/clang++ -S -emit-llvm test.cc -o test.ll

Chạy pass:

bash Copy
/opt/llvm/bin/opt -load-pass-plugin=./libHelloPass.so -passes=hello-world -disable-output < test.ll

Bạn sẽ thấy đầu ra như sau:

plaintext Copy
Hello from: add

Kết luận

Bạn đã:

  • Biên dịch LLVM từ mã nguồn
  • Thực hiện pass LLVM đầu tiên của bạn

Từ đây, bạn có thể mở rộng pass dummy để phân tích luồng điều khiển, tối ưu hóa các lệnh hoặc thậm chí biến đổi IR. Bước "Hello World" này là cánh cửa vào phát triển trình biên dịch thực sự.

Câu hỏi thường gặp

1. LLVM là gì?
LLVM là một dự án mã nguồn mở cho phép phát triển các trình biên dịch và công cụ tối ưu hóa.

2. Tôi cần kiến thức gì trước khi bắt đầu?
Kiến thức cơ bản về C++ và trình biên dịch là hữu ích.

3. Pass trong LLVM là gì?
Pass là các khối xây dựng của tối ưu hóa và phân tích trong trình biên dịch.

4. Làm thế nào để cài đặt LLVM trên hệ điều hành khác?
Bạn có thể tham khảo tài liệu chính thức của LLVM cho từng hệ điều hành.

5. Tôi có thể phát triển các pass phức tạp hơn không?
Có, bạn có thể mở rộng và phát triển các pass với nhiều chức năng hơn.

Thực hành tốt nhất

  • Đảm bảo sử dụng phiên bản LLVM mới nhất để tránh lỗi.
  • Tạo ra nhiều test case để kiểm tra pass của bạn.
  • Tham gia vào cộng đồng LLVM để học hỏi và chia sẻ kinh nghiệm.

Cạm bẫy phổ biến

  • Không kiểm tra các lỗi biên dịch có thể xảy ra khi viết pass.
  • Quên đăng ký pass của bạn đúng cách, dẫn đến không chạy được.

Mẹo hiệu suất

  • Sử dụng ccache để giảm thời gian biên dịch.
  • Tối ưu hóa mã của bạn bằng cách sử dụng các công cụ phân tích hiệu suất để tìm các điểm nghẽn.

Khắc phục sự cố

  • Nếu pass không chạy, kiểm tra lại đăng ký pass và cách bạn biên dịch nó.
  • Kiểm tra các thông báo lỗi từ LLVM để hiểu rõ hơn về nguyên nhân.

Tài nguyên tham khảo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào