From 053b8bff5a81a0cb6c347f8c371fa5b66f48dbda Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Fri, 12 Aug 2011 18:43:44 -0700 Subject: [PATCH] Accept main(args: [str]) as main signature --- src/comp/middle/trans.rs | 149 ++++++++++++++++++++++++++++++--- src/comp/middle/typeck.rs | 8 ++ src/rt/main.ll.in | 37 ++++++-- src/rt/rust.cpp | 48 ++++++++++- src/rt/rustrt.def.in | 1 + src/test/run-pass/main-ivec.rs | 3 + 6 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 src/test/run-pass/main-ivec.rs diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index ea0a793780a..e6f40709f38 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -6345,28 +6345,153 @@ fn decl_fn_and_pair_full(ccx: &@crate_ctxt, sp: &span, path: &[str], } _ { ccx.sess.bug("decl_fn_and_pair(): fn item doesn't have fn type!"); } } - let is_main: bool = is_main_name(path) && !ccx.sess.get_opts().library; - // Declare the function itself. - let s: str = - if is_main { - "_rust_main" - } else { mangle_internal_name_by_path(ccx, path) }; + let s: str = mangle_internal_name_by_path(ccx, path); let llfn: ValueRef = decl_internal_fastcall_fn(ccx.llmod, s, llfty); // Declare the global constant pair that points to it. let ps: str = mangle_exported_name(ccx, path, node_type); register_fn_pair(ccx, ps, llfty, llfn, node_id); + + let is_main: bool = is_main_name(path) && !ccx.sess.get_opts().library; if is_main { - if ccx.main_fn != none[ValueRef] { - ccx.sess.span_fatal(sp, "multiple 'main' functions"); - } - llvm::LLVMSetLinkage(llfn, - lib::llvm::LLVMExternalLinkage as llvm::Linkage); - ccx.main_fn = some(llfn); + create_main_wrapper(ccx, sp, llfn, node_type); } } +fn create_main_wrapper(ccx: &@crate_ctxt, sp: &span, + main_llfn: ValueRef, main_node_type: ty::t) { + + if ccx.main_fn != none[ValueRef] { + ccx.sess.span_fatal(sp, "multiple 'main' functions"); + } + + tag main_mode { + mm_nil; + mm_vec; + mm_ivec; + }; + + let main_mode = alt ty::struct(ccx.tcx, main_node_type) { + ty::ty_fn(_, args, _ ,_ ,_) { + if std::ivec::len(args) == 0u { + mm_nil + } else { + alt ty::struct(ccx.tcx, args.(0).ty) { + ty::ty_ivec(_) { mm_ivec } + ty::ty_vec(_) { mm_vec } + } + } + } + }; + + // Have to create two different main functions depending on whether + // main was declared to take vec or ivec + let llfn_vec = create_main_wrapper_vec(ccx, sp, main_llfn, main_mode); + let llfn_ivec = create_main_wrapper_ivec(ccx, sp, main_llfn, main_mode); + let takes_ivec = main_mode == mm_ivec; + // Create a global to tell main.ll which main we want to use + create_main_type_indicator(ccx, takes_ivec); + ccx.main_fn = takes_ivec ? some(llfn_ivec) : some(llfn_vec); + + fn create_main_wrapper_vec(ccx: &@crate_ctxt, + sp: &span, + main_llfn: ValueRef, + main_mode: main_mode) -> ValueRef { + + let vecarg = { + mode: ty::mo_val, + ty: ty::mk_vec(ccx.tcx, { + ty: ty::mk_str(ccx.tcx), + mut: ast::imm + }) + }; + let llfty = type_of_fn(ccx, sp, + ast::proto_fn, + ~[vecarg], + ty::mk_nil(ccx.tcx), + 0u); + let llfdecl = decl_fastcall_fn(ccx.llmod, "_rust_main", llfty); + + let fcx = new_fn_ctxt(new_local_ctxt(ccx), sp, llfdecl); + let bcx = new_top_block_ctxt(fcx); + + if main_mode != mm_ivec { + let lloutputarg = llvm::LLVMGetParam(llfdecl, 0u); + let lltaskarg = llvm::LLVMGetParam(llfdecl, 1u); + let llenvarg = llvm::LLVMGetParam(llfdecl, 2u); + let llargvarg = llvm::LLVMGetParam(llfdecl, 3u); + let args = alt main_mode { + mm_nil. { ~[lloutputarg, + lltaskarg, + llenvarg] } + mm_vec. { ~[lloutputarg, + lltaskarg, + llenvarg, + llargvarg] } + }; + bcx.build.FastCall(main_llfn, args); + } + bcx.build.RetVoid(); + + let lltop = bcx.llbb; + finish_fn(fcx, lltop); + + ret llfdecl; + } + + fn create_main_wrapper_ivec(ccx: &@crate_ctxt, + sp: &span, + main_llfn: ValueRef, + main_mode: main_mode) -> ValueRef { + let ivecarg = { + mode: ty::mo_val, + ty: ty::mk_ivec(ccx.tcx, { + ty: ty::mk_str(ccx.tcx), + mut: ast::imm + }) + }; + let llfty = type_of_fn(ccx, sp, + ast::proto_fn, + ~[ivecarg], + ty::mk_nil(ccx.tcx), + 0u); + let llfdecl = decl_fastcall_fn(ccx.llmod, "_rust_main_ivec", llfty); + + let fcx = new_fn_ctxt(new_local_ctxt(ccx), sp, llfdecl); + let bcx = new_top_block_ctxt(fcx); + + if main_mode == mm_ivec { + let lloutputarg = llvm::LLVMGetParam(llfdecl, 0u); + let lltaskarg = llvm::LLVMGetParam(llfdecl, 1u); + let llenvarg = llvm::LLVMGetParam(llfdecl, 2u); + let llargvarg = llvm::LLVMGetParam(llfdecl, 3u); + let args = ~[lloutputarg, + lltaskarg, + llenvarg, + llargvarg]; + bcx.build.FastCall(main_llfn, args); + } + bcx.build.RetVoid(); + + let lltop = bcx.llbb; + finish_fn(fcx, lltop); + + ret llfdecl; + } + + // FIXME: Remove after main takes only ivec + // Sets a global value hinting to the runtime whether main takes + // a vec or an ivec + fn create_main_type_indicator(ccx: &@crate_ctxt, takes_ivec: bool) { + let i = llvm::LLVMAddGlobal(ccx.llmod, T_int(), + str::buf("_rust_main_is_ivec")); + llvm::LLVMSetInitializer(i, C_int(takes_ivec as int)); + llvm::LLVMSetGlobalConstant(i, True); + } +} + + // Create a closure: a pair containing (1) a ValueRef, pointing to where the // fn's definition is in the executable we're creating, and (2) a pointer to // space for the function's environment. diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs index c2efce8d7b1..b2a232c33c7 100644 --- a/src/comp/middle/typeck.rs +++ b/src/comp/middle/typeck.rs @@ -2715,6 +2715,7 @@ fn check_item(ccx: @crate_ctxt, it: &@ast::item) { fn arg_is_argv_ty(tcx: &ty::ctxt, a: &ty::arg) -> bool { alt ty::struct(tcx, a.ty) { + // FIXME: Remove after main takes only ivec ty::ty_vec(mt) { if mt.mut != ast::imm { ret false; } alt ty::struct(tcx, mt.ty) { @@ -2722,6 +2723,13 @@ fn arg_is_argv_ty(tcx: &ty::ctxt, a: &ty::arg) -> bool { _ { ret false; } } } + ty::ty_ivec(mt) { + if mt.mut != ast::imm { ret false; } + alt ty::struct(tcx, mt.ty) { + ty::ty_str. { ret true; } + _ { ret false; } + } + } _ { ret false; } } } diff --git a/src/rt/main.ll.in b/src/rt/main.ll.in index aa5d96a3719..2081200ba94 100644 --- a/src/rt/main.ll.in +++ b/src/rt/main.ll.in @@ -6,23 +6,48 @@ %5 = type { i32, i32, i32, i32, [0 x %6*] } %6 = type { i32, i32, i32, i32, [0 x i8] } - -@_rust_crate_map_toplevel = external global %0 - -declare fastcc void @_rust_main(i1* nocapture, %task*, %2* nocapture, %5*); -declare i32 @rust_start(i32, i32, i32, i32) - %tydesc = type { %tydesc**, i32, i32, void (i1*, %task*, i1*, %tydesc**, i8*)*, void (i1*, %task*, i1*, %tydesc**, i8*)*, void (i1*, %task*, i1*, %tydesc**, i8*)*, void (i1*, %task*, i1*, %tydesc**, i8*)*, void (i1*, %task*, i1*, %tydesc**, i8*)*, void (i1*, %task*, i1*, %tydesc**, i8*)*, void (i1*, %task*, i1*, %tydesc**, i8*)*, void (i1*, %task*, i1*, %tydesc**, i8*, i8*, i8)* } %task = type { i32, i32, i32, i32, i32, i32, i32, i32 } +%ivec = type { i32, i32, [4 x { i32, i32, i32, i32, [0 x i8] }*] } + +@_rust_crate_map_toplevel = external global %0 + +; FIXME: Remove after main takes only ivec +@_rust_main_is_ivec = external global i32 + +declare i32 @rust_start(i32, i32, i32, i32) + +declare external fastcc void @_rust_main(i1* nocapture, %task*, %2* nocapture, %5*); + define void @_rust_main_wrap(i1* nocapture, %task *, %2* nocapture, %5 *) { tail call fastcc void @_rust_main(i1* %0, %task *%1, %2* nocapture %2, %5 *%3) ret void } +declare i32 @rust_start_ivec(i32, i32, i32, i32, i32) + +declare external fastcc void @_rust_main_ivec(i1* nocapture, %task*, %2* nocapture, %ivec) + +define void @_rust_main_wrap_ivec(i1* nocapture, %task *, %2* nocapture, %ivec *) +{ + %ivec = load %ivec *%3 + tail call fastcc void @_rust_main_ivec(i1* %0, %task *%1, %2* nocapture %2, %ivec %ivec) + ret void +} + define i32 @"MAIN"(i32, i32) { + %is_ivec = load i32 *@_rust_main_is_ivec + %is_ivec1 = trunc i32 %is_ivec to i1 + br i1 %is_ivec1, label %ivec, label %evec + +evec: %3 = tail call i32 @rust_start(i32 ptrtoint (void (i1*, %task*, %2*, %5*)* @_rust_main_wrap to i32), i32 %0, i32 %1, i32 ptrtoint (%0* @_rust_crate_map_toplevel to i32)) ret i32 %3 + +ivec: + %4 = tail call i32 @rust_start_ivec(i32 ptrtoint (void (i1*, %task*, %2*, %ivec*)* @_rust_main_wrap_ivec to i32), i32 %0, i32 %1, i32 ptrtoint (%0* @_rust_crate_map_toplevel to i32), i32 %is_ivec) + ret i32 %4 } diff --git a/src/rt/rust.cpp b/src/rt/rust.cpp index cc2f86da4b2..c211408d4d4 100644 --- a/src/rt/rust.cpp +++ b/src/rt/rust.cpp @@ -10,10 +10,12 @@ command_line_args : public kernel_owned // vec[str] passed to rust_task::start. rust_vec *args; + rust_ivec *args_ivec; command_line_args(rust_task *task, int sys_argc, - char **sys_argv) + char **sys_argv, + bool main_is_ivec) : kernel(task->kernel), task(task), argc(sys_argc), @@ -49,15 +51,39 @@ command_line_args : public kernel_owned mem = kernel->malloc(str_alloc, "command line arg"); strs[i] = new (mem) rust_str(str_alloc, str_fill, (uint8_t const *)argv[i]); + strs[i]->ref_count++; } args->fill = vec_fill; // If the caller has a declared args array, they may drop; but // we don't know if they have such an array. So we pin the args // array here to ensure it survives to program-shutdown. args->ref(); + + if (main_is_ivec) { + size_t ivec_interior_sz = + sizeof(size_t) * 2 + sizeof(rust_str *) * 4; + args_ivec = (rust_ivec *) + kernel->malloc(ivec_interior_sz, + "command line arg interior"); + args_ivec->fill = 0; + size_t ivec_exterior_sz = sizeof(rust_str *) * argc; + args_ivec->alloc = ivec_exterior_sz; + // NB: This is freed by some ivec machinery, probably the drop + // glue in main, so we don't free it ourselves + args_ivec->payload.ptr = (rust_ivec_heap *) + kernel->malloc(ivec_exterior_sz + sizeof(size_t), + "command line arg exterior"); + args_ivec->payload.ptr->fill = ivec_exterior_sz; + memcpy(&args_ivec->payload.ptr->data, strs, ivec_exterior_sz); + } else { + args_ivec = NULL; + } } ~command_line_args() { + if (args_ivec) { + kernel->free(args_ivec); + } if (args) { // Drop the args we've had pinned here. rust_str **strs = (rust_str**) &args->data[0]; @@ -84,7 +110,8 @@ command_line_args : public kernel_owned int check_claims = 0; extern "C" CDECL int -rust_start(uintptr_t main_fn, int argc, char **argv, void* crate_map) { +rust_start_ivec(uintptr_t main_fn, int argc, char **argv, + void* crate_map, int main_is_ivec) { rust_env *env = load_env(); @@ -99,7 +126,7 @@ rust_start(uintptr_t main_fn, int argc, char **argv, void* crate_map) { rust_scheduler *sched = root_task->sched; command_line_args *args = new (kernel, "main command line args") - command_line_args(root_task, argc, argv); + command_line_args(root_task, argc, argv, main_is_ivec); DLOG(sched, dom, "startup: %d args in 0x%" PRIxPTR, args->argc, (uintptr_t)args->args); @@ -107,7 +134,13 @@ rust_start(uintptr_t main_fn, int argc, char **argv, void* crate_map) { DLOG(sched, dom, "startup: arg[%d] = '%s'", i, args->argv[i]); } - root_task->start(main_fn, (uintptr_t)args->args); + if (main_is_ivec) { + DLOG(sched, dom, "main takes ivec"); + root_task->start(main_fn, (uintptr_t)args->args_ivec); + } else { + DLOG(sched, dom, "main takes vec"); + root_task->start(main_fn, (uintptr_t)args->args); + } root_task->deref(); root_task = NULL; @@ -127,6 +160,13 @@ rust_start(uintptr_t main_fn, int argc, char **argv, void* crate_map) { return ret; } +extern "C" CDECL int +rust_start(uintptr_t main_fn, int argc, char **argv, + void* crate_map) { + return rust_start_ivec(main_fn, argc, argv, crate_map, 0); +} + + // // Local Variables: // mode: C++ diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index ce8434240d5..88da5814c61 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -62,6 +62,7 @@ rust_process_wait rust_ptr_eq rust_run_program rust_start +rust_start_ivec rust_getcwd set_min_stack sched_threads diff --git a/src/test/run-pass/main-ivec.rs b/src/test/run-pass/main-ivec.rs new file mode 100644 index 00000000000..e3d1403411b --- /dev/null +++ b/src/test/run-pass/main-ivec.rs @@ -0,0 +1,3 @@ +fn main(args: [str]) { + for s in args { log s } +} \ No newline at end of file