Auto merge of #36421 - TimNN:check-abis, r=alexcrichton

check target abi support

This PR checks for each extern function / block whether the ABI / calling convention used is supported by the current target.

This was achieved by adding an `abi_blacklist` field to the target specifications, listing the calling conventions unsupported for that target.
This commit is contained in:
bors 2016-10-25 21:49:59 -07:00 committed by GitHub
commit 586a988313
25 changed files with 153 additions and 49 deletions

View File

@ -26,6 +26,7 @@ pub fn target() -> TargetResult {
features: "+neon,+fp-armv8,+cyclone".to_string(),
eliminate_frame_pointer: false,
max_atomic_width: Some(128),
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use target::{Target, TargetResult};
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::android_base::opts();
@ -25,6 +25,9 @@ pub fn target() -> TargetResult {
target_os: "android".to_string(),
target_env: "".to_string(),
target_vendor: "unknown".to_string(),
options: base,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use target::{Target, TargetResult};
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::linux_base::opts();
@ -22,6 +22,9 @@ pub fn target() -> TargetResult {
arch: "aarch64".to_string(),
target_os: "linux".to_string(),
target_vendor: "unknown".to_string(),
options: base,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})
}

View File

@ -0,0 +1,16 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use syntax::abi::Abi;
// All the calling conventions trigger an assertion(Unsupported calling convention) in llvm on arm
pub fn abi_blacklist() -> Vec<Abi> {
vec![Abi::Stdcall, Abi::Fastcall, Abi::Vectorcall, Abi::Win64, Abi::SysV64]
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use target::{Target, TargetResult};
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::android_base::opts();
@ -24,6 +24,9 @@ pub fn target() -> TargetResult {
target_os: "android".to_string(),
target_env: "".to_string(),
target_vendor: "unknown".to_string(),
options: base,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})
}

View File

@ -25,6 +25,7 @@ pub fn target() -> TargetResult {
options: TargetOptions {
features: "+v6".to_string(),
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})

View File

@ -25,6 +25,7 @@ pub fn target() -> TargetResult {
options: TargetOptions {
features: "+v6,+vfp2".to_string(),
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
}
})

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use target::{Target, TargetResult};
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::linux_musl_base::opts();
@ -29,6 +29,9 @@ pub fn target() -> TargetResult {
target_os: "linux".to_string(),
target_env: "musl".to_string(),
target_vendor: "unknown".to_string(),
options: base,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use target::{Target, TargetResult};
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::linux_musl_base::opts();
@ -29,6 +29,9 @@ pub fn target() -> TargetResult {
target_os: "linux".to_string(),
target_env: "musl".to_string(),
target_vendor: "unknown".to_string(),
options: base,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})
}

View File

@ -25,6 +25,7 @@ pub fn target() -> TargetResult {
options: TargetOptions {
features: "+v7,+vfp3,+neon".to_string(),
max_atomic_width: Some(64),
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
}
})

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use target::{Target, TargetResult};
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::android_base::opts();
@ -24,6 +24,9 @@ pub fn target() -> TargetResult {
target_os: "android".to_string(),
target_env: "".to_string(),
target_vendor: "unknown".to_string(),
options: base,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})
}

View File

@ -27,6 +27,7 @@ pub fn target() -> TargetResult {
features: "+v7,+vfp3,+d16,+thumb2,-neon".to_string(),
cpu: "generic".to_string(),
max_atomic_width: Some(64),
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
}
})

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use target::{Target, TargetResult};
use target::{Target, TargetOptions, TargetResult};
pub fn target() -> TargetResult {
let mut base = super::linux_musl_base::opts();
@ -30,6 +30,9 @@ pub fn target() -> TargetResult {
target_os: "linux".to_string(),
target_env: "musl".to_string(),
target_vendor: "unknown".to_string(),
options: base,
options: TargetOptions {
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
},
})
}

View File

@ -25,6 +25,7 @@ pub fn target() -> TargetResult {
options: TargetOptions {
features: "+v7,+vfp4,+neon".to_string(),
max_atomic_width: Some(64),
abi_blacklist: super::arm_base::abi_blacklist(),
.. base
}
})

View File

