Skip to content

Commit 3e90a12

Browse files
committed
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 😄
2 parents 3dde9e1 + e9e084f commit 3e90a12

File tree

13 files changed

+711
-13
lines changed

13 files changed

+711
-13
lines changed

src/libcore/ffi.rs

+185
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![stable(feature = "", since = "1.30.0")]
22

33
#![allow(non_camel_case_types)]
4+
#![cfg_attr(stage0, allow(dead_code))]
45

56
//! Utilities related to FFI bindings.
67
@@ -40,3 +41,187 @@ impl fmt::Debug for c_void {
4041
f.pad("c_void")
4142
}
4243
}
44+
45+
/// Basic implementation of a `va_list`.
46+
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
47+
not(target_arch = "x86_64")),
48+
windows))]
49+
#[unstable(feature = "c_variadic",
50+
reason = "the `c_variadic` feature has not been properly tested on \
51+
all supported platforms",
52+
issue = "27745")]
53+
extern {
54+
type VaListImpl;
55+
}
56+
57+
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
58+
not(target_arch = "x86_64")),
59+
windows))]
60+
impl fmt::Debug for VaListImpl {
61+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62+
write!(f, "va_list* {:p}", self)
63+
}
64+
}
65+
66+
/// AArch64 ABI implementation of a `va_list`. See the
67+
/// [Aarch64 Procedure Call Standard] for more details.
68+
///
69+
/// [AArch64 Procedure Call Standard]:
70+
/// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
71+
#[cfg(all(target_arch = "aarch64", not(windows)))]
72+
#[repr(C)]
73+
#[derive(Debug)]
74+
#[unstable(feature = "c_variadic",
75+
reason = "the `c_variadic` feature has not been properly tested on \
76+
all supported platforms",
77+
issue = "27745")]
78+
struct VaListImpl {
79+
stack: *mut (),
80+
gr_top: *mut (),
81+
vr_top: *mut (),
82+
gr_offs: i32,
83+
vr_offs: i32,
84+
}
85+
86+
/// PowerPC ABI implementation of a `va_list`.
87+
#[cfg(all(target_arch = "powerpc", not(windows)))]
88+
#[repr(C)]
89+
#[derive(Debug)]
90+
#[unstable(feature = "c_variadic",
91+
reason = "the `c_variadic` feature has not been properly tested on \
92+
all supported platforms",
93+
issue = "27745")]
94+
struct VaListImpl {
95+
gpr: u8,
96+
fpr: u8,
97+
reserved: u16,
98+
overflow_arg_area: *mut (),
99+
reg_save_area: *mut (),
100+
}
101+
102+
/// x86_64 ABI implementation of a `va_list`.
103+
#[cfg(all(target_arch = "x86_64", not(windows)))]
104+
#[repr(C)]
105+
#[derive(Debug)]
106+
#[unstable(feature = "c_variadic",
107+
reason = "the `c_variadic` feature has not been properly tested on \
108+
all supported platforms",
109+
issue = "27745")]
110+
struct VaListImpl {
111+
gp_offset: i32,
112+
fp_offset: i32,
113+
overflow_arg_area: *mut (),
114+
reg_save_area: *mut (),
115+
}
116+
117+
/// A wrapper for a `va_list`
118+
#[lang = "va_list"]
119+
#[derive(Debug)]
120+
#[unstable(feature = "c_variadic",
121+
reason = "the `c_variadic` feature has not been properly tested on \
122+
all supported platforms",
123+
issue = "27745")]
124+
#[repr(transparent)]
125+
#[cfg(not(stage0))]
126+
pub struct VaList<'a>(&'a mut VaListImpl);
127+
128+
// The VaArgSafe trait needs to be used in public interfaces, however, the trait
129+
// itself must not be allowed to be used outside this module. Allowing users to
130+
// implement the trait for a new type (thereby allowing the va_arg intrinsic to
131+
// be used on a new type) is likely to cause undefined behavior.
132+
//
133+
// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface
134+
// but also ensure it cannot be used elsewhere, the trait needs to be public
135+
// within a private module. Once RFC 2145 has been implemented look into
136+
// improving this.
137+
mod sealed_trait {
138+
/// Trait which whitelists the allowed types to be used with [VaList::arg]
139+
///
140+
/// [VaList::va_arg]: struct.VaList.html#method.arg
141+
#[unstable(feature = "c_variadic",
142+
reason = "the `c_variadic` feature has not been properly tested on \
143+
all supported platforms",
144+
issue = "27745")]
145+
pub trait VaArgSafe {}
146+
}
147+
148+
macro_rules! impl_va_arg_safe {
149+
($($t:ty),+) => {
150+
$(
151+
#[unstable(feature = "c_variadic",
152+
reason = "the `c_variadic` feature has not been properly tested on \
153+
all supported platforms",
154+
issue = "27745")]
155+
impl sealed_trait::VaArgSafe for $t {}
156+
)+
157+
}
158+
}
159+
160+
impl_va_arg_safe!{i8, i16, i32, i64, usize}
161+
impl_va_arg_safe!{u8, u16, u32, u64, isize}
162+
impl_va_arg_safe!{f64}
163+
164+
#[unstable(feature = "c_variadic",
165+
reason = "the `c_variadic` feature has not been properly tested on \
166+
all supported platforms",
167+
issue = "27745")]
168+
impl<T> sealed_trait::VaArgSafe for *mut T {}
169+
#[unstable(feature = "c_variadic",
170+
reason = "the `c_variadic` feature has not been properly tested on \
171+
all supported platforms",
172+
issue = "27745")]
173+
impl<T> sealed_trait::VaArgSafe for *const T {}
174+
175+
#[cfg(not(stage0))]
176+
impl<'a> VaList<'a> {
177+
/// Advance to the next arg.
178+
#[unstable(feature = "c_variadic",
179+
reason = "the `c_variadic` feature has not been properly tested on \
180+
all supported platforms",
181+
issue = "27745")]
182+
pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
183+
va_arg(self)
184+
}
185+
186+
/// Copy the `va_list` at the current location.
187+
#[unstable(feature = "c_variadic",
188+
reason = "the `c_variadic` feature has not been properly tested on \
189+
all supported platforms",
190+
issue = "27745")]
191+
pub unsafe fn copy<F, R>(&mut self, f: F) -> R
192+
where F: for<'copy> FnOnce(VaList<'copy>) -> R {
193+
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
194+
not(target_arch = "x86_64")),
195+
windows))]
196+
let mut ap = va_copy(self);
197+
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
198+
not(windows)))]
199+
let mut ap_inner = va_copy(self);
200+
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
201+
not(windows)))]
202+
let mut ap = VaList(&mut ap_inner);
203+
let ret = f(VaList(ap.0));
204+
va_end(&mut ap);
205+
ret
206+
}
207+
}
208+
209+
#[cfg(not(stage0))]
210+
extern "rust-intrinsic" {
211+
/// Destroy the arglist `ap` after initialization with `va_start` or
212+
/// `va_copy`.
213+
fn va_end(ap: &mut VaList);
214+
215+
/// Copy the current location of arglist `src` to the arglist `dst`.
216+
#[cfg(any(all(not(target_arch = "aarch64"), not(target_arch = "powerpc"),
217+
not(target_arch = "x86_64")),
218+
windows))]
219+
fn va_copy<'a>(src: &VaList<'a>) -> VaList<'a>;
220+
#[cfg(all(any(target_arch = "aarch64", target_arch = "powerpc", target_arch = "x86_64"),
221+
not(windows)))]
222+
fn va_copy(src: &VaList) -> VaListImpl;
223+
224+
/// Loads an argument of type `T` from the `va_list` `ap` and increment the
225+
/// argument `ap` points to.
226+
fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaList) -> T;
227+
}

