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 😄
This commit is contained in:
commit
3e90a12a8a
@ -1,6 +1,7 @@
|
||||
#![stable(feature = "", since = "1.30.0")]
|
||||
|
||||
#![allow(non_camel_case_types)]
|
||||
#![cfg_attr(stage0, allow(dead_code))]
|
||||
|
||||
//! Utilities related to FFI bindings.
|
||||
|
||||
@ -40,3 +41,187 @@ impl fmt::Debug for c_void {
|
||||
f.pad("c_void")
|
||||
}
|
||||
}
|
||||
|
||||
/// Basic implementation of a `va_list`.
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
windows))]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
extern {
|
||||
type VaListImpl;
|
||||
}
|
||||
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
windows))]
|
||||
impl fmt::Debug for VaListImpl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "va_list* {:p}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// AArch64 ABI implementation of a `va_list`. See the
|
||||
/// [Aarch64 Procedure Call Standard] for more details.
|
||||
///
|
||||
/// [AArch64 Procedure Call Standard]:
|
||||
/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
|
||||
#[cfg(all(target_arch = "aarch64", not(windows)))]
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
struct VaListImpl {
|
||||
stack: *mut (),
|
||||
gr_top: *mut (),
|
||||
vr_top: *mut (),
|
||||
gr_offs: i32,
|
||||
vr_offs: i32,
|
||||
}
|
||||
|
||||
/// PowerPC ABI implementation of a `va_list`.
|
||||
#[cfg(all(target_arch = "powerpc", not(windows)))]
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
struct VaListImpl {
|
||||
gpr: u8,
|
||||
fpr: u8,
|
||||
reserved: u16,
|
||||
overflow_arg_area: *mut (),
|
||||
reg_save_area: *mut (),
|
||||
}
|
||||
|
||||
/// x86_64 ABI implementation of a `va_list`.
|
||||
#[cfg(all(target_arch = "x86_64", not(windows)))]
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
struct VaListImpl {
|
||||
gp_offset: i32,
|
||||
fp_offset: i32,
|
||||
overflow_arg_area: *mut (),
|
||||
reg_save_area: *mut (),
|
||||
}
|
||||
|
||||
/// A wrapper for a `va_list`
|
||||
#[lang = "va_list"]
|
||||
#[derive(Debug)]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
#[repr(transparent)]
|
||||
#[cfg(not(stage0))]
|
||||
pub struct VaList<'a>(&'a mut VaListImpl);
|
||||
|
||||
// The VaArgSafe trait needs to be used in public interfaces, however, the trait
|
||||
// itself must not be allowed to be used outside this module. Allowing users to
|
||||
// implement the trait for a new type (thereby allowing the va_arg intrinsic to
|
||||
// be used on a new type) is likely to cause undefined behavior.
|
||||
//
|
||||
// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface
|
||||
// but also ensure it cannot be used elsewhere, the trait needs to be public
|
||||
// within a private module. Once RFC 2145 has been implemented look into
|
||||
// improving this.
|
||||
mod sealed_trait {
|
||||
/// Trait which whitelists the allowed types to be used with [VaList::arg]
|
||||
///
|
||||
/// [VaList::va_arg]: struct.VaList.html#method.arg
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
pub trait VaArgSafe {}
|
||||
}
|
||||
|
||||
macro_rules! impl_va_arg_safe {
|
||||
($($t:ty),+) => {
|
||||
$(
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
impl sealed_trait::VaArgSafe for $t {}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
impl_va_arg_safe!{i8, i16, i32, i64, usize}
|
||||
impl_va_arg_safe!{u8, u16, u32, u64, isize}
|
||||
impl_va_arg_safe!{f64}
|
||||
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
impl<T> sealed_trait::VaArgSafe for *mut T {}
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
impl<T> sealed_trait::VaArgSafe for *const T {}
|
||||
|
||||
#[cfg(not(stage0))]
|
||||
impl<'a> VaList<'a> {
|
||||
/// Advance to the next arg.
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
|
||||
va_arg(self)
|
||||
}
|
||||
|
||||
/// Copy the `va_list` at the current location.
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
pub unsafe fn copy<F, R>(&mut self, f: F) -> R
|
||||
where F: for<'copy> FnOnce(VaList<'copy>) -> R {
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
windows))]
|
||||
let mut ap = va_copy(self);
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
|
||||
not(windows)))]
|
||||
let mut ap_inner = va_copy(self);
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
|
||||
not(windows)))]
|
||||
let mut ap = VaList(&mut ap_inner);
|
||||
let ret = f(VaList(ap.0));
|
||||
va_end(&mut ap);
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(stage0))]
|
||||
extern "rust-intrinsic" {
|
||||
/// Destroy the arglist `ap` after initialization with `va_start` or
|
||||
/// `va_copy`.
|
||||
fn va_end(ap: &mut VaList);
|
||||
|
||||
/// Copy the current location of arglist `src` to the arglist `dst`.
|
||||
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
|
||||
not(target_arch = "x86_64")),
|
||||
windows))]
|
||||
fn va_copy<'a>(src: &VaList<'a>) -> VaList<'a>;
|
||||
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
|
||||
not(windows)))]
|
||||
fn va_copy(src: &VaList) -> VaListImpl;
|
||||
|
||||
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
|
||||
/// argument `ap` points to.
|
||||
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaList) -> T;
|
||||
}
|
||||
|
@ -297,6 +297,7 @@ language_item_table! {
|
||||
IndexMutTraitLangItem, "index_mut", index_mut_trait, Target::Trait;
|
||||
|
||||
UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type, Target::Struct;
|
||||
VaListTypeLangItem, "va_list", va_list, Target::Struct;
|
||||
|
||||
DerefTraitLangItem, "deref", deref_trait, Target::Trait;
|
||||
DerefMutTraitLangItem, "deref_mut", deref_mut_trait, Target::Trait;
|
||||
|
@ -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.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.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);
|
||||
|
@ -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 {
|
||||
|
@ -127,6 +127,7 @@ mod mono_item;
|
||||
mod type_;
|
||||
mod type_of;
|
||||
mod value;
|
||||
mod va_arg;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LlvmCodegenBackend(());
|
||||
|
142
src/librustc_codegen_llvm/va_arg.rs
Normal file
142
src/librustc_codegen_llvm/va_arg.rs
Normal file
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
use intrinsics;
|
||||
use rustc::traits::{ObligationCause, ObligationCauseCode};
|
||||
use rustc::ty::{self, TyCtxt, Ty};
|
||||
use rustc::ty::subst::Subst;
|
||||
use rustc::util::nodemap::FxHashMap;
|
||||
use require_same_types;
|
||||
|
||||
@ -81,6 +82,16 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
it: &hir::ForeignItem) {
|
||||
let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)).as_interned_str());
|
||||
let name = it.name.as_str();
|
||||
|
||||
let mk_va_list_ty = || {
|
||||
tcx.lang_items().va_list().map(|did| {
|
||||
let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0)));
|
||||
let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
|
||||
let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
|
||||
tcx.mk_mut_ref(tcx.mk_region(env_region), va_list_ty)
|
||||
})
|
||||
};
|
||||
|
||||
let (n_tps, inputs, output, unsafety) = if name.starts_with("atomic_") {
|
||||
let split : Vec<&str> = name.split('_').collect();
|
||||
assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format");
|
||||
@ -323,6 +334,47 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
(0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32)
|
||||
}
|
||||
|
||||
"va_start" | "va_end" => {
|
||||
match mk_va_list_ty() {
|
||||
Some(va_list_ty) => (0, vec![va_list_ty], tcx.mk_unit()),
|
||||
None => bug!("va_list lang_item must be defined to use va_list intrinsics")
|
||||
}
|
||||
}
|
||||
|
||||
"va_copy" => {
|
||||
match tcx.lang_items().va_list() {
|
||||
Some(did) => {
|
||||
let region = tcx.mk_region(ty::ReLateBound(ty::INNERMOST, ty::BrAnon(0)));
|
||||
let env_region = ty::ReLateBound(ty::INNERMOST, ty::BrEnv);
|
||||
let va_list_ty = tcx.type_of(did).subst(tcx, &[region.into()]);
|
||||
let ret_ty = match va_list_ty.sty {
|
||||
ty::Adt(def, _) if def.is_struct() => {
|
||||
let fields = &def.non_enum_variant().fields;
|
||||
match tcx.type_of(fields[0].did).subst(tcx, &[region.into()]).sty {
|
||||
ty::Ref(_, element_ty, _) => match element_ty.sty {
|
||||
ty::Adt(..) => element_ty,
|
||||
_ => va_list_ty
|
||||
}
|
||||
_ => bug!("va_list structure is invalid")
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
bug!("va_list structure is invalid")
|
||||
}
|
||||
};
|
||||
(0, vec![tcx.mk_imm_ref(tcx.mk_region(env_region), va_list_ty)], ret_ty)
|
||||
}
|
||||
None => bug!("va_list lang_item must be defined to use va_list intrinsics")
|
||||
}
|
||||
}
|
||||
|
||||
"va_arg" => {
|
||||
match mk_va_list_ty() {
|
||||
Some(va_list_ty) => (1, vec![va_list_ty], param(0)),
|
||||
None => bug!("va_list lang_item must be defined to use va_list intrinsics")
|
||||
}
|
||||
}
|
||||
|
||||
"nontemporal_store" => {
|
||||
(1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_unit())
|
||||
}
|
||||
|
@ -174,5 +174,12 @@ pub use self::os_str::{OsString, OsStr};
|
||||
#[stable(feature = "raw_os", since = "1.1.0")]
|
||||
pub use core::ffi::c_void;
|
||||
|
||||
#[cfg(not(stage0))]
|
||||
#[unstable(feature = "c_variadic",
|
||||
reason = "the `c_variadic` feature has not been properly tested on \
|
||||
all supported platforms",
|
||||
issue = "27745")]
|
||||
pub use core::ffi::VaList;
|
||||
|
||||
mod c_str;
|
||||
mod os_str;
|
||||
|
@ -244,6 +244,7 @@
|
||||
#![feature(array_error_internals)]
|
||||
#![feature(asm)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(cfg_target_has_atomic)]
|
||||
#![feature(cfg_target_thread_local)]
|
||||
#![feature(cfg_target_vendor)]
|
||||
|
@ -0,0 +1,6 @@
|
||||
-include ../tools.mk
|
||||
|
||||
all:
|
||||
$(RUSTC) checkrust.rs
|
||||
$(CC) test.c $(call STATICLIB,checkrust) $(call OUT_EXE,test) $(EXTRACFLAGS)
|
||||
$(call RUN,test)
|
@ -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.
|
||||
|
||||
#![crate_type = "staticlib"]
|
||||
#![feature(c_variadic)]
|
||||
#![feature(libc)]
|
||||
|
||||
extern crate libc;
|
||||
|
||||
use libc::{c_char, c_double, c_int, c_long, c_longlong};
|
||||
use std::ffi::VaList;
|
||||
use std::slice;
|
||||
use std::ffi::CStr;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum AnswerType {
|
||||
Double,
|
||||
Long,
|
||||
LongLong,
|
||||
Int,
|
||||
Byte,
|
||||
CStr,
|
||||
Skip,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub union AnswerData {
|
||||
pub double: c_double,
|
||||
pub long: c_long,
|
||||
pub longlong: c_longlong,
|
||||
pub int: c_int,
|
||||
pub byte: c_char,
|
||||
pub cstr: *const c_char,
|
||||
pub skip_ty: AnswerType,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Answer {
|
||||
tag: AnswerType,
|
||||
data: AnswerData,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe fn compare_answers(answers: &[Answer], mut ap: VaList) -> usize {
|
||||
for (i, answer) in answers.iter().enumerate() {
|
||||
match answer {
|
||||
Answer { tag: AnswerType::Double, data: AnswerData { double: d } } => {
|
||||
let tmp = ap.arg::<c_double>();
|
||||
if d.floor() != tmp.floor() {
|
||||
println!("Double: {} != {}", d, tmp);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
Answer { tag: AnswerType::Long, data: AnswerData { long: l } } => {
|
||||
let tmp = ap.arg::<c_long>();
|
||||
if *l != tmp {
|
||||
println!("Long: {} != {}", l, tmp);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
Answer { tag: AnswerType::LongLong, data: AnswerData { longlong: l } } => {
|
||||
let tmp = ap.arg::<c_longlong>();
|
||||
if *l != tmp {
|
||||
println!("Long Long: {} != {}", l, tmp);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
Answer { tag: AnswerType::Int, data: AnswerData { int: n } } => {
|
||||
let tmp = ap.arg::<c_int>();
|
||||
if *n != tmp {
|
||||
println!("Int: {} != {}", n, tmp);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
Answer { tag: AnswerType::Byte, data: AnswerData { byte: b } } => {
|
||||
let tmp = ap.arg::<c_char>();
|
||||
if *b != tmp {
|
||||
println!("Byte: {} != {}", b, tmp);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
Answer { tag: AnswerType::CStr, data: AnswerData { cstr: c0 } } => {
|
||||
let c1 = ap.arg::<*const c_char>();
|
||||
let cstr0 = CStr::from_ptr(*c0);
|
||||
let cstr1 = CStr::from_ptr(c1);
|
||||
if cstr0 != cstr1 {
|
||||
println!("C String: {:?} != {:?}", cstr0, cstr1);
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Unknown type!");
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn check_rust(argc: usize, answers: *const Answer, ap: VaList) -> usize {
|
||||
let slice = slice::from_raw_parts(answers, argc);
|
||||
compare_answers(slice, ap)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn check_rust_copy(argc: usize, answers: *const Answer,
|
||||
mut ap: VaList) -> usize {
|
||||
let slice = slice::from_raw_parts(answers, argc);
|
||||
let mut skip_n = 0;
|
||||
for (i, answer) in slice.iter().enumerate() {
|
||||
match answer {
|
||||
Answer { tag: AnswerType::Skip, data: AnswerData { skip_ty } } => {
|
||||
match skip_ty {
|
||||
AnswerType::Double => { ap.arg::<c_double>(); }
|
||||
AnswerType::Long => { ap.arg::<c_long>(); }
|
||||
AnswerType::LongLong => { ap.arg::<c_longlong>(); }
|
||||
AnswerType::Int => { ap.arg::<c_int>(); }
|
||||
AnswerType::Byte => { ap.arg::<c_char>(); }
|
||||
AnswerType::CStr => { ap.arg::<*const c_char>(); }
|
||||
_ => { return i; }
|
||||
};
|
||||
}
|
||||
_ => {
|
||||
skip_n = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ap.copy(|ap| {
|
||||
compare_answers(&slice[skip_n..], ap)
|
||||
})
|
||||
}
|
95
src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
Normal file
95
src/test/run-make-fulldeps/c-link-to-rust-va-list-fn/test.c
Normal file
@ -0,0 +1,95 @@
|
||||
// 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.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef enum {
|
||||
TAG_DOUBLE,
|
||||
TAG_LONG,
|
||||
TAG_LONGLONG,
|
||||
TAG_INT,
|
||||
TAG_BYTE,
|
||||
TAG_CSTR,
|
||||
TAG_SKIP,
|
||||
} tag;
|
||||
|
||||
typedef struct {
|
||||
tag answer_type;
|
||||
union {
|
||||
double double_precision;
|
||||
long num_long;
|
||||
long long num_longlong;
|
||||
int num_int;
|
||||
int8_t byte;
|
||||
char* cstr;
|
||||
tag skip_ty;
|
||||
} answer_data;
|
||||
} answer;
|
||||
|
||||
#define MK_DOUBLE(n) \
|
||||
{ TAG_DOUBLE, { .double_precision = n } }
|
||||
#define MK_LONG(n) \
|
||||
{ TAG_LONG, { .num_long = n } }
|
||||
#define MK_LONGLONG(n) \
|
||||
{ TAG_LONGLONG, { .num_longlong = n } }
|
||||
#define MK_INT(n) \
|
||||
{ TAG_INT, { .num_int = n } }
|
||||
#define MK_BYTE(b) \
|
||||
{ TAG_BYTE, { .byte = b } }
|
||||
#define MK_CSTR(s) \
|
||||
{ TAG_CSTR, { .cstr = s } }
|
||||
#define MK_SKIP(ty) \
|
||||
{ TAG_SKIP, { .skip_ty = TAG_ ## ty } }
|
||||
|
||||
extern size_t check_rust(size_t argc, const answer* answers, va_list ap);
|
||||
extern size_t check_rust_copy(size_t argc, const answer* answers, va_list ap);
|
||||
|
||||
size_t test_check_rust(size_t argc, const answer* answers, ...) {
|
||||
size_t ret = 0;
|
||||
va_list ap;
|
||||
va_start(ap, answers);
|
||||
ret = check_rust(argc, answers, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t test_check_rust_copy(size_t argc, const answer* answers, ...) {
|
||||
size_t ret = 0;
|
||||
va_list ap;
|
||||
va_start(ap, answers);
|
||||
ret = check_rust_copy(argc, answers, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
answer test_alignment0[] = {MK_LONGLONG(0x01LL), MK_INT(0x02), MK_LONGLONG(0x03LL)};
|
||||
assert(test_check_rust(3, test_alignment0, 0x01LL, 0x02, 0x03LL) == 0);
|
||||
|
||||
answer test_alignment1[] = {MK_INT(-1), MK_BYTE('A'), MK_BYTE('4'), MK_BYTE(';'),
|
||||
MK_INT(0x32), MK_INT(0x10000001), MK_CSTR("Valid!")};
|
||||
assert(test_check_rust(7, test_alignment1, -1, 'A', '4', ';', 0x32, 0x10000001,
|
||||
"Valid!") == 0);
|
||||
|
||||
answer basic_answers[] = {MK_DOUBLE(3.14), MK_LONG(12l), MK_BYTE('a'),
|
||||
MK_DOUBLE(6.28), MK_CSTR("Hello"), MK_INT(42),
|
||||
MK_CSTR("World")};
|
||||
assert(test_check_rust(7, basic_answers, 3.14, 12l, 'a', 6.28, "Hello",
|
||||
42, "World") == 0);
|
||||
|
||||
answer copy_answers[] = { MK_SKIP(DOUBLE), MK_SKIP(INT), MK_SKIP(BYTE), MK_SKIP(CSTR),
|
||||
MK_CSTR("Correctly skipped and copied list") };
|
||||
assert(test_check_rust_copy(5, copy_answers, 6.28, 16, 'A', "Skip Me!",
|
||||
"Correctly skipped and copied list") == 0);
|
||||
return 0;
|
||||
}
|
@ -74,6 +74,13 @@ const EXCEPTION_PATHS: &[&str] = &[
|
||||
"src/libcore/tests",
|
||||
"src/liballoc/tests/lib.rs",
|
||||
|
||||
// The `VaList` implementation must have platform specific code.
|
||||
// The Windows implementation of a `va_list` is always a character
|
||||
// pointer regardless of the target architecture. As a result,
|
||||
// we must use `#[cfg(windows)]` to conditionally compile the
|
||||
// correct `VaList` structure for windows.
|
||||
"src/libcore/ffi.rs",
|
||||
|
||||
// non-std crates
|
||||
"src/test",
|
||||
"src/tools",
|
||||
|
Loading…
x
Reference in New Issue
Block a user