about summary refs log tree commit diff
path: root/src/librustc_codegen_llvm
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2018-11-29 19:28:21 +0000
committerbors <bors@rust-lang.org>2018-11-29 19:28:21 +0000
commit3e90a12a8a95933604a8b609197fce61bb24a38c (patch)
treecf4c1b0648179447fb3b5de1f51d2645c8b693f6 /src/librustc_codegen_llvm
parent3dde9e132207b5a40e12f8d5a1a363ebea60e0b0 (diff)
parente9e084f5fa8820aca67e5cf0ec301e52bbae1028 (diff)
downloadrust-3e90a12a8a95933604a8b609197fce61bb24a38c.tar.gz
rust-3e90a12a8a95933604a8b609197fce61bb24a38c.zip
Auto merge of #49878 - dlrobertson:va_list_pt0, r=eddyb
libcore: Add VaList and variadic arg handling intrinsics

## Summary

 - Add intrinsics for `va_start`, `va_end`, `va_copy`, and `va_arg`.
 - Add `core::va_list::VaList` to `libcore`.

Part 1 of (at least) 3 for #44930

Comments and critiques are very much welcomed 😄
Diffstat (limited to 'src/librustc_codegen_llvm')
-rw-r--r--src/librustc_codegen_llvm/context.rs27
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs58
-rw-r--r--src/librustc_codegen_llvm/lib.rs1
-rw-r--r--src/librustc_codegen_llvm/va_arg.rs142
4 files changed, 215 insertions, 13 deletions
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs
index 5b088ad2908..d954eb838cb 100644
--- a/src/librustc_codegen_llvm/context.rs
+++ b/src/librustc_codegen_llvm/context.rs
@@ -723,17 +723,17 @@ impl IntrinsicDeclarationMethods<'tcx> for CodegenCx<'b, 'tcx> {
         ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64);
         ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128);
 
-    ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
-    ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
-    ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
-    ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
-    ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
-
-    ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
-    ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
-    ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
-    ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
-    ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
+        ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
+        ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
+        ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
+        ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
+        ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
+
+        ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
+        ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
+        ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
+        ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
+        ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
 
         ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1});
         ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1});
@@ -783,6 +783,11 @@ impl IntrinsicDeclarationMethods<'tcx> for CodegenCx<'b, 'tcx> {
         ifn!("llvm.assume", fn(i1) -> void);
         ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void);
 
+        // variadic intrinsics
+        ifn!("llvm.va_start", fn(i8p) -> void);
+        ifn!("llvm.va_end", fn(i8p) -> void);
+        ifn!("llvm.va_copy", fn(i8p, i8p) -> void);
+
         if self.sess().opts.debuginfo != DebugInfo::None {
             ifn!("llvm.dbg.declare", fn(self.type_metadata(), self.type_metadata()) -> void);
             ifn!("llvm.dbg.value", fn(self.type_metadata(), t_i64, self.type_metadata()) -> void);
diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs
index 3548ccfd5a5..9c9b73f63fa 100644
--- a/src/librustc_codegen_llvm/intrinsic.rs
+++ b/src/librustc_codegen_llvm/intrinsic.rs
@@ -24,13 +24,14 @@ use context::CodegenCx;
 use type_::Type;
 use type_of::LayoutLlvmExt;
 use rustc::ty::{self, Ty};
-use rustc::ty::layout::{LayoutOf, HasTyCtxt};
+use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, Primitive};
 use rustc_codegen_ssa::common::TypeKind;
 use rustc::hir;
-use syntax::ast;
+use syntax::ast::{self, FloatTy};
 use syntax::symbol::Symbol;
 use builder::Builder;
 use value::Value;
+use va_arg::emit_va_arg;
 
 use rustc_codegen_ssa::traits::*;
 
