mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
Auto merge of #1884 - DrMeepster:start, r=RalfJung
add support for `#[start]` This PR adds support for the `#[start]` attribute and fixes #1825. It also renames `eval_main` to `eval_entry` to reflect that it can evaluate any entry function.
This commit is contained in:
commit
1cc822e6af
@ -20,11 +20,12 @@ impl rustc_driver::Callbacks for MiriCompilerCalls<'_> {
|
|||||||
compiler.session().abort_if_errors();
|
compiler.session().abort_if_errors();
|
||||||
|
|
||||||
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
|
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(|| {
|
self.bencher.iter(|| {
|
||||||
let config = miri::MiriConfig::default();
|
let config = miri::MiriConfig::default();
|
||||||
miri::eval_main(tcx, entry_def_id, config);
|
miri::eval_entry(tcx, entry_def_id, entry_type, config);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -58,8 +58,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
|||||||
|
|
||||||
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
|
queries.global_ctxt().unwrap().peek_mut().enter(|tcx| {
|
||||||
init_late_loggers(tcx);
|
init_late_loggers(tcx);
|
||||||
let (entry_def_id, _) = if let Some((entry_def, x)) = tcx.entry_fn(()) {
|
let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) {
|
||||||
(entry_def, x)
|
entry_def
|
||||||
} else {
|
} else {
|
||||||
let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(
|
let output_ty = ErrorOutputType::HumanReadable(HumanReadableErrorType::Default(
|
||||||
ColorConfig::Auto,
|
ColorConfig::Auto,
|
||||||
@ -79,7 +79,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
|
|||||||
env::set_current_dir(cwd).unwrap();
|
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(
|
std::process::exit(
|
||||||
i32::try_from(return_code).expect("Return value was too large!"),
|
i32::try_from(return_code).expect("Return value was too large!"),
|
||||||
);
|
);
|
||||||
|
80
src/eval.rs
80
src/eval.rs
@ -13,6 +13,8 @@ use rustc_middle::ty::{
|
|||||||
};
|
};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
|
|
||||||
|
use rustc_session::config::EntryFnType;
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
@ -120,12 +122,13 @@ impl Default for MiriConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a freshly created `InterpCx`, along with an `MPlaceTy` representing
|
/// 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.
|
/// written to.
|
||||||
/// Public because this is also used by `priroda`.
|
/// Public because this is also used by `priroda`.
|
||||||
pub fn create_ecx<'mir, 'tcx: 'mir>(
|
pub fn create_ecx<'mir, 'tcx: 'mir>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
main_id: DefId,
|
entry_id: DefId,
|
||||||
|
entry_type: EntryFnType,
|
||||||
config: MiriConfig,
|
config: MiriConfig,
|
||||||
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Tag>)> {
|
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Tag>)> {
|
||||||
let param_env = ty::ParamEnv::reveal_all();
|
let param_env = ty::ParamEnv::reveal_all();
|
||||||
@ -148,26 +151,10 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup first stack-frame
|
// Setup first stack-frame
|
||||||
let main_instance = ty::Instance::mono(tcx, main_id);
|
let entry_instance = ty::Instance::mono(tcx, entry_id);
|
||||||
let main_mir = ecx.load_mir(main_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();
|
// First argument is constructed later, because its skipped if the entry function uses #[start]
|
||||||
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: pointer to `main()`.
|
|
||||||
let main_ptr = ecx.memory.create_fn_alloc(FnVal::Instance(main_instance));
|
|
||||||
// Second argument (argc): length of `config.args`.
|
// Second argument (argc): length of `config.args`.
|
||||||
let argc = Scalar::from_machine_usize(u64::try_from(config.args.len()).unwrap(), &ecx);
|
let argc = Scalar::from_machine_usize(u64::try_from(config.args.len()).unwrap(), &ecx);
|
||||||
// Third argument (`argv`): created from `config.args`.
|
// Third argument (`argv`): created from `config.args`.
|
||||||
@ -243,25 +230,58 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
|
|||||||
// Return place (in static memory so that it does not count as leak).
|
// 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())?;
|
let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
|
||||||
// Call start function.
|
// Call start function.
|
||||||
ecx.call_function(
|
|
||||||
start_instance,
|
match entry_type {
|
||||||
Abi::Rust,
|
EntryFnType::Main => {
|
||||||
&[Scalar::from_pointer(main_ptr, &ecx).into(), argc.into(), argv],
|
let start_id = tcx.lang_items().start_fn().unwrap();
|
||||||
Some(&ret_place.into()),
|
let main_ret_ty = tcx.fn_sig(entry_id).output();
|
||||||
StackPopCleanup::None { cleanup: true },
|
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))
|
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 `Some(return_code)` if program executed completed.
|
||||||
/// Returns `None` if an evaluation error occured.
|
/// 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`.
|
// Copy setting before we move `config`.
|
||||||
let ignore_leaks = config.ignore_leaks;
|
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,
|
Ok(v) => v,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
err.print_backtrace();
|
err.print_backtrace();
|
||||||
|
@ -60,7 +60,7 @@ pub use crate::diagnostics::{
|
|||||||
NonHaltingDiagnostic, TerminationInfo,
|
NonHaltingDiagnostic, TerminationInfo,
|
||||||
};
|
};
|
||||||
pub use crate::eval::{
|
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::helpers::EvalContextExt as HelpersEvalContextExt;
|
||||||
pub use crate::machine::{
|
pub use crate::machine::{
|
||||||
|
8
tests/run-pass/start.rs
Normal file
8
tests/run-pass/start.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#![feature(start)]
|
||||||
|
|
||||||
|
#[start]
|
||||||
|
fn start(_: isize, _: *const *const u8) -> isize {
|
||||||
|
println!("Hello from start!");
|
||||||
|
|
||||||
|
0
|
||||||
|
}
|
1
tests/run-pass/start.stdout
Normal file
1
tests/run-pass/start.stdout
Normal file
@ -0,0 +1 @@
|
|||||||
|
Hello from start!
|
Loading…
Reference in New Issue
Block a user