Create "platform-intrinsic" ABI for SIMD/platform intrinsics.

This is purposely separate to the "rust-intrinsic" ABI, because these
intrinsics are theoretically going to become stable, and should be fine
to be independent of the compiler/language internals since they're
intimately to the platform.
This commit is contained in:
Huon Wilson 2015-08-06 11:11:26 -07:00
parent 58891278a3
commit 717da9513f
10 changed files with 208 additions and 140 deletions

View File

@ -704,7 +704,7 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
}
fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) {
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic || fm.abi == abi::PlatformIntrinsic {
return;
}

View File

@ -1501,7 +1501,7 @@ fn encode_info_for_foreign_item(ecx: &EncodeContext,
encode_family(rbml_w, FN_FAMILY);
encode_bounds_and_type_for_item(rbml_w, ecx, nitem.id);
encode_name(rbml_w, nitem.ident.name);
if abi == abi::RustIntrinsic {
if abi == abi::RustIntrinsic || abi == abi::PlatformIntrinsic {
encode_inlined_item(ecx, rbml_w, InlinedItemRef::Foreign(nitem));
}
encode_attributes(rbml_w, &*nitem.attrs);

View File

@ -603,6 +603,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
match bare_fn.abi {
abi::Rust |
abi::RustIntrinsic |
abi::PlatformIntrinsic |
abi::RustCall => {
return FfiUnsafe(
"found function pointer with Rust calling \
@ -717,7 +718,9 @@ impl LintPass for ImproperCTypes {
}
match it.node {
ast::ItemForeignMod(ref nmod) if nmod.abi != abi::RustIntrinsic => {
ast::ItemForeignMod(ref nmod)
if nmod.abi != abi::RustIntrinsic &&
nmod.abi != abi::PlatformIntrinsic => {
for ni in &nmod.items {
match ni.node {
ast::ForeignItemFn(ref decl, _) => check_foreign_fn(cx, &**decl),

View File

@ -91,7 +91,7 @@ use std::collections::{HashMap, HashSet};
use std::mem;
use std::str;
use std::{i8, i16, i32, i64};
use syntax::abi::{Rust, RustCall, RustIntrinsic, Abi};
use syntax::abi::{Rust, RustCall, RustIntrinsic, PlatformIntrinsic, Abi};
use syntax::ast_util::local_def;
use syntax::attr::AttrMetaMethods;
use syntax::attr;
@ -671,7 +671,7 @@ pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
Rust | RustCall => {
get_extern_rust_fn(ccx, t, &name[..], did)
}
RustIntrinsic => {
RustIntrinsic | PlatformIntrinsic => {
ccx.sess().bug("unexpected intrinsic in trans_external_path")
}
_ => {

View File

@ -150,7 +150,8 @@ fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr)
}
}
def::DefFn(did, _) if match expr_ty.sty {
ty::TyBareFn(_, ref f) => f.abi == synabi::RustIntrinsic,
ty::TyBareFn(_, ref f) => f.abi == synabi::RustIntrinsic ||
f.abi == synabi::PlatformIntrinsic,
_ => false
} => {
let substs = common::node_id_substs(bcx.ccx(),
@ -671,7 +672,7 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
(d.llfn, Some(d.llself))
}
Intrinsic(node, substs) => {
assert!(abi == synabi::RustIntrinsic);
assert!(abi == synabi::RustIntrinsic || abi == synabi::PlatformIntrinsic);
assert!(dest.is_some());
let call_info = match debug_loc {
@ -701,7 +702,7 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
// Intrinsics should not become actual functions.
// We trans them in place in `trans_intrinsic_call`
assert!(abi != synabi::RustIntrinsic);
assert!(abi != synabi::RustIntrinsic && abi != synabi::PlatformIntrinsic);
let is_rust_fn = abi == synabi::Rust || abi == synabi::RustCall;

View File

@ -34,7 +34,7 @@ use middle::subst::Substs;
use std::cmp;
use libc::c_uint;
use syntax::abi::{Cdecl, Aapcs, C, Win64, Abi};
use syntax::abi::{RustIntrinsic, Rust, RustCall, Stdcall, Fastcall, System};
use syntax::abi::{PlatformIntrinsic, RustIntrinsic, Rust, RustCall, Stdcall, Fastcall, System};
use syntax::codemap::Span;
use syntax::parse::token::{InternedString, special_idents};
use syntax::ast;
@ -81,6 +81,10 @@ pub fn llvm_calling_convention(ccx: &CrateContext,
// Intrinsics are emitted at the call site
ccx.sess().bug("asked to register intrinsic fn");
}
PlatformIntrinsic => {
// Intrinsics are emitted at the call site
ccx.sess().bug("asked to register platform intrinsic fn");
}
Rust => {
// FIXME(#3678) Implement linking to foreign fns with Rust ABI
@ -475,7 +479,7 @@ pub fn trans_foreign_mod(ccx: &CrateContext, foreign_mod: &ast::ForeignMod) {
if let ast::ForeignItemFn(ref decl, _) = foreign_item.node {
match foreign_mod.abi {
Rust | RustIntrinsic => {}
Rust | RustIntrinsic | PlatformIntrinsic => {}
abi => {
let ty = ccx.tcx().node_id_to_type(foreign_item.id);
match ty.sty {
@ -612,7 +616,7 @@ pub fn trans_rust_fn_with_foreign_abi<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
// normal Rust function. This will be the type of the wrappee fn.
match t.sty {
ty::TyBareFn(_, ref f) => {
assert!(f.abi != Rust && f.abi != RustIntrinsic);
assert!(f.abi != Rust && f.abi != RustIntrinsic && f.abi != PlatformIntrinsic);
}
_ => {
ccx.sess().bug(&format!("build_rust_fn: extern fn {} has ty {:?}, \

View File

@ -90,7 +90,8 @@ pub fn monomorphic_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
});
if let ast_map::NodeForeignItem(_) = map_node {
if ccx.tcx().map.get_foreign_abi(fn_id.node) != abi::RustIntrinsic {
let abi = ccx.tcx().map.get_foreign_abi(fn_id.node);
if abi != abi::RustIntrinsic && abi != abi::PlatformIntrinsic {
// Foreign externs don't have to be monomorphized.
return (get_item_val(ccx, fn_id.node), mono_ty, true);
}

View File

@ -720,6 +720,10 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) {
for item in &m.items {
check_intrinsic_type(ccx, &**item);
}
} else if m.abi == abi::PlatformIntrinsic {
for item in &m.items {
check_platform_intrinsic_type(ccx, &**item);
}
} else {
for item in &m.items {
let pty = ccx.tcx.lookup_item_type(local_def(item.id));
@ -5093,7 +5097,6 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
let tcx = ccx.tcx;
let name = it.ident.name.as_str();
let mut infer_ctxt = None;
let (n_tps, inputs, output) = if name.starts_with("atomic_") {
let split : Vec<&str> = name.split('_').collect();
assert!(split.len() >= 2, "Atomic intrinsic not correct format");
@ -5342,35 +5345,6 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::DebruijnIndex::new(1),
ty::BrAnon(0))),
param(ccx, 0))], tcx.types.u64),
"simd_eq" | "simd_ne" | "simd_lt" | "simd_le" | "simd_gt" | "simd_ge" => {
(2, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 1))
}
"simd_add" | "simd_sub" | "simd_mul" |
"simd_div" | "simd_shl" | "simd_shr" |
"simd_and" | "simd_or" | "simd_xor" => {
(1, vec![param(ccx, 0), param(ccx, 0)], param(ccx, 0))
}
"simd_insert" => (2, vec![param(ccx, 0), tcx.types.u32, param(ccx, 1)], param(ccx, 0)),
"simd_extract" => (2, vec![param(ccx, 0), tcx.types.u32], param(ccx, 1)),
"simd_cast" => (2, vec![param(ccx, 0)], param(ccx, 1)),
name if name.starts_with("simd_shuffle") => {
match name["simd_shuffle".len()..].parse() {
Ok(n) => {
let mut params = vec![param(ccx, 0), param(ccx, 0)];
params.extend(iter::repeat(tcx.types.u32).take(n));
let ictxt = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
let ret = ictxt.next_ty_var();
infer_ctxt = Some(ictxt);
(2, params, ret)
}
Err(_) => {
span_err!(tcx.sess, it.span, E0439,
"invalid `simd_shuffle`, needs length: `{}`", name);
return
}
}
}
"try" => {
let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8);
@ -5388,17 +5362,9 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
}
ref other => {
match intrinsics::Intrinsic::find(tcx, other) {
Some(intr) => {
check_platform_intrinsic_type(ccx, intr, it);
return
}
None => {
span_err!(tcx.sess, it.span, E0093,
"unrecognized intrinsic function: `{}`", *other);
return;
}
}
span_err!(tcx.sess, it.span, E0093,
"unrecognized intrinsic function: `{}`", *other);
return;
}
};
(n_tps, inputs, ty::FnConverging(output))
@ -5421,7 +5387,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
i_n_tps, n_tps);
} else {
require_same_types(tcx,
infer_ctxt.as_ref(),
None,
false,
it.span,
i_ty.ty,
@ -5434,95 +5400,177 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &ast::ForeignItem) {
}
fn check_platform_intrinsic_type(ccx: &CrateCtxt,
expected: intrinsics::Intrinsic, it: &ast::ForeignItem) {
it: &ast::ForeignItem) {
let param = |n| {
let name = token::intern(&format!("P{}", n));
ccx.tcx.mk_param(subst::FnSpace, n, name)
};
let tcx = ccx.tcx;
let i_ty = tcx.lookup_item_type(local_def(it.id));
let i_n_tps = i_ty.generics.types.len(subst::FnSpace);
if i_n_tps != 0 {
tcx.sess.span_err(it.span,
&format!("intrinsic has wrong number of type parameters: \
found {}, expected 0",
i_n_tps));
return
}
let name = it.ident.name.as_str();
let mut infer_ctxt = None;
let mut structural_to_nomimal = HashMap::new();
let (n_tps, inputs, output) = match &*name {
"simd_eq" | "simd_ne" | "simd_lt" | "simd_le" | "simd_gt" | "simd_ge" => {
(2, vec![param(0), param(0)], param(1))
}
"simd_add" | "simd_sub" | "simd_mul" |
"simd_div" | "simd_shl" | "simd_shr" |
"simd_and" | "simd_or" | "simd_xor" => {
(1, vec![param(0), param(0)], param(0))
}
"simd_insert" => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
"simd_extract" => (2, vec![param(0), tcx.types.u32], param(1)),
"simd_cast" => (2, vec![param(0)], param(1)),
name if name.starts_with("simd_shuffle") => {
match name["simd_shuffle".len()..].parse() {
Ok(n) => {
let mut params = vec![param(0), param(0)];
params.extend(iter::repeat(tcx.types.u32).take(n));
let sig = tcx.no_late_bound_regions(i_ty.ty.fn_sig()).unwrap();
for (i, (expected_arg, arg)) in expected.inputs.iter().zip(&sig.inputs).enumerate() {
match_types(tcx, &format!("argument {}", i + 1), it.span,
&mut structural_to_nomimal, expected_arg, arg);
}
match_types(tcx, "return value", it.span, &mut structural_to_nomimal,
&expected.output, sig.output.unwrap());
// walk the expected type and the actual type in lock step, checking they're
// the same, in a kinda-structural way, i.e. `Vector`s have to be simd structs with
// exactly the right element type
fn match_types<'tcx, 'a>(tcx: &ty::ctxt<'tcx>,
position: &str,
span: Span,
structural_to_nominal: &mut HashMap<&'a intrinsics::Type,
ty::Ty<'tcx>>,
expected: &'a intrinsics::Type, t: ty::Ty<'tcx>) {
use intrinsics::Type::*;
match *expected {
Integer(bits) => match (bits, &t.sty) {
(8, &ty::TyInt(ast::TyI8)) | (8, &ty::TyUint(ast::TyU8)) |
(16, &ty::TyInt(ast::TyI16)) | (16, &ty::TyUint(ast::TyU16)) |
(32, &ty::TyInt(ast::TyI32)) | (32, &ty::TyUint(ast::TyU32)) |
(64, &ty::TyInt(ast::TyI64)) | (64, &ty::TyUint(ast::TyU64)) => {},
_ => tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found `{}`, \
expected `i{n}` or `u{n}`",
position,
t, n = bits)),
},
Float(bits) => match (bits, &t.sty) {
(32, &ty::TyFloat(ast::TyF32)) |
(64, &ty::TyFloat(ast::TyF64)) => {},
_ => tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found `{}`, \
expected `f{n}`",
position,
t, n = bits)),
},
Pointer(_) => unimplemented!(),
Vector(ref inner_expected, len) => {
if t.is_simd(tcx) {
let t_len = t.simd_size(tcx);
if len as usize != t_len {
tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found \
vector with length {}, expected length {}",
position,
t_len, len));
return;
}
let t_ty = t.simd_type(tcx);
{
let previous = structural_to_nominal.entry(expected).or_insert(t);
if *previous != t {
tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found `{}`, \
but already seen this vector type as `{}`",
position, t, previous));
return;
}
}
match_types(tcx,
position,
span,
structural_to_nominal,
inner_expected,
t_ty)
} else {
tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found non-simd type {}, \
expected simd type",
position, t));
let ictxt = infer::new_infer_ctxt(tcx, &tcx.tables, None, false);
let ret = ictxt.next_ty_var();
infer_ctxt = Some(ictxt);
(2, params, ret)
}
Err(_) => {
span_err!(tcx.sess, it.span, E0439,
"invalid `simd_shuffle`, needs length: `{}`", name);
return
}
}
}
_ => {
match intrinsics::Intrinsic::find(tcx, &name) {
Some(intr) => {
// this function is a platform specific intrinsic
if i_n_tps != 0 {
tcx.sess.span_err(it.span,
&format!("intrinsic has wrong number of type parameters: \
found {}, expected 0",
i_n_tps));
return
}
let mut structural_to_nomimal = HashMap::new();
let sig = tcx.no_late_bound_regions(i_ty.ty.fn_sig()).unwrap();
let input_pairs = intr.inputs.iter().zip(&sig.inputs);
for (i, (expected_arg, arg)) in input_pairs.enumerate() {
match_intrinsic_type_to_type(tcx, &format!("argument {}", i + 1), it.span,
&mut structural_to_nomimal, expected_arg, arg);
}
match_intrinsic_type_to_type(tcx, "return value", it.span,
&mut structural_to_nomimal,
&intr.output, sig.output.unwrap());
return
}
None => {
tcx.sess.span_err(it.span,
&format!("unrecognized intrinsic function: `{}`", name));
return;
}
}
}
};
let fty = tcx.mk_fn(None, tcx.mk_bare_fn(ty::BareFnTy {
unsafety: ast::Unsafety::Unsafe,
abi: abi::PlatformIntrinsic,
sig: ty::Binder(FnSig {
inputs: inputs,
output: ty::FnConverging(output),
variadic: false,
}),
}));
if i_n_tps != n_tps {
span_err!(tcx.sess, it.span, E0094,
"intrinsic has wrong number of type \
parameters: found {}, expected {}",
i_n_tps, n_tps);
} else {
require_same_types(tcx,
infer_ctxt.as_ref(),
false,
it.span,
i_ty.ty,
fty,
|| {
format!("intrinsic has wrong type: expected `{}`",
fty)
});
}
}
// walk the expected type and the actual type in lock step, checking they're
// the same, in a kinda-structural way, i.e. `Vector`s have to be simd structs with
// exactly the right element type
fn match_intrinsic_type_to_type<'tcx, 'a>(
tcx: &ty::ctxt<'tcx>,
position: &str,
span: Span,
structural_to_nominal: &mut HashMap<&'a intrinsics::Type, ty::Ty<'tcx>>,
expected: &'a intrinsics::Type, t: ty::Ty<'tcx>)
{
use intrinsics::Type::*;
match *expected {
Integer(bits) => match (bits, &t.sty) {
(8, &ty::TyInt(ast::TyI8)) | (8, &ty::TyUint(ast::TyU8)) |
(16, &ty::TyInt(ast::TyI16)) | (16, &ty::TyUint(ast::TyU16)) |
(32, &ty::TyInt(ast::TyI32)) | (32, &ty::TyUint(ast::TyU32)) |
(64, &ty::TyInt(ast::TyI64)) | (64, &ty::TyUint(ast::TyU64)) => {},
_ => tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found `{}`, \
expected `i{n}` or `u{n}`",
position,
t, n = bits)),
},
Float(bits) => match (bits, &t.sty) {
(32, &ty::TyFloat(ast::TyF32)) |
(64, &ty::TyFloat(ast::TyF64)) => {},
_ => tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found `{}`, \
expected `f{n}`",
position,
t, n = bits)),
},
Pointer(_) => unimplemented!(),
Vector(ref inner_expected, len) => {
if !t.is_simd(tcx) {
tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found non-simd type {}, \
expected simd type",
position, t));
return;
}
let t_len = t.simd_size(tcx);
if len as usize != t_len {
tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found \
vector with length {}, expected length {}",
position,
t_len, len));
return;
}
let t_ty = t.simd_type(tcx);
{
let previous = structural_to_nominal.entry(expected).or_insert(t);
if *previous != t {
tcx.sess.span_err(span,
&format!("intrinsic {} has wrong type: found `{}`, \
but already seen this vector type as `{}`",
position, t, previous));
return;
}
}
match_intrinsic_type_to_type(tcx,
position,
span,
structural_to_nominal,
inner_expected,
t_ty)
}
}
}

View File

@ -47,6 +47,7 @@ pub enum Abi {
System,
RustIntrinsic,
RustCall,
PlatformIntrinsic,
}
#[allow(non_camel_case_types)]
@ -95,6 +96,7 @@ const AbiDatas: &'static [AbiData] = &[
AbiData {abi: System, name: "system" },
AbiData {abi: RustIntrinsic, name: "rust-intrinsic" },
AbiData {abi: RustCall, name: "rust-call" },
AbiData {abi: PlatformIntrinsic, name: "platform-intrinsic" }
];
/// Returns the ABI with the given name (if any).

View File

@ -184,6 +184,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
// Allows cfg(target_feature = "...").
("cfg_target_feature", "1.3.0", Active),
// allow `extern "platform-intrinsic" { ... }`
("platform_intrinsics", "1.4.0", Active),
];
// (changing above list without updating src/doc/reference.md makes @cmr sad)
@ -694,10 +697,16 @@ impl<'a, 'v> Visitor<'v> for PostExpansionVisitor<'a> {
across platforms, it is recommended to \
use `#[link(name = \"foo\")]` instead")
}
if foreign_module.abi == Abi::RustIntrinsic {
self.gate_feature("intrinsics",
i.span,
"intrinsics are subject to change")
let maybe_feature = match foreign_module.abi {
Abi::RustIntrinsic => Some(("intrinsics", "intrinsics are subject to change")),
Abi::PlatformIntrinsic => {
Some(("platform_intrinsics",
"platform intrinsics are experimental and possibly buggy"))
}
_ => None
};
if let Some((feature, msg)) = maybe_feature {
self.gate_feature(feature, i.span, msg)
}
}