@@ -146,6 +147,59 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
                 let tp_ty = substs.type_at(0);
                 self.cx().const_usize(self.cx().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_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")
+                };
+                let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
+                self.call(intrinsic, &[llresult, va_list], None);
+                return;
+            }
+            "va_arg" => {
+                match fn_ty.ret.layout.abi {
+                    layout::Abi::Scalar(ref scalar) => {
+                        match scalar.value {
+                            Primitive::Int(..) => {
+                                if self.cx().size_of(ret_ty).bytes() < 4 {
+                                    // va_arg should not be called on a integer type
+                                    // less than 4 bytes in length. If it is, promote
+                                    // the integer to a `i32` and truncate the result
+                                    // back to the smaller type.
+                                    let promoted_result = emit_va_arg(self, args[0],
+                                                                      tcx.types.i32);
+                                    self.trunc(promoted_result, llret_ty)
+                                } else {
+                                    emit_va_arg(self, args[0], ret_ty)
+                                }
+                            }
+                            Primitive::Float(FloatTy::F64) |
+                            Primitive::Pointer => {
+                                emit_va_arg(self, args[0], ret_ty)
+                            }
+                            // `va_arg` should never be used with the return type f32.
+                            Primitive::Float(FloatTy::F32) => {
+                                bug!("the va_arg intrinsic does not work with `f32`")
+                            }
+                        }
+                    }
+                    _ => {
+                        bug!("the va_arg intrinsic does not work with non-scalar types")
+                    }
+                }
+            }
             "size_of_val" => {
                 let tp_ty = substs.type_at(0);
                 if let OperandValue::Pair(_, meta) = args[0].val {
diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs
index f904a928d53..4f90cb793b6 100644
--- a/src/librustc_codegen_llvm/lib.rs
+++ b/src/librustc_codegen_llvm/lib.rs
@@ -127,6 +127,7 @@ mod mono_item;
 mod type_;
 mod type_of;
 mod value;
+mod va_arg;
 
 #[derive(Clone)]
 pub struct LlvmCodegenBackend(());
diff --git a/src/librustc_codegen_llvm/va_arg.rs b/src/librustc_codegen_llvm/va_arg.rs
new file mode 100644
index 00000000000..fbc3e6f06d1
--- /dev/null
+++ b/src/librustc_codegen_llvm/va_arg.rs
@@ -0,0 +1,142 @@
+// Copyright 2018 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use builder::Builder;
+use rustc_codegen_ssa::mir::operand::OperandRef;
+use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods};
+use rustc::ty::layout::{Align, HasDataLayout, HasTyCtxt, LayoutOf, Size};
+use rustc::ty::Ty;
+use type_::Type;
+use type_of::LayoutLlvmExt;
+use value::Value;
+
+#[allow(dead_code)]
+fn round_pointer_up_to_alignment(
+    bx: &mut Builder<'a, 'll, 'tcx>,
+    addr: &'ll Value,
+    align: Align,
+    ptr_ty: &'ll Type
+) -> &'ll Value {
+    let mut ptr_as_int = bx.ptrtoint(addr, bx.cx().type_isize());
+    ptr_as_int = bx.add(ptr_as_int, bx.cx().const_i32(align.bytes() as i32 - 1));
+    ptr_as_int = bx.and(ptr_as_int, bx.cx().const_i32(-(align.bytes() as i32)));
+    bx.inttoptr(ptr_as_int, ptr_ty)
+}
+
+fn emit_direct_ptr_va_arg(
+    bx: &mut Builder<'a, 'll, 'tcx>,
+    list: OperandRef<'tcx, &'ll Value>,
+    llty: &'ll Type,
+    size: Size,
+    align: Align,
+    slot_size: Align,
+    allow_higher_align: bool
+) -> (&'ll Value, Align) {
+    let va_list_ptr_ty = bx.cx().type_ptr_to(bx.cx.type_i8p());
+    let va_list_addr = if list.layout.llvm_type(bx.cx) != va_list_ptr_ty {
+        bx.bitcast(list.immediate(), va_list_ptr_ty)
+    } else {
+        list.immediate()
+    };
+
+    let ptr = bx.load(va_list_addr, bx.tcx().data_layout.pointer_align.abi);
+
+    let (addr, addr_align) = if allow_higher_align && align > slot_size {
+        (round_pointer_up_to_alignment(bx, ptr, align, bx.cx().type_i8p()), align)
+    } else {
+        (ptr, slot_size)
+    };
+
+
+    let aligned_size = size.align_to(slot_size).bytes() as i32;
+    let full_direct_size = bx.cx().const_i32(aligned_size);
+    let next = bx.inbounds_gep(addr, &[full_direct_size]);
+    bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi);
+
+    if size.bytes() < slot_size.bytes() &&
+            &*bx.tcx().sess.target.target.target_endian == "big" {
+        let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32);
+        let adjusted = bx.inbounds_gep(addr, &[adjusted_size]);
+        (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align)
+    } else {
+        (bx.bitcast(addr, bx.cx().type_ptr_to(llty)), addr_align)
+    }
+}
+
+fn emit_ptr_va_arg(
+    bx: &mut Builder<'a, 'll, 'tcx>,
+    list: OperandRef<'tcx, &'ll Value>,
+    target_ty: Ty<'tcx>,
+    indirect: bool,
+    slot_size: Align,
+    allow_higher_align: bool
+) -> &'ll Value {
+    let layout = bx.cx.layout_of(target_ty);
+    let (llty, size, align) = if indirect {
+        (bx.cx.layout_of(bx.cx.tcx.mk_imm_ptr(target_ty)).llvm_type(bx.cx),
+         bx.cx.data_layout().pointer_size,
+         bx.cx.data_layout().pointer_align)
+    } else {
+        (layout.llvm_type(bx.cx),
+         layout.size,
+         layout.align)
+    };
+    let (addr, addr_align) = emit_direct_ptr_va_arg(bx, list, llty, size, align.abi,
+                                                    slot_size, allow_higher_align);
+    if indirect {
+        let tmp_ret = bx.load(addr, addr_align);
+        bx.load(tmp_ret, align.abi)
+    } else {
+        bx.load(addr, addr_align)
+    }
+}
+
+pub(super) fn emit_va_arg(
+    bx: &mut Builder<'a, 'll, 'tcx>,
+    addr: OperandRef<'tcx, &'ll Value>,
+    target_ty: Ty<'tcx>,
+) -> &'ll Value {
+    // Determine the va_arg implementation to use. The LLVM va_arg instruction
+    // is lacking in some instances, so we should only use it as a fallback.
+    let arch = &bx.cx.tcx.sess.target.target.arch;
+    match (&**arch,
+           bx.cx.tcx.sess.target.target.options.is_like_windows) {
+        ("x86", true) => {
+            emit_ptr_va_arg(bx, addr, target_ty, false,
+                            Align::from_bytes(4).unwrap(), false)
+        }
+        ("x86_64", true) => {
+            let target_ty_size = bx.cx.size_of(target_ty).bytes();
+            let indirect = if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
+                true
+            } else {
+                false
+            };
+            emit_ptr_va_arg(bx, addr, target_ty, indirect,
+                            Align::from_bytes(8).unwrap(), false)
+        }
+        ("x86", false) => {
+            emit_ptr_va_arg(bx, addr, target_ty, false,
+                            Align::from_bytes(4).unwrap(), true)
+        }
+        _ => {
+            let va_list = if (bx.tcx().sess.target.target.arch == "aarch64" ||
+                              bx.tcx().sess.target.target.arch == "x86_64" ||
+                              bx.tcx().sess.target.target.arch == "powerpc") &&
+                             !bx.tcx().sess.target.target.options.is_like_windows {
+                bx.load(addr.immediate(), bx.tcx().data_layout.pointer_align.abi)
+            } else {
+                addr.immediate()
+            };
+            bx.va_arg(va_list, bx.cx.layout_of(target_ty).llvm_type(bx.cx))
+        }
+    }
+}
+