about summary refs log tree commit diff
path: root/src/librustc_codegen_ssa
diff options
context:
space:
mode:
authorDan Robertson <dan@dlrobertson.com>2018-11-30 15:53:44 +0000
committerDan Robertson <dan@dlrobertson.com>2019-02-27 10:21:35 -0500
commit58147d486bc26eb59d18d8e0d83aa6fe0b467fb9 (patch)
tree3e25be958b33c7e5727b059511157ac9f1a22722 /src/librustc_codegen_ssa
parentcd56472cc47981e62c684ceada7922ac3731b785 (diff)
downloadrust-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.rs50
-rw-r--r--src/librustc_codegen_ssa/mir/mod.rs64
-rw-r--r--src/librustc_codegen_ssa/traits/intrinsic.rs6
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;
 }