Skip to content

add support for #[start] #1884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions benches/helpers/miri_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});

Expand Down
6 changes: 3 additions & 3 deletions src/bin/miri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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!"),
);
Expand Down
80 changes: 50 additions & 30 deletions src/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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();
Expand All @@ -145,26 +148,10 @@ 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)?;
if main_mir.arg_count != 0 {
bug!("main function must not take any arguments");
}
let entry_instance = ty::Instance::mono(tcx, entry_id);

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`.
Expand Down Expand Up @@ -240,25 +227,58 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
// 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();
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down
8 changes: 8 additions & 0 deletions tests/run-pass/start.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![feature(start)]

#[start]
fn start(_: isize, _: *const *const u8) -> isize {
println!("Hello from start!");

0
}
1 change: 1 addition & 0 deletions tests/run-pass/start.stdout
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello from start!