This is an archive of the discontinued LLVM Phabricator instance.

Add loop unroll pragma support
ClosedPublic

Authored by meheff on Jun 10 2014, 11:54 AM.

Details

Summary

Piggy-backing on the support for "#pragma clang loop vectorize..." which was added recently by Tyler. This patch adds support for loop unrolling pragmas. The pragmas must immediately precede a loop statement and take the following forms:

#pragma clang loop unroll(enable) unroll the loop completely
#pragma clang loop unroll(disable)
do not unroll the loop.
#pragma clang loop unroll_count(N) // unroll the loop N times

if both unroll(enable) and unroll_count(N) are specified then the unroll_count takes precedence (ie, unroll the loop N times).

Tyler, I changed the logic a bit in CheckForIncompatibleAttributes, specifically it now rejects any combination of the disable form of the pragma and the numeric form (eg, vectorize(disable) and vectorize_width(N)). As a special case, it previously allowed this combination if the numeric value is 1. The logic seems cleaner without that special case. Lemme know if that's reasonable.

I'll be sending out a LLVM patch which consumes the generated metadata right after this.

Diff Detail

Event Timeline

meheff updated this revision to Diff 10288.Jun 10 2014, 11:54 AM
meheff retitled this revision from to Add loop unroll pragma support.
meheff updated this object.
meheff edited the test plan for this revision. (Show Details)
meheff added a subscriber: Unknown Object (MLST).
rnk added a subscriber: rnk.Jun 10 2014, 2:33 PM

As far as the clang side goes, this all looks good.

As a separate chagne, we should have documentation for '#pragma clang loop ...' that explains the difference between 'vectorize', 'interleave', and 'unroll'. This should be in clang/docs/LanguageExtensions.html after '#pragma optnone'. The loop_hint attribute should point at it as the main documentation.

lib/Sema/SemaStmtAttr.cpp
126–136

Rather than having 6 parallel arrays, maybe this would be better as an array of a struct:

struct {
  int EnableOptionId;
  int NumericOptionId;
  bool EnabledIsSet;
  bool ValueIsSet;
  bool Enabled;
  int Value;
} Options[] = {
  {LoopHintAttr::Vectorize, LoopHintAttr::VectorizeWidth},
  {LoopHintAttr::Interleave, LoopHintAttr::InterleaveCount},
  {LoopHintAttr::Unroll, LoopHintAttr::UnrollCount}
};
eliben added inline comments.Jun 10 2014, 3:55 PM
lib/Sema/SemaStmtAttr.cpp
126

The [size] on these arrays is superfluous? Also, Reid's suggestion makes sense if it ends up simplifying the code

148

Would a local enum {Vectorize = 0, ...} type would make this code cleaner instead of magic Category numbers?

196

Just wondering: could this duplication be avoided if the check was separate and not part of setting hte attributes?

aaron.ballman edited edge metadata.Jun 10 2014, 4:04 PM

Index: lib/Parse/ParsePragma.cpp

  • lib/Parse/ParsePragma.cpp

