Support defining C compatible variadic functions

Add support for defining C compatible variadic functions in unsafe rust
with extern "C".
This commit is contained in:
Dan Robertson 2018-11-30 15:53:44 +00:00
parent cd56472cc4
commit 58147d486b
No known key found for this signature in database
GPG key ID: 4DE6EEF84B5C9783
48 changed files with 848 additions and 152 deletions

View file

@ -258,7 +258,7 @@ impl ArgTypeExt<'ll, 'tcx> for ArgType<'tcx, Ty<'tcx>> {
val
};
match self.mode {
PassMode::Ignore => {},
PassMode::Ignore(_) => {}
PassMode::Pair(..) => {
OperandValue::Pair(next(), next()).store(bx, dst);
}
@ -507,6 +507,14 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
}
};
// Store the index of the last argument. This is useful for working with
// C-compatible variadic arguments.
let last_arg_idx = if sig.inputs().is_empty() {
None
} else {
Some(sig.inputs().len() - 1)
};
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| {
let is_return = arg_idx.is_none();
let mut arg = mk_arg_type(ty, arg_idx);
@ -516,7 +524,30 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
// The same is true for s390x-unknown-linux-gnu
// and sparc64-unknown-linux-gnu.
if is_return || rust_abi || (!win_x64_gnu && !linux_s390x && !linux_sparc64) {
arg.mode = PassMode::Ignore;
arg.mode = PassMode::Ignore(IgnoreMode::Zst);
}
}
// If this is a C-variadic function, this is not the return value,
// and there is one or more fixed arguments; ensure that the `VaList`
// is ignored as an argument.
if sig.variadic {
match (last_arg_idx, arg_idx) {
(Some(last_idx), Some(cur_idx)) if last_idx == cur_idx => {
let va_list_did = match cx.tcx.lang_items().va_list() {
Some(did) => did,
None => bug!("`va_list` lang item required for C-variadic functions"),
};
match ty.sty {
ty::Adt(def, _) if def.did == va_list_did => {
// This is the "spoofed" `VaList`. Set the arguments mode
// so that it will be ignored.
arg.mode = PassMode::Ignore(IgnoreMode::CVarArgs);
},
_ => (),
}
}
_ => {}
}
}
@ -646,7 +677,9 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
);
let llreturn_ty = match self.ret.mode {
PassMode::Ignore => cx.type_void(),
PassMode::Ignore(IgnoreMode::Zst) => cx.type_void(),
PassMode::Ignore(IgnoreMode::CVarArgs) =>
bug!("`va_list` should never be a return type"),
PassMode::Direct(_) | PassMode::Pair(..) => {
self.ret.layout.immediate_llvm_type(cx)
}
@ -664,7 +697,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
}
let llarg_ty = match arg.mode {
PassMode::Ignore => continue,
PassMode::Ignore(_) => continue,
PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx),
PassMode::Pair(..) => {
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>> {
apply(&ArgAttributes::new());
}
match arg.mode {
PassMode::Ignore => {}
PassMode::Ignore(_) => {}
PassMode::Direct(ref attrs) |
PassMode::Indirect(ref attrs, None) => apply(attrs),
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {
@ -780,7 +813,7 @@ impl<'tcx> FnTypeExt<'tcx> for FnType<'tcx, Ty<'tcx>> {
apply(&ArgAttributes::new());
}
match arg.mode {
PassMode::Ignore => {}
PassMode::Ignore(_) => {}
PassMode::Direct(ref attrs) |
PassMode::Indirect(ref attrs, None) => apply(attrs),
PassMode::Indirect(ref attrs, Some(ref extra_attrs)) => {

View file

@ -136,22 +136,18 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
let tp_ty = substs.type_at(0);
self.const_usize(self.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_start" => {
self.va_start(args[0].immediate())
}
"va_end" => {
self.va_end(args[0].immediate())
}
"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")
(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);
@ -722,6 +718,41 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
let expect = self.get_intrinsic(&"llvm.expect.i1");
self.call(expect, &[cond, self.const_bool(expected)], None)
}
fn va_start(&mut self, list: &'ll Value) -> &'ll Value {
let target = &self.cx.tcx.sess.target.target;
let arch = &target.arch;
// A pointer to the architecture specific structure is passed to this
// function. For pointer variants (i686, RISC-V, Windows, etc), we
// should do do nothing, as the address to the pointer is needed. For
// architectures with a architecture specific structure (`Aarch64`,
// `X86_64`, etc), this function should load the structure from the
// address provided.
let va_list = match &**arch {
_ if target.options.is_like_windows => list,
"aarch64" if target.target_os == "ios" => list,
"aarch64" | "x86_64" | "powerpc" =>
self.load(list, self.tcx().data_layout.pointer_align.abi),
_ => list,
};
let intrinsic = self.cx().get_intrinsic("llvm.va_start");
self.call(intrinsic, &[va_list], None)
}
fn va_end(&mut self, list: &'ll Value) -> &'ll Value {
let target = &self.cx.tcx.sess.target.target;
let arch = &target.arch;
// See the comment in `va_start` for the purpose of the following.
let va_list = match &**arch {
_ if target.options.is_like_windows => list,
"aarch64" if target.target_os == "ios" => list,
"aarch64" | "x86_64" | "powerpc" =>
self.load(list, self.tcx().data_layout.pointer_align.abi),
_ => list,
};
let intrinsic = self.cx().get_intrinsic("llvm.va_end");
self.call(intrinsic, &[va_list], None)
}
}
fn copy_intrinsic(

View file

@ -36,10 +36,10 @@ impl PreDefineMethods<'tcx> for CodegenCx<'ll, 'tcx> {
}
fn predefine_fn(&self,
instance: Instance<'tcx>,
linkage: Linkage,
visibility: Visibility,
symbol_name: &str) {
instance: Instance<'tcx>,
linkage: Linkage,
visibility: Visibility,
symbol_name: &str) {
assert!(!instance.substs.needs_infer() &&
!instance.substs.has_param_types());

View file

@ -109,12 +109,12 @@ pub(super) fn emit_va_arg(
Align::from_bytes(4).unwrap(), true)
}
// Windows Aarch64
("aarch4", true) => {
("aarch64", true) => {
emit_ptr_va_arg(bx, addr, target_ty, false,
Align::from_bytes(8).unwrap(), false)
}
// iOS Aarch64
("aarch4", _) if target.target_os == "ios" => {
("aarch64", _) if target.target_os == "ios" => {
emit_ptr_va_arg(bx, addr, target_ty, false,
Align::from_bytes(8).unwrap(), true)
}