about summary refs log tree commit diff
path: root/src/librustc_codegen_llvm
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_llvm
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_llvm')
-rw-r--r--src/librustc_codegen_llvm/abi.rs45
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs51
-rw-r--r--src/librustc_codegen_llvm/mono_item.rs8
-rw-r--r--src/librustc_codegen_llvm/va_arg.rs4
4 files changed, 86 insertions, 22 deletions
diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs
index 992149f7a47..aea62360651 100644
--- a/src/librustc_codegen_llvm/abi.rs
+++ b/src/librustc_codegen_llvm/abi.rs
@@ -258,7 +258,7 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
             val
         };
         match self.mode {
-            PassMode::Ignore => {},
+            PassMode::Ignore(_) => {}
             PassMode::Pair(..) => {
                 OperandValue::Pair(next(), next()).store(bx, dst);
             }
@@ -507,6 +507,14 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
             }
         };
 
+        // Store the index of the last argument. This is useful for working with
+        // C-compatible variadic arguments.
+        let last_arg_idx = if sig.inputs().is_empty() {
+            None
+        } else {
+            Some(sig.inputs().len() - 1)
+        };
+
         let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| {
             let is_return = arg_idx.is_none();
             let mut arg = mk_arg_type(ty, arg_idx);
@@ -516,7 +524,30 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                 // The same is true for s390x-unknown-linux-gnu
                 // and sparc64-unknown-linux-gnu.
                 if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) {
-                    arg.mode = PassMode::Ignore;
+                    arg.mode = PassMode::Ignore(IgnoreMode::Zst);
+                }
+            }
+
+            // If this is a C-variadic function, this is not the return value,
+            // and there is one or more fixed arguments; ensure that the `VaList`
+            // is ignored as an argument.
+            if sig.variadic {
+                match (last_arg_idx, arg_idx) {
+                    (Some(last_idx), Some(cur_idx)) if last_idx == cur_idx => {
+                        let va_list_did = match cx.tcx.lang_items().va_list() {
+                            Some(did) => did,
+                            None => bug!("`va_list` lang item required for C-variadic functions"),
+                        };
+                        match ty.sty {
+                            ty::Adt(def, _) if def.did == va_list_did => {
+                                // This is the "spoofed" `VaList`. Set the arguments mode
+                                // so that it will be ignored.
+                                arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs);
+                            },
+                            _ => (),
+                        }
+                    }
+                    _ => {}
                 }
             }
 
@@ -646,7 +677,9 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
         );
 
         let llreturn_ty = match self.ret.mode {
-            PassMode::Ignore => cx.type_void(),
+            PassMode::Ignore(IgnoreMode::Zst) => cx.type_void(),
+            PassMode::Ignore(IgnoreMode::CVarArgs) =>
+                bug!("`va_list` should never be a return type"),
             PassMode::Direct(_) | PassMode::Pair(..) => {
                 self.ret.layout.immediate_llvm_type(cx)
             }
@@ -664,7 +697,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
             }
 
             let llarg_ty = match arg.mode {
-                PassMode::Ignore => continue,
+                PassMode::Ignore(_) => continue,
                 PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
                 PassMode::Pair(..) => {
                     llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true));
@@ -733,7 +766,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                 apply(&ArgAttributes::new());
             }
             match arg.mode {
-                PassMode::Ignore => {}
+                PassMode::Ignore(_) => {}
                 PassMode::Direct(ref attrs) |
                 PassMode::Indirect(ref attrs, None) => apply(attrs),
                 PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
@@ -780,7 +813,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
                 apply(&ArgAttributes::new());
             }
             match arg.mode {
-                PassMode::Ignore => {}
+                PassMode::Ignore(_) => {}
                 PassMode::Direct(ref attrs) |
                 PassMode::Indirect(ref attrs, None) => apply(attrs),
                 PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs
index d1cbe1d4dd6..3268af396a2 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -136,22 +136,18 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                 let tp_ty = substs.type_at(0);
                 self.const_usize(self.size_of(tp_ty).bytes())
             }
