Skip to content

Commit b998449

Browse files
committed
Auto merge of #156816 - saethlin:loop-attributes, r=JonathanBrouwer,folkertdev
Add unstable loop unrolling hint attributes Tracking issue: #156874 This adds as new attribute `#[unroll]`/`#[unroll(full)]`/`#[unroll(never)]`/`#[unroll(16)]` (or any u32). `#[unroll]` is behind a new feature gate `#![feature(loop_hints)]` because I intend to add an attribute for loop vectorization as well. If a user wants to turn off loop unrolling to locally minimize code size, LLVM may vectorize the loop even though it isn't unrolled which can produce a similar code size explosion.
2 parents 9d862dd + 8ff48f5 commit b998449

57 files changed

Lines changed: 691 additions & 64 deletions

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/loop_match.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::prelude::*;
55
pub(crate) struct LoopMatchParser;
66
impl NoArgsAttributeParser for LoopMatchParser {
77
const PATH: &[Symbol] = &[sym::loop_match];
8-
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Expression)]);
8+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Loop)]);
99
const STABILITY: AttributeStability = unstable!(loop_match);
1010
const CREATE: fn(Span) -> AttributeKind = AttributeKind::LoopMatch;
1111
}

compiler/rustc_attr_parsing/src/attributes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ pub(crate) mod stability;
7474
pub(crate) mod test_attrs;
7575
pub(crate) mod traits;
7676
pub(crate) mod transparency;
77+
pub(crate) mod unroll;
7778
pub(crate) mod util;
7879

7980
type AcceptFn<T> = for<'sess> fn(&mut T, &mut AcceptContext<'_, 'sess>, &ArgParser);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use rustc_ast::{LitIntType, LitKind};
2+
use rustc_feature::AttributeStability;
3+
use rustc_hir::attrs::UnrollAttr;
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(&[
11+
Allow(Target::Loop),
12+
Allow(Target::ForLoop),
13+
Allow(Target::While),
14+
]);
15+
const STABILITY: AttributeStability = unstable!(loop_hints);
16+
const TEMPLATE: AttributeTemplate = template!(
17+
Word,
18+
List: &["full", "never", "<integer>"]
19+
);
20+
21+
fn convert(cx: &mut AcceptContext<'_, '_>, args: &ArgParser) -> Option<AttributeKind> {
22+
match args {
23+
ArgParser::NoArgs => Some(AttributeKind::Unroll(UnrollAttr::Hint)),
24+
ArgParser::List(list) => {
25+
let l = cx.expect_single(list)?;
26+
27+
if let Some(lit) = l.as_lit()
28+
&& let LitKind::Int(val, LitIntType::Unsuffixed) = lit.kind
29+
{
30+
if let Ok(val) = u32::try_from(val.get()) {
31+
return Some(AttributeKind::Unroll(UnrollAttr::Count(val)));
32+
} else {
33+
cx.adcx().expected_integer_literal_in_range(l.span(), 0, u32::MAX as isize);
34+
return None;
35+
}
36+
}
37+
38+
match l.meta_item_no_args().and_then(|i| i.path().word_sym()) {
39+
Some(sym::full) => Some(AttributeKind::Unroll(UnrollAttr::Full)),
40+
Some(sym::never) => Some(AttributeKind::Unroll(UnrollAttr::Never)),
41+
_ => {
42+
cx.adcx().expected_specific_argument(l.span(), &[sym::full, sym::never]);
43+
None
44+
}
45+
}
46+
}
47+
ArgParser::NameValue(_) => {
48+
let inner_span = cx.inner_span;
49+
cx.adcx().expected_list_or_no_args(inner_span);
50+
return None;
51+
}
52+
}
53+
}
54+
}

compiler/rustc_attr_parsing/src/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ use crate::attributes::stability::*;
6363
use crate::attributes::test_attrs::*;
6464
use crate::attributes::traits::*;
6565
use crate::attributes::transparency::*;
66+
use crate::attributes::unroll::*;
6667
use crate::attributes::{AttributeParser as _, AttributeSafety, Combine, Single, WithoutArgs};
6768
use crate::parser::{
6869
ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser, NameValueParser,
@@ -232,6 +233,7 @@ attribute_parsers!(
232233
Single<ShouldPanicParser>,
233234
Single<TestRunnerParser>,
234235
Single<TypeLengthLimitParser>,
236+
Single<UnrollParser>,
235237
Single<WindowsSubsystemParser>,
236238
Single<WithoutArgs<AllowInternalUnsafeParser>>,
237239
Single<WithoutArgs<AutomaticallyDerivedParser>>,

compiler/rustc_attr_parsing/src/target_checking.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,5 +524,8 @@ pub(crate) const ALL_TARGETS: &'static [Policy] = {
524524
kind: rustc_hir::target::GenericParamKind::Type,
525525
has_default: true,
526526
}),
527+
Allow(Target::Loop),
528+
Allow(Target::ForLoop),
529+
Allow(Target::While),
527530
]
528531
};

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ 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::attrs::{AttributeKind, UnrollAttr};
1718
use rustc_hir::def_id::DefId;
1819
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
1920
use rustc_middle::ty::layout::{
@@ -336,6 +337,51 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
336337
}
337338
}
338339

