From 84b058ac47e2ea5f29887a4ed5ae286e37b22194 Mon Sep 17 00:00:00 2001
From: DrMeepster <19316085+DrMeepster@users.noreply.github.com>
Date: Thu, 2 Sep 2021 15:41:10 -0700
Subject: [PATCH] add support for #[start]

---
 benches/helpers/miri_helper.rs |  5 +-
 src/bin/miri.rs                |  6 +--
 src/eval.rs                    | 90 +++++++++++++++++++++++-----------
 src/lib.rs                     |  2 +-
 tests/run-pass/start.rs        |  8 +++
 tests/run-pass/start.stdout    |  1 +
 6 files changed, 78 insertions(+), 34 deletions(-)
 create mode 100644 tests/run-pass/start.rs
 create mode 100644 tests/run-pass/start.stdout

diff --git a/benches/helpers/miri_helper.rs b/benches/helpers/miri_helper.rs
index 2d27616a361..be542c2bc0a 100644
--- a/benches/helpers/miri_helper.rs
+++ b/benches/helpers/miri_helper.rs
@@ -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);
             });
         });
 
diff --git a/src/bin/miri.rs b/src/bin/miri.rs
index 18c393815ca..fbc87148ec7 100644
--- a/src/bin/miri.rs
+++ b/src/bin/miri.rs
@@ -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!"),
                 );
diff --git a/src/eval.rs b/src/eval.rs
index 2b8fc0f5ade..f90a77fafd5 100644
--- a/src/eval.rs
+++ b/src/eval.rs
@@ -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();
diff --git a/src/lib.rs b/src/lib.rs
index f8d8aacce3c..7ed915b6d1d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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::{
diff --git a/tests/run-pass/start.rs b/tests/run-pass/start.rs
new file mode 100644
index 00000000000..f25d62fa8c3
--- /dev/null
+++ b/tests/run-pass/start.rs
@@ -0,0 +1,8 @@
+#![feature(start)]
+
+#[start]
+fn start(_: isize, _: *const *const u8) -> isize {
+    println!("Hello from start!");
+
+    0
+}
diff --git a/tests/run-pass/start.stdout b/tests/run-pass/start.stdout
new file mode 100644
index 00000000000..d7f627d237c
--- /dev/null
+++ b/tests/run-pass/start.stdout
@@ -0,0 +1 @@
+Hello from start!