Skip to content

Commit ec9cf15

Browse files
committed
Add #![feature(loop_hints)] and #[unroll]
1 parent 54333ff commit ec9cf15

49 files changed

Lines changed: 472 additions & 53 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ pub(crate) mod stability;
6868
pub(crate) mod test_attrs;
6969
pub(crate) mod traits;
7070
pub(crate) mod transparency;
71+
pub(crate) mod unroll;
7172
pub(crate) mod util;
7273

7374
type AcceptFn<T> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess>, &ArgParser);
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use rustc_ast::{LitIntType, LitKind};
2+
use rustc_hir::attrs::UnrollAttr;
3+
use rustc_lint_defs::builtin::ILL_FORMED_ATTRIBUTE_INPUT;
4+
5+
use super::prelude::*;
6+
7+
pub(crate) struct UnrollParser;
8+
impl SingleAttributeParser for UnrollParser {
9+
const PATH: &[Symbol] = &[sym::unroll];
10+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Expression)]);
11+
const TEMPLATE: AttributeTemplate = template!(
12+
Word,
13+
List: &["always", "never", "<integer>"],
14+
"https://doc.rust-lang.org/reference/attributes/codegen.html#the-inline-attribute"
15+
);
16+
17+
fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
18+
match args {
19+
ArgParser::NoArgs => Some(AttributeKind::Unroll(UnrollAttr::Hint)),
20+
ArgParser::List(list) => {
21+
let l = cx.expect_single(list)?;
22+
23+
if let Some(lit) = l.as_lit() {
24+
if let LitKind::Int(val, LitIntType::Unsuffixed) = lit.kind {
25+
let Ok(val) = u32::try_from(val.get()) else {
26+
cx.adcx().expected_integer_literal_in_range(
27+
l.span(),
28+
0,
29+
u32::MAX as isize,
30+
);
31+
return None;
32+
};
33+
return Some(AttributeKind::Unroll(UnrollAttr::Count(val)));
34+
}
35+
}
36+
37+
match l.meta_item().and_then(|i| i.path().word_sym()) {
38+
Some(sym::always) => Some(AttributeKind::Unroll(UnrollAttr::Always)),
39+
Some(sym::never) => Some(AttributeKind::Unroll(UnrollAttr::Never)),
40+
_ => {
41+
cx.adcx().expected_specific_argument(l.span(), &[sym::always, sym::never]);
42+
None
43+
}
44+
}
45+
}
46+
ArgParser::NameValue(_) => {
47+
cx.adcx().warn_ill_formed_attribute_input(ILL_FORMED_ATTRIBUTE_INPUT);
48+
return None;
49+
}
50+
}
51+
}
52+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ use crate::attributes::stability::*;
5858
use crate::attributes::test_attrs::*;
5959
use crate::attributes::traits::*;
6060
use crate::attributes::transparency::*;
61+
use crate::attributes::unroll::*;
6162
use crate::attributes::{AttributeParser as _, AttributeSafety, Combine, Single, WithoutArgs};
6263
use crate::parser::{
6364
ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, NameValueParser,
@@ -224,6 +225,7 @@ attribute_parsers!(
224225
Single<ShouldPanicParser>,
225226
Single<TestRunnerParser>,
226227
Single<TypeLengthLimitParser>,
228+
Single<UnrollParser>,
227229
Single<WindowsSubsystemParser>,
228230
Single<WithoutArgs<AllowInternalUnsafeParser>>,
229231
Single<WithoutArgs<AutomaticallyDerivedParser>>,

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
1414
use rustc_codegen_ssa::mir::place::PlaceRef;
1515
use rustc_codegen_ssa::traits::*;
1616
use rustc_data_structures::small_c_str::SmallCStr;
17+
use rustc_hir::Attribute;
18+
use rustc_hir::attrs::{AttributeKind, UnrollAttr};
1719
use rustc_hir::def_id::DefId;
1820
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
1921
use rustc_middle::ty::layout::{
@@ -336,6 +338,40 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
336338
}
337339
}
338340

341+
fn br_with_attrs(&mut self, dest: &'ll BasicBlock, attributes: &[Attribute]) {
342+
unsafe {
343+
let val = llvm::LLVMBuildBr(self.llbuilder, dest);
344+
345+
if let [rustc_hir::Attribute::Parsed(AttributeKind::Unroll(unroll))] = attributes {
346+
let unroll_meta = if let UnrollAttr::Count(count) = unroll {
347+
let unroll_meta = self.create_metadata("llvm.loop.unroll.count".as_bytes());
348+
let count = llvm::LLVMValueAsMetadata(self.get_const_i32(u64::from(*count)));
349+
self.md_node_in_context(&[unroll_meta, count])
350+
} else {
351+
let metadata_str = match unroll {
352+
UnrollAttr::Hint => "llvm.loop.unroll",
353+
UnrollAttr::Always => "llvm.loop.unroll.full",
354+
UnrollAttr::Never => "llvm.loop.unroll.disable",
355+
_ => unreachable!(),
356+
};
357+
let unroll_meta = self.create_metadata(metadata_str.as_bytes());
358+
self.md_node_in_context(&[unroll_meta])
359+
};
360+
361+
// Create the metadata node
362+
let loop_meta_mdnode =
363+
self.set_metadata_node(val, llvm::MD_loop, &[unroll_meta, unroll_meta]);
364+
365+
// Look up the metadata node as a value
366+
let loop_meta_val = llvm::LLVMGetMetadata(val, llvm::MD_loop).unwrap();
367+
368+
// Replace the first entry with a reference to itself
369+
// This is required by LLVM. See the LangRef page for llvm.loop metadata.
370+
llvm::LLVMReplaceMDNodeOperandWith(loop_meta_val, 0, loop_meta_mdnode);
371+
}
372+
}
373+
}
374+
339375
fn cond_br(
340376
&mut self,
341377
cond: &'ll Value,

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1062,9 +1062,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
10621062
instruction: &'ll Value,
10631063
kind_id: MetadataKindId,
10641064
md_list: &[&'ll Metadata],
1065-
) {
1065+
) -> &'ll Metadata {
10661066
let md = self.md_node_in_context(md_list);
10671067
self.set_metadata(instruction, kind_id, md);
1068+
md
10681069
}
10691070

10701071
/// Helper method for the sequence of calls:

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,10 @@ unsafe extern "C" {
961961
pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char;
962962
pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t);
963963
pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value);
964+
pub(crate) safe fn LLVMGetMetadata<'a>(
965+
Val: &'a Value,
966+
KindID: MetadataKindId,
967+
) -> Option<&'a Value>;
964968
pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value);
965969
pub(crate) fn LLVMGlobalSetMetadata<'a>(
966970
Val: &'a Value,
@@ -990,6 +994,7 @@ unsafe extern "C" {
990994
Name: *const c_char,
991995
Val: &'a Value,
992996
);
997+
pub(crate) fn LLVMReplaceMDNodeOperandWith(Val: &Value, index: u32, replacement: &Metadata);
993998

