rustc: Add #[rustc_args_required_const]

This commit adds a new unstable attribute to the compiler which requires that
arguments to a function are always provided as constants. The primary use case
for this is SIMD intrinsics where arguments are defined by vendors to be
constant and in LLVM they indeed must be constant as well.

For now this is mostly just a semantic guarantee in rustc that an argument is a
constant when invoked, phases like trans don't actually take advantage of it
yet. This means that we'll be able to use this in stdsimd but we won't be able
to remove the `constify_*` macros just yet. Hopefully soon though!
This commit is contained in:
Alex Crichton 2018-02-05 10:48:22 -08:00
parent b0a396bb0a
commit 27a4e73ca5
3 changed files with 89 additions and 11 deletions

View File

@ -71,9 +71,12 @@ pub enum Candidate {
/// Borrow of a constant temporary.
Ref(Location),
/// Array of indices found in the third argument of
/// a call to one of the simd_shuffleN intrinsics.
ShuffleIndices(BasicBlock)
/// Currently applied to function calls where the callee has the unstable
/// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle
/// intrinsic. The intrinsic requires the arguments are indeed constant and
/// the attribute currently provides the semantic requirement that arguments
/// must be constant.
Argument { bb: BasicBlock, index: usize },
}
struct TempCollector<'tcx> {
@ -303,10 +306,10 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
_ => bug!()
}
}
Candidate::ShuffleIndices(bb) => {
Candidate::Argument { bb, index } => {
match self.source[bb].terminator_mut().kind {
TerminatorKind::Call { ref mut args, .. } => {
Rvalue::Use(mem::replace(&mut args[2], new_operand))
Rvalue::Use(mem::replace(&mut args[index], new_operand))
}
_ => bug!()
}
@ -359,15 +362,15 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
}
(statement.source_info.span, dest.ty(mir, tcx).to_ty(tcx))
}
Candidate::ShuffleIndices(bb) => {
Candidate::Argument { bb, index } => {
let terminator = mir[bb].terminator();
let ty = match terminator.kind {
TerminatorKind::Call { ref args, .. } => {
args[2].ty(mir, tcx)
args[index].ty(mir, tcx)
}
_ => {
span_bug!(terminator.source_info.span,
"expected simd_shuffleN call to promote");
"expected call argument to promote");
}
};
(terminator.source_info.span, ty)

View File

@ -17,6 +17,7 @@
use rustc_data_structures::bitvec::BitVector;
use rustc_data_structures::indexed_set::IdxSetBuf;
use rustc_data_structures::indexed_vec::{IndexVec, Idx};
use rustc_data_structures::fx::FxHashSet;
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::const_val::ConstVal;
@ -30,6 +31,7 @@ use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::middle::lang_items;
use syntax::abi::Abi;
use syntax::attr;
use syntax::ast::LitKind;
use syntax::feature_gate::UnstableFeatures;
use syntax_pos::{Span, DUMMY_SP};
@ -407,7 +409,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> {
_ => {}
}
}
Candidate::ShuffleIndices(_) => {}
Candidate::Argument { .. } => {}
}
}
@ -730,8 +732,10 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
self.visit_operand(func, location);
let fn_ty = func.ty(self.mir, self.tcx);
let mut callee_def_id = None;
let (mut is_shuffle, mut is_const_fn) = (false, None);
if let ty::TyFnDef(def_id, _) = fn_ty.sty {
callee_def_id = Some(def_id);
match self.tcx.fn_sig(def_id).abi() {
Abi::RustIntrinsic |
Abi::PlatformIntrinsic => {
@ -754,17 +758,39 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
}
}
let constant_arguments = callee_def_id.and_then(|id| {
args_required_const(self.tcx, id)
});
for (i, arg) in args.iter().enumerate() {
self.nest(|this| {
this.visit_operand(arg, location);
if is_shuffle && i == 2 && this.mode == Mode::Fn {
let candidate = Candidate::ShuffleIndices(bb);
if this.mode != Mode::Fn {
return
}
let candidate = Candidate::Argument { bb, index: i };
if is_shuffle && i == 2 {
if this.can_promote() {
this.promotion_candidates.push(candidate);
} else {
span_err!(this.tcx.sess, this.span, E0526,
"shuffle indices are not constant");
}
return
}
let constant_arguments = match constant_arguments.as_ref() {
Some(s) => s,
None => return,
};
if !constant_arguments.contains(&i) {
return
}
if this.can_promote() {
this.promotion_candidates.push(candidate);
} else {
this.tcx.sess.span_err(this.span,
&format!("argument {} is required to be a constant",
i + 1));
}
});
}
@ -1085,3 +1111,16 @@ impl MirPass for QualifyAndPromoteConstants {
}
}
}
fn args_required_const(tcx: TyCtxt, def_id: DefId) -> Option<FxHashSet<usize>> {
let attrs = tcx.get_attrs(def_id);
let attr = attrs.iter().find(|a| a.check_name("rustc_args_required_const"))?;
let mut ret = FxHashSet();
for meta in attr.meta_item_list()? {
match meta.literal()?.node {
LitKind::Int(a, _) => { ret.insert(a as usize); }
_ => return None,
}
}
Some(ret)
}

View File

@ -0,0 +1,36 @@
// Copyright 2018 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.
#![feature(attr_literals, rustc_attrs, const_fn)]
#[rustc_args_required_const(0)]
fn foo(_a: i32) {
}
#[rustc_args_required_const(1)]
fn bar(_a: i32, _b: i32) {
}
const A: i32 = 3;
const fn baz() -> i32 {
3
}
fn main() {
foo(2);
foo(2 + 3);
foo(baz());
let a = 4;
foo(A);
foo(a); //~ ERROR: argument 1 is required to be a constant
bar(a, 3);
bar(a, a); //~ ERROR: argument 2 is required to be a constant
}