@ -48,13 +48,14 @@ use serialize::json::{Json, ToJson};
use std::collections::BTreeMap;
use std::default::Default;
use std::io::prelude::*;
use syntax::abi::Abi;
use syntax::abi::{Abi, lookup as lookup_abi};
use PanicStrategy;
mod android_base;
mod apple_base;
mod apple_ios_base;
mod arm_base;
mod bitrig_base;
mod dragonfly_base;
mod freebsd_base;
@ -361,6 +362,10 @@ pub struct TargetOptions {
/// Panic strategy: "unwind" or "abort"
pub panic_strategy: PanicStrategy,
/// A blacklist of ABIs unsupported by the current target. Note that generic
/// ABIs are considered to be supported on all platforms and cannot be blacklisted.
pub abi_blacklist: Vec<Abi>,
}
impl Default for TargetOptions {
@ -411,6 +416,7 @@ impl Default for TargetOptions {
obj_is_bitcode: false,
max_atomic_width: None,
panic_strategy: PanicStrategy::Unwind,
abi_blacklist: vec![],
}
}
}
@ -436,6 +442,10 @@ impl Target {
self.options.max_atomic_width.unwrap_or(self.target_pointer_width.parse().unwrap())
}
pub fn is_abi_supported(&self, abi: Abi) -> bool {
abi.generic() || !self.options.abi_blacklist.contains(&abi)
}
/// Load a target descriptor from a JSON object.
pub fn from_json(obj: Json) -> TargetResult {
// While ugly, this code must remain this way to retain
@ -567,6 +577,22 @@ impl Target {
key!(max_atomic_width, Option<u64>);
try!(key!(panic_strategy, PanicStrategy));
if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
for name in array.iter().filter_map(|abi| abi.as_string()) {
match lookup_abi(name) {
Some(abi) => {
if abi.generic() {
return Err(format!("The ABI \"{}\" is considered to be supported on \
all targets and cannot be blacklisted", abi))
}
base.options.abi_blacklist.push(abi)
}
None => return Err(format!("Unknown ABI \"{}\" in target specification", name))
}
}
}
Ok(base)
}
@ -710,6 +736,12 @@ impl ToJson for Target {
target_option_val!(max_atomic_width);
target_option_val!(panic_strategy);
if default.abi_blacklist != self.options.abi_blacklist {
d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()
.map(Abi::name).map(|name| name.to_json())
.collect::<Vec<_>>().to_json());
}
Json::Object(d)
}
}

View File

@ -52,6 +52,7 @@ pub fn opts() -> TargetOptions {
// Similarly, one almost always never wants to use relocatable code because of the extra
// costs it involves.
relocation_model: "static".to_string(),
abi_blacklist: super::arm_base::abi_blacklist(),
.. Default::default()
}
}

View File

