diff options
| author | Dan Robertson <dan@dlrobertson.com> | 2018-11-30 15:53:44 +0000 |
|---|---|---|
| committer | Dan Robertson <dan@dlrobertson.com> | 2019-02-27 10:21:35 -0500 |
| commit | 58147d486bc26eb59d18d8e0d83aa6fe0b467fb9 (patch) | |
| tree | 3e25be958b33c7e5727b059511157ac9f1a22722 /src/librustc_codegen_ssa | |
| parent | cd56472cc47981e62c684ceada7922ac3731b785 (diff) | |
| download | rust-58147d486bc26eb59d18d8e0d83aa6fe0b467fb9.tar.gz rust-58147d486bc26eb59d18d8e0d83aa6fe0b467fb9.zip | |
Support defining C compatible variadic functions
Add support for defining C compatible variadic functions in unsafe rust with extern "C".
Diffstat (limited to 'src/librustc_codegen_ssa')
| -rw-r--r-- | src/librustc_codegen_ssa/mir/block.rs | 50 | ||||
| -rw-r--r-- | src/librustc_codegen_ssa/mir/mod.rs | 64 | ||||
| -rw-r--r-- | src/librustc_codegen_ssa/traits/intrinsic.rs | 6 |
3 files changed, 110 insertions, 10 deletions
diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index caca1789fc9..f40aa0cb6d1 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -3,7 +3,7 @@ use rustc::ty::{self, Ty, TypeFoldable}; use rustc::ty::layout::{self, LayoutOf, HasTyCtxt}; use rustc::mir; use rustc::mir::interpret::EvalErrorKind; -use rustc_target::abi::call::{ArgType, FnType, PassMode}; +use rustc_target::abi::call::{ArgType, FnType, PassMode, IgnoreMode}; use rustc_target::spec::abi::Abi; use rustc_mir::monomorphize; use crate::base; @@ -18,7 +18,7 @@ use syntax_pos::Pos; use super::{FunctionCx, LocalRef}; use super::place::PlaceRef; -use super::operand::OperandRef; +use super::operand::{OperandValue, OperandRef}; use super::operand::OperandValue::{Pair, Ref, Immediate}; impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -232,12 +232,21 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } mir::TerminatorKind::Return => { + if self.fn_ty.variadic { + if let Some(va_list) = self.va_list_ref { + bx.va_end(va_list.llval); + } + } let llval = match self.fn_ty.ret.mode { - PassMode::Ignore | PassMode::Indirect(..) => { + PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => { bx.ret_void(); return; } + PassMode::Ignore(IgnoreMode::CVarArgs) => { + bug!("C variadic arguments should never be the return type"); + } + PassMode::Direct(_) | PassMode::Pair(..) => { let op = self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE)); @@ -481,7 +490,10 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return; } - let extra_args = &args[sig.inputs().len()..]; + // The "spoofed" `VaList` added to a C-variadic functions signature + // should not be included in the `extra_args` calculation. + let extra_args_start_idx = sig.inputs().len() - if sig.variadic { 1 } else { 0 }; + let extra_args = &args[extra_args_start_idx..]; let extra_args = extra_args.iter().map(|op_arg| { let op_ty = op_arg.ty(self.mir, bx.tcx()); self.monomorphize(&op_ty) @@ -658,7 +670,37 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { (&args[..], None) }; + // Useful determining if the current argument is the "spoofed" `VaList` + let last_arg_idx = if sig.inputs().is_empty() { + None + } else { + Some(sig.inputs().len() - 1) + }; 'make_args: for (i, arg) in first_args.iter().enumerate() { + // If this is a C-variadic function the function signature contains + // an "spoofed" `VaList`. This argument is ignored, but we need to + // populate it with a dummy operand so that the users real arguments + // are not overwritten. + let i = if sig.variadic && last_arg_idx.map(|x| x == i).unwrap_or(false) { + let layout = match tcx.lang_items().va_list() { + Some(did) => bx.cx().layout_of(bx.tcx().type_of(did)), + None => bug!("va_list language item required for C variadics"), + }; + let op = OperandRef { + val: OperandValue::Immediate( + bx.cx().const_undef(bx.cx().immediate_backend_type(layout)) + ), + layout: layout, + }; + self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]); + if i + 1 < fn_ty.args.len() { + i + 1 + } else { + break 'make_args + } + } else { + i + }; let mut op = self.codegen_operand(&mut bx, arg); if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { diff --git a/src/librustc_codegen_ssa/mir/mod.rs b/src/librustc_codegen_ssa/mir/mod.rs index e1528921a59..95cf8cfe2d0 100644 --- a/src/librustc_codegen_ssa/mir/mod.rs +++ b/src/librustc_codegen_ssa/mir/mod.rs @@ -5,7 +5,7 @@ use rustc::mir::{self, Mir}; use rustc::ty::subst::SubstsRef; use rustc::session::config::DebugInfo; use rustc_mir::monomorphize::Instance; -use rustc_target::abi::call::{FnType, PassMode}; +use rustc_target::abi::call::{FnType, PassMode, IgnoreMode}; use crate::base; use crate::debuginfo::{self, VariableAccess, VariableKind, FunctionDebugContext}; use crate::traits::*; @@ -86,6 +86,10 @@ pub struct FunctionCx<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> { /// If this function is being monomorphized, this contains the type substitutions used. param_substs: SubstsRef<'tcx>, + + /// If this function is a C-variadic function, this contains the `PlaceRef` of the + /// "spoofed" `VaList`. + va_list_ref: Option<PlaceRef<'tcx, Bx::Value>>, } impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { @@ -246,13 +250,18 @@ pub fn codegen_mir<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( assert!(!instance.substs.needs_infer()); instance.substs }, + va_list_ref: None, }; let memory_locals = analyze::non_ssa_locals(&fx); // Allocate variable and temp allocas fx.locals = { - let args = arg_local_refs(&mut bx, &fx, &fx.scopes, &memory_locals); + // FIXME(dlrobertson): This is ugly. Find a better way of getting the `PlaceRef` or + // `LocalRef` from `arg_local_refs` + let mut va_list_ref = None; + let args = arg_local_refs(&mut bx, &fx, &fx.scopes, &memory_locals, &mut va_list_ref); + fx.va_list_ref = va_list_ref; let mut allocate_local = |local| { let decl = &mir.local_decls[local]; @@ -433,6 +442,7 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( debuginfo::MirDebugScope<Bx::DIScope> >, memory_locals: &BitSet<mir::Local>, + va_list_ref: &mut Option<PlaceRef<'tcx, Bx::Value>>, ) -> Vec<LocalRef<'tcx, Bx::Value>> { let mir = fx.mir; let tcx = fx.cx.tcx(); @@ -447,6 +457,15 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( None }; + // Store the index of the last argument. This is used to + // call va_start on the va_list instead of attempting + // to store_fn_arg. + let last_arg_idx = if fx.fn_ty.args.is_empty() { + None + } else { + Some(fx.fn_ty.args.len() - 1) + }; + mir.args_iter().enumerate().map(|(arg_index, local)| { let arg_decl = &mir.local_decls[local]; @@ -510,9 +529,16 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( // of putting everything in allocas just so we can use llvm.dbg.declare. let local = |op| LocalRef::Operand(Some(op)); match arg.mode { - PassMode::Ignore => { + PassMode::Ignore(IgnoreMode::Zst) => { return local(OperandRef::new_zst(bx.cx(), arg.layout)); } + PassMode::Ignore(IgnoreMode::CVarArgs) => { + let backend_type = bx.cx().immediate_backend_type(arg.layout); + return local(OperandRef { + val: OperandValue::Immediate(bx.cx().const_undef(backend_type)), + layout: arg.layout, + }); + } PassMode::Direct(_) => { let llarg = bx.get_param(bx.llfn(), llarg_idx as c_uint); bx.set_value_name(llarg, &name); @@ -559,9 +585,35 @@ fn arg_local_refs<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>( indirect_operand.store(bx, tmp); tmp } else { - let tmp = PlaceRef::alloca(bx, arg.layout, &name); - bx.store_fn_arg(arg, &mut llarg_idx, tmp); - tmp + if fx.fn_ty.variadic && last_arg_idx.map(|idx| arg_index == idx).unwrap_or(false) { + let va_list_impl = match arg_decl.ty.ty_adt_def() { + Some(adt) => adt.non_enum_variant(), + None => bug!("`va_list` language item improperly constructed") + }; + match tcx.type_of(va_list_impl.fields[0].did).sty { + ty::Ref(_, ty, _) => { + // If the underlying structure the `VaList` contains is a structure, + // we need to allocate it (e.g., X86_64 on Linux). + let tmp = PlaceRef::alloca(bx, arg.layout, &name); + if let ty::Adt(..) = ty.sty { + let layout = bx.layout_of(ty); + // Create an unnamed allocation for the backing structure + // and store it in the the spoofed `VaList`. + let backing = PlaceRef::alloca(bx, layout, ""); + bx.store(backing.llval, tmp.llval, layout.align.abi); + } + // Call `va_start` on the spoofed `VaList`. + bx.va_start(tmp.llval); + *va_list_ref = Some(tmp); + tmp + } + _ => bug!("improperly constructed `va_list` lang item"), + } + } else { + let tmp = PlaceRef::alloca(bx, arg.layout, &name); + bx.store_fn_arg(arg, &mut llarg_idx, tmp); + tmp + } }; arg_scope.map(|scope| { // Is this a regular argument? diff --git a/src/librustc_codegen_ssa/traits/intrinsic.rs b/src/librustc_codegen_ssa/traits/intrinsic.rs index 3cd0c39d413..cd527898977 100644 --- a/src/librustc_codegen_ssa/traits/intrinsic.rs +++ b/src/librustc_codegen_ssa/traits/intrinsic.rs @@ -20,4 +20,10 @@ pub trait IntrinsicCallMethods<'tcx>: BackendTypes { fn abort(&mut self); fn assume(&mut self, val: Self::Value); fn expect(&mut self, cond: Self::Value, expected: bool) -> Self::Value; + /// Trait method used to inject `va_start` on the "spoofed" `VaList` in + /// Rust defined C-variadic functions. + fn va_start(&mut self, val: Self::Value) -> Self::Value; + /// Trait method used to inject `va_end` on the "spoofed" `VaList` before + /// Rust defined C-variadic functions return. + fn va_end(&mut self, val: Self::Value) -> Self::Value; } |
