diff options
| author | bors <bors@rust-lang.org> | 2018-11-29 19:28:21 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-11-29 19:28:21 +0000 |
| commit | 3e90a12a8a95933604a8b609197fce61bb24a38c (patch) | |
| tree | cf4c1b0648179447fb3b5de1f51d2645c8b693f6 /src/librustc_codegen_llvm | |
| parent | 3dde9e132207b5a40e12f8d5a1a363ebea60e0b0 (diff) | |
| parent | e9e084f5fa8820aca67e5cf0ec301e52bbae1028 (diff) | |
| download | rust-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.rs | 27 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/intrinsic.rs | 58 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/lib.rs | 1 | ||||
| -rw-r--r-- | src/librustc_codegen_llvm/va_arg.rs | 142 |
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)) + } + } +} + |