994999
// Operations on scalar constants
9951000
pub(crate) fn LLVMConstInt(IntTy: &Type, N: c_ulonglong, SignExtend: Bool) -> &Value;

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use rustc_abi::{Align, BackendRepr, ExternAbi, HasDataLayout, Reg, Size, Wrappin
44
use rustc_ast as ast;
55
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
66
use rustc_data_structures::packed::Pu128;
7+
use rustc_hir::Attribute;
78
use rustc_hir::lang_items::LangItem;
89
use rustc_lint_defs::builtin::TAIL_CALL_TRACK_CALLER;
910
use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason};
@@ -134,6 +135,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
134135
bx: &mut Bx,
135136
target: mir::BasicBlock,
136137
mergeable_succ: bool,
138+
attributes: &[Attribute],
137139
) -> MergingSucc {
138140
let (needs_landing_pad, is_cleanupret) = self.llbb_characteristics(fx, target);
139141
if mergeable_succ && !needs_landing_pad && !is_cleanupret {
@@ -149,7 +151,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
149151
// to a trampoline.
150152
bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
151153
} else {
152-
bx.br(lltarget);
154+
bx.br_with_attrs(lltarget, attributes);
153155
}
154156
MergingSucc::False
155157
}
@@ -284,7 +286,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
284286
bx.lifetime_end(tmp, size);
285287
}
286288
fx.store_return(bx, ret_dest, &fn_abi.ret, llret);
287-
self.funclet_br(fx, bx, target, mergeable_succ)
289+
self.funclet_br(fx, bx, target, mergeable_succ, &[])
288290
} else {
289291
bx.unreachable();
290292
MergingSucc::False
@@ -352,7 +354,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
352354
bx.codegen_inline_asm(template, operands, options, line_spans, instance, None, None);
353355

354356
if let Some(target) = destination {
355-
self.funclet_br(fx, bx, target, mergeable_succ)
357+
self.funclet_br(fx, bx, target, mergeable_succ, &[])
356358
} else {
357359
bx.unreachable();
358360
MergingSucc::False
@@ -607,7 +609,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
607609

608610
if let ty::InstanceKind::DropGlue(_, None) = drop_fn.def {
609611
// we don't actually need to drop anything.
610-
return helper.funclet_br(self, bx, target, mergeable_succ);
612+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
611613
}
612614

613615
let place = self.codegen_place(bx, location.as_ref());
@@ -716,7 +718,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
716718

717719
// Don't codegen the panic block if success if known.
718720
if const_cond == Some(expected) {
719-
return helper.funclet_br(self, bx, target, mergeable_succ);
721+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
720722
}
721723

722724
// Because we're branching to a panic block (either a `#[cold]` one
@@ -850,7 +852,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
850852
if is_valid {
851853
// a NOP
852854
let target = target.unwrap();
853-
return Some(helper.funclet_br(self, bx, target, mergeable_succ));
855+
return Some(helper.funclet_br(self, bx, target, mergeable_succ, &[]));
854856
}
855857

856858
let layout = bx.layout_of(ty);
@@ -924,7 +926,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
924926
ty::InstanceKind::DropGlue(_, None) => {
925927
// Empty drop glue; a no-op.
926928
let target = target.unwrap();
927-
return helper.funclet_br(self, bx, target, mergeable_succ);
929+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
928930
}
929931
ty::InstanceKind::Intrinsic(def_id) => {
930932
let intrinsic = bx.tcx().intrinsic(def_id).unwrap();
@@ -993,7 +995,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
993995
}
994996

995997
return if let Some(target) = target {
996-
helper.funclet_br(self, bx, target, mergeable_succ)
998+
helper.funclet_br(self, bx, target, mergeable_succ, &[])
997999
} else {
9981000
bx.unreachable();
9991001
MergingSucc::False
@@ -1086,7 +1088,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10861088
&ArgAbi { layout: result_layout, mode: PassMode::Direct(ArgAttributes::new()) },
10871089
llret,
10881090
);
1089-
return helper.funclet_br(self, bx, target, mergeable_succ);
1091+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
10901092
} else {
10911093
bx.unreachable();
10921094
return MergingSucc::False;
@@ -1530,7 +1532,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
15301532
}
15311533

15321534
mir::TerminatorKind::Goto { target } => {
1533-
helper.funclet_br(self, bx, target, mergeable_succ())
1535+
helper.funclet_br(self, bx, target, mergeable_succ(), &terminator.attributes)
15341536
}
15351537

15361538
mir::TerminatorKind::SwitchInt { ref discr, ref targets } => {

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::assert_matches;
22
use std::ops::Deref;
33

44
use rustc_abi::{Align, Scalar, Size, WrappingRange};
5+
use rustc_hir::Attribute;
56
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
67
use rustc_middle::mir;
78
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, TyAndLayout};
@@ -77,6 +78,9 @@ pub trait BuilderMethods<'a, 'tcx>:
7778
fn ret_void(&mut self);
7879
fn ret(&mut self, v: Self::Value);
7980
fn br(&mut self, dest: Self::BasicBlock);
81+
fn br_with_attrs(&mut self, dest: Self::BasicBlock, _attributes: &[Attribute]) {
82+
self.br(dest)
83+
}
8084
fn cond_br(
8185
&mut self,
8286
cond: Self::Value,

compiler/rustc_feature/src/builtin_attrs.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
401401
// - https://github.com/rust-lang/rust/issues/130494
402402
gated!(pin_v2, pin_ergonomics, experimental!(pin_v2)),
403403

404+
gated!(unroll, loop_hints, experimental!(loop_hints)),
405+
404406
// ==========================================================================
405407
// Internal attributes: Stability, deprecation, and unsafe:
406408
// ==========================================================================

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,8 @@ declare_features! (
606606
(unstable, link_arg_attribute, "1.76.0", Some(99427)),
607607
/// Target features on loongarch.
608608
(unstable, loongarch_target_feature, "1.73.0", Some(150252)),
609+
/// Allows use of loop optimization hints via attributes.
610+
(unstable, loop_hints, "CURRENT_RUSTC_VERSION", Some(15701)),
609611
/// Allows fused `loop`/`match` for direct intraprocedural jumps.
610612
(incomplete, loop_match, "1.90.0", Some(132306)),
611613
/// Target features on m68k.

0 commit comments

Comments
 (0)