-            func @ "va_start" | func @ "va_end" => {
-                let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
-                    (Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
-                    (Some(_), _) => self.load(args[0].immediate(),
-                                              tcx.data_layout.pointer_align.abi),
-                    (None, _) => bug!("va_list language item must be defined")
-                };
-                let intrinsic = self.cx().get_intrinsic(&format!("llvm.{}", func));
-                self.call(intrinsic, &[va_list], None)
+            "va_start" => {
+                self.va_start(args[0].immediate())
+            }
+            "va_end" => {
+                self.va_end(args[0].immediate())
             }
             "va_copy" => {
                 let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
                     (Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
                     (Some(_), _)  => self.load(args[0].immediate(),
                                                tcx.data_layout.pointer_align.abi),
-                    (None, _) => bug!("va_list language item must be defined")
+                    (None, _) => bug!("`va_list` language item must be defined")
                 };
                 let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
                 self.call(intrinsic, &[llresult, va_list], None);
@@ -722,6 +718,41 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
         let expect = self.get_intrinsic(&"llvm.expect.i1");
         self.call(expect, &[cond, self.const_bool(expected)], None)
     }
+
+    fn va_start(&mut self, list: &'ll Value) -> &'ll Value {
+        let target = &self.cx.tcx.sess.target.target;
+        let arch = &target.arch;
+        // A pointer to the architecture specific structure is passed to this
+        // function. For pointer variants (i686, RISC-V, Windows, etc), we
+        // should do do nothing, as the address to the pointer is needed. For
+        // architectures with a architecture specific structure (`Aarch64`,
+        // `X86_64`, etc), this function should load the structure from the
+        // address provided.
+        let va_list = match &**arch {
+            _ if target.options.is_like_windows => list,
+            "aarch64" if target.target_os == "ios" => list,
+            "aarch64" | "x86_64" | "powerpc" =>
+                self.load(list, self.tcx().data_layout.pointer_align.abi),
+            _ => list,
+        };
+        let intrinsic = self.cx().get_intrinsic("llvm.va_start");
+        self.call(intrinsic, &[va_list], None)
+    }
+
+    fn va_end(&mut self, list: &'ll Value) -> &'ll Value {
+        let target = &self.cx.tcx.sess.target.target;
+        let arch = &target.arch;
+        // See the comment in `va_start` for the purpose of the following.
+        let va_list = match &**arch {
+            _ if target.options.is_like_windows => list,
+            "aarch64" if target.target_os == "ios" => list,
+            "aarch64" | "x86_64" | "powerpc" =>
+                self.load(list, self.tcx().data_layout.pointer_align.abi),
+            _ => list,
+        };
+        let intrinsic = self.cx().get_intrinsic("llvm.va_end");
+        self.call(intrinsic, &[va_list], None)
+    }
 }
 
 fn copy_intrinsic(
diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs
index 4fe6a1f4f4b..7f0cdb9f580 100644
--- a/src/librustc_codegen_llvm/mono_item.rs
+++ b/src/librustc_codegen_llvm/mono_item.rs
@@ -36,10 +36,10 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     }
 
     fn predefine_fn(&self,
-                              instance: Instance<'tcx>,
-                              linkage: Linkage,
-                              visibility: Visibility,
-                              symbol_name: &str) {
+                    instance: Instance<'tcx>,
+                    linkage: Linkage,
+                    visibility: Visibility,
+                    symbol_name: &str) {
         assert!(!instance.substs.needs_infer() &&
                 !instance.substs.has_param_types());
 
diff --git a/src/librustc_codegen_llvm/va_arg.rs b/src/librustc_codegen_llvm/va_arg.rs
index 8719390b51a..7aceaea4510 100644
--- a/src/librustc_codegen_llvm/va_arg.rs
+++ b/src/librustc_codegen_llvm/va_arg.rs
@@ -109,12 +109,12 @@ pub(super) fn emit_va_arg(
                             Align::from_bytes(4).unwrap(), true)
         }
         // Windows Aarch64
-        ("aarch4", true) => {
+        ("aarch64", true) => {
             emit_ptr_va_arg(bx, addr, target_ty, false,
                             Align::from_bytes(8).unwrap(), false)
         }
         // iOS Aarch64
-        ("aarch4", _) if target.target_os == "ios" => {
+        ("aarch64", _) if target.target_os == "ios" => {
             emit_ptr_va_arg(bx, addr, target_ty, false,
                             Align::from_bytes(8).unwrap(), true)
         }