add support for #[start]

This commit is contained in:
DrMeepster 2021-09-02 15:41:10 -07:00
parent 4f1fca7512
commit 84b058ac47
6 changed files with 78 additions and 34 deletions

View File

@ -20,11 +20,12 @@ impl rustc_driver::Callbacks for MiriCompilerCalls<'_> {
compiler.session().abort_if_errors();
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
let (entry_def_id, _) = tcx.entry_fn(()).expect("no main or start function found");
let (entry_def_id, entry_type) =
tcx.entry_fn(()).expect("no main or start function found");
self.bencher.iter(|| {
let config = miri::MiriConfig::default();
miri::eval_main(tcx, entry_def_id, config);
miri::eval_entry(tcx, entry_def_id, entry_type, config);
});
});

View File

@ -58,8 +58,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
init_late_loggers(tcx);
let (entry_def_id, _) = if let Some((entry_def, x)) = tcx.entry_fn(()) {
(entry_def, x)
let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) {
entry_def
} else {
let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(
ColorConfig::Auto,
@ -79,7 +79,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
env::set_current_dir(cwd).unwrap();
}
if let Some(return_code) = miri::eval_main(tcx, entry_def_id, config) {
if let Some(return_code) = miri::eval_entry(tcx, entry_def_id, entry_type, config) {
std::process::exit(
i32::try_from(return_code).expect("Return value was too large!"),
);

View File

@ -10,6 +10,8 @@ use rustc_middle::ty::{self, layout::LayoutCx, TyCtxt};
use rustc_target::abi::LayoutOf;
use rustc_target::spec::abi::Abi;
use rustc_session::config::EntryFnType;
use crate::*;
#[derive(Copy, Clone, Debug, PartialEq)]
@ -117,12 +119,13 @@ impl Default for MiriConfig {
}
/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
/// the location where the return value of the `start` lang item will be
/// the location where the return value of the `start` function will be
/// written to.
/// Public because this is also used by `priroda`.
pub fn create_ecx<'mir, 'tcx: 'mir>(
tcx: TyCtxt<'tcx>,
main_id: DefId,
entry_id: DefId,
entry_type: EntryFnType,
config: MiriConfig,
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Tag>)> {
let param_env = ty::ParamEnv::reveal_all();
@ -145,26 +148,14 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
}
// Setup first stack-frame
let main_instance = ty::Instance::mono(tcx, main_id);
let main_mir = ecx.load_mir(main_instance.def, None)?;
let entry_instance = ty::Instance::mono(tcx, entry_id);
/*let entry_mir = ecx.load_mir(entry_instance.def, None)?;
if main_mir.arg_count != 0 {
bug!("main function must not take any arguments");
}
}*/
let start_id = tcx.lang_items().start_fn().unwrap();
let main_ret_ty = tcx.fn_sig(main_id).output();
let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
let start_instance = ty::Instance::resolve(
tcx,
ty::ParamEnv::reveal_all(),
start_id,
tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(main_ret_ty))),
)
.unwrap()
.unwrap();
// First argument is constructed later, because its skipped if the entry function uses #[start]
// First argument: pointer to `main()`.
let main_ptr = ecx.memory.create_fn_alloc(FnVal::Instance(main_instance));
// Second argument (argc): length of `config.args`.
let argc = Scalar::from_machine_usize(u64::try_from(config.args.len()).unwrap(), &ecx);
// Third argument (`argv`): created from `config.args`.
@ -237,28 +228,71 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
argv
};
/*let args: &[_] = match entry_type {
EntryFnType::Main => {
// First argument: pointer to `main()`.
let main_ptr = ecx.memory.create_fn_alloc(FnVal::Instance(main_instance));
&[Scalar::from_pointer(main_ptr, &ecx).into(), argc.into(), argv]
}
EntryFnType::Start => &[argc.into(), argv],
};*/
// Return place (in static memory so that it does not count as leak).
let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
// Call start function.
ecx.call_function(
start_instance,
Abi::Rust,
&[Scalar::from_pointer(main_ptr, &ecx).into(), argc.into(), argv],
Some(&ret_place.into()),
StackPopCleanup::None { cleanup: true },
)?;
match entry_type {
EntryFnType::Main => {
let start_id = tcx.lang_items().start_fn().unwrap();
let main_ret_ty = tcx.fn_sig(entry_id).output();
let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
let start_instance = ty::Instance::resolve(
tcx,
ty::ParamEnv::reveal_all(),
start_id,
tcx.mk_substs(::std::iter::once(ty::subst::GenericArg::from(main_ret_ty))),
)
.unwrap()
.unwrap();
let main_ptr = ecx.memory.create_fn_alloc(FnVal::Instance(entry_instance));
ecx.call_function(
start_instance,
Abi::Rust,
&[Scalar::from_pointer(main_ptr, &ecx).into(), argc.into(), argv],
Some(&ret_place.into()),
StackPopCleanup::None { cleanup: true },
)?;
}
EntryFnType::Start => {
ecx.call_function(
entry_instance,
Abi::Rust,
&[argc.into(), argv],
Some(&ret_place.into()),
StackPopCleanup::None { cleanup: true },
)?;
}
}
Ok((ecx, ret_place))
}
/// Evaluates the main function specified by `main_id`.
/// Evaluates the entry function specified by `entry_id`.
/// Returns `Some(return_code)` if program executed completed.
/// Returns `None` if an evaluation error occured.
pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) -> Option<i64> {
pub fn eval_entry<'tcx>(
tcx: TyCtxt<'tcx>,
entry_id: DefId,
entry_type: EntryFnType,
config: MiriConfig,
) -> Option<i64> {
// Copy setting before we move `config`.
let ignore_leaks = config.ignore_leaks;
let (mut ecx, ret_place) = match create_ecx(tcx, main_id, config) {
let (mut ecx, ret_place) = match create_ecx(tcx, entry_id, entry_type, config) {
Ok(v) => v,
Err(err) => {
err.print_backtrace();

View File

@ -60,7 +60,7 @@ pub use crate::diagnostics::{
NonHaltingDiagnostic, TerminationInfo,
};
pub use crate::eval::{
create_ecx, eval_main, AlignmentCheck, IsolatedOp, MiriConfig, RejectOpWith,
create_ecx, eval_entry, AlignmentCheck, IsolatedOp, MiriConfig, RejectOpWith,
};
pub use crate::helpers::EvalContextExt as HelpersEvalContextExt;
pub use crate::machine::{

8
tests/run-pass/start.rs Normal file
View File

@ -0,0 +1,8 @@
#![feature(start)]
#[start]
fn start(_: isize, _: *const *const u8) -> isize {
println!("Hello from start!");
0
}

View File

@ -0,0 +1 @@
Hello from start!