340+
fn br_with_attrs(&mut self, dest: &'ll BasicBlock, attributes: &[AttributeKind]) {
341+
unsafe {
342+
let val = llvm::LLVMBuildBr(self.llbuilder, dest);
343+
344+
let mut nodes = Vec::new();
345+
346+
for attribute in attributes {
347+
let AttributeKind::Unroll(unroll) = attribute else {
348+
continue;
349+
};
350+
// UnrollAttr::Count needs a second operand, the provided count, but the other
351+
// unroll hints do not.
352+
let md_node = if let UnrollAttr::Count(count) = unroll {
353+
let unroll_meta = self.create_metadata("llvm.loop.unroll.count".as_bytes());
354+
let count = llvm::LLVMValueAsMetadata(self.get_const_i32(u64::from(*count)));
355+
self.md_node_in_context(&[unroll_meta, count])
356+
} else {
357+
let metadata_str = match unroll {
358+
UnrollAttr::Hint => "llvm.loop.unroll.enable",
359+
UnrollAttr::Full => "llvm.loop.unroll.full",
360+
UnrollAttr::Never => "llvm.loop.unroll.disable",
361+
UnrollAttr::Count(_) => unreachable!(),
362+
};
363+
let unroll_meta = self.create_metadata(metadata_str.as_bytes());
364+
self.md_node_in_context(&[unroll_meta])
365+
};
366+
nodes.push(md_node);
367+
}
368+
369+
if let [first, ..] = nodes[..] {
370+
nodes.insert(0, first);
371+
372+
// Create the loop metadata node
373+
let loop_meta_mdnode = self.set_metadata_node(val, llvm::MD_loop, &nodes);
374+
375+
// Look up the metadata node as a value
376+
let loop_meta_val = llvm::LLVMGetMetadata(val, llvm::MD_loop).unwrap();
377+
378+
// Replace the first entry with a reference to itself
379+
// This is required by LLVM. See the LangRef page for llvm.loop metadata.
380+
llvm::LLVMReplaceMDNodeOperandWith(loop_meta_val, 0, loop_meta_mdnode);
381+
}
382+
}
383+
}
384+
339385
fn cond_br(
340386
&mut self,
341387
cond: &'ll Value,

compiler/rustc_codegen_llvm/src/context.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1091,9 +1091,10 @@ impl<'ll, CX: Borrow<SCx<'ll>>> GenericCx<'ll, CX> {
10911091
instruction: &'ll Value,
10921092
kind_id: MetadataKindId,
10931093
md_list: &[&'ll Metadata],
1094-
) {
1094+
) -> &'ll Metadata {
10951095
let md = self.md_node_in_context(md_list);
10961096
self.set_metadata(instruction, kind_id, md);
1097+
md
10971098
}
10981099

10991100
/// 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
@@ -984,6 +984,10 @@ unsafe extern "C" {
984984
pub(crate) fn LLVMGetValueName2(Val: &Value, Length: *mut size_t) -> *const c_char;
985985
pub(crate) fn LLVMSetValueName2(Val: &Value, Name: *const c_char, NameLen: size_t);
986986
pub(crate) fn LLVMReplaceAllUsesWith<'a>(OldVal: &'a Value, NewVal: &'a Value);
987+
pub(crate) safe fn LLVMGetMetadata<'a>(
988+
Val: &'a Value,
989+
KindID: MetadataKindId,
990+
) -> Option<&'a Value>;
987991
pub(crate) safe fn LLVMSetMetadata<'a>(Val: &'a Value, KindID: MetadataKindId, Node: &'a Value);
988992
pub(crate) fn LLVMGlobalSetMetadata<'a>(
989993
Val: &'a Value,
@@ -1013,6 +1017,7 @@ unsafe extern "C" {
10131017
Name: *const c_char,
10141018
Val: &'a Value,
10151019
);
1020+
pub(crate) fn LLVMReplaceMDNodeOperandWith(Val: &Value, index: u32, replacement: &Metadata);
10161021

