]> git.proxmox.com Git - rustc.git/blob - src/librustc_trans/abi.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / librustc_trans / abi.rs
1 // Copyright 2012-2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use llvm::{self, ValueRef};
12 use base;
13 use builder::Builder;
14 use common::{type_is_fat_ptr, BlockAndBuilder};
15 use context::CrateContext;
16 use cabi_x86;
17 use cabi_x86_64;
18 use cabi_x86_win64;
19 use cabi_arm;
20 use cabi_aarch64;
21 use cabi_powerpc;
22 use cabi_powerpc64;
23 use cabi_mips;
24 use cabi_asmjs;
25 use machine::{llalign_of_min, llsize_of, llsize_of_real};
26 use type_::Type;
27 use type_of;
28
29 use rustc::hir;
30 use rustc::ty::{self, Ty};
31
32 use libc::c_uint;
33
34 pub use syntax::abi::Abi;
35 pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA};
36
37 #[derive(Clone, Copy, PartialEq, Debug)]
38 enum ArgKind {
39 /// Pass the argument directly using the normal converted
40 /// LLVM type or by coercing to another specified type
41 Direct,
42 /// Pass the argument indirectly via a hidden pointer
43 Indirect,
44 /// Ignore the argument (useful for empty struct)
45 Ignore,
46 }
47
48 /// Information about how a specific C type
49 /// should be passed to or returned from a function
50 ///
51 /// This is borrowed from clang's ABIInfo.h
52 #[derive(Clone, Copy, Debug)]
53 pub struct ArgType {
54 kind: ArgKind,
55 /// Original LLVM type
56 pub original_ty: Type,
57 /// Sizing LLVM type (pointers are opaque).
58 /// Unlike original_ty, this is guaranteed to be complete.
59 ///
60 /// For example, while we're computing the function pointer type in
61 /// `struct Foo(fn(Foo));`, `original_ty` is still LLVM's `%Foo = {}`.
62 /// The field type will likely end up being `void(%Foo)*`, but we cannot
63 /// use `%Foo` to compute properties (e.g. size and alignment) of `Foo`,
64 /// until `%Foo` is completed by having all of its field types inserted,
65 /// so `ty` holds the "sizing type" of `Foo`, which replaces all pointers
66 /// with opaque ones, resulting in `{i8*}` for `Foo`.
67 /// ABI-specific logic can then look at the size, alignment and fields of
68 /// `{i8*}` in order to determine how the argument will be passed.
69 /// Only later will `original_ty` aka `%Foo` be used in the LLVM function
70 /// pointer type, without ever having introspected it.
71 pub ty: Type,
72 /// Signedness for integer types, None for other types
73 pub signedness: Option<bool>,
74 /// Coerced LLVM Type
75 pub cast: Option<Type>,
76 /// Dummy argument, which is emitted before the real argument
77 pub pad: Option<Type>,
78 /// LLVM attributes of argument
79 pub attrs: llvm::Attributes
80 }
81
82 impl ArgType {
83 fn new(original_ty: Type, ty: Type) -> ArgType {
84 ArgType {
85 kind: ArgKind::Direct,
86 original_ty: original_ty,
87 ty: ty,
88 signedness: None,
89 cast: None,
90 pad: None,
91 attrs: llvm::Attributes::default()
92 }
93 }
94
95 pub fn make_indirect(&mut self, ccx: &CrateContext) {
96 assert_eq!(self.kind, ArgKind::Direct);
97
98 // Wipe old attributes, likely not valid through indirection.
99 self.attrs = llvm::Attributes::default();
100
101 let llarg_sz = llsize_of_real(ccx, self.ty);
102
103 // For non-immediate arguments the callee gets its own copy of
104 // the value on the stack, so there are no aliases. It's also
105 // program-invisible so can't possibly capture
106 self.attrs.set(llvm::Attribute::NoAlias)
107 .set(llvm::Attribute::NoCapture)
108 .set_dereferenceable(llarg_sz);
109
110 self.kind = ArgKind::Indirect;
111 }
112
113 pub fn ignore(&mut self) {
114 assert_eq!(self.kind, ArgKind::Direct);
115 self.kind = ArgKind::Ignore;
116 }
117
118 pub fn extend_integer_width_to(&mut self, bits: u64) {
119 // Only integers have signedness
120 if let Some(signed) = self.signedness {
121 if self.ty.int_width() < bits {
122 self.attrs.set(if signed {
123 llvm::Attribute::SExt
124 } else {
125 llvm::Attribute::ZExt
126 });
127 }
128 }
129 }
130
131 pub fn is_indirect(&self) -> bool {
132 self.kind == ArgKind::Indirect
133 }
134
135 pub fn is_ignore(&self) -> bool {
136 self.kind == ArgKind::Ignore
137 }
138
139 /// Get the LLVM type for an lvalue of the original Rust type of
140 /// this argument/return, i.e. the result of `type_of::type_of`.
141 pub fn memory_ty(&self, ccx: &CrateContext) -> Type {
142 if self.original_ty == Type::i1(ccx) {
143 Type::i8(ccx)
144 } else {
145 self.original_ty
146 }
147 }
148
149 /// Store a direct/indirect value described by this ArgType into a
150 /// lvalue for the original Rust type of this argument/return.
151 /// Can be used for both storing formal arguments into Rust variables
152 /// or results of call/invoke instructions into their destinations.
153 pub fn store(&self, b: &Builder, mut val: ValueRef, dst: ValueRef) {
154 if self.is_ignore() {
155 return;
156 }
157 if self.is_indirect() {
158 let llsz = llsize_of(b.ccx, self.ty);
159 let llalign = llalign_of_min(b.ccx, self.ty);
160 base::call_memcpy(b, dst, val, llsz, llalign as u32);
161 } else if let Some(ty) = self.cast {
162 let cast_dst = b.pointercast(dst, ty.ptr_to());
163 let store = b.store(val, cast_dst);
164 let llalign = llalign_of_min(b.ccx, self.ty);
165 unsafe {
166 llvm::LLVMSetAlignment(store, llalign);
167 }
168 } else {
169 if self.original_ty == Type::i1(b.ccx) {
170 val = b.zext(val, Type::i8(b.ccx));
171 }
172 b.store(val, dst);
173 }
174 }
175
176 pub fn store_fn_arg(&self, bcx: &BlockAndBuilder, idx: &mut usize, dst: ValueRef) {
177 if self.pad.is_some() {
178 *idx += 1;
179 }
180 if self.is_ignore() {
181 return;
182 }
183 let val = llvm::get_param(bcx.fcx().llfn, *idx as c_uint);
184 *idx += 1;
185 self.store(bcx, val, dst);
186 }
187 }
188
189 /// Metadata describing how the arguments to a native function
190 /// should be passed in order to respect the native ABI.
191 ///
192 /// I will do my best to describe this structure, but these
193 /// comments are reverse-engineered and may be inaccurate. -NDM
194 pub struct FnType {
195 /// The LLVM types of each argument.
196 pub args: Vec<ArgType>,
197
198 /// LLVM return type.
199 pub ret: ArgType,
200
201 pub variadic: bool,
202
203 pub cconv: llvm::CallConv
204 }
205
206 impl FnType {
207 pub fn new<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
208 abi: Abi,
209 sig: &ty::FnSig<'tcx>,
210 extra_args: &[Ty<'tcx>]) -> FnType {
211 let mut fn_ty = FnType::unadjusted(ccx, abi, sig, extra_args);
212 fn_ty.adjust_for_abi(ccx, abi, sig);
213 fn_ty
214 }
215
216 pub fn unadjusted<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
217 abi: Abi,
218 sig: &ty::FnSig<'tcx>,
219 extra_args: &[Ty<'tcx>]) -> FnType {
220 use self::Abi::*;
221 let cconv = match ccx.sess().target.target.adjust_abi(abi) {
222 RustIntrinsic | PlatformIntrinsic |
223 Rust | RustCall => llvm::CCallConv,
224
225 // It's the ABI's job to select this, not us.
226 System => bug!("system abi should be selected elsewhere"),
227
228 Stdcall => llvm::X86StdcallCallConv,
229 Fastcall => llvm::X86FastcallCallConv,
230 Vectorcall => llvm::X86_VectorCall,
231 C => llvm::CCallConv,
232 Win64 => llvm::X86_64_Win64,
233
234 // These API constants ought to be more specific...
235 Cdecl => llvm::CCallConv,
236 Aapcs => llvm::CCallConv,
237 };
238
239 let mut inputs = &sig.inputs[..];
240 let extra_args = if abi == RustCall {
241 assert!(!sig.variadic && extra_args.is_empty());
242
243 match inputs[inputs.len() - 1].sty {
244 ty::TyTuple(ref tupled_arguments) => {
245 inputs = &inputs[..inputs.len() - 1];
246 &tupled_arguments[..]
247 }
248 _ => {
249 bug!("argument to function with \"rust-call\" ABI \
250 is not a tuple");
251 }
252 }
253 } else {
254 assert!(sig.variadic || extra_args.is_empty());
255 extra_args
256 };
257
258 let target = &ccx.sess().target.target;
259 let win_x64_gnu = target.target_os == "windows"
260 && target.arch == "x86_64"
261 && target.target_env == "gnu";
262 let rust_abi = match abi {
263 RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true,
264 _ => false
265 };
266
267 let arg_of = |ty: Ty<'tcx>, is_return: bool| {
268 if ty.is_bool() {
269 let llty = Type::i1(ccx);
270 let mut arg = ArgType::new(llty, llty);
271 arg.attrs.set(llvm::Attribute::ZExt);
272 arg
273 } else {
274 let mut arg = ArgType::new(type_of::type_of(ccx, ty),
275 type_of::sizing_type_of(ccx, ty));
276 if ty.is_integral() {
277 arg.signedness = Some(ty.is_signed());
278 }
279 if llsize_of_real(ccx, arg.ty) == 0 {
280 // For some forsaken reason, x86_64-pc-windows-gnu
281 // doesn't ignore zero-sized struct arguments.
282 if is_return || rust_abi || !win_x64_gnu {
283 arg.ignore();
284 }
285 }
286 arg
287 }
288 };
289
290 let ret_ty = match sig.output {
291 ty::FnConverging(ret_ty) => ret_ty,
292 ty::FnDiverging => ccx.tcx().mk_nil()
293 };
294 let mut ret = arg_of(ret_ty, true);
295
296 if !type_is_fat_ptr(ccx.tcx(), ret_ty) {
297 // The `noalias` attribute on the return value is useful to a
298 // function ptr caller.
299 if let ty::TyBox(_) = ret_ty.sty {
300 // `Box` pointer return values never alias because ownership
301 // is transferred
302 ret.attrs.set(llvm::Attribute::NoAlias);
303 }
304
305 // We can also mark the return value as `dereferenceable` in certain cases
306 match ret_ty.sty {
307 // These are not really pointers but pairs, (pointer, len)
308 ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
309 ty::TyBox(ty) => {
310 let llty = type_of::sizing_type_of(ccx, ty);
311 let llsz = llsize_of_real(ccx, llty);
312 ret.attrs.set_dereferenceable(llsz);
313 }
314 _ => {}
315 }
316 }
317
318 let mut args = Vec::with_capacity(inputs.len() + extra_args.len());
319
320 // Handle safe Rust thin and fat pointers.
321 let rust_ptr_attrs = |ty: Ty<'tcx>, arg: &mut ArgType| match ty.sty {
322 // `Box` pointer parameters never alias because ownership is transferred
323 ty::TyBox(inner) => {
324 arg.attrs.set(llvm::Attribute::NoAlias);
325 Some(inner)
326 }
327
328 ty::TyRef(b, mt) => {
329 use rustc::ty::{BrAnon, ReLateBound};
330
331 // `&mut` pointer parameters never alias other parameters, or mutable global data
332 //
333 // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as
334 // both `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely
335 // on memory dependencies rather than pointer equality
336 let interior_unsafe = mt.ty.type_contents(ccx.tcx()).interior_unsafe();
337
338 if mt.mutbl != hir::MutMutable && !interior_unsafe {
339 arg.attrs.set(llvm::Attribute::NoAlias);
340 }
341
342 if mt.mutbl == hir::MutImmutable && !interior_unsafe {
343 arg.attrs.set(llvm::Attribute::ReadOnly);
344 }
345
346 // When a reference in an argument has no named lifetime, it's
347 // impossible for that reference to escape this function
348 // (returned or stored beyond the call by a closure).
349 if let ReLateBound(_, BrAnon(_)) = *b {
350 arg.attrs.set(llvm::Attribute::NoCapture);
351 }
352
353 Some(mt.ty)
354 }
355 _ => None
356 };
357
358 for ty in inputs.iter().chain(extra_args.iter()) {
359 let mut arg = arg_of(ty, false);
360
361 if type_is_fat_ptr(ccx.tcx(), ty) {
362 let original_tys = arg.original_ty.field_types();
363 let sizing_tys = arg.ty.field_types();
364 assert_eq!((original_tys.len(), sizing_tys.len()), (2, 2));
365
366 let mut data = ArgType::new(original_tys[0], sizing_tys[0]);
367 let mut info = ArgType::new(original_tys[1], sizing_tys[1]);
368
369 if let Some(inner) = rust_ptr_attrs(ty, &mut data) {
370 data.attrs.set(llvm::Attribute::NonNull);
371 if ccx.tcx().struct_tail(inner).is_trait() {
372 info.attrs.set(llvm::Attribute::NonNull);
373 }
374 }
375 args.push(data);
376 args.push(info);
377 } else {
378 if let Some(inner) = rust_ptr_attrs(ty, &mut arg) {
379 let llty = type_of::sizing_type_of(ccx, inner);
380 let llsz = llsize_of_real(ccx, llty);
381 arg.attrs.set_dereferenceable(llsz);
382 }
383 args.push(arg);
384 }
385 }
386
387 FnType {
388 args: args,
389 ret: ret,
390 variadic: sig.variadic,
391 cconv: cconv
392 }
393 }
394
395 pub fn adjust_for_abi<'a, 'tcx>(&mut self,
396 ccx: &CrateContext<'a, 'tcx>,
397 abi: Abi,
398 sig: &ty::FnSig<'tcx>) {
399 if abi == Abi::Rust || abi == Abi::RustCall ||
400 abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic {
401 let fixup = |arg: &mut ArgType| {
402 let mut llty = arg.ty;
403
404 // Replace newtypes with their inner-most type.
405 while llty.kind() == llvm::TypeKind::Struct {
406 let inner = llty.field_types();
407 if inner.len() != 1 {
408 break;
409 }
410 llty = inner[0];
411 }
412
413 if !llty.is_aggregate() {
414 // Scalars and vectors, always immediate.
415 if llty != arg.ty {
416 // Needs a cast as we've unpacked a newtype.
417 arg.cast = Some(llty);
418 }
419 return;
420 }
421
422 let size = llsize_of_real(ccx, llty);
423 if size > llsize_of_real(ccx, ccx.int_type()) {
424 arg.make_indirect(ccx);
425 } else if size > 0 {
426 // We want to pass small aggregates as immediates, but using
427 // a LLVM aggregate type for this leads to bad optimizations,
428 // so we pick an appropriately sized integer type instead.
429 arg.cast = Some(Type::ix(ccx, size * 8));
430 }
431 };
432 // Fat pointers are returned by-value.
433 if !self.ret.is_ignore() {
434 if !type_is_fat_ptr(ccx.tcx(), sig.output.unwrap()) {
435 fixup(&mut self.ret);
436 }
437 }
438 for arg in &mut self.args {
439 if arg.is_ignore() { continue; }
440 fixup(arg);
441 }
442 if self.ret.is_indirect() {
443 self.ret.attrs.set(llvm::Attribute::StructRet);
444 }
445 return;
446 }
447
448 match &ccx.sess().target.target.arch[..] {
449 "x86" => cabi_x86::compute_abi_info(ccx, self),
450 "x86_64" => if ccx.sess().target.target.options.is_like_windows {
451 cabi_x86_win64::compute_abi_info(ccx, self);
452 } else {
453 cabi_x86_64::compute_abi_info(ccx, self);
454 },
455 "aarch64" => cabi_aarch64::compute_abi_info(ccx, self),
456 "arm" => {
457 let flavor = if ccx.sess().target.target.target_os == "ios" {
458 cabi_arm::Flavor::Ios
459 } else {
460 cabi_arm::Flavor::General
461 };
462 cabi_arm::compute_abi_info(ccx, self, flavor);
463 },
464 "mips" => cabi_mips::compute_abi_info(ccx, self),
465 "powerpc" => cabi_powerpc::compute_abi_info(ccx, self),
466 "powerpc64" => cabi_powerpc64::compute_abi_info(ccx, self),
467 "asmjs" => cabi_asmjs::compute_abi_info(ccx, self),
468 a => ccx.sess().fatal(&format!("unrecognized arch \"{}\" in target specification", a))
469 }
470
471 if self.ret.is_indirect() {
472 self.ret.attrs.set(llvm::Attribute::StructRet);
473 }
474 }
475
476 pub fn llvm_type(&self, ccx: &CrateContext) -> Type {
477 let mut llargument_tys = Vec::new();
478
479 let llreturn_ty = if self.ret.is_ignore() {
480 Type::void(ccx)
481 } else if self.ret.is_indirect() {
482 llargument_tys.push(self.ret.original_ty.ptr_to());
483 Type::void(ccx)
484 } else {
485 self.ret.cast.unwrap_or(self.ret.original_ty)
486 };
487
488 for arg in &self.args {
489 if arg.is_ignore() {
490 continue;
491 }
492 // add padding
493 if let Some(ty) = arg.pad {
494 llargument_tys.push(ty);
495 }
496
497 let llarg_ty = if arg.is_indirect() {
498 arg.original_ty.ptr_to()
499 } else {
500 arg.cast.unwrap_or(arg.original_ty)
501 };
502
503 llargument_tys.push(llarg_ty);
504 }
505
506 if self.variadic {
507 Type::variadic_func(&llargument_tys, &llreturn_ty)
508 } else {
509 Type::func(&llargument_tys, &llreturn_ty)
510 }
511 }
512
513 pub fn apply_attrs_llfn(&self, llfn: ValueRef) {
514 let mut i = if self.ret.is_indirect() { 1 } else { 0 };
515 if !self.ret.is_ignore() {
516 self.ret.attrs.apply_llfn(i, llfn);
517 }
518 i += 1;
519 for arg in &self.args {
520 if !arg.is_ignore() {
521 if arg.pad.is_some() { i += 1; }
522 arg.attrs.apply_llfn(i, llfn);
523 i += 1;
524 }
525 }
526 }
527
528 pub fn apply_attrs_callsite(&self, callsite: ValueRef) {
529 let mut i = if self.ret.is_indirect() { 1 } else { 0 };
530 if !self.ret.is_ignore() {
531 self.ret.attrs.apply_callsite(i, callsite);
532 }
533 i += 1;
534 for arg in &self.args {
535 if !arg.is_ignore() {
536 if arg.pad.is_some() { i += 1; }
537 arg.attrs.apply_callsite(i, callsite);
538 i += 1;
539 }
540 }
541
542 if self.cconv != llvm::CCallConv {
543 llvm::SetInstructionCallConv(callsite, self.cconv);
544 }
545 }
546 }