]> git.lizzy.rs Git - rust.git/blob - src/librustc_codegen_llvm/va_arg.rs
Rollup merge of #58975 - jtdowney:iter_arith_traits_option, r=dtolnay
[rust.git] / src / librustc_codegen_llvm / va_arg.rs
1 use crate::builder::Builder;
2 use crate::type_::Type;
3 use crate::type_of::LayoutLlvmExt;
4 use crate::value::Value;
5 use rustc_codegen_ssa::mir::operand::OperandRef;
6 use rustc_codegen_ssa::traits::{BaseTypeMethods, BuilderMethods, ConstMethods, DerivedTypeMethods};
7 use rustc::ty::layout::{Align, HasDataLayout, HasTyCtxt, LayoutOf, Size};
8 use rustc::ty::Ty;
9
10 #[allow(dead_code)]
11 fn round_pointer_up_to_alignment(
12     bx: &mut Builder<'a, 'll, 'tcx>,
13     addr: &'ll Value,
14     align: Align,
15     ptr_ty: &'ll Type
16 ) -> &'ll Value {
17     let mut ptr_as_int = bx.ptrtoint(addr, bx.cx().type_isize());
18     ptr_as_int = bx.add(ptr_as_int, bx.cx().const_i32(align.bytes() as i32 - 1));
19     ptr_as_int = bx.and(ptr_as_int, bx.cx().const_i32(-(align.bytes() as i32)));
20     bx.inttoptr(ptr_as_int, ptr_ty)
21 }
22
23 fn emit_direct_ptr_va_arg(
24     bx: &mut Builder<'a, 'll, 'tcx>,
25     list: OperandRef<'tcx, &'ll Value>,
26     llty: &'ll Type,
27     size: Size,
28     align: Align,
29     slot_size: Align,
30     allow_higher_align: bool
31 ) -> (&'ll Value, Align) {
32     let va_list_ptr_ty = bx.cx().type_ptr_to(bx.cx.type_i8p());
33     let va_list_addr = if list.layout.llvm_type(bx.cx) != va_list_ptr_ty {
34         bx.bitcast(list.immediate(), va_list_ptr_ty)
35     } else {
36         list.immediate()
37     };
38
39     let ptr = bx.load(va_list_addr, bx.tcx().data_layout.pointer_align.abi);
40
41     let (addr, addr_align) = if allow_higher_align && align > slot_size {
42         (round_pointer_up_to_alignment(bx, ptr, align, bx.cx().type_i8p()), align)
43     } else {
44         (ptr, slot_size)
45     };
46
47
48     let aligned_size = size.align_to(slot_size).bytes() as i32;
49     let full_direct_size = bx.cx().const_i32(aligned_size);
50     let next = bx.inbounds_gep(addr, &[full_direct_size]);
51     bx.store(next, va_list_addr, bx.tcx().data_layout.pointer_align.abi);
52
53     if size.bytes() < slot_size.bytes() &&
54             &*bx.tcx().sess.target.target.target_endian == "big" {
55         let adjusted_size = bx.cx().const_i32((slot_size.bytes() - size.bytes()) as i32);
56         let adjusted = bx.inbounds_gep(addr, &[adjusted_size]);
57         (bx.bitcast(adjusted, bx.cx().type_ptr_to(llty)), addr_align)
58     } else {
59         (bx.bitcast(addr, bx.cx().type_ptr_to(llty)), addr_align)
60     }
61 }
62
63 fn emit_ptr_va_arg(
64     bx: &mut Builder<'a, 'll, 'tcx>,
65     list: OperandRef<'tcx, &'ll Value>,
66     target_ty: Ty<'tcx>,
67     indirect: bool,
68     slot_size: Align,
69     allow_higher_align: bool
70 ) -> &'ll Value {
71     let layout = bx.cx.layout_of(target_ty);
72     let (llty, size, align) = if indirect {
73         (bx.cx.layout_of(bx.cx.tcx.mk_imm_ptr(target_ty)).llvm_type(bx.cx),
74          bx.cx.data_layout().pointer_size,
75          bx.cx.data_layout().pointer_align)
76     } else {
77         (layout.llvm_type(bx.cx),
78          layout.size,
79          layout.align)
80     };
81     let (addr, addr_align) = emit_direct_ptr_va_arg(bx, list, llty, size, align.abi,
82                                                     slot_size, allow_higher_align);
83     if indirect {
84         let tmp_ret = bx.load(addr, addr_align);
85         bx.load(tmp_ret, align.abi)
86     } else {
87         bx.load(addr, addr_align)
88     }
89 }
90
91 pub(super) fn emit_va_arg(
92     bx: &mut Builder<'a, 'll, 'tcx>,
93     addr: OperandRef<'tcx, &'ll Value>,
94     target_ty: Ty<'tcx>,
95 ) -> &'ll Value {
96     // Determine the va_arg implementation to use. The LLVM va_arg instruction
97     // is lacking in some instances, so we should only use it as a fallback.
98     let target = &bx.cx.tcx.sess.target.target;
99     let arch = &bx.cx.tcx.sess.target.target.arch;
100     match (&**arch, target.options.is_like_windows) {
101         // Windows x86
102         ("x86", true) => {
103             emit_ptr_va_arg(bx, addr, target_ty, false,
104                             Align::from_bytes(4).unwrap(), false)
105         }
106         // Generic x86
107         ("x86", _) => {
108             emit_ptr_va_arg(bx, addr, target_ty, false,
109                             Align::from_bytes(4).unwrap(), true)
110         }
111         // Windows AArch64
112         ("aarch64", true) => {
113             emit_ptr_va_arg(bx, addr, target_ty, false,
114                             Align::from_bytes(8).unwrap(), false)
115         }
116         // iOS AArch64
117         ("aarch64", _) if target.target_os == "ios" => {
118             emit_ptr_va_arg(bx, addr, target_ty, false,
119                             Align::from_bytes(8).unwrap(), true)
120         }
121         // Windows x86_64
122         ("x86_64", true) => {
123             let target_ty_size = bx.cx.size_of(target_ty).bytes();
124             let indirect = if target_ty_size > 8 || !target_ty_size.is_power_of_two() {
125                 true
126             } else {
127                 false
128             };
129             emit_ptr_va_arg(bx, addr, target_ty, indirect,
130                             Align::from_bytes(8).unwrap(), false)
131         }
132         // For all other architecture/OS combinations fall back to using
133         // the LLVM va_arg instruction.
134         // https://llvm.org/docs/LangRef.html#va-arg-instruction
135         _ => {
136             let va_list = if (target.arch == "aarch64" ||
137                               target.arch == "x86_64" ||
138                               target.arch == "powerpc") &&
139                              !target.options.is_like_windows {
140                 bx.load(addr.immediate(), bx.tcx().data_layout.pointer_align.abi)
141             } else {
142                 addr.immediate()
143             };
144             bx.va_arg(va_list, bx.cx.layout_of(target_ty).llvm_type(bx.cx))
145         }
146     }
147 }