src/librustc/middle/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ language_item_table! {
297297
IndexMutTraitLangItem, "index_mut", index_mut_trait, Target::Trait;
298298

299299
UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type, Target::Struct;
300+
VaListTypeLangItem, "va_list", va_list, Target::Struct;
300301

301302
DerefTraitLangItem, "deref", deref_trait, Target::Trait;
302303
DerefMutTraitLangItem, "deref_mut", deref_mut_trait, Target::Trait;

src/librustc_codegen_llvm/context.rs

+16-11
Original file line numberDiff line numberDiff line change
@@ -723,17 +723,17 @@ impl IntrinsicDeclarationMethods<'tcx> for CodegenCx<'b, 'tcx> {
723723
ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64);
724724
ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128);
725725

726-
ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
727-
ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
728-
ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
729-
ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
730-
ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
731-
732-
ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
733-
ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
734-
ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
735-
ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
736-
ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
726+
ifn!("llvm.fshl.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
727+
ifn!("llvm.fshl.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
728+
ifn!("llvm.fshl.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
729+
ifn!("llvm.fshl.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
730+
ifn!("llvm.fshl.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
731+
732+
ifn!("llvm.fshr.i8", fn(t_i8, t_i8, t_i8) -> t_i8);
733+
ifn!("llvm.fshr.i16", fn(t_i16, t_i16, t_i16) -> t_i16);
734+
ifn!("llvm.fshr.i32", fn(t_i32, t_i32, t_i32) -> t_i32);
735+
ifn!("llvm.fshr.i64", fn(t_i64, t_i64, t_i64) -> t_i64);
736+
ifn!("llvm.fshr.i128", fn(t_i128, t_i128, t_i128) -> t_i128);
737737

738738
ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1});
739739
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> {
783783
ifn!("llvm.assume", fn(i1) -> void);
784784
ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void);
785785

786+
// variadic intrinsics
787+
ifn!("llvm.va_start", fn(i8p) -> void);
788+
ifn!("llvm.va_end", fn(i8p) -> void);
789+
ifn!("llvm.va_copy", fn(i8p, i8p) -> void);
790+
786791
if self.sess().opts.debuginfo != DebugInfo::None {
787792
ifn!("llvm.dbg.declare", fn(self.type_metadata(), self.type_metadata()) -> void);
788793
ifn!("llvm.dbg.value", fn(self.type_metadata(), t_i64, self.type_metadata()) -> void);

src/librustc_codegen_llvm/intrinsic.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ use context::CodegenCx;
2424
use type_::Type;
2525
use type_of::LayoutLlvmExt;
2626
use rustc::ty::{self, Ty};
27-
use rustc::ty::layout::{LayoutOf, HasTyCtxt};
27+
use rustc::ty::layout::{self, LayoutOf, HasTyCtxt, Primitive};
2828
use rustc_codegen_ssa::common::TypeKind;
2929
use rustc::hir;
30-
use syntax::ast;
30+
use syntax::ast::{self, FloatTy};
3131
use syntax::symbol::Symbol;
3232
use builder::Builder;
3333
use value::Value;
34+
use va_arg::emit_va_arg;
3435

3536
use rustc_codegen_ssa::traits::*;
3637

@@ -146,6 +147,59 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
146147
let tp_ty = substs.type_at(0);
147148
self.cx().const_usize(self.cx().size_of(tp_ty).bytes())
148149
}
150+
func @ "va_start" | func @ "va_end" => {
151+
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
152+
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
153+
(Some(_), _) => self.load(args[0].immediate(),
154+
tcx.data_layout.pointer_align.abi),
155+
(None, _) => bug!("va_list language item must be defined")
156+
};
157+
let intrinsic = self.cx().get_intrinsic(&format!("llvm.{}", func));
158+
self.call(intrinsic, &[va_list], None)
159+
}
160+
"va_copy" => {
161+
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
162+
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
163+
(Some(_), _) => self.load(args[0].immediate(),
164+
tcx.data_layout.pointer_align.abi),
165+
(None, _) => bug!("va_list language item must be defined")
166+
};
167+
let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
168+
self.call(intrinsic, &[llresult, va_list], None);
169+
return;
170+
}
171+
"va_arg" => {
172+
match fn_ty.ret.layout.abi {
173+
layout::Abi::Scalar(ref scalar) => {
174+
match scalar.value {
175+
Primitive::Int(..) => {
176+
if self.cx().size_of(ret_ty).bytes() < 4 {
177+
// va_arg should not be called on a integer type
178+
// less than 4 bytes in length. If it is, promote
179+
// the integer to a `i32` and truncate the result
180+
// back to the smaller type.
181+
let promoted_result = emit_va_arg(self, args[0],
182+
tcx.types.i32);
183+
self.trunc(promoted_result, llret_ty)
184+
} else {
185+
emit_va_arg(self, args[0], ret_ty)
186+
}
187+
}
188+
Primitive::Float(FloatTy::F64) |
189+
Primitive::Pointer => {
190+
emit_va_arg(self, args[0], ret_ty)
191+
}
192+
// `va_arg` should never be used with the return type f32.
193+
Primitive::Float(FloatTy::F32) => {
194+
bug!("the va_arg intrinsic does not work with `f32`")
195+
}
196+
}
197+
}
198+
_ => {
199+
bug!("the va_arg intrinsic does not work with non-scalar types")
200+
}
201+
}
202+
}
149203
"size_of_val" => {
150204
let tp_ty = substs.type_at(0);
151205
if let OperandValue::Pair(_, meta) = args[0].val {

src/librustc_codegen_llvm/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ mod mono_item;
127127
mod type_;
128128
mod type_of;
129129
mod value;
130+
mod va_arg;
130131

131132
#[derive(Clone)]
132133
pub struct LlvmCodegenBackend(());

0 commit comments

Comments
 (0)