Skip to content

Commit 8117c30

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 63d51e8 + a7a955a commit 8117c30

File tree

11 files changed

+486
-2
lines changed

11 files changed

+486
-2
lines changed

Diff for: src/libcore/ffi.rs

+211
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
// Copyright 2018 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+
111
#![stable(feature = "", since = "1.30.0")]
212

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

Diff for: src/librustc/middle/lang_items.rs

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ language_item_table! {
279279
IndexMutTraitLangItem, "index_mut", index_mut_trait;
280280

281281
UnsafeCellTypeLangItem, "unsafe_cell", unsafe_cell_type;
282+
VaListTypeLangItem, "va_list", va_list;
282283

283284
DerefTraitLangItem, "deref", deref_trait;
284285
DerefMutTraitLangItem, "deref_mut", deref_mut_trait;

Diff for: src/librustc_codegen_llvm/context.rs

+5
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,11 @@ fn declare_intrinsic(cx: &CodegenCx<'ll, '_>, key: &str) -> Option<&'ll Value> {
768768
ifn!("llvm.assume", fn(i1) -> void);
769769
ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void);
770770

771+
// variadic intrinsics
772+
ifn!("llvm.va_start", fn(i8p) -> void);
773+
ifn!("llvm.va_end", fn(i8p) -> void);
774+
ifn!("llvm.va_copy", fn(i8p, i8p) -> void);
775+
771776
if cx.sess().opts.debuginfo != DebugInfo::None {
772777
ifn!("llvm.dbg.declare", fn(Type::metadata(cx), Type::metadata(cx)) -> void);
773778
ifn!("llvm.dbg.value", fn(Type::metadata(cx), t_i64, Type::metadata(cx)) -> void);

Diff for: src/librustc_codegen_llvm/intrinsic.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ use glue;
2323
use type_::Type;
2424
use type_of::LayoutLlvmExt;
2525
use rustc::ty::{self, Ty};
26-
use rustc::ty::layout::{HasDataLayout, LayoutOf};
26+
use rustc::ty::layout::{self, Primitive, HasDataLayout, LayoutOf};
2727
use rustc::hir;
28-
use syntax::ast;
28+
use syntax::ast::{self, FloatTy};
2929
use syntax::symbol::Symbol;
3030
use builder::Builder;
3131
use value::Value;
@@ -78,6 +78,9 @@ fn get_simple_intrinsic(cx: &CodegenCx<'ll, '_>, name: &str) -> Option<&'ll Valu
7878
"roundf64" => "llvm.round.f64",
7979
"assume" => "llvm.assume",
8080
"abort" => "llvm.trap",
81+
"va_start" => "llvm.va_start",
82+
"va_copy" => "llvm.va_copy",
83+
"va_end" => "llvm.va_end",
8184
_ => return None
8285
};
8386
Some(cx.get_intrinsic(&llvm_name))
@@ -141,6 +144,26 @@ pub fn codegen_intrinsic_call(
141144
let llfn = cx.get_intrinsic(&("llvm.debugtrap"));
142145
bx.call(llfn, &[], None)
143146
}
147+
"va_arg" => {
148+
match fn_ty.ret.layout.abi {
149+
layout::Abi::Scalar(ref scalar) => {
150+
match scalar.value {
151+
Primitive::Int(..) |
152+
Primitive::Float(FloatTy::F64) |
153+
Primitive::Pointer => {
154+
bx.va_arg(args[0].immediate(), llret_ty)
155+
}
156+
// `va_arg` should never be used with the return type f32.
157+
Primitive::Float(FloatTy::F32) => {
158+
bug!("the va_arg intrinsic does not work with `f32`")
159+
}
160+
}
161+
}
162+
_ => {
163+
bug!("the va_arg intrinsic does not work with non-scalar types")
164+
}
165+
}
166+
}
144167
"size_of" => {
145168
let tp_ty = substs.type_at(0);
146169
C_usize(cx, cx.size_of(tp_ty).bytes())

Diff for: src/librustc_typeck/check/intrinsic.rs

+12
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,18 @@ pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
321321
(0, vec![tcx.mk_fn_ptr(fn_ty), mut_u8, mut_u8], tcx.types.i32)
322322
}
323323

324+
"va_start" | "va_end" => {
325+
(0, vec![tcx.mk_mut_ptr(tcx.types.i8)], tcx.mk_unit())
326+
}
327+
328+
"va_copy" => {
329+
(0, vec![tcx.mk_mut_ptr(tcx.types.i8), tcx.mk_imm_ptr(tcx.types.i8)], tcx.mk_unit())
330+
}
331+
332+
"va_arg" => {
333+
(1, vec![tcx.mk_mut_ptr(tcx.types.i8)], param(0))
334+
}
335+
324336
"nontemporal_store" => {
325337
(1, vec![ tcx.mk_mut_ptr(param(0)), param(0) ], tcx.mk_unit())
326338
}

Diff for: src/libstd/ffi/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,12 @@ pub use core::ffi::c_void;
176176

177177
mod c_str;
178178
mod os_str;
179+
#[unstable(feature = "c_variadic",
180+
reason = "the `c_variadic` feature has not been properly tested on \
181+
all supported platforms",
182+
issue = "27745")]
183+
#[cfg(any(target_arch = "aarch64", target_arch = "arm", target_arch = "mips",
184+
target_arch = "powerpc", target_arch = "powerpc64", target_arch = "x86",
185+
target_arch = "x86_64"))]
186+
#[cfg(not(stage0))]
187+
pub use core::ffi::*;

Diff for: src/libstd/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@
245245
#![feature(array_error_internals)]
246246
#![feature(asm)]
247247
#![feature(box_syntax)]
248+
#![feature(c_variadic)]
248249
#![feature(cfg_target_has_atomic)]
249250
#![feature(cfg_target_thread_local)]
250251
#![feature(cfg_target_vendor)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-include ../tools.mk
2+
3+
all:
4+
$(RUSTC) checkrust.rs
5+
$(CC) test.c $(call STATICLIB,checkrust) $(call OUT_EXE,test) $(EXTRACFLAGS)
6+
$(call RUN,test)

0 commit comments

Comments
 (0)