+++ lib/Parse/ParsePragma.cpp
@@ -1641,8 +1641,10 @@
/ loop-hint:
/ 'vectorize' '(' loop-hint-keyword ')'
/ 'interleave' '(' loop-hint-keyword ')'
+
/ 'unroll' '(' loop-hint-keyword ')'
/ 'vectorize_width' '(' loop-hint-value ')'
/ 'interleave_count' '(' loop-hint-value ')'
+/ 'unroll_count' '(' loop-hint-value ')'
/
/ loop-hint-keyword:
/ 'enable'
@@ -1661,6 +1663,13 @@
/ possible and profitable, and 0 is invalid. The loop vectorizer currently
/ only works on inner loops.
/
+
/ The unroll and unroll_count directives control the concatenation
+/ unroller. Specifying unroll(enable) instructs llvm to try to
+
/ unroll the loop completely, and unroll(disable) disables unrolling
+/ for the loop. Specifying unroll_count(_value_) instructs llvm to
+
/ try to unroll the loop the number of times indicated by the value.
+/ If unroll(enable) and unroll_count are both specified only
+
/ unroll_count takes effect.
void PragmaLoopHintHandler::HandlePragma(Preprocessor &PP,

PragmaIntroducerKind Introducer,
Token &Tok) {

@@ -1680,8 +1689,9 @@

IdentifierInfo *OptionInfo = Tok.getIdentifierInfo();

if (!OptionInfo->isStr("vectorize") && !OptionInfo->isStr("interleave") &&
  • !OptionInfo->isStr("vectorize_width") &&
  • !OptionInfo->isStr("interleave_count")) {

+ !OptionInfo->isStr("unroll") && !OptionInfo->isStr("vectorize_width") &&
+ !OptionInfo->isStr("interleave_count") &&
+ !OptionInfo->isStr("unroll_count")) {

At this point, I think a StringSwitch would make more sense.

Index: lib/Sema/SemaStmtAttr.cpp

  • lib/Sema/SemaStmtAttr.cpp

+++ lib/Sema/SemaStmtAttr.cpp
@@ -67,10 +67,13 @@

.Case("vectorize_width", LoopHintAttr::VectorizeWidth)
.Case("interleave", LoopHintAttr::Interleave)
.Case("interleave_count", LoopHintAttr::InterleaveCount)

+ .Case("unroll", LoopHintAttr::Unroll)
+ .Case("unroll_count", LoopHintAttr::UnrollCount)

        .Default(LoopHintAttr::Vectorize);

int ValueInt;
  • if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave) {

+ if (Option == LoopHintAttr::Vectorize || Option == LoopHintAttr::Interleave ||
+ Option == LoopHintAttr::Unroll) {

if (!ValueInfo) {
  S.Diag(ValueLoc->Loc, diag::err_pragma_loop_invalid_keyword)
      << /*MissingKeyword=*/true << "";

@@ -87,7 +90,8 @@

    return nullptr;
  }
} else if (Option == LoopHintAttr::VectorizeWidth ||
  • Option == LoopHintAttr::InterleaveCount) {

+ Option == LoopHintAttr::InterleaveCount ||
+ Option == LoopHintAttr::UnrollCount) {

// FIXME: We should support template parameters for the loop hint value.
// See bug report #19610.
llvm::APSInt ValueAPS;

@@ -111,10 +115,26 @@

static void
CheckForIncompatibleAttributes(Sema &S, SmallVectorImpl<const Attr *> &Attrs) {

  • int PrevOptionValue[4] = {-1, -1, -1, -1};
  • int OptionId[4] = {LoopHintAttr::Vectorize, LoopHintAttr::VectorizeWidth,
  • LoopHintAttr::Interleave, LoopHintAttr::InterleaveCount};

+ There are 3 categories of loop hints: vectorize, interleave, and
+
unroll. Each comes in two variants: an enable/disable form and a
+ form which takes a numeric argument. For example:
+
unroll(enable|disable) and unroll_count(N). The following arrays
+ accumulate the hints encountered while iterating through the
+
attributes to check for compatibility
+
+
Accumulated state of enable|disable hints for each hint category.
+ bool EnabledIsSet[3] = {false, false, false};
+ int EnabledValue[3];
+ // Accumulated state of numeric hints for each hint category.
+ bool NumericValueIsSet[3] = {false, false, false};
+ int NumericValue[3];

+ int EnableOptionId[3] = {LoopHintAttr::Vectorize, LoopHintAttr::Interleave,
+ LoopHintAttr::Unroll};
+ int NumericOptionId[3] = {LoopHintAttr::VectorizeWidth,
+ LoopHintAttr::InterleaveCount,
+ LoopHintAttr::UnrollCount};

I agree with Reid's comments about perhaps making this into a struct
instead of parallel arrays. Also, the array size isn't needed.

Aside from these nits, LGTM, thanks!

~Aaron

Thanks for the suggestions. They have been addressed. Pekka (cc'd) on the optimizer patch thread suggested changing the metadata string from llvm.loopunroll.* to llvm.loop.unroll.*. I like the suggestion as it partitions the namespace more logically. It makes sense then similarly to change the existing llvm.vectorizer.* and llvm.interleave.* names to llvm.loop.vectorizer.* and llvm.loop.interleave.*. Anybody have an opinion?

If I do make this change, then there will be a mismatch between the emitted metadata names for the vectorizer and what the optimizer expects. What's the protocol for handling this? Is submitting the optimizer change immediately after this clang change good enough?

lib/Sema/SemaStmtAttr.cpp
126

Removed.

126–136

Good idea. Done.

148

That's what I originally had when I wrote it, but the value is only used once to index into the Options array. Since all categories are treated identically there is no code like, say, 'if (Category == Unroll)...' where the enum would add value. As is the enum seems superfluous to me.

196

Done. It also required changing a couple tests as it ends up swapping the pragma options in the error string.

meheff updated this revision to Diff 10313.Jun 10 2014, 9:52 PM
meheff edited edge metadata.
eliben edited edge metadata.Jun 11 2014, 8:09 AM
In D4089#8, @meheff wrote:

Thanks for the suggestions. They have been addressed. Pekka (cc'd) on the optimizer patch thread suggested changing the metadata string from llvm.loopunroll.* to llvm.loop.unroll.*. I like the suggestion as it partitions the namespace more logically. It makes sense then similarly to change the existing llvm.vectorizer.* and llvm.interleave.* names to llvm.loop.vectorizer.* and llvm.loop.interleave.*. Anybody have an opinion?

If I do make this change, then there will be a mismatch between the emitted metadata names for the vectorizer and what the optimizer expects. What's the protocol for handling this? Is submitting the optimizer change immediately after this clang change good enough?

In any case, I'd leave changing the vectorize/interleave names to a separate patch, after this one lands.

In any case, I'd leave changing the vectorize/interleave names to a separate patch, after this one lands.

Sounds good. I'll change the metadata name in another patch.

eliben accepted this revision.Jun 11 2014, 11:05 AM
eliben edited edge metadata.
This revision is now accepted and ready to land.Jun 11 2014, 11:05 AM
meheff closed this revision.Jun 11 2014, 11:05 AM

r210667