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:
parent
cd56472cc4
commit
58147d486b
48 changed files with 848 additions and 152 deletions
|
|
@ -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)) => {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue