Skip to content

Commit 58147d4

Browse files
committed
Support defining C compatible variadic functions
Add support for defining C compatible variadic functions in unsafe rust with extern "C".
1 parent cd56472 commit 58147d4

File tree

48 files changed

+848
-152
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+848
-152
lines changed

src/librustc/hir/intravisit.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty) {
617617
TyKind::Typeof(ref expression) => {
618618
visitor.visit_anon_const(expression)
619619
}
620+
TyKind::CVarArgs(ref lt) => {
621+
visitor.visit_lifetime(lt)
622+
}
620623
TyKind::Infer | TyKind::Err => {}
621624
}
622625
}

src/librustc/hir/lowering.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,12 @@ impl<'a> LoweringContext<'a> {
13451345
}
13461346
}
13471347
TyKind::Mac(_) => panic!("TyMac should have been expanded by now."),
1348+
TyKind::CVarArgs => {
1349+
// Create the implicit lifetime of the "spoofed" `VaList`.
1350+
let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
1351+
let lt = self.new_implicit_lifetime(span);
1352+
hir::TyKind::CVarArgs(lt)
1353+
},
13481354
};
13491355

13501356
let LoweredNodeId { node_id: _, hir_id } = self.lower_node_id(t.id);

src/librustc/hir/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1829,6 +1829,9 @@ pub enum TyKind {
18291829
Infer,
18301830
/// Placeholder for a type that has failed to be defined.
18311831
Err,
1832+
/// Placeholder for C-variadic arguments. We "spoof" the `VaList` created
1833+
/// from the variadic arguments. This type is only valid up to typeck.
1834+
CVarArgs(Lifetime),
18321835
}
18331836

18341837
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]

src/librustc/hir/print.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,9 @@ impl<'a> State<'a> {
434434
self.s.word("/*ERROR*/")?;
435435
self.pclose()?;
436436
}
437+
hir::TyKind::CVarArgs(_) => {
438+
self.s.word("...")?;
439+
}
437440
}
438441
self.end()
439442
}

src/librustc/ich/impls_hir.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,8 @@ impl_stable_hash_for!(enum hir::TyKind {
361361
TraitObject(trait_refs, lifetime),
362362
Typeof(body_id),
363363
Err,
364-
Infer
364+
Infer,
365+
CVarArgs(lt),
365366
});
366367

