Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 84 additions & 4 deletions compiler/rustc_codegen_gcc/src/intrinsic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ mod simd;
#[cfg(feature = "master")]
use std::iter;

#[cfg(feature = "master")]
use gccjit::Type;
use gccjit::{ComparisonOp, Function, FunctionType, RValue, ToRValue, UnaryOp};
#[cfg(feature = "master")]
use gccjit::{FnAttribute, Type};
#[cfg(feature = "master")]
use rustc_abi::ExternAbi;
use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange};
use rustc_codegen_ssa::MemFlags;
Expand All @@ -22,13 +22,18 @@ use rustc_codegen_ssa::traits::{
ArgAbiBuilderMethods, BaseTypeCodegenMethods, BuilderMethods, ConstCodegenMethods,
IntrinsicCallBuilderMethods, LayoutTypeCodegenMethods,
};
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::bug;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
use rustc_middle::ty::{self, Instance, Ty};
#[cfg(feature = "master")]
use rustc_session::config;
use rustc_span::{Span, Symbol, sym};
#[cfg(feature = "master")]
use rustc_target::callconv::ArgAttributes;
use rustc_target::callconv::{ArgAbi, PassMode};

use crate::abi::{FnAbiGccExt, GccType};
use crate::abi::{FnAbiGcc, FnAbiGccExt, GccType};
use crate::builder::Builder;
use crate::common::{SignType, TypeReflection};
use crate::context::CodegenCx;
Expand Down Expand Up @@ -621,7 +626,82 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
} else {
self.linkage.set(FunctionType::Extern);
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
let fn_ty = fn_abi.gcc_type(self);
assert!(!fn_abi.ret.is_indirect());
assert!(!fn_abi.c_variadic);

let return_type = match fn_abi.ret.mode {
PassMode::Ignore => self.type_void(),
PassMode::Direct(_) | PassMode::Pair(..) => {
fn_abi.ret.layout.immediate_gcc_type(self)
}
PassMode::Cast { .. } | PassMode::Indirect { .. } => {
unreachable!()
}
};

#[cfg(feature = "master")]
let mut non_null_args = Vec::new();

#[cfg(feature = "master")]
let mut apply_attrs =
|mut ty: Type<'gcc>, attrs: &ArgAttributes, arg_index: usize| {
if self.sess().opts.optimize == config::OptLevel::No {
return ty;
}
if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NoAlias) {
ty = ty.make_restrict()
}
if attrs.regular.contains(rustc_target::callconv::ArgAttribute::NonNull) {
non_null_args.push(arg_index as i32 + 1);
}
ty
};
#[cfg(not(feature = "master"))]
let apply_attrs = |ty: Type<'gcc>, _attrs: &ArgAttributes, _arg_index: usize| ty;

let mut argument_tys = Vec::with_capacity(fn_abi.args.len());
for arg in fn_abi.args.iter() {
match arg.mode {
PassMode::Ignore => {}
PassMode::Pair(a, b) => {
let arg_pos = argument_tys.len();
argument_tys.push(apply_attrs(
arg.layout.scalar_pair_element_gcc_type(self, 0),
&a,
arg_pos,
));
argument_tys.push(apply_attrs(
arg.layout.scalar_pair_element_gcc_type(self, 1),
&b,
arg_pos + 1,
));
}
PassMode::Direct(attrs) => argument_tys.push(apply_attrs(
arg.layout.immediate_gcc_type(self),
&attrs,
argument_tys.len(),
)),
PassMode::Indirect { .. } | PassMode::Cast { .. } => {
unreachable!()
}
}
}

#[cfg(feature = "master")]
let fn_attrs = if non_null_args.is_empty() {
Vec::new()
} else {
vec![FnAttribute::NonNull(non_null_args)]
};

let fn_ty = FnAbiGcc {
return_type,
arguments_type: argument_tys,
is_c_variadic: false,
on_stack_param_indices: FxHashSet::default(),
#[cfg(feature = "master")]
fn_attributes: fn_attrs,
};

let func = match sym {
"llvm.fma.f16" => {
Expand Down
30 changes: 27 additions & 3 deletions compiler/rustc_codegen_llvm/src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,32 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
// FIXME remove usage of fn_abi
let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty());
assert!(!fn_abi.ret.is_indirect());
let fn_ty = fn_abi.llvm_type(self);
assert!(!fn_abi.c_variadic);

let llreturn_ty = match &fn_abi.ret.mode {
PassMode::Ignore => self.type_void(),
PassMode::Direct(_) | PassMode::Pair(..) => fn_abi.ret.layout.immediate_llvm_type(self),
PassMode::Cast { .. } | PassMode::Indirect { .. } => {
unreachable!()
}
};

let mut llargument_tys = Vec::with_capacity(fn_abi.args.len());
for arg in &fn_abi.args {
match &arg.mode {
PassMode::Ignore => {}
PassMode::Direct(_) => llargument_tys.push(arg.layout.immediate_llvm_type(self)),
PassMode::Pair(..) => {
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(self, 0, true));
llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(self, 1, true));
}
PassMode::Indirect { .. } | PassMode::Cast { .. } => {
unreachable!()
}
};
}

let fn_ty = self.type_func(&llargument_tys, llreturn_ty);

let fn_ptr = if let Some(&llfn) = self.intrinsic_instances.borrow().get(&instance) {
llfn
Expand All @@ -665,12 +690,11 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
let llfn = declare_raw_fn(
self,
sym,
fn_abi.llvm_cconv(self),
llvm::CCallConv,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this effectively just a lie? Or how does LLVM model the ABI for intrinsics? Is there a hidden assumption here that intrinsics only ever take ~integers/pointers that don't have any special handling in terms of ABI lowering of the arguments?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This matches what was effectively done previously. I don't know if LLVM actually cares about the calling convention for intrinsics. It recognizes intrinsics based on a builtin list of symbol names. (if you declare an llvm.* function that doesn't match a known intrinsic, it will treat it like a regular function and emit an import of said function in the emitted object file)

llvm::UnnamedAddr::Global,
llvm::Visibility::Default,
fn_ty,
);
fn_abi.apply_attrs_llfn(self, llfn, Some(instance));

llfn
};
Expand Down