Rust编译器深入

文摘   2024-08-20 10:42   湖北  

点击上方蓝字 江湖评谈设为关注/星标




Main

通过前面几篇的了解,Rustc调用了三个main入口,构成了整个Rustc编译器的关键部分。分别为glibc-main,以及glibc-main入口处变量rustc-main(register rax),最后一个就是compiler-main。一C-main,两Rust-main。下面分析下。

这三个main顾名思义,第一个是glibc启动rust编译器的main。它的作用主要是里面赋值了一个rustc-main的地址,以及调用了lang_start_internal(rust/library/std/src/rt.rs:120)。lang_start_internal通过无参闭包,panic,catch_unwind,map_err等等进行安全性的处理,确保万无一失的调用main,为了rust安全?lang_start_internal至少体现了这一点。通过前面的文章(本公众号:jianghupt的之前文章,可参考)了解到了被cargo/rustc编译出来的独立可执行二进制文件,实际上也是通过lang_start_internal来进行调用运行的。

那么这里说明下,lang_start_internal不仅是rustc的glibc-main到rust-main的过程安全调用护航,同时也是rust complie出来之后的最终可执行文件的过程安全调用护航。

rustc-main,如果说glic-main是rustc承接系统与rust的链接,这里的main才是rustc编译器的真正入口点。它主要也是做了两件事情,其一它调用了rustc编译的入口main。其二它进行了一些条件编译,比如Cargo.toml 文件中启用了 jemalloc 特性,它就会进行一些库函数调用和变量初始化:

//rust/compiler/rustc/src/main.rsfn main() {    // See the comment at the top of this file for an explanation of this.    #[cfg(feature = "jemalloc")]    {        use std::os::raw::{c_int, c_void};
#[used] static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc; #[used] static _F2: unsafe extern "C" fn(*mut *mut c_void, usize, usize) -> c_int =            jemalloc_sys::posix_memalign;        //此处省略部分        }    rustc_driver::main()}

最后的第三个compiler-main,它是rustc编译的入口:

//rust/compiler/rustc_driver_impl/src/lib.rspub fn main() -> ! {    let start_time = Instant::now();    let start_rss = get_resident_set_size();
let early_dcx = EarlyDiagCtxt::new(ErrorOutputType::default());
init_rustc_env_logger(&early_dcx); signal_handler::install(); let mut callbacks = TimePassesCallbacks::default(); let using_internal_features = install_ice_hook(DEFAULT_BUG_REPORT_URL, |_| ()); install_ctrlc_handler();
let exit_code = catch_with_exit_code(|| { RunCompiler::new(&args::raw_args(&early_dcx)?, &mut callbacks) .set_using_internal_features(using_internal_features) .run() });
if let Some(format) = callbacks.time_passes { let end_rss = get_resident_set_size(); print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss, format); }
process::exit(exit_code)}

首先实例化一个RunCompiler,然后设置下属性,最后调用run进行rust源码编译。

LLVM

rust后端是llvm生成的,这种高度契合了rust的工程化写法,看着简洁舒适。初始化入口如下:

//rust/compiler/rustc_llvm/src/lib.rspub fn initialize_available_targets() {    macro_rules! init_target(        ($cfg:meta, $($method:ident),*) => { {            #[cfg($cfg)]            fn init() {                extern "C" {                    $(fn $method();)*                }                unsafe {                    $($method();)*                }            }            #[cfg(not($cfg))]            fn init() { }            init();        } }    );

可以通过一个例子看下这种语法:

macro_rules! init_target(    ($cfg:meta, $($method:ident),*) => { {        #[cfg($cfg)]        fn init() {            extern "C" {                $(fn $method();)*            }            unsafe {                $($method();)*            }        }        #[cfg(not($cfg))]        fn init() { }        init();    } });

//下面是调用:pub fn initialize_available_targets() {// 初始化 x86_64 目标架构init_target!(target_arch = "x86_64", _rjem_init_x86_64);// 初始化 armv7 目标架构init_target!(target_arch = "arm", _rjem_init_arm, _rjem_init_armv7);}

initialize_available_targets实际上有N多个参数,如下:

//rust/compiler/rustc_llvm/src/lib.rs   init_target!(        llvm_component = "x86",        LLVMInitializeX86TargetInfo,        LLVMInitializeX86Target,        LLVMInitializeX86TargetMC,        LLVMInitializeX86AsmPrinter,        LLVMInitializeX86AsmParser    );    init_target!(        llvm_component = "arm",        LLVMInitializeARMTargetInfo,        LLVMInitializeARMTarget,        LLVMInitializeARMTargetMC,        LLVMInitializeARMAsmPrinter,        LLVMInitializeARMAsmParser    );        ...................后面省略N多

调用initialize_available_targets是llvm init也即初始化的时候configure_llvm里面Call的

//rust/compiler/rustc_codegen_llvm/src/llvm_util.rspub(crate) fn init(sess: &Session) {    unsafe {        // Before we touch LLVM, make sure that multithreading is enabled.        if llvm::LLVMIsMultithreaded() != 1 {            bug!("LLVM compiled without support for threads");        }        INIT.call_once(|| {            configure_llvm(sess);        });    }}

llvm init

 //rust/compiler/rustc_codegen_llvm/src/lib.rs    fn init(&self, sess: &Session) {        llvm_util::init(sess); // Make sure llvm is inited    }

生成Machine Code Place

 //rust/compiler/rustc_codegen_llvm/src/lib.rs  unsafe fn codegen(        cgcx: &CodegenContext<Self>,        dcx: DiagCtxtHandle<'_>,        module: ModuleCodegen<Self::Module>,        config: &ModuleConfig,    ) -> Result<CompiledModule, FatalError> {        unsafe { back::write::codegen(cgcx, dcx, module, config) }    }

其实我们可以看到lib.rs里面的模块化设计和函数的顾名思义,比如以下:

 //rust/compiler/rustc_codegen_llvm/src/lib.rsunsafe fn optimize(        cgcx: &CodegenContext<Self>,        dcx: DiagCtxtHandle<'_>,        module: &ModuleCodegen<Self::Module>,        config: &ModuleConfig,    ) -> Result<(), FatalError> {        unsafe { back::write::optimize(cgcx, dcx, module, config) }    }//back::write::optimize表示生成选项或者生成配置

最后的codegen调用了llvm API

//rust/compiler/rustc_codegen_llvm/src/allocator.rspub(crate) unsafe fn codegen(    tcx: TyCtxt<'_>,    module_llvm: &mut ModuleLlvm,    module_name: &str,    kind: AllocatorKind,    alloc_error_handler_kind: AllocatorKind,) {    let llcx = &*module_llvm.llcx;    let llmod = module_llvm.llmod();    let usize = unsafe {        match tcx.sess.target.pointer_width {            16 => llvm::LLVMInt16TypeInContext(llcx),            32 => llvm::LLVMInt32TypeInContext(llcx),            64 => llvm::LLVMInt64TypeInContext(llcx),            tws => bug!("Unsupported target word size for int: {}", tws),        }    };        //此处省略一万行    ....................    ....................  }


结尾

本篇主要是从Rustc着手,总结了下前几篇的缺失地方。总体来说rust+llvm是个较为不错的工程级模块化项目。

往期精彩回顾

Rust语法简析

Rust编译器研究+.NET9 PreView7

Rust编译器+语法探究


江湖评谈
记录,分享,自由。
 最新文章