367368
impl_stable_hash_for!(struct hir::FnDecl {

src/librustc/middle/resolve_lifetime.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,13 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
764764
});
765765
}
766766
}
767+
hir::TyKind::CVarArgs(ref lt) => {
768+
// Resolve the generated lifetime for the C-variadic arguments.
769+
// The lifetime is generated in AST -> HIR lowering.
770+
if lt.name.is_elided() {
771+
self.resolve_elided_lifetimes(vec![lt])
772+
}
773+
}
767774
_ => intravisit::walk_ty(self, ty),
768775
}
769776
}
@@ -2225,18 +2232,22 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
22252232
if let hir::TyKind::BareFn(_) = ty.node {
22262233
self.outer_index.shift_in(1);
22272234
}
2228-
if let hir::TyKind::TraitObject(ref bounds, ref lifetime) = ty.node {
2229-
for bound in bounds {
2230-
self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
2231-
}
2235+
match ty.node {
2236+
hir::TyKind::TraitObject(ref bounds, ref lifetime) => {
2237+
for bound in bounds {
2238+
self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None);
2239+
}
22322240

2233-
// Stay on the safe side and don't include the object
2234-
// lifetime default (which may not end up being used).
2235-
if !lifetime.is_elided() {
2236-
self.visit_lifetime(lifetime);
2241+
// Stay on the safe side and don't include the object
2242+
// lifetime default (which may not end up being used).
2243+
if !lifetime.is_elided() {
2244+
self.visit_lifetime(lifetime);
2245+
}
2246+
}
2247+
hir::TyKind::CVarArgs(_) => {}
2248+
_ => {
2249+
intravisit::walk_ty(self, ty);
22372250
}
2238-
} else {
2239-
intravisit::walk_ty(self, ty);
22402251
}
22412252
if let hir::TyKind::BareFn(_) = ty.node {
22422253
self.outer_index.shift_out(1);

src/librustc/ty/sty.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -977,9 +977,9 @@ impl<'tcx> PolyGenSig<'tcx> {
977977
/// Signature of a function type, which I have arbitrarily
978978
/// decided to use to refer to the input/output types.
979979
///
980-
/// - `inputs` is the list of arguments and their modes.
981-
/// - `output` is the return type.
982-
/// - `variadic` indicates whether this is a variadic function. (only true for foreign fns)
980+
/// - `inputs`: is the list of arguments and their modes.
981+
/// - `output`: is the return type.
982+
/// - `variadic`: indicates whether this is a C-variadic function.
983983
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
984984
pub struct FnSig<'tcx> {
985985
pub inputs_and_output: &'tcx List<Ty<'tcx>>,

src/librustc_codegen_llvm/abi.rs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
258258
val
259259
};
260260
match self.mode {
261-
PassMode::Ignore => {},
261+
PassMode::Ignore(_) => {}
262262
PassMode::Pair(..) => {
263263
OperandValue::Pair(next(), next()).store(bx, dst);
264264
}
@@ -507,6 +507,14 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
507507
}
508508
};
509509

510+
// Store the index of the last argument. This is useful for working with
511+
// C-compatible variadic arguments.
512+
let last_arg_idx = if sig.inputs().is_empty() {
513+
None
514+
} else {
515+
Some(sig.inputs().len() - 1)
516+
};
517+
510518
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| {
511519
let is_return = arg_idx.is_none();
512520
let mut arg = mk_arg_type(ty, arg_idx);
@@ -516,7 +524,30 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
516524
// The same is true for s390x-unknown-linux-gnu
517525
// and sparc64-unknown-linux-gnu.
518526
if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) {
519-
arg.mode = PassMode::Ignore;
527+
arg.mode = PassMode::Ignore(IgnoreMode::Zst);
528+
}
529+
}
530+
531+
// If this is a C-variadic function, this is not the return value,
532+
// and there is one or more fixed arguments; ensure that the `VaList`
533+
// is ignored as an argument.
534+
if sig.variadic {
535+
match (last_arg_idx, arg_idx) {
536+
(Some(last_idx), Some(cur_idx)) if last_idx == cur_idx => {
537+
let va_list_did = match cx.tcx.lang_items().va_list() {
538+
Some(did) => did,
539+
None => bug!("`va_list` lang item required for C-variadic functions"),
540+
};
541+
match ty.sty {
542+
ty::Adt(def, _) if def.did == va_list_did => {
543+
// This is the "spoofed" `VaList`. Set the arguments mode
544+
// so that it will be ignored.
545+
arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs);
546+
},
547+
_ => (),
548+
}
549+
}
550+
_ => {}
520551
}
521552
}
522553

@@ -646,7 +677,9 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
646677
);
647678

648679
let llreturn_ty = match self.ret.mode {
649-
PassMode::Ignore => cx.type_void(),
680+
PassMode::Ignore(IgnoreMode::Zst) => cx.type_void(),
681+
PassMode::Ignore(IgnoreMode::CVarArgs) =>
682+
bug!("`va_list` should never be a return type"),
650683
PassMode::Direct(_) | PassMode::Pair(..) => {
651684
self.ret.layout.immediate_llvm_type(cx)
652685
}
@@ -664,7 +697,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
664697
}
665698

