点击上方蓝字 江湖评谈设为关注/星标
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.rs
fn 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.rs
pub 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.rs
pub 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.rs
pub(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.rs
unsafe fn optimize(
cgcx: &CodegenContext<Self>,
dcx: DiagCtxtHandle<'_>,
module: &ModuleCodegen<Self::Module>,
config: &ModuleConfig,
-> Result<(), FatalError> {
unsafe { back::write::optimize(cgcx, dcx, module, config) }
}
:write::optimize表示生成选项或者生成配置 :
最后的codegen调用了llvm API
//rust/compiler/rustc_codegen_llvm/src/allocator.rs
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是个较为不错的工程级模块化项目。
往期精彩回顾