10171022
// Operations on scalar constants
10181023
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::attrs::AttributeKind;
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};
@@ -135,6 +136,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
135136
bx: &mut Bx,
136137
target: mir::BasicBlock,
137138
mergeable_succ: bool,
139+
attributes: &[AttributeKind],
138140
) -> MergingSucc {
139141
let (needs_landing_pad, is_cleanupret) = self.llbb_characteristics(fx, target);
140142
if mergeable_succ && !needs_landing_pad && !is_cleanupret {
@@ -150,7 +152,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
150152
// to a trampoline.
151153
bx.cleanup_ret(self.funclet(fx).unwrap(), Some(lltarget));
152154
} else {
153-
bx.br(lltarget);
155+
bx.br_with_attrs(lltarget, attributes);
154156
}
155157
MergingSucc::False
156158
}
@@ -291,7 +293,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
291293
bx.lifetime_end(tmp, size);
292294
}
293295
fx.store_return(bx, ret_dest, &fn_abi.ret, llret);
294-
self.funclet_br(fx, bx, target, mergeable_succ)
296+
self.funclet_br(fx, bx, target, mergeable_succ, &[])
295297
} else {
296298
bx.unreachable();
297299
MergingSucc::False
@@ -359,7 +361,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
359361
bx.codegen_inline_asm(template, operands, options, line_spans, instance, None, None);
360362

361363
if let Some(target) = destination {
362-
self.funclet_br(fx, bx, target, mergeable_succ)
364+
self.funclet_br(fx, bx, target, mergeable_succ, &[])
363365
} else {
364366
bx.unreachable();
365367
MergingSucc::False
@@ -618,7 +620,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
618620

619621
if let ty::InstanceKind::DropGlue(_, None) = drop_fn.def {
620622
// we don't actually need to drop anything.
621-
return helper.funclet_br(self, bx, target, mergeable_succ);
623+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
622624
}
623625

624626
let place = self.codegen_place(bx, location.as_ref());
@@ -727,7 +729,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
727729

728730
// Don't codegen the panic block if success if known.
729731
if const_cond == Some(expected) {
730-
return helper.funclet_br(self, bx, target, mergeable_succ);
732+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
731733
}
732734

733735
// Because we're branching to a panic block (either a `#[cold]` one
@@ -861,7 +863,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
861863
if is_valid {
862864
// a NOP
863865
let target = target.unwrap();
864-
return Some(helper.funclet_br(self, bx, target, mergeable_succ));
866+
return Some(helper.funclet_br(self, bx, target, mergeable_succ, &[]));
865867
}
866868

867869
let layout = bx.layout_of(ty);
@@ -935,7 +937,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
935937
ty::InstanceKind::DropGlue(_, None) => {
936938
// Empty drop glue; a no-op.
937939
let target = target.unwrap();
938-
return helper.funclet_br(self, bx, target, mergeable_succ);
940+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
939941
}
940942
ty::InstanceKind::Intrinsic(def_id) => {
941943
let intrinsic = bx.tcx().intrinsic(def_id).unwrap();
@@ -1023,7 +1025,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10231025
match intrinsic_result {
10241026
IntrinsicResult::Operand(_) | IntrinsicResult::WroteIntoPlace => {
10251027
return if let Some(target) = target {
1026-
helper.funclet_br(self, bx, target, mergeable_succ)
1028+
helper.funclet_br(self, bx, target, mergeable_succ, &[])
10271029
} else {
10281030
bx.unreachable();
10291031
MergingSucc::False
@@ -1133,7 +1135,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
11331135
&ArgAbi { layout: result_layout, mode: PassMode::Direct(ArgAttributes::new()) },
11341136
llret,
11351137
);
1136-
return helper.funclet_br(self, bx, target, mergeable_succ);
1138+
return helper.funclet_br(self, bx, target, mergeable_succ, &[]);
11371139
} else {
11381140
bx.unreachable();
11391141
return MergingSucc::False;
@@ -1577,7 +1579,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
15771579
}
15781580

15791581
mir::TerminatorKind::Goto { target } => {
1580-
helper.funclet_br(self, bx, target, mergeable_succ())
1582+
helper.funclet_br(self, bx, target, mergeable_succ(), &terminator.attributes)
15811583
}
15821584

15831585
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::attrs::AttributeKind;
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: &[AttributeKind]) {
82+
self.br(dest)
83+
}
8084
fn cond_br(
8185
&mut self,
8286
cond: Self::Value,

0 commit comments

Comments
 (0)