@ -531,13 +531,16 @@ pub fn check_drop_impls(ccx: &CrateCtxt) -> CompileResult {
fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
decl: &'tcx hir::FnDecl,
body: &'tcx hir::Block,
fn_id: ast::NodeId) {
fn_id: ast::NodeId,
span: Span) {
let raw_fty = ccx.tcx.lookup_item_type(ccx.tcx.map.local_def_id(fn_id)).ty;
let fn_ty = match raw_fty.sty {
ty::TyFnDef(.., f) => f,
_ => span_bug!(body.span, "check_bare_fn: function type expected")
};
check_abi(ccx, span, fn_ty.abi);
ccx.inherited(fn_id).enter(|inh| {
// Compute the fty from point of view of inside fn.
let fn_scope = inh.tcx.region_maps.call_site_extent(fn_id, body.id);
@ -561,6 +564,13 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
});
}
fn check_abi<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, span: Span, abi: Abi) {
if !ccx.tcx.sess.target.target.is_abi_supported(abi) {
struct_span_err!(ccx.tcx.sess, span, E0570,
"The ABI `{}` is not supported for the current target", abi).emit()
}
}
struct GatherLocalsVisitor<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
fcx: &'a FnCtxt<'a, 'gcx, 'tcx>
}
@ -767,6 +777,8 @@ pub fn check_item_type<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
check_bounds_are_used(ccx, generics, pty_ty);
}
hir::ItemForeignMod(ref m) => {
check_abi(ccx, it.span, m.abi);
if m.abi == Abi::RustIntrinsic {
for item in &m.items {
intrinsic::check_intrinsic_type(ccx, item);
@ -804,7 +816,7 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
let _indenter = indenter();
match it.node {
hir::ItemFn(ref decl, .., ref body) => {
check_bare_fn(ccx, &decl, &body, it.id);
check_bare_fn(ccx, &decl, &body, it.id, it.span);
}
hir::ItemImpl(.., ref impl_items) => {
debug!("ItemImpl {} with id {}", it.name, it.id);
@ -815,7 +827,7 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
check_const(ccx, &expr, impl_item.id)
}
hir::ImplItemKind::Method(ref sig, ref body) => {
check_bare_fn(ccx, &sig.decl, body, impl_item.id);
check_bare_fn(ccx, &sig.decl, body, impl_item.id, impl_item.span);
}
hir::ImplItemKind::Type(_) => {
// Nothing to do here.
@ -830,7 +842,7 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx hir::Item) {
check_const(ccx, &expr, trait_item.id)
}
hir::MethodTraitItem(ref sig, Some(ref body)) => {
check_bare_fn(ccx, &sig.decl, body, trait_item.id);
check_bare_fn(ccx, &sig.decl, body, trait_item.id, trait_item.span);
}
hir::MethodTraitItem(_, None) |
hir::ConstTraitItem(_, None) |

View File

@ -4156,6 +4156,16 @@ let s = Simba { mother: 1, father: 0 }; // ok!
```
"##,
E0570: r##"
The requested ABI is unsupported by the current target.
The rust compiler maintains for each target a blacklist of ABIs unsupported on
that target. If an ABI is present in such a list this usually means that the
target / ABI combination is currently unsupported by llvm.
If necessary, you can circumvent this check using custom target specifications.
"##,
}
register_diagnostics! {

View File

@ -33,7 +33,7 @@ pub enum Abi {
// NB: This ordering MUST match the AbiDatas array below.
// (This is ensured by the test indices_are_correct().)
// Single platform ABIs come first (`for_arch()` relies on this)
// Single platform ABIs
Cdecl,
Stdcall,
Fastcall,
@ -42,7 +42,7 @@ pub enum Abi {
Win64,
SysV64,
// Multiplatform ABIs second
// Multiplatform / generic ABIs
Rust,
C,
System,
@ -65,41 +65,31 @@ pub enum Architecture {
pub struct AbiData {
abi: Abi,
// Name of this ABI as we like it called.
/// Name of this ABI as we like it called.
name: &'static str,
}
#[derive(Copy, Clone)]
pub enum AbiArchitecture {
/// Not a real ABI (e.g., intrinsic)
Rust,
/// An ABI that specifies cross-platform defaults (e.g., "C")
All,
/// Multiple architectures (bitset)
Archs(u32)
/// A generic ABI is supported on all platforms.
generic: bool,
}
#[allow(non_upper_case_globals)]
const AbiDatas: &'static [AbiData] = &[
// Platform-specific ABIs
AbiData {abi: Abi::Cdecl, name: "cdecl" },
AbiData {abi: Abi::Stdcall, name: "stdcall" },
AbiData {abi: Abi::Fastcall, name: "fastcall" },
AbiData {abi: Abi::Vectorcall, name: "vectorcall"},
AbiData {abi: Abi::Aapcs, name: "aapcs" },
AbiData {abi: Abi::Win64, name: "win64" },
AbiData {abi: Abi::SysV64, name: "sysv64" },
AbiData {abi: Abi::Cdecl, name: "cdecl", generic: false },
AbiData {abi: Abi::Stdcall, name: "stdcall", generic: false },
AbiData {abi: Abi::Fastcall, name: "fastcall", generic: false },
AbiData {abi: Abi::Vectorcall, name: "vectorcall", generic: false},
AbiData {abi: Abi::Aapcs, name: "aapcs", generic: false },
AbiData {abi: Abi::Win64, name: "win64", generic: false },
AbiData {abi: Abi::SysV64, name: "sysv64", generic: false },
// Cross-platform ABIs
//
// NB: Do not adjust this ordering without
// adjusting the indices below.
AbiData {abi: Abi::Rust, name: "Rust" },
AbiData {abi: Abi::C, name: "C" },
AbiData {abi: Abi::System, name: "system" },
AbiData {abi: Abi::RustIntrinsic, name: "rust-intrinsic" },
AbiData {abi: Abi::RustCall, name: "rust-call" },
AbiData {abi: Abi::PlatformIntrinsic, name: "platform-intrinsic" }
AbiData {abi: Abi::Rust, name: "Rust", generic: true },
AbiData {abi: Abi::C, name: "C", generic: true },
AbiData {abi: Abi::System, name: "system", generic: true },
AbiData {abi: Abi::RustIntrinsic, name: "rust-intrinsic", generic: true },
AbiData {abi: Abi::RustCall, name: "rust-call", generic: true },
AbiData {abi: Abi::PlatformIntrinsic, name: "platform-intrinsic", generic: true },
];
/// Returns the ABI with the given name (if any).
@ -125,6 +115,10 @@ impl Abi {
pub fn name(&self) -> &'static str {
self.data().name
}
pub fn generic(&self) -> bool {
self.data().generic
}
}
impl fmt::Display for Abi {

View File

@ -12,6 +12,9 @@
// llvm. Also checks that the abi-sysv64 feature gate allows usage
// of the sysv64 abi.
// ignore-arm
// ignore-aarch64
// compile-flags: -C no-prepopulate-passes
#![crate_type = "lib"]

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-arm
// ignore-aarch64
// compile-flags: -C no-prepopulate-passes
struct Foo;

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
fn baz(f: extern "stdcall" fn(usize, ...)) {
fn baz(f: extern "cdecl" fn(usize, ...)) {
//~^ ERROR: variadic function must have C calling convention
f(22, 44);
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern "stdcall" {
extern "cdecl" {
fn printf(_: *const u8, ...); //~ ERROR: variadic function must have C calling convention
}

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-arm
// ignore-aarch64
trait A {
extern "fastcall" fn test1(i: i32);
extern fn test2(i: i32);

View File

@ -8,6 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// ignore-arm
// ignore-aarch64
#![feature(abi_vectorcall)]
trait A {