666699
let llarg_ty = match arg.mode {
667-
PassMode::Ignore => continue,
700+
PassMode::Ignore(_) => continue,
668701
PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
669702
PassMode::Pair(..) => {
670703
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0, true));
@@ -733,7 +766,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
733766
apply(&ArgAttributes::new());
734767
}
735768
match arg.mode {
736-
PassMode::Ignore => {}
769+
PassMode::Ignore(_) => {}
737770
PassMode::Direct(ref attrs) |
738771
PassMode::Indirect(ref attrs, None) => apply(attrs),
739772
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
@@ -780,7 +813,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
780813
apply(&ArgAttributes::new());
781814
}
782815
match arg.mode {
783-
PassMode::Ignore => {}
816+
PassMode::Ignore(_) => {}
784817
PassMode::Direct(ref attrs) |
785818
PassMode::Indirect(ref attrs, None) => apply(attrs),
786819
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {

src/librustc_codegen_llvm/intrinsic.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,22 +136,18 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
136136
let tp_ty = substs.type_at(0);
137137
self.const_usize(self.size_of(tp_ty).bytes())
138138
}
139-
func @ "va_start" | func @ "va_end" => {
140-
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
141-
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
142-
(Some(_), _) => self.load(args[0].immediate(),
143-
tcx.data_layout.pointer_align.abi),
144-
(None, _) => bug!("va_list language item must be defined")
145-
};
146-
let intrinsic = self.cx().get_intrinsic(&format!("llvm.{}", func));
147-
self.call(intrinsic, &[va_list], None)
139+
"va_start" => {
140+
self.va_start(args[0].immediate())
141+
}
142+
"va_end" => {
143+
self.va_end(args[0].immediate())
148144
}
149145
"va_copy" => {
150146
let va_list = match (tcx.lang_items().va_list(), &result.layout.ty.sty) {
151147
(Some(did), ty::Adt(def, _)) if def.did == did => args[0].immediate(),
152148
(Some(_), _) => self.load(args[0].immediate(),
153149
tcx.data_layout.pointer_align.abi),
154-
(None, _) => bug!("va_list language item must be defined")
150+
(None, _) => bug!("`va_list` language item must be defined")
155151
};
156152
let intrinsic = self.cx().get_intrinsic(&("llvm.va_copy"));
157153
self.call(intrinsic, &[llresult, va_list], None);
@@ -722,6 +718,41 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
722718
let expect = self.get_intrinsic(&"llvm.expect.i1");
723719
self.call(expect, &[cond, self.const_bool(expected)], None)
724720
}
721+
722+
fn va_start(&mut self, list: &'ll Value) -> &'ll Value {
723+
let target = &self.cx.tcx.sess.target.target;
724+
let arch = &target.arch;
725+
// A pointer to the architecture specific structure is passed to this
726+
// function. For pointer variants (i686, RISC-V, Windows, etc), we
727+
// should do do nothing, as the address to the pointer is needed. For
728+
// architectures with a architecture specific structure (`Aarch64`,
729+
// `X86_64`, etc), this function should load the structure from the
730+
// address provided.
731+
let va_list = match &**arch {
732+
_ if target.options.is_like_windows => list,
733+
"aarch64" if target.target_os == "ios" => list,
734+
"aarch64" | "x86_64" | "powerpc" =>
735+
self.load(list, self.tcx().data_layout.pointer_align.abi),
736+
_ => list,
737+
};
738+
let intrinsic = self.cx().get_intrinsic("llvm.va_start");
739+
self.call(intrinsic, &[va_list], None)
740+
}
741+
742+
fn va_end(&mut self, list: &'ll Value) -> &'ll Value {
743+
let target = &self.cx.tcx.sess.target.target;
744+
let arch = &target.arch;
745+
// See the comment in `va_start` for the purpose of the following.
746+
let va_list = match &**arch {
747+
_ if target.options.is_like_windows => list,
748+
"aarch64" if target.target_os == "ios" => list,
749+
"aarch64" | "x86_64" | "powerpc" =>
750+
self.load(list, self.tcx().data_layout.pointer_align.abi),
751+
_ => list,
752+
};
753+
let intrinsic = self.cx().get_intrinsic("llvm.va_end");
754+
self.call(intrinsic, &[va_list], None)
755+
}
725756
}
726757

727758
fn copy_intrinsic(

src/librustc_codegen_llvm/mono_item.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
3636
}
3737

3838
fn predefine_fn(&self,
39-
instance: Instance<'tcx>,
40-
linkage: Linkage,
41-
visibility: Visibility,
42-
symbol_name: &str) {
39+
instance: Instance<'tcx>,
40+
linkage: Linkage,
41+
visibility: Visibility,
42+
symbol_name: &str) {
4343
assert!(!instance.substs.needs_infer() &&
4444
!instance.substs.has_param_types());
4545

src/librustc_codegen_llvm/va_arg.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@ pub(super) fn emit_va_arg(
109109
Align::from_bytes(4).unwrap(), true)
110110
}
111111
// Windows Aarch64
112-
("aarch4", true) => {
112+
("aarch64", true) => {
113113
emit_ptr_va_arg(bx, addr, target_ty, false,
114114
Align::from_bytes(8).unwrap(), false)
115115
}
116116
// iOS Aarch64
117-
("aarch4", _) if target.target_os == "ios" => {
117+
("aarch64", _) if target.target_os == "ios" => {
118118
emit_ptr_va_arg(bx, addr, target_ty, false,
119119
Align::from_bytes(8).unwrap(), true)
120120
}

src/librustc_codegen_ssa/mir/block.rs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc::ty::{self, Ty, TypeFoldable};
33
use rustc::ty::layout::{self, LayoutOf, HasTyCtxt};
44
use rustc::mir;
55
use rustc::mir::interpret::EvalErrorKind;
6-
use rustc_target::abi::call::{ArgType, FnType, PassMode};
6+
use rustc_target::abi::call::{ArgType, FnType, PassMode, IgnoreMode};
77
use rustc_target::spec::abi::Abi;
88
use rustc_mir::monomorphize;
99
use crate::base;
@@ -18,7 +18,7 @@ use syntax_pos::Pos;
1818

1919
use super::{FunctionCx, LocalRef};
2020
use super::place::PlaceRef;
21-
use super::operand::OperandRef;
21+
use super::operand::{OperandValue, OperandRef};
2222
use super::operand::OperandValue::{Pair, Ref, Immediate};
2323

2424
impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
@@ -232,12 +232,21 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
232232
}
233233

234234
mir::TerminatorKind::Return => {
235+
if self.fn_ty.variadic {
236+
if let Some(va_list) = self.va_list_ref {
237+
bx.va_end(va_list.llval);
238+
}
239+
}
235240
let llval = match self.fn_ty.ret.mode {
236-
PassMode::Ignore | PassMode::Indirect(..) => {
241+
PassMode::Ignore(IgnoreMode::Zst) | PassMode::Indirect(..) => {
237242
bx.ret_void();
238243
return;
239244
}
240245

246+
PassMode::Ignore(IgnoreMode::CVarArgs) => {
247+
bug!("C variadic arguments should never be the return type");
248+
}
249+
241250
PassMode::Direct(_) | PassMode::Pair(..) => {
242251
let op =
243252
self.codegen_consume(&mut bx, &mir::Place::Local(mir::RETURN_PLACE));
@@ -481,7 +490,10 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
481490
return;
482491
}
483492

484-
let extra_args = &args[sig.inputs().len()..];
493+
// The "spoofed" `VaList` added to a C-variadic functions signature
494+
// should not be included in the `extra_args` calculation.
495+
let extra_args_start_idx = sig.inputs().len() - if sig.variadic { 1 } else { 0 };
496+
let extra_args = &args[extra_args_start_idx..];
485497
let extra_args = extra_args.iter().map(|op_arg| {
486498
let op_ty = op_arg.ty(self.mir, bx.tcx());
487499
self.monomorphize(&op_ty)
@@ -658,7 +670,37 @@ impl<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
658670
(&args[..], None)
659671
};
660672

673+
// Useful determining if the current argument is the "spoofed" `VaList`
674+
let last_arg_idx = if sig.inputs().is_empty() {
675+
None
676+
} else {
677+
Some(sig.inputs().len() - 1)
678+
};
661679
'make_args: for (i, arg) in first_args.iter().enumerate() {
680+
// If this is a C-variadic function the function signature contains
681+
// an "spoofed" `VaList`. This argument is ignored, but we need to
682+
// populate it with a dummy operand so that the users real arguments
683+
// are not overwritten.
684+
let i = if sig.variadic && last_arg_idx.map(|x| x == i).unwrap_or(false) {
685+
let layout = match tcx.lang_items().va_list() {
686+
Some(did) => bx.cx().layout_of(bx.tcx().type_of(did)),
687+
None => bug!("va_list language item required for C variadics"),
688+
};
689+
let op = OperandRef {
690+
val: OperandValue::Immediate(
691+
bx.cx().const_undef(bx.cx().immediate_backend_type(layout))
692+
),
693+
layout: layout,
694+
};
695+
self.codegen_argument(&mut bx, op, &mut llargs, &fn_ty.args[i]);
696+
if i + 1 < fn_ty.args.len() {
697+
i + 1
698+
} else {
699+
break 'make_args
700+
}
701+
} else {
702+
i
703+
};
662704
let mut op = self.codegen_operand(&mut bx, arg);
663705

664706
if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) {

0 commit comments

Comments
 (0)