From 6ad0d936d9346d5deaacf03175faf19651a5766b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 26 Dec 2013 13:11:10 -0800 Subject: [PATCH] Implement the #[boot] attribute Before this patch there was no ability to link to both libnative and libgreen and specify which should be used to boot the main function. This commit moves this logic into a #[boot] attribute which can allow control over this. The changes made are: 1. The lang_start function is moved back into libstd with the signature: fn(*u8, *u8, int, *u8) or (main, boot, argc, argv) 2. Functions can now be tagged with #[boot] to indicate that the function can be used to boot a rust crate. This boot function is serialized to metadata. 3. Imports of external modules can also be tagged with #[boot] to indicate that the crate's boot-function should be used to boot the runtime 4. Only one #[boot] function/crate is allowed in an executable, more than one is an error (along with zero as well). 5. The compiler will automatically inject '#[boot] extern mod green;' for now, this can be overrided with #[no_boot]. With this new attribute, control over how a crate boots can be fine-tuned per executable and libraries can specify arbitrary boot functions. The boot function is purely responsible for dictating what context the 'main' function is run in, runtime initialization via rt::init is not the responsibility of the boot function. Finally, the boot function is also responsible for blocking until all rust tasks have exited. --- src/libgreen/lib.rs | 12 ++- src/libnative/lib.rs | 16 ++-- src/libnative/task.rs | 8 +- src/librustc/driver/driver.rs | 4 + src/librustc/driver/session.rs | 14 +-- src/librustc/front/std_inject.rs | 31 ++++--- src/librustc/metadata/common.rs | 2 + src/librustc/metadata/csearch.rs | 5 + src/librustc/metadata/decoder.rs | 6 ++ src/librustc/metadata/encoder.rs | 19 ++++ src/librustc/middle/entry.rs | 107 +++++++++++++++++++-- src/librustc/middle/lint.rs | 4 +- src/librustc/middle/trans/base.rs | 134 +++++++++++++-------------- src/librustc/middle/trans/foreign.rs | 3 - src/libstd/unstable/lang.rs | 16 ++++ src/test/compile-fail/boot-1.rs | 15 +++ src/test/compile-fail/boot-2.rs | 19 ++++ src/test/compile-fail/boot-3.rs | 22 +++++ src/test/compile-fail/boot-4.rs | 19 ++++ src/test/run-pass/boot-1.rs | 16 ++++ src/test/run-pass/boot-2.rs | 29 ++++++ src/test/run-pass/boot-3.rs | 30 ++++++ src/test/run-pass/boot-4.rs | 15 +++ 23 files changed, 430 insertions(+), 116 deletions(-) create mode 100644 src/test/compile-fail/boot-1.rs create mode 100644 src/test/compile-fail/boot-2.rs create mode 100644 src/test/compile-fail/boot-3.rs create mode 100644 src/test/compile-fail/boot-4.rs create mode 100644 src/test/run-pass/boot-1.rs create mode 100644 src/test/run-pass/boot-2.rs create mode 100644 src/test/run-pass/boot-3.rs create mode 100644 src/test/run-pass/boot-4.rs diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs index d7e94c6d48eba..ff35141530b9d 100644 --- a/src/libgreen/lib.rs +++ b/src/libgreen/lib.rs @@ -56,8 +56,18 @@ pub mod sleeper_list; pub mod stack; pub mod task; +#[boot] +#[cfg(not(stage0))] +pub fn boot(main: fn()) { + simple::task().run(|| { + do run { + main(); + }; + }); +} + #[lang = "start"] -#[cfg(not(test))] +#[cfg(not(test), stage0)] pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { use std::cast; do start(argc, argv) { diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs index 80eb8c00cfdf7..fcd2df4559957 100644 --- a/src/libnative/lib.rs +++ b/src/libnative/lib.rs @@ -33,27 +33,23 @@ mod bookeeping; pub mod io; pub mod task; -// XXX: this should not exist here -#[cfg(stage0, nativestart)] -#[lang = "start"] -pub fn lang_start(main: *u8, argc: int, argv: **u8) -> int { - use std::cast; - use std::task; - - do start(argc, argv) { +#[cfg(not(stage0))] +#[boot] +pub fn boot(main: fn()) { + task::new().run(||{ + use std::task; // Instead of invoking main directly on this thread, invoke it on // another spawned thread that we are guaranteed to know the size of the // stack of. Currently, we do not have a method of figuring out the size // of the main thread's stack, so for stack overflow detection to work // we must spawn the task in a subtask which we know the stack size of. - let main: extern "Rust" fn() = unsafe { cast::transmute(main) }; let mut task = task::task(); task.name("
"); match do task.try { main() } { Ok(()) => { os::set_exit_status(0); } Err(..) => { os::set_exit_status(rt::DEFAULT_ERROR_CODE); } } - } + }); } /// Executes the given procedure after initializing the runtime with the given diff --git a/src/libnative/task.rs b/src/libnative/task.rs index 9b6a26291a1c3..f8b8bf5392c68 100644 --- a/src/libnative/task.rs +++ b/src/libnative/task.rs @@ -107,10 +107,10 @@ pub fn spawn_opts(opts: TaskOpts, f: proc()) { // This structure is the glue between channels and the 1:1 scheduling mode. This // structure is allocated once per task. -struct Ops { - lock: Mutex, // native synchronization - awoken: bool, // used to prevent spurious wakeups - io: io::IoFactory, // local I/O factory +pub struct Ops { + priv lock: Mutex, // native synchronization + priv awoken: bool, // used to prevent spurious wakeups + priv io: io::IoFactory, // local I/O factory // This field holds the known bounds of the stack in (lo, hi) form. Not all // native tasks necessarily know their precise bounds, hence this is diff --git a/src/librustc/driver/driver.rs b/src/librustc/driver/driver.rs index 4657d69732354..1dbed2e87b23c 100644 --- a/src/librustc/driver/driver.rs +++ b/src/librustc/driver/driver.rs @@ -248,6 +248,9 @@ pub fn phase_3_run_analysis_passes(sess: Session, time(time_passes, "looking for entry point", (), |_| middle::entry::find_entry_point(sess, crate, ast_map)); + time(time_passes, "looking for boot function", (), + |_| middle::entry::find_boot_fn(sess, crate)); + let freevars = time(time_passes, "freevar finding", (), |_| freevars::annotate_freevars(def_map, crate)); @@ -912,6 +915,7 @@ pub fn build_session_(sopts: @session::options, // For a library crate, this is always none entry_fn: RefCell::new(None), entry_type: Cell::new(None), + boot_fn: Cell::new(None), span_diagnostic: span_diagnostic_handler, filesearch: filesearch, building_library: Cell::new(false), diff --git a/src/librustc/driver/session.rs b/src/librustc/driver/session.rs index 0176c3fa1e06f..40492b3181fb2 100644 --- a/src/librustc/driver/session.rs +++ b/src/librustc/driver/session.rs @@ -17,16 +17,15 @@ use metadata::filesearch; use metadata; use middle::lint; +use syntax; +use syntax::abi; +use syntax::ast; use syntax::attr::AttrMetaMethods; -use syntax::ast::NodeId; -use syntax::ast::{IntTy, UintTy}; use syntax::codemap::Span; +use syntax::codemap; use syntax::diagnostic; use syntax::parse::ParseSess; -use syntax::{ast, codemap}; -use syntax::abi; use syntax::parse::token; -use syntax; use std::cell::{Cell, RefCell}; use std::hashmap::{HashMap,HashSet}; @@ -35,8 +34,8 @@ pub struct config { os: abi::Os, arch: abi::Architecture, target_strs: target_strs::t, - int_type: IntTy, - uint_type: UintTy, + int_type: ast::IntTy, + uint_type: ast::UintTy, } pub static verbose: uint = 1 << 0; @@ -210,6 +209,7 @@ pub struct Session_ { entry_fn: RefCell>, entry_type: Cell>, span_diagnostic: @diagnostic::SpanHandler, + boot_fn: Cell>, filesearch: @filesearch::FileSearch, building_library: Cell, working_dir: Path, diff --git a/src/librustc/front/std_inject.rs b/src/librustc/front/std_inject.rs index ae593783213ce..3a20b94fccdb4 100644 --- a/src/librustc/front/std_inject.rs +++ b/src/librustc/front/std_inject.rs @@ -40,6 +40,10 @@ fn use_uv(crate: &ast::Crate) -> bool { !attr::contains_name(crate.attrs, "no_uv") } +fn use_stdboot(crate: &ast::Crate) -> bool { + !attr::contains_name(crate.attrs, "no_boot") +} + fn no_prelude(attrs: &[ast::Attribute]) -> bool { attr::contains_name(attrs, "no_implicit_prelude") } @@ -67,25 +71,28 @@ impl fold::Folder for StandardLibraryInjector { span: DUMMY_SP }]; - if use_uv(&crate) && !self.sess.building_library.get() { - vis.push(ast::ViewItem { + if use_stdboot(&crate) && !self.sess.building_library.get() { + let boot = attr::mk_word_item(@"boot"); + vis.push(ast::view_item { node: ast::ViewItemExternMod(self.sess.ident_of("green"), Some((format!("green\\#{}", VERSION).to_managed(), ast::CookedStr)), ast::DUMMY_NODE_ID), - attrs: ~[], - vis: ast::Private, - span: DUMMY_SP - }); - vis.push(ast::ViewItem { - node: ast::ViewItemExternMod(self.sess.ident_of("rustuv"), - Some((format!("rustuv\\#{}", VERSION).to_managed(), - ast::CookedStr)), - ast::DUMMY_NODE_ID), - attrs: ~[], + attrs: ~[attr::mk_attr(boot)], vis: ast::Private, span: DUMMY_SP }); + if use_uv(&crate) { + vis.push(ast::view_item { + node: ast::view_item_extern_mod(self.sess.ident_of("rustuv"), + Some((format!("rustuv\\#{}", VERSION).to_managed(), + ast::CookedStr)), + ast::DUMMY_NODE_ID), + attrs: ~[], + vis: ast::Private, + span: DUMMY_SP + }); + } } vis.push_all(crate.module.view_items); diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 2d86a36dd60ab..f304993409a4c 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -204,6 +204,8 @@ pub static tag_native_libraries_lib: uint = 0x104; pub static tag_native_libraries_name: uint = 0x105; pub static tag_native_libraries_kind: uint = 0x106; +pub static tag_boot_fn: uint = 0x107; + #[deriving(Clone)] pub struct LinkMeta { crateid: CrateId, diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index 6dad364e661ba..cb322870e0824 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -301,3 +301,8 @@ pub fn get_trait_of_method(cstore: @cstore::CStore, decoder::get_trait_of_method(cdata, def_id.node, tcx) } +pub fn get_boot_fn(cstore: @mut cstore::CStore, + crate: ast::CrateNum) -> Option { + let cdata = cstore.get_crate_data(crate); + decoder::get_boot_fn(cdata) +} diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index ca35b1ae96b02..d1963ec6d88ae 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -1275,3 +1275,9 @@ pub fn get_native_libraries(cdata: Cmd) -> ~[(cstore::NativeLibaryKind, ~str)] { }); return result; } + +pub fn get_boot_fn(cdata: Cmd) -> Option { + reader::maybe_get_doc(reader::Doc(cdata.data()), tag_boot_fn).map(|doc| { + item_def_id(doc, cdata) + }) +} diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index a860b148aa9d2..d812263c307b5 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -79,6 +79,7 @@ struct Stats { dep_bytes: Cell, lang_item_bytes: Cell, native_lib_bytes: Cell, + boot_fn_bytes: Cell, impl_bytes: Cell, misc_bytes: Cell, item_bytes: Cell, @@ -1692,6 +1693,17 @@ fn encode_native_libraries(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) { ebml_w.end_tag(); } +fn encode_boot_fn(ecx: &EncodeContext, ebml_w: &mut writer::Encoder) { + match *ecx.tcx.sess.boot_fn { + Some(did) => { + ebml_w.start_tag(tag_boot_fn); + encode_def_id(ebml_w, did); + ebml_w.end_tag(); + } + None => {} + }; +} + struct ImplVisitor<'a,'b> { ecx: &'a EncodeContext<'a>, ebml_w: &'a mut writer::Encoder<'b>, @@ -1816,6 +1828,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, crate: &Crate) dep_bytes: Cell::new(0), lang_item_bytes: Cell::new(0), native_lib_bytes: Cell::new(0), + boot_fn_bytes: Cell::new(0), impl_bytes: Cell::new(0), misc_bytes: Cell::new(0), item_bytes: Cell::new(0), @@ -1874,6 +1887,11 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, crate: &Crate) encode_native_libraries(&ecx, &mut ebml_w); ecx.stats.native_lib_bytes.set(ebml_w.writer.tell() - i); + // Encode the native libraries used + i = wr.tell(); + encode_boot_fn(&ecx, &mut ebml_w); + ecx.stats.boot_fn_bytes = wr.tell() - i; + // Encode the def IDs of impls, for coherence checking. i = ebml_w.writer.tell(); encode_impls(&ecx, crate, &mut ebml_w); @@ -1911,6 +1929,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, crate: &Crate) println!(" dep bytes: {}", ecx.stats.dep_bytes.get()); println!(" lang item bytes: {}", ecx.stats.lang_item_bytes.get()); println!(" native bytes: {}", ecx.stats.native_lib_bytes.get()); + println!(" boot fn bytes: {}", ecx.stats.boot_fn_bytes.get()); println!(" impl bytes: {}", ecx.stats.impl_bytes.get()); println!(" misc bytes: {}", ecx.stats.misc_bytes.get()); println!(" item bytes: {}", ecx.stats.item_bytes.get()); diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index d6e4ad641822a..55a72195c5215 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -8,10 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - use driver::session; use driver::session::Session; -use syntax::ast::{Crate, NodeId, Item, ItemFn}; +use metadata::csearch; + +use syntax::ast; use syntax::ast_map; use syntax::attr; use syntax::codemap::Span; @@ -25,26 +26,26 @@ struct EntryContext { ast_map: ast_map::Map, // The top-level function called 'main' - main_fn: Option<(NodeId, Span)>, + main_fn: Option<(ast::NodeId, Span)>, // The function that has attribute named 'main' - attr_main_fn: Option<(NodeId, Span)>, + attr_main_fn: Option<(ast::NodeId, Span)>, // The function that has the attribute 'start' on it - start_fn: Option<(NodeId, Span)>, + start_fn: Option<(ast::NodeId, Span)>, // The functions that one might think are 'main' but aren't, e.g. // main functions not defined at the top level. For diagnostics. - non_main_fns: ~[(NodeId, Span)], + non_main_fns: ~[(ast::NodeId, Span)], } impl Visitor<()> for EntryContext { - fn visit_item(&mut self, item: &Item, _:()) { + fn visit_item(&mut self, item: &ast::Item, _:()) { find_item(item, self); } } -pub fn find_entry_point(session: Session, crate: &Crate, ast_map: ast_map::Map) { +pub fn find_entry_point(session: Session, crate: &ast::Crate, ast_map: ast_map::Map) { if session.building_library.get() { // No need to find a main function return; @@ -70,9 +71,9 @@ pub fn find_entry_point(session: Session, crate: &Crate, ast_map: ast_map::Map) configure_main(&mut ctxt); } -fn find_item(item: &Item, ctxt: &mut EntryContext) { +fn find_item(item: &ast::Item, ctxt: &mut EntryContext) { match item.node { - ItemFn(..) => { + ast::ItemFn(..) => { if item.ident.name == special_idents::main.name { { let ast_map = ctxt.ast_map.borrow(); @@ -151,3 +152,89 @@ fn configure_main(this: &mut EntryContext) { } } } + +struct BootFinder { + local_candidates: ~[(ast::NodeId, Span)], + extern_candidates: ~[(ast::CrateNum, Span)], + sess: Session, +} + +pub fn find_boot_fn(sess: Session, crate: &ast::Crate) { + let mut cx = BootFinder { + local_candidates: ~[], + extern_candidates: ~[], + sess: sess, + }; + visit::walk_crate(&mut cx, crate, ()); + match cx.local_candidates.len() + cx.extern_candidates.len() { + 0 => { + if !sess.building_library.get() { + match sess.entry_type.get() { + Some(session::EntryNone) => {}, + Some(session::EntryStart) => {}, + Some(session::EntryMain) => { + sess.err("main function found but no #[boot] \ + functions found"); + } + None => { + sess.bug("expected entry calculation by now"); + } + } + } + } + + 1 => { + for &(id, _) in cx.local_candidates.iter() { + sess.boot_fn.set(Some(ast::DefId { + crate: ast::LOCAL_CRATE, + node: id, + })); + } + for &(cnum, span) in cx.extern_candidates.iter() { + sess.boot_fn.set(csearch::get_boot_fn(sess.cstore, cnum)); + if sess.boot_fn.get().is_none() { + sess.span_err(span, "no #[boot] function found in crate"); + } + } + } + + _ => { + sess.err("too many #[boot] functions found"); + let lcandidates = cx.local_candidates.iter().map(|&(_, span)| span); + let ecandidates = cx.extern_candidates.iter().map(|&(_, span)| span); + for (i, span) in ecandidates.chain(lcandidates).enumerate() { + sess.span_note(span, format!(r"candidate \#{}", i)); + } + } + } + + sess.abort_if_errors(); +} + +impl Visitor<()> for BootFinder { + fn visit_item(&mut self, it: @ast::item, _: ()) { + match it.node { + ast::item_fn(..) if attr::contains_name(it.attrs, "boot") => { + self.local_candidates.push((it.id, it.span)) + } + _ => {} + } + visit::walk_item(self, it, ()); + } + + fn visit_view_item(&mut self, it: &ast::view_item, _: ()) { + match it.node { + ast::view_item_extern_mod(name, _, _, _) => { + if attr::contains_name(it.attrs, "boot") { + self.sess.cstore.iter_crate_data(|num, meta| { + if meta.name == self.sess.str_of(name) { + self.extern_candidates.push((num, it.span)); + } + }); + } + } + _ => {} + } + visit::walk_view_item(self, it, ()); + } +} diff --git a/src/librustc/middle/lint.rs b/src/librustc/middle/lint.rs index a954c59ff1276..db6e4ab83beed 100644 --- a/src/librustc/middle/lint.rs +++ b/src/librustc/middle/lint.rs @@ -877,7 +877,7 @@ fn check_heap_item(cx: &Context, it: &ast::Item) { } static crate_attrs: &'static [&'static str] = &[ - "crate_type", "feature", "no_uv", "no_main", "no_std", "crate_id", + "crate_type", "feature", "no_uv", "no_main", "no_std", "crate_id", "no_boot", "desc", "comment", "license", "copyright", // not used in rustc now ]; @@ -906,7 +906,7 @@ static other_attrs: &'static [&'static str] = &[ // fn-level "test", "bench", "should_fail", "ignore", "inline", "lang", "main", "start", - "no_split_stack", "cold", + "no_split_stack", "cold", "boot", // internal attribute: bypass privacy inside items "!resolve_unexported", diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index aaa8d071aff5d..eced8b7302d94 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -2429,7 +2429,7 @@ pub fn trans_mod(ccx: @CrateContext, m: &ast::Mod) { } } -fn finish_register_fn(ccx: @CrateContext, sp: Span, sym: ~str, node_id: ast::NodeId, +fn finish_register_fn(ccx: @CrateContext, sym: ~str, node_id: ast::NodeId, llfn: ValueRef) { { let mut item_symbols = ccx.item_symbols.borrow_mut(); @@ -2442,14 +2442,9 @@ fn finish_register_fn(ccx: @CrateContext, sp: Span, sym: ~str, node_id: ast::Nod lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage); } } - - if is_entry_fn(&ccx.sess, node_id) && !ccx.sess.building_library.get() { - create_entry_wrapper(ccx, sp, llfn); - } } pub fn register_fn(ccx: @CrateContext, - sp: Span, sym: ~str, node_id: ast::NodeId, node_type: ty::t) @@ -2463,13 +2458,12 @@ pub fn register_fn(ccx: @CrateContext, }; let llfn = decl_rust_fn(ccx, f.sig.inputs, f.sig.output, sym); - finish_register_fn(ccx, sp, sym, node_id, llfn); + finish_register_fn(ccx, sym, node_id, llfn); llfn } // only use this for foreign function ABIs and glue, use `register_fn` for Rust functions pub fn register_fn_llvmty(ccx: @CrateContext, - sp: Span, sym: ~str, node_id: ast::NodeId, cc: lib::llvm::CallConv, @@ -2480,34 +2474,35 @@ pub fn register_fn_llvmty(ccx: @CrateContext, ast_map::path_to_str(item_path(ccx, &node_id), token::get_ident_interner())); let llfn = decl_fn(ccx.llmod, sym, cc, fn_ty); - finish_register_fn(ccx, sp, sym, node_id, llfn); + finish_register_fn(ccx, sym, node_id, llfn); llfn } -pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool { - match sess.entry_fn.get() { - Some((entry_id, _)) => node_id == entry_id, - None => false - } -} - // Create a _rust_main(args: ~[str]) function which will be called from the // runtime rust_start function -pub fn create_entry_wrapper(ccx: @CrateContext, - _sp: Span, - main_llfn: ValueRef) { - let et = ccx.sess.entry_type.get().unwrap(); - match et { +pub fn create_entry_wrapper(ccx: @CrateContext) { + if *ccx.sess.building_library { return } + + let main_llfn = ccx.sess.entry_fn.map(|(id, _)| get_item_val(ccx, id)); + let main_llfn = main_llfn.expect("needs main function in trans"); + match ccx.sess.entry_type.expect("need entry calculation in trans") { + session::EntryNone => {} // Do nothing. session::EntryMain => { - create_entry_fn(ccx, main_llfn, true); + let did = ccx.sess.boot_fn.expect("need boot function in trans"); + let boot_llfn = if ast_util::is_local(did) { + get_item_val(ccx, did.node) + } else { + let boot_fn_type = csearch::get_type(ccx.tcx, did).ty; + trans_external_path(ccx, did, boot_fn_type) + }; + create_entry_fn(ccx, main_llfn, Some(boot_llfn)); } - session::EntryStart => create_entry_fn(ccx, main_llfn, false), - session::EntryNone => {} // Do nothing. + session::EntryStart => create_entry_fn(ccx, main_llfn, None), } fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef, - use_start_lang_item: bool) { + rust_boot: Option) { let llfty = Type::func([ccx.int_type, Type::i8().ptr_to().ptr_to()], &ccx.int_type); @@ -2521,47 +2516,53 @@ pub fn create_entry_wrapper(ccx: @CrateContext, unsafe { llvm::LLVMPositionBuilderAtEnd(bld, llbb); - let (start_fn, args) = if use_start_lang_item { - let start_def_id = match ccx.tcx.lang_items.require(StartFnLangItem) { - Ok(id) => id, - Err(s) => { ccx.tcx.sess.fatal(s); } - }; - let start_fn = if start_def_id.crate == ast::LOCAL_CRATE { - get_item_val(ccx, start_def_id.node) - } else { - let start_fn_type = csearch::get_type(ccx.tcx, - start_def_id).ty; - trans_external_path(ccx, start_def_id, start_fn_type) - }; - - let args = { - let opaque_rust_main = "rust_main".with_c_str(|buf| { - llvm::LLVMBuildPointerCast(bld, rust_main, Type::i8p().to_ref(), buf) - }); - - ~[ + let (start_fn, args) = match rust_boot { + Some(rust_boot) => { + let start = ccx.tcx.lang_items.require(StartFnLangItem); + let start_def_id = match start { + Ok(id) => id, + Err(s) => { ccx.tcx.sess.fatal(s); } + }; + let start_fn = if start_def_id.crate == ast::LOCAL_CRATE { + get_item_val(ccx, start_def_id.node) + } else { + let start_fn_type = csearch::get_type(ccx.tcx, + start_def_id).ty; + trans_external_path(ccx, start_def_id, start_fn_type) + }; + let args = { + let opaque_rust_main = "rust_main".with_c_str(|buf| { + llvm::LLVMBuildPointerCast(bld, rust_main, + Type::i8p().to_ref(), buf) + }); + let opaque_rust_boot = "rust_boot".with_c_str(|buf| { + llvm::LLVMBuildPointerCast(bld, rust_boot, + Type::i8p().to_ref(), buf) + }); + ~[ + C_null(Type::opaque_box(ccx).ptr_to()), + opaque_rust_main, + opaque_rust_boot, + llvm::LLVMGetParam(llfn, 0), + llvm::LLVMGetParam(llfn, 1) + ] + }; + (start_fn, args) + } + None => { + debug!("using user-defined start fn"); + let args = ~[ C_null(Type::opaque_box(ccx).ptr_to()), - opaque_rust_main, - llvm::LLVMGetParam(llfn, 0), - llvm::LLVMGetParam(llfn, 1) - ] - }; - (start_fn, args) - } else { - debug!("using user-defined start fn"); - let args = ~[ - C_null(Type::opaque_box(ccx).ptr_to()), - llvm::LLVMGetParam(llfn, 0 as c_uint), - llvm::LLVMGetParam(llfn, 1 as c_uint) - ]; - - (rust_main, args) - }; + llvm::LLVMGetParam(llfn, 0 as c_uint), + llvm::LLVMGetParam(llfn, 1 as c_uint) + ]; + (rust_main, args) + } + }; let result = llvm::LLVMBuildCall(bld, start_fn, args.as_ptr(), args.len() as c_uint, noname()); - llvm::LLVMBuildRet(bld, result); } } @@ -2722,10 +2723,9 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::NodeId) -> ValueRef { ast::ItemFn(_, purity, _, _, _) => { let llfn = if purity != ast::ExternFn { - register_fn(ccx, i.span, sym, i.id, ty) + register_fn(ccx, sym, i.id, ty) } else { foreign::register_rust_fn_with_foreign_abi(ccx, - i.span, sym, i.id) }; @@ -2826,7 +2826,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::NodeId) -> ValueRef { llfn = match enm.node { ast::ItemEnum(_, _) => { - register_fn(ccx, (*v).span, sym, id, ty) + register_fn(ccx, sym, id, ty) } _ => fail!("NodeVariant, shouldn't happen") }; @@ -2850,8 +2850,7 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::NodeId) -> ValueRef { let ty = ty::node_id_to_type(ccx.tcx, ctor_id); let sym = exported_name(ccx, (*struct_path).clone(), ty, struct_item.attrs); - let llfn = register_fn(ccx, struct_item.span, - sym, ctor_id, ty); + let llfn = register_fn(ccx, sym, ctor_id, ty); set_inline_hint(llfn); llfn } @@ -2892,7 +2891,7 @@ pub fn register_method(ccx: @CrateContext, let sym = exported_name(ccx, path, mty, m.attrs); - let llfn = register_fn(ccx, m.span, sym, id, mty); + let llfn = register_fn(ccx, sym, id, mty); set_llvm_fn_attrs(m.attrs, llfn); llfn } @@ -3338,6 +3337,7 @@ pub fn trans_crate(sess: session::Session, trans_mod(ccx, &crate.module); } + create_entry_wrapper(ccx); decl_gc_metadata(ccx, llmod_id); fill_crate_map(ccx, ccx.crate_map); diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 487aaa528eda2..0f6b74b7ad439 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -30,7 +30,6 @@ use std::libc::c_uint; use std::vec; use syntax::abi::{Cdecl, Aapcs, C, AbiSet, Win64}; use syntax::abi::{RustIntrinsic, Rust, Stdcall, Fastcall, System}; -use syntax::codemap::Span; use syntax::parse::token::special_idents; use syntax::{ast}; use syntax::{attr, ast_map}; @@ -408,7 +407,6 @@ pub fn trans_foreign_mod(ccx: @CrateContext, // correct code in the first place, but this is much simpler. pub fn register_rust_fn_with_foreign_abi(ccx: @CrateContext, - sp: Span, sym: ~str, node_id: ast::NodeId) -> ValueRef { @@ -425,7 +423,6 @@ pub fn register_rust_fn_with_foreign_abi(ccx: @CrateContext, _ => lib::llvm::CCallConv }; let llfn = base::register_fn_llvmty(ccx, - sp, sym, node_id, cconv, diff --git a/src/libstd/unstable/lang.rs b/src/libstd/unstable/lang.rs index e7e8cec9d5f17..53db28eb87f22 100644 --- a/src/libstd/unstable/lang.rs +++ b/src/libstd/unstable/lang.rs @@ -79,3 +79,19 @@ pub unsafe fn check_not_borrowed(a: *u8, line: size_t) { borrowck::check_not_borrowed(a, file, line) } + +#[lang = "start"] +#[cfg(not(stage0))] +pub fn lang_start(main: *u8, boot: *u8, argc: int, argv: **u8) -> int { + use cast; + use rt; + use os; + rt::init(argc, argv); + { + let main: fn() = unsafe { cast::transmute(main) }; + let boot: fn(fn()) = unsafe { cast::transmute(boot) }; + boot(main); + } + unsafe { rt::cleanup(); } + os::get_exit_status() +} diff --git a/src/test/compile-fail/boot-1.rs b/src/test/compile-fail/boot-1.rs new file mode 100644 index 0000000000000..e648493463798 --- /dev/null +++ b/src/test/compile-fail/boot-1.rs @@ -0,0 +1,15 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern: no #[boot] functions found + +#[no_boot]; + +fn main() {} diff --git a/src/test/compile-fail/boot-2.rs b/src/test/compile-fail/boot-2.rs new file mode 100644 index 0000000000000..b3a8dfc7c1400 --- /dev/null +++ b/src/test/compile-fail/boot-2.rs @@ -0,0 +1,19 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern: too many #[boot] functions found + +#[no_boot]; + +#[boot] extern mod green; +#[boot] extern mod native; + +fn main() {} + diff --git a/src/test/compile-fail/boot-3.rs b/src/test/compile-fail/boot-3.rs new file mode 100644 index 0000000000000..1965804f8c01b --- /dev/null +++ b/src/test/compile-fail/boot-3.rs @@ -0,0 +1,22 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern: too many #[boot] functions found + +#[no_boot]; + +#[boot] extern mod green; + +#[boot] +fn boot() {} + +fn main() {} + + diff --git a/src/test/compile-fail/boot-4.rs b/src/test/compile-fail/boot-4.rs new file mode 100644 index 0000000000000..ae693b9fef44d --- /dev/null +++ b/src/test/compile-fail/boot-4.rs @@ -0,0 +1,19 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern: too many #[boot] functions found + +#[boot] +fn boot() {} + +fn main() {} + + + diff --git a/src/test/run-pass/boot-1.rs b/src/test/run-pass/boot-1.rs new file mode 100644 index 0000000000000..ce3dac6c8f9d2 --- /dev/null +++ b/src/test/run-pass/boot-1.rs @@ -0,0 +1,16 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[no_boot]; + +#[boot] fn boot(_: fn()) {} + +fn main() {} + diff --git a/src/test/run-pass/boot-2.rs b/src/test/run-pass/boot-2.rs new file mode 100644 index 0000000000000..4947ba20199d6 --- /dev/null +++ b/src/test/run-pass/boot-2.rs @@ -0,0 +1,29 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[no_boot]; + +#[boot] extern mod green; + +use std::rt::task::Task; +use std::rt::local::Local; +use std::rt::Runtime; + +fn main() { + let mut t = Local::borrow(None::); + match t.get().maybe_take_runtime::() { + Some(rt) => { + t.get().put_runtime(rt as ~Runtime); + } + None => fail!() + } +} + + diff --git a/src/test/run-pass/boot-3.rs b/src/test/run-pass/boot-3.rs new file mode 100644 index 0000000000000..327f6585ee018 --- /dev/null +++ b/src/test/run-pass/boot-3.rs @@ -0,0 +1,30 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#[no_boot]; + +#[boot] extern mod native; + +use std::rt::task::Task; +use std::rt::local::Local; +use std::rt::Runtime; + +fn main() { + let mut t = Local::borrow(None::); + match t.get().maybe_take_runtime::() { + Some(rt) => { + t.get().put_runtime(rt as ~Runtime); + } + None => fail!() + } +} + + + diff --git a/src/test/run-pass/boot-4.rs b/src/test/run-pass/boot-4.rs new file mode 100644 index 0000000000000..547026feb5c62 --- /dev/null +++ b/src/test/run-pass/boot-4.rs @@ -0,0 +1,15 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern mod native; +extern mod green; + +fn main() { +}